Home | History | Annotate | Line # | Download | only in ex
      1 /*	$NetBSD: ex_shell.c,v 1.3 2014/01/26 21:43:45 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 #include <sys/cdefs.h>
     14 #if 0
     15 #ifndef lint
     16 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 ";
     17 #endif /* not lint */
     18 #else
     19 __RCSID("$NetBSD: ex_shell.c,v 1.3 2014/01/26 21:43:45 christos Exp $");
     20 #endif
     21 
     22 #include <sys/param.h>
     23 #include <sys/queue.h>
     24 #include <sys/wait.h>
     25 
     26 #include <bitstring.h>
     27 #include <ctype.h>
     28 #include <errno.h>
     29 #include <limits.h>
     30 #include <signal.h>
     31 #include <stdio.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <unistd.h>
     35 
     36 #include "../common/common.h"
     37 
     38 static const char *sigmsg __P((int));
     39 
     40 /*
     41  * ex_shell -- :sh[ell]
     42  *	Invoke the program named in the SHELL environment variable
     43  *	with the argument -i.
     44  *
     45  * PUBLIC: int ex_shell __P((SCR *, EXCMD *));
     46  */
     47 int
     48 ex_shell(SCR *sp, EXCMD *cmdp)
     49 {
     50 	int rval;
     51 	char buf[MAXPATHLEN];
     52 
     53 	/* We'll need a shell. */
     54 	if (opts_empty(sp, O_SHELL, 0))
     55 		return (1);
     56 
     57 	/*
     58 	 * XXX
     59 	 * Assumes all shells use -i.
     60 	 */
     61 	(void)snprintf(buf, sizeof(buf), "%s -i", O_STR(sp, O_SHELL));
     62 
     63 	/* Restore the window name. */
     64 	(void)sp->gp->scr_rename(sp, NULL, 0);
     65 
     66 	/* If we're still in a vi screen, move out explicitly. */
     67 	rval = ex_exec_proc(sp, cmdp, buf, NULL, !F_ISSET(sp, SC_SCR_EXWROTE));
     68 
     69 	/* Set the window name. */
     70 	(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
     71 
     72 	/*
     73 	 * !!!
     74 	 * Historically, vi didn't require a continue message after the
     75 	 * return of the shell.  Match it.
     76 	 */
     77 	F_SET(sp, SC_EX_WAIT_NO);
     78 
     79 	return (rval);
     80 }
     81 
     82 /*
     83  * ex_exec_proc --
     84  *	Run a separate process.
     85  *
     86  * PUBLIC: int ex_exec_proc __P((SCR *, EXCMD *, const char *, const char *, int));
     87  */
     88 int
     89 ex_exec_proc(SCR *sp, EXCMD *cmdp, const char *cmd, const char *msg, 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_wemsg(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 (gp->scr_child)
    126 			gp->scr_child(sp);
    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, (long)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 __P((SCR *, long, const char *, int, int));
    152  */
    153 int
    154 proc_wait(SCR *sp, long int pid, const char *cmd, int silent, int 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_t)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((unsigned char)*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 		    (int)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((unsigned char)*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 			    (int)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 	const 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(int signo)
    364 {
    365 	static char buf[40];
    366 	const SIGS *sigp;
    367 	size_t n;
    368 
    369 	for (n = 0,
    370 	    sigp = &sigs[0]; n < sizeof(sigs) / sizeof(sigs[0]); ++n, ++sigp)
    371 		if (sigp->number == signo)
    372 			return (sigp->message);
    373 	(void)snprintf(buf, sizeof(buf), "Unknown signal: %d", signo);
    374 	return (buf);
    375 }
    376