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