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