/* $OpenBSD: ex_shell.c,v 1.8 2002/02/16 21:27:57 millert Exp $ */ /*- * Copyright (c) 1992, 1993, 1994 * The Regents of the University of California. All rights reserved. * Copyright (c) 1992, 1993, 1994, 1995, 1996 * Keith Bostic. All rights reserved. * * See the LICENSE file for redistribution information. */ #include "config.h" #ifndef lint static const char sccsid[] = "@(#)ex_shell.c 10.38 (Berkeley) 8/19/96"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #include #include "../common/common.h" static const char *sigmsg(int); /* * ex_shell -- :sh[ell] * Invoke the program named in the SHELL environment variable * with the argument -i. * * PUBLIC: int ex_shell(SCR *, EXCMD *); */ int ex_shell(sp, cmdp) SCR *sp; EXCMD *cmdp; { int rval; char buf[MAXPATHLEN]; /* We'll need a shell. */ if (opts_empty(sp, O_SHELL, 0)) return (1); /* * XXX * Assumes all shells use -i. */ (void)snprintf(buf, sizeof(buf), "%s -i", O_STR(sp, O_SHELL)); /* Restore the window name. */ (void)sp->gp->scr_rename(sp, NULL, 0); /* If we're still in a vi screen, move out explicitly. */ rval = ex_exec_proc(sp, cmdp, buf, NULL, !F_ISSET(sp, SC_SCR_EXWROTE)); /* Set the window name. */ (void)sp->gp->scr_rename(sp, sp->frp->name, 1); /* * !!! * Historically, vi didn't require a continue message after the * return of the shell. Match it. */ F_SET(sp, SC_EX_WAIT_NO); return (rval); } /* * ex_exec_proc -- * Run a separate process. * * PUBLIC: int ex_exec_proc(SCR *, EXCMD *, char *, const char *, int); */ int ex_exec_proc(sp, cmdp, cmd, msg, need_newline) SCR *sp; EXCMD *cmdp; char *cmd; const char *msg; int need_newline; { GS *gp; const char *name; pid_t pid; gp = sp->gp; /* We'll need a shell. */ if (opts_empty(sp, O_SHELL, 0)) return (1); /* Enter ex mode. */ if (F_ISSET(sp, SC_VI)) { if (gp->scr_screen(sp, SC_EX)) { ex_emsg(sp, cmdp->cmd->name, EXM_NOCANON); return (1); } (void)gp->scr_attr(sp, SA_ALTERNATE, 0); F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE); } /* Put out additional newline, message. */ if (need_newline) (void)ex_puts(sp, "\n"); if (msg != NULL) { (void)ex_puts(sp, msg); (void)ex_puts(sp, "\n"); } (void)ex_fflush(sp); switch (pid = vfork()) { case -1: /* Error. */ msgq(sp, M_SYSERR, "vfork"); return (1); case 0: /* Utility. */ if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL) name = O_STR(sp, O_SHELL); else ++name; execl(O_STR(sp, O_SHELL), name, "-c", cmd, (char *)NULL); msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s"); _exit(127); /* NOTREACHED */ default: /* Parent. */ return (proc_wait(sp, (long)pid, cmd, 0, 0)); } /* NOTREACHED */ } /* * proc_wait -- * Wait for one of the processes. * * !!! * The pid_t type varies in size from a short to a long depending on the * system. It has to be cast into something or the standard promotion * rules get you. I'm using a long based on the belief that nobody is * going to make it unsigned and it's unlikely to be a quad. * * PUBLIC: int proc_wait(SCR *, long, const char *, int, int); */ int proc_wait(sp, pid, cmd, silent, okpipe) SCR *sp; long pid; const char *cmd; int silent, okpipe; { size_t len; int nf, pstat; char *p; /* Wait for the utility, ignoring interruptions. */ for (;;) { errno = 0; if (waitpid((pid_t)pid, &pstat, 0) != -1) break; if (errno != EINTR) { msgq(sp, M_SYSERR, "waitpid"); return (1); } } /* * Display the utility's exit status. Ignore SIGPIPE from the * parent-writer, as that only means that the utility chose to * exit before reading all of its input. */ if (WIFSIGNALED(pstat) && (!okpipe || WTERMSIG(pstat) != SIGPIPE)) { for (; isblank(*cmd); ++cmd); p = msg_print(sp, cmd, &nf); len = strlen(p); msgq(sp, M_ERR, "%.*s%s: received signal: %s%s", MIN(len, 20), p, len > 20 ? " ..." : "", sigmsg(WTERMSIG(pstat)), WCOREDUMP(pstat) ? "; core dumped" : ""); if (nf) FREE_SPACE(sp, p, 0); return (1); } if (WIFEXITED(pstat) && WEXITSTATUS(pstat)) { /* * Remain silent for "normal" errors when doing shell file * name expansions, they almost certainly indicate nothing * more than a failure to match. * * Remain silent for vi read filter errors. It's historic * practice. */ if (!silent) { for (; isblank(*cmd); ++cmd); p = msg_print(sp, cmd, &nf); len = strlen(p); msgq(sp, M_ERR, "%.*s%s: exited with status %d", MIN(len, 20), p, len > 20 ? " ..." : "", WEXITSTATUS(pstat)); if (nf) FREE_SPACE(sp, p, 0); } return (1); } return (0); } /* * XXX * The sys_siglist[] table in the C library has this information, but there's * no portable way to get to it. (Believe me, I tried.) */ typedef struct _sigs { int number; /* signal number */ char *message; /* related message */ } SIGS; SIGS const sigs[] = { #ifdef SIGABRT SIGABRT, "Abort trap", #endif #ifdef SIGALRM SIGALRM, "Alarm clock", #endif #ifdef SIGBUS SIGBUS, "Bus error", #endif #ifdef SIGCLD SIGCLD, "Child exited or stopped", #endif #ifdef SIGCHLD SIGCHLD, "Child exited", #endif #ifdef SIGCONT SIGCONT, "Continued", #endif #ifdef SIGDANGER SIGDANGER, "System crash imminent", #endif #ifdef SIGEMT SIGEMT, "EMT trap", #endif #ifdef SIGFPE SIGFPE, "Floating point exception", #endif #ifdef SIGGRANT SIGGRANT, "HFT monitor mode granted", #endif #ifdef SIGHUP SIGHUP, "Hangup", #endif #ifdef SIGILL SIGILL, "Illegal instruction", #endif #ifdef SIGINFO SIGINFO, "Information request", #endif #ifdef SIGINT SIGINT, "Interrupt", #endif #ifdef SIGIO SIGIO, "I/O possible", #endif #ifdef SIGIOT SIGIOT, "IOT trap", #endif #ifdef SIGKILL SIGKILL, "Killed", #endif #ifdef SIGLOST SIGLOST, "Record lock", #endif #ifdef SIGMIGRATE SIGMIGRATE, "Migrate process to another CPU", #endif #ifdef SIGMSG SIGMSG, "HFT input data pending", #endif #ifdef SIGPIPE SIGPIPE, "Broken pipe", #endif #ifdef SIGPOLL SIGPOLL, "I/O possible", #endif #ifdef SIGPRE SIGPRE, "Programming error", #endif #ifdef SIGPROF SIGPROF, "Profiling timer expired", #endif #ifdef SIGPWR SIGPWR, "Power failure imminent", #endif #ifdef SIGRETRACT SIGRETRACT, "HFT monitor mode retracted", #endif #ifdef SIGQUIT SIGQUIT, "Quit", #endif #ifdef SIGSAK SIGSAK, "Secure Attention Key", #endif #ifdef SIGSEGV SIGSEGV, "Segmentation fault", #endif #ifdef SIGSOUND SIGSOUND, "HFT sound sequence completed", #endif #ifdef SIGSTOP SIGSTOP, "Suspended (signal)", #endif #ifdef SIGSYS SIGSYS, "Bad system call", #endif #ifdef SIGTERM SIGTERM, "Terminated", #endif #ifdef SIGTRAP SIGTRAP, "Trace/BPT trap", #endif #ifdef SIGTSTP SIGTSTP, "Suspended", #endif #ifdef SIGTTIN SIGTTIN, "Stopped (tty input)", #endif #ifdef SIGTTOU SIGTTOU, "Stopped (tty output)", #endif #ifdef SIGURG SIGURG, "Urgent I/O condition", #endif #ifdef SIGUSR1 SIGUSR1, "User defined signal 1", #endif #ifdef SIGUSR2 SIGUSR2, "User defined signal 2", #endif #ifdef SIGVTALRM SIGVTALRM, "Virtual timer expired", #endif #ifdef SIGWINCH SIGWINCH, "Window size changes", #endif #ifdef SIGXCPU SIGXCPU, "Cputime limit exceeded", #endif #ifdef SIGXFSZ SIGXFSZ, "Filesize limit exceeded", #endif }; /* * sigmsg -- * Return a pointer to a message describing a signal. */ static const char * sigmsg(signo) int signo; { static char buf[40]; const SIGS *sigp; int n; for (n = 0, sigp = &sigs[0]; n < sizeof(sigs) / sizeof(sigs[0]); ++n, ++sigp) if (sigp->number == signo) return (sigp->message); (void)snprintf(buf, sizeof(buf), "Unknown signal: %d", signo); return (buf); }