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