1*2f698edbSchristos /* $NetBSD: ex_shell.c,v 1.3 2014/01/26 21:43:45 christos Exp $ */
2dbd550edSchristos /*-
3dbd550edSchristos * Copyright (c) 1992, 1993, 1994
4dbd550edSchristos * The Regents of the University of California. All rights reserved.
5dbd550edSchristos * Copyright (c) 1992, 1993, 1994, 1995, 1996
6dbd550edSchristos * Keith Bostic. All rights reserved.
7dbd550edSchristos *
8dbd550edSchristos * See the LICENSE file for redistribution information.
9dbd550edSchristos */
10dbd550edSchristos
11dbd550edSchristos #include "config.h"
12dbd550edSchristos
13*2f698edbSchristos #include <sys/cdefs.h>
14*2f698edbSchristos #if 0
15dbd550edSchristos #ifndef lint
16dbd550edSchristos 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 ";
17dbd550edSchristos #endif /* not lint */
18*2f698edbSchristos #else
19*2f698edbSchristos __RCSID("$NetBSD: ex_shell.c,v 1.3 2014/01/26 21:43:45 christos Exp $");
20*2f698edbSchristos #endif
21dbd550edSchristos
22dbd550edSchristos #include <sys/param.h>
23dbd550edSchristos #include <sys/queue.h>
24dbd550edSchristos #include <sys/wait.h>
25dbd550edSchristos
26dbd550edSchristos #include <bitstring.h>
278d01a27eSchristos #include <ctype.h>
28dbd550edSchristos #include <errno.h>
29dbd550edSchristos #include <limits.h>
30dbd550edSchristos #include <signal.h>
31dbd550edSchristos #include <stdio.h>
32dbd550edSchristos #include <stdlib.h>
33dbd550edSchristos #include <string.h>
34dbd550edSchristos #include <unistd.h>
35dbd550edSchristos
36dbd550edSchristos #include "../common/common.h"
37dbd550edSchristos
38dbd550edSchristos static const char *sigmsg __P((int));
39dbd550edSchristos
40dbd550edSchristos /*
41dbd550edSchristos * ex_shell -- :sh[ell]
42dbd550edSchristos * Invoke the program named in the SHELL environment variable
43dbd550edSchristos * with the argument -i.
44dbd550edSchristos *
45dbd550edSchristos * PUBLIC: int ex_shell __P((SCR *, EXCMD *));
46dbd550edSchristos */
47dbd550edSchristos int
ex_shell(SCR * sp,EXCMD * cmdp)48dbd550edSchristos ex_shell(SCR *sp, EXCMD *cmdp)
49dbd550edSchristos {
50dbd550edSchristos int rval;
51dbd550edSchristos char buf[MAXPATHLEN];
52dbd550edSchristos
53dbd550edSchristos /* We'll need a shell. */
54dbd550edSchristos if (opts_empty(sp, O_SHELL, 0))
55dbd550edSchristos return (1);
56dbd550edSchristos
57dbd550edSchristos /*
58dbd550edSchristos * XXX
59dbd550edSchristos * Assumes all shells use -i.
60dbd550edSchristos */
61dbd550edSchristos (void)snprintf(buf, sizeof(buf), "%s -i", O_STR(sp, O_SHELL));
62dbd550edSchristos
63dbd550edSchristos /* Restore the window name. */
64dbd550edSchristos (void)sp->gp->scr_rename(sp, NULL, 0);
65dbd550edSchristos
66dbd550edSchristos /* If we're still in a vi screen, move out explicitly. */
67dbd550edSchristos rval = ex_exec_proc(sp, cmdp, buf, NULL, !F_ISSET(sp, SC_SCR_EXWROTE));
68dbd550edSchristos
69dbd550edSchristos /* Set the window name. */
70dbd550edSchristos (void)sp->gp->scr_rename(sp, sp->frp->name, 1);
71dbd550edSchristos
72dbd550edSchristos /*
73dbd550edSchristos * !!!
74dbd550edSchristos * Historically, vi didn't require a continue message after the
75dbd550edSchristos * return of the shell. Match it.
76dbd550edSchristos */
77dbd550edSchristos F_SET(sp, SC_EX_WAIT_NO);
78dbd550edSchristos
79dbd550edSchristos return (rval);
80dbd550edSchristos }
81dbd550edSchristos
82dbd550edSchristos /*
83dbd550edSchristos * ex_exec_proc --
84dbd550edSchristos * Run a separate process.
85dbd550edSchristos *
868d01a27eSchristos * PUBLIC: int ex_exec_proc __P((SCR *, EXCMD *, const char *, const char *, int));
87dbd550edSchristos */
88dbd550edSchristos int
ex_exec_proc(SCR * sp,EXCMD * cmdp,const char * cmd,const char * msg,int need_newline)898d01a27eSchristos ex_exec_proc(SCR *sp, EXCMD *cmdp, const char *cmd, const char *msg, int need_newline)
90dbd550edSchristos {
91dbd550edSchristos GS *gp;
92dbd550edSchristos const char *name;
93dbd550edSchristos pid_t pid;
94dbd550edSchristos
95dbd550edSchristos gp = sp->gp;
96dbd550edSchristos
97dbd550edSchristos /* We'll need a shell. */
98dbd550edSchristos if (opts_empty(sp, O_SHELL, 0))
99dbd550edSchristos return (1);
100dbd550edSchristos
101dbd550edSchristos /* Enter ex mode. */
102dbd550edSchristos if (F_ISSET(sp, SC_VI)) {
103dbd550edSchristos if (gp->scr_screen(sp, SC_EX)) {
104dbd550edSchristos ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON);
105dbd550edSchristos return (1);
106dbd550edSchristos }
107dbd550edSchristos (void)gp->scr_attr(sp, SA_ALTERNATE, 0);
108dbd550edSchristos F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE);
109dbd550edSchristos }
110dbd550edSchristos
111dbd550edSchristos /* Put out additional newline, message. */
112dbd550edSchristos if (need_newline)
113dbd550edSchristos (void)ex_puts(sp, "\n");
114dbd550edSchristos if (msg != NULL) {
115dbd550edSchristos (void)ex_puts(sp, msg);
116dbd550edSchristos (void)ex_puts(sp, "\n");
117dbd550edSchristos }
118dbd550edSchristos (void)ex_fflush(sp);
119dbd550edSchristos
120dbd550edSchristos switch (pid = vfork()) {
121dbd550edSchristos case -1: /* Error. */
122dbd550edSchristos msgq(sp, M_SYSERR, "vfork");
123dbd550edSchristos return (1);
124dbd550edSchristos case 0: /* Utility. */
125dbd550edSchristos if (gp->scr_child)
126dbd550edSchristos gp->scr_child(sp);
127dbd550edSchristos if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL)
128dbd550edSchristos name = O_STR(sp, O_SHELL);
129dbd550edSchristos else
130dbd550edSchristos ++name;
131dbd550edSchristos execl(O_STR(sp, O_SHELL), name, "-c", cmd, (char *)NULL);
132dbd550edSchristos msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s");
133dbd550edSchristos _exit(127);
134dbd550edSchristos /* NOTREACHED */
135dbd550edSchristos default: /* Parent. */
136dbd550edSchristos return (proc_wait(sp, (long)pid, cmd, 0, 0));
137dbd550edSchristos }
138dbd550edSchristos /* NOTREACHED */
139dbd550edSchristos }
140dbd550edSchristos
141dbd550edSchristos /*
142dbd550edSchristos * proc_wait --
143dbd550edSchristos * Wait for one of the processes.
144dbd550edSchristos *
145dbd550edSchristos * !!!
146dbd550edSchristos * The pid_t type varies in size from a short to a long depending on the
147dbd550edSchristos * system. It has to be cast into something or the standard promotion
148dbd550edSchristos * rules get you. I'm using a long based on the belief that nobody is
149dbd550edSchristos * going to make it unsigned and it's unlikely to be a quad.
150dbd550edSchristos *
151dbd550edSchristos * PUBLIC: int proc_wait __P((SCR *, long, const char *, int, int));
152dbd550edSchristos */
153dbd550edSchristos int
proc_wait(SCR * sp,long int pid,const char * cmd,int silent,int okpipe)154dbd550edSchristos proc_wait(SCR *sp, long int pid, const char *cmd, int silent, int okpipe)
155dbd550edSchristos {
156dbd550edSchristos size_t len;
157dbd550edSchristos int nf, pstat;
158dbd550edSchristos char *p;
159dbd550edSchristos
160dbd550edSchristos /* Wait for the utility, ignoring interruptions. */
161dbd550edSchristos for (;;) {
162dbd550edSchristos errno = 0;
163dbd550edSchristos if (waitpid((pid_t)pid, &pstat, 0) != -1)
164dbd550edSchristos break;
165dbd550edSchristos if (errno != EINTR) {
166dbd550edSchristos msgq(sp, M_SYSERR, "waitpid");
167dbd550edSchristos return (1);
168dbd550edSchristos }
169dbd550edSchristos }
170dbd550edSchristos
171dbd550edSchristos /*
172dbd550edSchristos * Display the utility's exit status. Ignore SIGPIPE from the
173dbd550edSchristos * parent-writer, as that only means that the utility chose to
174dbd550edSchristos * exit before reading all of its input.
175dbd550edSchristos */
176dbd550edSchristos if (WIFSIGNALED(pstat) && (!okpipe || WTERMSIG(pstat) != SIGPIPE)) {
1778d01a27eSchristos for (; isblank((unsigned char)*cmd); ++cmd);
178dbd550edSchristos p = msg_print(sp, cmd, &nf);
179dbd550edSchristos len = strlen(p);
180dbd550edSchristos msgq(sp, M_ERR, "%.*s%s: received signal: %s%s",
1818d01a27eSchristos (int)MIN(len, 20), p, len > 20 ? " ..." : "",
182dbd550edSchristos sigmsg(WTERMSIG(pstat)),
183dbd550edSchristos WCOREDUMP(pstat) ? "; core dumped" : "");
184dbd550edSchristos if (nf)
185dbd550edSchristos FREE_SPACE(sp, p, 0);
186dbd550edSchristos return (1);
187dbd550edSchristos }
188dbd550edSchristos
189dbd550edSchristos if (WIFEXITED(pstat) && WEXITSTATUS(pstat)) {
190dbd550edSchristos /*
191dbd550edSchristos * Remain silent for "normal" errors when doing shell file
192dbd550edSchristos * name expansions, they almost certainly indicate nothing
193dbd550edSchristos * more than a failure to match.
194dbd550edSchristos *
195dbd550edSchristos * Remain silent for vi read filter errors. It's historic
196dbd550edSchristos * practice.
197dbd550edSchristos */
198dbd550edSchristos if (!silent) {
1998d01a27eSchristos for (; isblank((unsigned char)*cmd); ++cmd);
200dbd550edSchristos p = msg_print(sp, cmd, &nf);
201dbd550edSchristos len = strlen(p);
202dbd550edSchristos msgq(sp, M_ERR, "%.*s%s: exited with status %d",
2038d01a27eSchristos (int)MIN(len, 20), p, len > 20 ? " ..." : "",
204dbd550edSchristos WEXITSTATUS(pstat));
205dbd550edSchristos if (nf)
206dbd550edSchristos FREE_SPACE(sp, p, 0);
207dbd550edSchristos }
208dbd550edSchristos return (1);
209dbd550edSchristos }
210dbd550edSchristos return (0);
211dbd550edSchristos }
212dbd550edSchristos
213dbd550edSchristos /*
214dbd550edSchristos * XXX
215dbd550edSchristos * The sys_siglist[] table in the C library has this information, but there's
216dbd550edSchristos * no portable way to get to it. (Believe me, I tried.)
217dbd550edSchristos */
218dbd550edSchristos typedef struct _sigs {
219dbd550edSchristos int number; /* signal number */
2208d01a27eSchristos const char *message; /* related message */
221dbd550edSchristos } SIGS;
222dbd550edSchristos
223dbd550edSchristos SIGS const sigs[] = {
224dbd550edSchristos #ifdef SIGABRT
2258d01a27eSchristos { SIGABRT, "Abort trap" },
226dbd550edSchristos #endif
227dbd550edSchristos #ifdef SIGALRM
2288d01a27eSchristos { SIGALRM, "Alarm clock" },
229dbd550edSchristos #endif
230dbd550edSchristos #ifdef SIGBUS
2318d01a27eSchristos { SIGBUS, "Bus error" },
232dbd550edSchristos #endif
233dbd550edSchristos #ifdef SIGCLD
2348d01a27eSchristos { SIGCLD, "Child exited or stopped" },
235dbd550edSchristos #endif
236dbd550edSchristos #ifdef SIGCHLD
2378d01a27eSchristos { SIGCHLD, "Child exited" },
238dbd550edSchristos #endif
239dbd550edSchristos #ifdef SIGCONT
2408d01a27eSchristos { SIGCONT, "Continued" },
241dbd550edSchristos #endif
242dbd550edSchristos #ifdef SIGDANGER
2438d01a27eSchristos { SIGDANGER, "System crash imminent" },
244dbd550edSchristos #endif
245dbd550edSchristos #ifdef SIGEMT
2468d01a27eSchristos { SIGEMT, "EMT trap" },
247dbd550edSchristos #endif
248dbd550edSchristos #ifdef SIGFPE
2498d01a27eSchristos { SIGFPE, "Floating point exception" },
250dbd550edSchristos #endif
251dbd550edSchristos #ifdef SIGGRANT
2528d01a27eSchristos { SIGGRANT, "HFT monitor mode granted" },
253dbd550edSchristos #endif
254dbd550edSchristos #ifdef SIGHUP
2558d01a27eSchristos { SIGHUP, "Hangup" },
256dbd550edSchristos #endif
257dbd550edSchristos #ifdef SIGILL
2588d01a27eSchristos { SIGILL, "Illegal instruction" },
259dbd550edSchristos #endif
260dbd550edSchristos #ifdef SIGINFO
2618d01a27eSchristos { SIGINFO, "Information request" },
262dbd550edSchristos #endif
263dbd550edSchristos #ifdef SIGINT
2648d01a27eSchristos { SIGINT, "Interrupt" },
265dbd550edSchristos #endif
266dbd550edSchristos #ifdef SIGIO
2678d01a27eSchristos { SIGIO, "I/O possible" },
268dbd550edSchristos #endif
269dbd550edSchristos #ifdef SIGIOT
2708d01a27eSchristos { SIGIOT, "IOT trap" },
271dbd550edSchristos #endif
272dbd550edSchristos #ifdef SIGKILL
2738d01a27eSchristos { SIGKILL, "Killed" },
274dbd550edSchristos #endif
275dbd550edSchristos #ifdef SIGLOST
2768d01a27eSchristos { SIGLOST, "Record lock" },
277dbd550edSchristos #endif
278dbd550edSchristos #ifdef SIGMIGRATE
2798d01a27eSchristos { SIGMIGRATE, "Migrate process to another CPU" },
280dbd550edSchristos #endif
281dbd550edSchristos #ifdef SIGMSG
2828d01a27eSchristos { SIGMSG, "HFT input data pending" },
283dbd550edSchristos #endif
284dbd550edSchristos #ifdef SIGPIPE
2858d01a27eSchristos { SIGPIPE, "Broken pipe" },
286dbd550edSchristos #endif
287dbd550edSchristos #ifdef SIGPOLL
2888d01a27eSchristos { SIGPOLL, "I/O possible" },
289dbd550edSchristos #endif
290dbd550edSchristos #ifdef SIGPRE
2918d01a27eSchristos { SIGPRE, "Programming error" },
292dbd550edSchristos #endif
293dbd550edSchristos #ifdef SIGPROF
2948d01a27eSchristos { SIGPROF, "Profiling timer expired" },
295dbd550edSchristos #endif
296dbd550edSchristos #ifdef SIGPWR
2978d01a27eSchristos { SIGPWR, "Power failure imminent" },
298dbd550edSchristos #endif
299dbd550edSchristos #ifdef SIGRETRACT
3008d01a27eSchristos { SIGRETRACT, "HFT monitor mode retracted" },
301dbd550edSchristos #endif
302dbd550edSchristos #ifdef SIGQUIT
3038d01a27eSchristos { SIGQUIT, "Quit" },
304dbd550edSchristos #endif
305dbd550edSchristos #ifdef SIGSAK
3068d01a27eSchristos { SIGSAK, "Secure Attention Key" },
307dbd550edSchristos #endif
308dbd550edSchristos #ifdef SIGSEGV
3098d01a27eSchristos { SIGSEGV, "Segmentation fault" },
310dbd550edSchristos #endif
311dbd550edSchristos #ifdef SIGSOUND
3128d01a27eSchristos { SIGSOUND, "HFT sound sequence completed" },
313dbd550edSchristos #endif
314dbd550edSchristos #ifdef SIGSTOP
3158d01a27eSchristos { SIGSTOP, "Suspended (signal)" },
316dbd550edSchristos #endif
317dbd550edSchristos #ifdef SIGSYS
3188d01a27eSchristos { SIGSYS, "Bad system call" },
319dbd550edSchristos #endif
320dbd550edSchristos #ifdef SIGTERM
3218d01a27eSchristos { SIGTERM, "Terminated" },
322dbd550edSchristos #endif
323dbd550edSchristos #ifdef SIGTRAP
3248d01a27eSchristos { SIGTRAP, "Trace/BPT trap" },
325dbd550edSchristos #endif
326dbd550edSchristos #ifdef SIGTSTP
3278d01a27eSchristos { SIGTSTP, "Suspended" },
328dbd550edSchristos #endif
329dbd550edSchristos #ifdef SIGTTIN
3308d01a27eSchristos { SIGTTIN, "Stopped (tty input)" },
331dbd550edSchristos #endif
332dbd550edSchristos #ifdef SIGTTOU
3338d01a27eSchristos { SIGTTOU, "Stopped (tty output)" },
334dbd550edSchristos #endif
335dbd550edSchristos #ifdef SIGURG
3368d01a27eSchristos { SIGURG, "Urgent I/O condition" },
337dbd550edSchristos #endif
338dbd550edSchristos #ifdef SIGUSR1
3398d01a27eSchristos { SIGUSR1, "User defined signal 1" },
340dbd550edSchristos #endif
341dbd550edSchristos #ifdef SIGUSR2
3428d01a27eSchristos { SIGUSR2, "User defined signal 2" },
343dbd550edSchristos #endif
344dbd550edSchristos #ifdef SIGVTALRM
3458d01a27eSchristos { SIGVTALRM, "Virtual timer expired" },
346dbd550edSchristos #endif
347dbd550edSchristos #ifdef SIGWINCH
3488d01a27eSchristos { SIGWINCH, "Window size changes" },
349dbd550edSchristos #endif
350dbd550edSchristos #ifdef SIGXCPU
3518d01a27eSchristos { SIGXCPU, "Cputime limit exceeded" },
352dbd550edSchristos #endif
353dbd550edSchristos #ifdef SIGXFSZ
3548d01a27eSchristos { SIGXFSZ, "Filesize limit exceeded" },
355dbd550edSchristos #endif
356dbd550edSchristos };
357dbd550edSchristos
358dbd550edSchristos /*
359dbd550edSchristos * sigmsg --
360dbd550edSchristos * Return a pointer to a message describing a signal.
361dbd550edSchristos */
362dbd550edSchristos static const char *
sigmsg(int signo)363dbd550edSchristos sigmsg(int signo)
364dbd550edSchristos {
365dbd550edSchristos static char buf[40];
366dbd550edSchristos const SIGS *sigp;
3678d01a27eSchristos size_t n;
368dbd550edSchristos
369dbd550edSchristos for (n = 0,
370dbd550edSchristos sigp = &sigs[0]; n < sizeof(sigs) / sizeof(sigs[0]); ++n, ++sigp)
371dbd550edSchristos if (sigp->number == signo)
372dbd550edSchristos return (sigp->message);
373dbd550edSchristos (void)snprintf(buf, sizeof(buf), "Unknown signal: %d", signo);
374dbd550edSchristos return (buf);
375dbd550edSchristos }
376