1*0a6a1f1dSLionel Sambuc /* $NetBSD: ex_shell.c,v 1.3 2014/01/26 21:43:45 christos Exp $ */
284d9c625SLionel Sambuc /*-
384d9c625SLionel Sambuc * Copyright (c) 1992, 1993, 1994
484d9c625SLionel Sambuc * The Regents of the University of California. All rights reserved.
584d9c625SLionel Sambuc * Copyright (c) 1992, 1993, 1994, 1995, 1996
684d9c625SLionel Sambuc * Keith Bostic. All rights reserved.
784d9c625SLionel Sambuc *
884d9c625SLionel Sambuc * See the LICENSE file for redistribution information.
984d9c625SLionel Sambuc */
1084d9c625SLionel Sambuc
1184d9c625SLionel Sambuc #include "config.h"
1284d9c625SLionel Sambuc
13*0a6a1f1dSLionel Sambuc #include <sys/cdefs.h>
14*0a6a1f1dSLionel Sambuc #if 0
1584d9c625SLionel Sambuc #ifndef lint
1684d9c625SLionel Sambuc static const char sccsid[] = "Id: ex_shell.c,v 10.42 2003/11/05 17:11:54 skimo Exp (Berkeley) Date: 2003/11/05 17:11:54 ";
1784d9c625SLionel Sambuc #endif /* not lint */
18*0a6a1f1dSLionel Sambuc #else
19*0a6a1f1dSLionel Sambuc __RCSID("$NetBSD: ex_shell.c,v 1.3 2014/01/26 21:43:45 christos Exp $");
20*0a6a1f1dSLionel Sambuc #endif
2184d9c625SLionel Sambuc
2284d9c625SLionel Sambuc #include <sys/param.h>
2384d9c625SLionel Sambuc #include <sys/queue.h>
2484d9c625SLionel Sambuc #include <sys/wait.h>
2584d9c625SLionel Sambuc
2684d9c625SLionel Sambuc #include <bitstring.h>
2784d9c625SLionel Sambuc #include <ctype.h>
2884d9c625SLionel Sambuc #include <errno.h>
2984d9c625SLionel Sambuc #include <limits.h>
3084d9c625SLionel Sambuc #include <signal.h>
3184d9c625SLionel Sambuc #include <stdio.h>
3284d9c625SLionel Sambuc #include <stdlib.h>
3384d9c625SLionel Sambuc #include <string.h>
3484d9c625SLionel Sambuc #include <unistd.h>
3584d9c625SLionel Sambuc
3684d9c625SLionel Sambuc #include "../common/common.h"
3784d9c625SLionel Sambuc
3884d9c625SLionel Sambuc static const char *sigmsg __P((int));
3984d9c625SLionel Sambuc
4084d9c625SLionel Sambuc /*
4184d9c625SLionel Sambuc * ex_shell -- :sh[ell]
4284d9c625SLionel Sambuc * Invoke the program named in the SHELL environment variable
4384d9c625SLionel Sambuc * with the argument -i.
4484d9c625SLionel Sambuc *
4584d9c625SLionel Sambuc * PUBLIC: int ex_shell __P((SCR *, EXCMD *));
4684d9c625SLionel Sambuc */
4784d9c625SLionel Sambuc int
ex_shell(SCR * sp,EXCMD * cmdp)4884d9c625SLionel Sambuc ex_shell(SCR *sp, EXCMD *cmdp)
4984d9c625SLionel Sambuc {
5084d9c625SLionel Sambuc int rval;
5184d9c625SLionel Sambuc char buf[MAXPATHLEN];
5284d9c625SLionel Sambuc
5384d9c625SLionel Sambuc /* We'll need a shell. */
5484d9c625SLionel Sambuc if (opts_empty(sp, O_SHELL, 0))
5584d9c625SLionel Sambuc return (1);
5684d9c625SLionel Sambuc
5784d9c625SLionel Sambuc /*
5884d9c625SLionel Sambuc * XXX
5984d9c625SLionel Sambuc * Assumes all shells use -i.
6084d9c625SLionel Sambuc */
6184d9c625SLionel Sambuc (void)snprintf(buf, sizeof(buf), "%s -i", O_STR(sp, O_SHELL));
6284d9c625SLionel Sambuc
6384d9c625SLionel Sambuc /* Restore the window name. */
6484d9c625SLionel Sambuc (void)sp->gp->scr_rename(sp, NULL, 0);
6584d9c625SLionel Sambuc
6684d9c625SLionel Sambuc /* If we're still in a vi screen, move out explicitly. */
6784d9c625SLionel Sambuc rval = ex_exec_proc(sp, cmdp, buf, NULL, !F_ISSET(sp, SC_SCR_EXWROTE));
6884d9c625SLionel Sambuc
6984d9c625SLionel Sambuc /* Set the window name. */
7084d9c625SLionel Sambuc (void)sp->gp->scr_rename(sp, sp->frp->name, 1);
7184d9c625SLionel Sambuc
7284d9c625SLionel Sambuc /*
7384d9c625SLionel Sambuc * !!!
7484d9c625SLionel Sambuc * Historically, vi didn't require a continue message after the
7584d9c625SLionel Sambuc * return of the shell. Match it.
7684d9c625SLionel Sambuc */
7784d9c625SLionel Sambuc F_SET(sp, SC_EX_WAIT_NO);
7884d9c625SLionel Sambuc
7984d9c625SLionel Sambuc return (rval);
8084d9c625SLionel Sambuc }
8184d9c625SLionel Sambuc
8284d9c625SLionel Sambuc /*
8384d9c625SLionel Sambuc * ex_exec_proc --
8484d9c625SLionel Sambuc * Run a separate process.
8584d9c625SLionel Sambuc *
8684d9c625SLionel Sambuc * PUBLIC: int ex_exec_proc __P((SCR *, EXCMD *, const char *, const char *, int));
8784d9c625SLionel Sambuc */
8884d9c625SLionel Sambuc int
ex_exec_proc(SCR * sp,EXCMD * cmdp,const char * cmd,const char * msg,int need_newline)8984d9c625SLionel Sambuc ex_exec_proc(SCR *sp, EXCMD *cmdp, const char *cmd, const char *msg, int need_newline)
9084d9c625SLionel Sambuc {
9184d9c625SLionel Sambuc GS *gp;
9284d9c625SLionel Sambuc const char *name;
9384d9c625SLionel Sambuc pid_t pid;
9484d9c625SLionel Sambuc
9584d9c625SLionel Sambuc gp = sp->gp;
9684d9c625SLionel Sambuc
9784d9c625SLionel Sambuc /* We'll need a shell. */
9884d9c625SLionel Sambuc if (opts_empty(sp, O_SHELL, 0))
9984d9c625SLionel Sambuc return (1);
10084d9c625SLionel Sambuc
10184d9c625SLionel Sambuc /* Enter ex mode. */
10284d9c625SLionel Sambuc if (F_ISSET(sp, SC_VI)) {
10384d9c625SLionel Sambuc if (gp->scr_screen(sp, SC_EX)) {
10484d9c625SLionel Sambuc ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON);
10584d9c625SLionel Sambuc return (1);
10684d9c625SLionel Sambuc }
10784d9c625SLionel Sambuc (void)gp->scr_attr(sp, SA_ALTERNATE, 0);
10884d9c625SLionel Sambuc F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
10984d9c625SLionel Sambuc }
11084d9c625SLionel Sambuc
11184d9c625SLionel Sambuc /* Put out additional newline, message. */
11284d9c625SLionel Sambuc if (need_newline)
11384d9c625SLionel Sambuc (void)ex_puts(sp, "\n");
11484d9c625SLionel Sambuc if (msg != NULL) {
11584d9c625SLionel Sambuc (void)ex_puts(sp, msg);
11684d9c625SLionel Sambuc (void)ex_puts(sp, "\n");
11784d9c625SLionel Sambuc }
11884d9c625SLionel Sambuc (void)ex_fflush(sp);
11984d9c625SLionel Sambuc
12084d9c625SLionel Sambuc switch (pid = vfork()) {
12184d9c625SLionel Sambuc case -1: /* Error. */
12284d9c625SLionel Sambuc msgq(sp, M_SYSERR, "vfork");
12384d9c625SLionel Sambuc return (1);
12484d9c625SLionel Sambuc case 0: /* Utility. */
12584d9c625SLionel Sambuc if (gp->scr_child)
12684d9c625SLionel Sambuc gp->scr_child(sp);
12784d9c625SLionel Sambuc if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
12884d9c625SLionel Sambuc name = O_STR(sp, O_SHELL);
12984d9c625SLionel Sambuc else
13084d9c625SLionel Sambuc ++name;
13184d9c625SLionel Sambuc execl(O_STR(sp, O_SHELL), name, "-c", cmd, (char *)NULL);
13284d9c625SLionel Sambuc msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s");
13384d9c625SLionel Sambuc _exit(127);
13484d9c625SLionel Sambuc /* NOTREACHED */
13584d9c625SLionel Sambuc default: /* Parent. */
13684d9c625SLionel Sambuc return (proc_wait(sp, (long)pid, cmd, 0, 0));
13784d9c625SLionel Sambuc }
13884d9c625SLionel Sambuc /* NOTREACHED */
13984d9c625SLionel Sambuc }
14084d9c625SLionel Sambuc
14184d9c625SLionel Sambuc /*
14284d9c625SLionel Sambuc * proc_wait --
14384d9c625SLionel Sambuc * Wait for one of the processes.
14484d9c625SLionel Sambuc *
14584d9c625SLionel Sambuc * !!!
14684d9c625SLionel Sambuc * The pid_t type varies in size from a short to a long depending on the
14784d9c625SLionel Sambuc * system. It has to be cast into something or the standard promotion
14884d9c625SLionel Sambuc * rules get you. I'm using a long based on the belief that nobody is
14984d9c625SLionel Sambuc * going to make it unsigned and it's unlikely to be a quad.
15084d9c625SLionel Sambuc *
15184d9c625SLionel Sambuc * PUBLIC: int proc_wait __P((SCR *, long, const char *, int, int));
15284d9c625SLionel Sambuc */
15384d9c625SLionel Sambuc int
proc_wait(SCR * sp,long int pid,const char * cmd,int silent,int okpipe)15484d9c625SLionel Sambuc proc_wait(SCR *sp, long int pid, const char *cmd, int silent, int okpipe)
15584d9c625SLionel Sambuc {
15684d9c625SLionel Sambuc size_t len;
15784d9c625SLionel Sambuc int nf, pstat;
15884d9c625SLionel Sambuc char *p;
15984d9c625SLionel Sambuc
16084d9c625SLionel Sambuc /* Wait for the utility, ignoring interruptions. */
16184d9c625SLionel Sambuc for (;;) {
16284d9c625SLionel Sambuc errno = 0;
16384d9c625SLionel Sambuc if (waitpid((pid_t)pid, &pstat, 0) != -1)
16484d9c625SLionel Sambuc break;
16584d9c625SLionel Sambuc if (errno != EINTR) {
16684d9c625SLionel Sambuc msgq(sp, M_SYSERR, "waitpid");
16784d9c625SLionel Sambuc return (1);
16884d9c625SLionel Sambuc }
16984d9c625SLionel Sambuc }
17084d9c625SLionel Sambuc
17184d9c625SLionel Sambuc /*
17284d9c625SLionel Sambuc * Display the utility's exit status. Ignore SIGPIPE from the
17384d9c625SLionel Sambuc * parent-writer, as that only means that the utility chose to
17484d9c625SLionel Sambuc * exit before reading all of its input.
17584d9c625SLionel Sambuc */
17684d9c625SLionel Sambuc if (WIFSIGNALED(pstat) && (!okpipe || WTERMSIG(pstat) != SIGPIPE)) {
17784d9c625SLionel Sambuc for (; isblank((unsigned char)*cmd); ++cmd);
17884d9c625SLionel Sambuc p = msg_print(sp, cmd, &nf);
17984d9c625SLionel Sambuc len = strlen(p);
18084d9c625SLionel Sambuc msgq(sp, M_ERR, "%.*s%s: received signal: %s%s",
18184d9c625SLionel Sambuc (int)MIN(len, 20), p, len > 20 ? " ..." : "",
18284d9c625SLionel Sambuc sigmsg(WTERMSIG(pstat)),
18384d9c625SLionel Sambuc WCOREDUMP(pstat) ? "; core dumped" : "");
18484d9c625SLionel Sambuc if (nf)
18584d9c625SLionel Sambuc FREE_SPACE(sp, p, 0);
18684d9c625SLionel Sambuc return (1);
18784d9c625SLionel Sambuc }
18884d9c625SLionel Sambuc
18984d9c625SLionel Sambuc if (WIFEXITED(pstat) && WEXITSTATUS(pstat)) {
19084d9c625SLionel Sambuc /*
19184d9c625SLionel Sambuc * Remain silent for "normal" errors when doing shell file
19284d9c625SLionel Sambuc * name expansions, they almost certainly indicate nothing
19384d9c625SLionel Sambuc * more than a failure to match.
19484d9c625SLionel Sambuc *
19584d9c625SLionel Sambuc * Remain silent for vi read filter errors. It's historic
19684d9c625SLionel Sambuc * practice.
19784d9c625SLionel Sambuc */
19884d9c625SLionel Sambuc if (!silent) {
19984d9c625SLionel Sambuc for (; isblank((unsigned char)*cmd); ++cmd);
20084d9c625SLionel Sambuc p = msg_print(sp, cmd, &nf);
20184d9c625SLionel Sambuc len = strlen(p);
20284d9c625SLionel Sambuc msgq(sp, M_ERR, "%.*s%s: exited with status %d",
20384d9c625SLionel Sambuc (int)MIN(len, 20), p, len > 20 ? " ..." : "",
20484d9c625SLionel Sambuc WEXITSTATUS(pstat));
20584d9c625SLionel Sambuc if (nf)
20684d9c625SLionel Sambuc FREE_SPACE(sp, p, 0);
20784d9c625SLionel Sambuc }
20884d9c625SLionel Sambuc return (1);
20984d9c625SLionel Sambuc }
21084d9c625SLionel Sambuc return (0);
21184d9c625SLionel Sambuc }
21284d9c625SLionel Sambuc
21384d9c625SLionel Sambuc /*
21484d9c625SLionel Sambuc * XXX
21584d9c625SLionel Sambuc * The sys_siglist[] table in the C library has this information, but there's
21684d9c625SLionel Sambuc * no portable way to get to it. (Believe me, I tried.)
21784d9c625SLionel Sambuc */
21884d9c625SLionel Sambuc typedef struct _sigs {
21984d9c625SLionel Sambuc int number; /* signal number */
22084d9c625SLionel Sambuc const char *message; /* related message */
22184d9c625SLionel Sambuc } SIGS;
22284d9c625SLionel Sambuc
22384d9c625SLionel Sambuc SIGS const sigs[] = {
22484d9c625SLionel Sambuc #ifdef SIGABRT
22584d9c625SLionel Sambuc { SIGABRT, "Abort trap" },
22684d9c625SLionel Sambuc #endif
22784d9c625SLionel Sambuc #ifdef SIGALRM
22884d9c625SLionel Sambuc { SIGALRM, "Alarm clock" },
22984d9c625SLionel Sambuc #endif
23084d9c625SLionel Sambuc #ifdef SIGBUS
23184d9c625SLionel Sambuc { SIGBUS, "Bus error" },
23284d9c625SLionel Sambuc #endif
23384d9c625SLionel Sambuc #ifdef SIGCLD
23484d9c625SLionel Sambuc { SIGCLD, "Child exited or stopped" },
23584d9c625SLionel Sambuc #endif
23684d9c625SLionel Sambuc #ifdef SIGCHLD
23784d9c625SLionel Sambuc { SIGCHLD, "Child exited" },
23884d9c625SLionel Sambuc #endif
23984d9c625SLionel Sambuc #ifdef SIGCONT
24084d9c625SLionel Sambuc { SIGCONT, "Continued" },
24184d9c625SLionel Sambuc #endif
24284d9c625SLionel Sambuc #ifdef SIGDANGER
24384d9c625SLionel Sambuc { SIGDANGER, "System crash imminent" },
24484d9c625SLionel Sambuc #endif
24584d9c625SLionel Sambuc #ifdef SIGEMT
24684d9c625SLionel Sambuc { SIGEMT, "EMT trap" },
24784d9c625SLionel Sambuc #endif
24884d9c625SLionel Sambuc #ifdef SIGFPE
24984d9c625SLionel Sambuc { SIGFPE, "Floating point exception" },
25084d9c625SLionel Sambuc #endif
25184d9c625SLionel Sambuc #ifdef SIGGRANT
25284d9c625SLionel Sambuc { SIGGRANT, "HFT monitor mode granted" },
25384d9c625SLionel Sambuc #endif
25484d9c625SLionel Sambuc #ifdef SIGHUP
25584d9c625SLionel Sambuc { SIGHUP, "Hangup" },
25684d9c625SLionel Sambuc #endif
25784d9c625SLionel Sambuc #ifdef SIGILL
25884d9c625SLionel Sambuc { SIGILL, "Illegal instruction" },
25984d9c625SLionel Sambuc #endif
26084d9c625SLionel Sambuc #ifdef SIGINFO
26184d9c625SLionel Sambuc { SIGINFO, "Information request" },
26284d9c625SLionel Sambuc #endif
26384d9c625SLionel Sambuc #ifdef SIGINT
26484d9c625SLionel Sambuc { SIGINT, "Interrupt" },
26584d9c625SLionel Sambuc #endif
26684d9c625SLionel Sambuc #ifdef SIGIO
26784d9c625SLionel Sambuc { SIGIO, "I/O possible" },
26884d9c625SLionel Sambuc #endif
26984d9c625SLionel Sambuc #ifdef SIGIOT
27084d9c625SLionel Sambuc { SIGIOT, "IOT trap" },
27184d9c625SLionel Sambuc #endif
27284d9c625SLionel Sambuc #ifdef SIGKILL
27384d9c625SLionel Sambuc { SIGKILL, "Killed" },
27484d9c625SLionel Sambuc #endif
27584d9c625SLionel Sambuc #ifdef SIGLOST
27684d9c625SLionel Sambuc { SIGLOST, "Record lock" },
27784d9c625SLionel Sambuc #endif
27884d9c625SLionel Sambuc #ifdef SIGMIGRATE
27984d9c625SLionel Sambuc { SIGMIGRATE, "Migrate process to another CPU" },
28084d9c625SLionel Sambuc #endif
28184d9c625SLionel Sambuc #ifdef SIGMSG
28284d9c625SLionel Sambuc { SIGMSG, "HFT input data pending" },
28384d9c625SLionel Sambuc #endif
28484d9c625SLionel Sambuc #ifdef SIGPIPE
28584d9c625SLionel Sambuc { SIGPIPE, "Broken pipe" },
28684d9c625SLionel Sambuc #endif
28784d9c625SLionel Sambuc #ifdef SIGPOLL
28884d9c625SLionel Sambuc { SIGPOLL, "I/O possible" },
28984d9c625SLionel Sambuc #endif
29084d9c625SLionel Sambuc #ifdef SIGPRE
29184d9c625SLionel Sambuc { SIGPRE, "Programming error" },
29284d9c625SLionel Sambuc #endif
29384d9c625SLionel Sambuc #ifdef SIGPROF
29484d9c625SLionel Sambuc { SIGPROF, "Profiling timer expired" },
29584d9c625SLionel Sambuc #endif
29684d9c625SLionel Sambuc #ifdef SIGPWR
29784d9c625SLionel Sambuc { SIGPWR, "Power failure imminent" },
29884d9c625SLionel Sambuc #endif
29984d9c625SLionel Sambuc #ifdef SIGRETRACT
30084d9c625SLionel Sambuc { SIGRETRACT, "HFT monitor mode retracted" },
30184d9c625SLionel Sambuc #endif
30284d9c625SLionel Sambuc #ifdef SIGQUIT
30384d9c625SLionel Sambuc { SIGQUIT, "Quit" },
30484d9c625SLionel Sambuc #endif
30584d9c625SLionel Sambuc #ifdef SIGSAK
30684d9c625SLionel Sambuc { SIGSAK, "Secure Attention Key" },
30784d9c625SLionel Sambuc #endif
30884d9c625SLionel Sambuc #ifdef SIGSEGV
30984d9c625SLionel Sambuc { SIGSEGV, "Segmentation fault" },
31084d9c625SLionel Sambuc #endif
31184d9c625SLionel Sambuc #ifdef SIGSOUND
31284d9c625SLionel Sambuc { SIGSOUND, "HFT sound sequence completed" },
31384d9c625SLionel Sambuc #endif
31484d9c625SLionel Sambuc #ifdef SIGSTOP
31584d9c625SLionel Sambuc { SIGSTOP, "Suspended (signal)" },
31684d9c625SLionel Sambuc #endif
31784d9c625SLionel Sambuc #ifdef SIGSYS
31884d9c625SLionel Sambuc { SIGSYS, "Bad system call" },
31984d9c625SLionel Sambuc #endif
32084d9c625SLionel Sambuc #ifdef SIGTERM
32184d9c625SLionel Sambuc { SIGTERM, "Terminated" },
32284d9c625SLionel Sambuc #endif
32384d9c625SLionel Sambuc #ifdef SIGTRAP
32484d9c625SLionel Sambuc { SIGTRAP, "Trace/BPT trap" },
32584d9c625SLionel Sambuc #endif
32684d9c625SLionel Sambuc #ifdef SIGTSTP
32784d9c625SLionel Sambuc { SIGTSTP, "Suspended" },
32884d9c625SLionel Sambuc #endif
32984d9c625SLionel Sambuc #ifdef SIGTTIN
33084d9c625SLionel Sambuc { SIGTTIN, "Stopped (tty input)" },
33184d9c625SLionel Sambuc #endif
33284d9c625SLionel Sambuc #ifdef SIGTTOU
33384d9c625SLionel Sambuc { SIGTTOU, "Stopped (tty output)" },
33484d9c625SLionel Sambuc #endif
33584d9c625SLionel Sambuc #ifdef SIGURG
33684d9c625SLionel Sambuc { SIGURG, "Urgent I/O condition" },
33784d9c625SLionel Sambuc #endif
33884d9c625SLionel Sambuc #ifdef SIGUSR1
33984d9c625SLionel Sambuc { SIGUSR1, "User defined signal 1" },
34084d9c625SLionel Sambuc #endif
34184d9c625SLionel Sambuc #ifdef SIGUSR2
34284d9c625SLionel Sambuc { SIGUSR2, "User defined signal 2" },
34384d9c625SLionel Sambuc #endif
34484d9c625SLionel Sambuc #ifdef SIGVTALRM
34584d9c625SLionel Sambuc { SIGVTALRM, "Virtual timer expired" },
34684d9c625SLionel Sambuc #endif
34784d9c625SLionel Sambuc #ifdef SIGWINCH
34884d9c625SLionel Sambuc { SIGWINCH, "Window size changes" },
34984d9c625SLionel Sambuc #endif
35084d9c625SLionel Sambuc #ifdef SIGXCPU
35184d9c625SLionel Sambuc { SIGXCPU, "Cputime limit exceeded" },
35284d9c625SLionel Sambuc #endif
35384d9c625SLionel Sambuc #ifdef SIGXFSZ
35484d9c625SLionel Sambuc { SIGXFSZ, "Filesize limit exceeded" },
35584d9c625SLionel Sambuc #endif
35684d9c625SLionel Sambuc };
35784d9c625SLionel Sambuc
35884d9c625SLionel Sambuc /*
35984d9c625SLionel Sambuc * sigmsg --
36084d9c625SLionel Sambuc * Return a pointer to a message describing a signal.
36184d9c625SLionel Sambuc */
36284d9c625SLionel Sambuc static const char *
sigmsg(int signo)36384d9c625SLionel Sambuc sigmsg(int signo)
36484d9c625SLionel Sambuc {
36584d9c625SLionel Sambuc static char buf[40];
36684d9c625SLionel Sambuc const SIGS *sigp;
36784d9c625SLionel Sambuc size_t n;
36884d9c625SLionel Sambuc
36984d9c625SLionel Sambuc for (n = 0,
37084d9c625SLionel Sambuc sigp = &sigs[0]; n < sizeof(sigs) / sizeof(sigs[0]); ++n, ++sigp)
37184d9c625SLionel Sambuc if (sigp->number == signo)
37284d9c625SLionel Sambuc return (sigp->message);
37384d9c625SLionel Sambuc (void)snprintf(buf, sizeof(buf), "Unknown signal: %d", signo);
37484d9c625SLionel Sambuc return (buf);
37584d9c625SLionel Sambuc }
376