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