1 /* $OpenBSD: ex_shell.c,v 1.11 2009/10/27 23:59:47 deraadt 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 #include <sys/param.h> 15 #include <sys/queue.h> 16 #include <sys/wait.h> 17 18 #include <bitstring.h> 19 #include <errno.h> 20 #include <limits.h> 21 #include <signal.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <unistd.h> 26 27 #include "../common/common.h" 28 29 static const char *sigmsg(int); 30 31 /* 32 * ex_shell -- :sh[ell] 33 * Invoke the program named in the SHELL environment variable 34 * with the argument -i. 35 * 36 * PUBLIC: int ex_shell(SCR *, EXCMD *); 37 */ 38 int 39 ex_shell(sp, cmdp) 40 SCR *sp; 41 EXCMD *cmdp; 42 { 43 int rval; 44 char buf[MAXPATHLEN]; 45 46 /* We'll need a shell. */ 47 if (opts_empty(sp, O_SHELL, 0)) 48 return (1); 49 50 /* 51 * XXX 52 * Assumes all shells use -i. 53 */ 54 (void)snprintf(buf, sizeof(buf), "%s -i", O_STR(sp, O_SHELL)); 55 56 /* Restore the window name. */ 57 (void)sp->gp->scr_rename(sp, NULL, 0); 58 59 /* If we're still in a vi screen, move out explicitly. */ 60 rval = ex_exec_proc(sp, cmdp, buf, NULL, !F_ISSET(sp, SC_SCR_EXWROTE)); 61 62 /* Set the window name. */ 63 (void)sp->gp->scr_rename(sp, sp->frp->name, 1); 64 65 /* 66 * !!! 67 * Historically, vi didn't require a continue message after the 68 * return of the shell. Match it. 69 */ 70 F_SET(sp, SC_EX_WAIT_NO); 71 72 return (rval); 73 } 74 75 /* 76 * ex_exec_proc -- 77 * Run a separate process. 78 * 79 * PUBLIC: int ex_exec_proc(SCR *, EXCMD *, char *, const char *, int); 80 */ 81 int 82 ex_exec_proc(sp, cmdp, cmd, msg, need_newline) 83 SCR *sp; 84 EXCMD *cmdp; 85 char *cmd; 86 const char *msg; 87 int need_newline; 88 { 89 GS *gp; 90 const char *name; 91 pid_t pid; 92 93 gp = sp->gp; 94 95 /* We'll need a shell. */ 96 if (opts_empty(sp, O_SHELL, 0)) 97 return (1); 98 99 /* Enter ex mode. */ 100 if (F_ISSET(sp, SC_VI)) { 101 if (gp->scr_screen(sp, SC_EX)) { 102 ex_emsg(sp, cmdp->cmd->name, EXM_NOCANON); 103 return (1); 104 } 105 (void)gp->scr_attr(sp, SA_ALTERNATE, 0); 106 F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE); 107 } 108 109 /* Put out additional newline, message. */ 110 if (need_newline) 111 (void)ex_puts(sp, "\n"); 112 if (msg != NULL) { 113 (void)ex_puts(sp, msg); 114 (void)ex_puts(sp, "\n"); 115 } 116 (void)ex_fflush(sp); 117 118 switch (pid = vfork()) { 119 case -1: /* Error. */ 120 msgq(sp, M_SYSERR, "vfork"); 121 return (1); 122 case 0: /* Utility. */ 123 if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL) 124 name = O_STR(sp, O_SHELL); 125 else 126 ++name; 127 execl(O_STR(sp, O_SHELL), name, "-c", cmd, (char *)NULL); 128 msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s"); 129 _exit(127); 130 /* NOTREACHED */ 131 default: /* Parent. */ 132 return (proc_wait(sp, pid, cmd, 0, 0)); 133 } 134 /* NOTREACHED */ 135 } 136 137 /* 138 * proc_wait -- 139 * Wait for one of the processes. 140 * 141 * !!! 142 * The pid_t type varies in size from a short to a long depending on the 143 * system. It has to be cast into something or the standard promotion 144 * rules get you. I'm using a long based on the belief that nobody is 145 * going to make it unsigned and it's unlikely to be a quad. 146 * 147 * PUBLIC: int proc_wait(SCR *, pid_t, const char *, int, int); 148 */ 149 int 150 proc_wait(sp, pid, cmd, silent, okpipe) 151 SCR *sp; 152 pid_t pid; 153 const char *cmd; 154 int silent, 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, &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(*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 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(*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 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 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(signo) 364 int signo; 365 { 366 static char buf[40]; 367 const SIGS *sigp; 368 int n; 369 370 for (n = 0, 371 sigp = &sigs[0]; n < sizeof(sigs) / sizeof(sigs[0]); ++n, ++sigp) 372 if (sigp->number == signo) 373 return (sigp->message); 374 (void)snprintf(buf, sizeof(buf), "Unknown signal: %d", signo); 375 return (buf); 376 } 377