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