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