xref: /openbsd/usr.bin/vi/ex/ex_shell.c (revision 3d8817e4)
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
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
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
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 *
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