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 13 static 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 31 static 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 */ 40 int 41 ex_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 */ 83 int 84 ex_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 */ 151 int 152 proc_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 */ 220 typedef struct _sigs { 221 int number; /* signal number */ 222 char *message; /* related message */ 223 } SIGS; 224 225 SIGS 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 */ 364 static const char * 365 sigmsg(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