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