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