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