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