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[] = "$Id: ex_shell.c,v 10.44 2012/07/06 06:51:26 zy Exp $"; 14 #endif /* not lint */ 15 16 #include <sys/queue.h> 17 #include <sys/time.h> 18 #include <sys/wait.h> 19 20 #include <bitstring.h> 21 #include <ctype.h> 22 #include <errno.h> 23 #include <limits.h> 24 #include <signal.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <unistd.h> 29 30 #include "../common/common.h" 31 32 static const char *sigmsg(int); 33 34 /* 35 * ex_shell -- :sh[ell] 36 * Invoke the program named in the SHELL environment variable 37 * with the argument -i. 38 * 39 * PUBLIC: int ex_shell(SCR *, EXCMD *); 40 */ 41 int 42 ex_shell(SCR *sp, EXCMD *cmdp) 43 { 44 int rval; 45 char *buf; 46 47 /* We'll need a shell. */ 48 if (opts_empty(sp, O_SHELL, 0)) 49 return (1); 50 51 /* 52 * XXX 53 * Assumes all shells use -i. 54 */ 55 (void)asprintf(&buf, "%s -i", O_STR(sp, O_SHELL)); 56 if (buf == NULL) { 57 msgq(sp, M_SYSERR, NULL); 58 return (1); 59 } 60 61 /* Restore the window name. */ 62 (void)sp->gp->scr_rename(sp, NULL, 0); 63 64 /* If we're still in a vi screen, move out explicitly. */ 65 rval = ex_exec_proc(sp, cmdp, buf, NULL, !F_ISSET(sp, SC_SCR_EXWROTE)); 66 free(buf); 67 68 /* Set the window name. */ 69 (void)sp->gp->scr_rename(sp, sp->frp->name, 1); 70 71 /* 72 * !!! 73 * Historically, vi didn't require a continue message after the 74 * return of the shell. Match it. 75 */ 76 F_SET(sp, SC_EX_WAIT_NO); 77 78 return (rval); 79 } 80 81 /* 82 * ex_exec_proc -- 83 * Run a separate process. 84 * 85 * PUBLIC: int ex_exec_proc(SCR *, EXCMD *, char *, const char *, int); 86 */ 87 int 88 ex_exec_proc(SCR *sp, EXCMD *cmdp, char *cmd, const char *msg, int need_newline) 89 { 90 GS *gp; 91 const char *name; 92 pid_t pid; 93 94 gp = sp->gp; 95 96 /* We'll need a shell. */ 97 if (opts_empty(sp, O_SHELL, 0)) 98 return (1); 99 100 /* Enter ex mode. */ 101 if (F_ISSET(sp, SC_VI)) { 102 if (gp->scr_screen(sp, SC_EX)) { 103 ex_wemsg(sp, cmdp->cmd->name, EXM_NOCANON); 104 return (1); 105 } 106 (void)gp->scr_attr(sp, SA_ALTERNATE, 0); 107 F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE); 108 } 109 110 /* Put out additional newline, message. */ 111 if (need_newline) 112 (void)ex_puts(sp, "\n"); 113 if (msg != NULL) { 114 (void)ex_puts(sp, msg); 115 (void)ex_puts(sp, "\n"); 116 } 117 (void)ex_fflush(sp); 118 119 switch (pid = vfork()) { 120 case -1: /* Error. */ 121 msgq(sp, M_SYSERR, "vfork"); 122 return (1); 123 case 0: /* Utility. */ 124 if (gp->scr_child) 125 gp->scr_child(sp); 126 if ((name = strrchr(O_STR(sp, O_SHELL), '/')) == NULL) 127 name = O_STR(sp, O_SHELL); 128 else 129 ++name; 130 execl(O_STR(sp, O_SHELL), name, "-c", cmd, (char *)NULL); 131 msgq_str(sp, M_SYSERR, O_STR(sp, O_SHELL), "execl: %s"); 132 _exit(127); 133 /* NOTREACHED */ 134 default: /* Parent. */ 135 return (proc_wait(sp, (long)pid, cmd, 0, 0)); 136 } 137 /* NOTREACHED */ 138 } 139 140 /* 141 * proc_wait -- 142 * Wait for one of the processes. 143 * 144 * !!! 145 * The pid_t type varies in size from a short to a long depending on the 146 * system. It has to be cast into something or the standard promotion 147 * rules get you. I'm using a long based on the belief that nobody is 148 * going to make it unsigned and it's unlikely to be a quad. 149 * 150 * PUBLIC: int proc_wait(SCR *, long, const char *, int, int); 151 */ 152 int 153 proc_wait(SCR *sp, long int pid, const char *cmd, int silent, int okpipe) 154 { 155 size_t len; 156 int nf, pstat; 157 char *p; 158 159 /* Wait for the utility, ignoring interruptions. */ 160 for (;;) { 161 errno = 0; 162 if (waitpid((pid_t)pid, &pstat, 0) != -1) 163 break; 164 if (errno != EINTR) { 165 msgq(sp, M_SYSERR, "waitpid"); 166 return (1); 167 } 168 } 169 170 /* 171 * Display the utility's exit status. Ignore SIGPIPE from the 172 * parent-writer, as that only means that the utility chose to 173 * exit before reading all of its input. 174 */ 175 if (WIFSIGNALED(pstat) && (!okpipe || WTERMSIG(pstat) != SIGPIPE)) { 176 for (; cmdskip(*cmd); ++cmd); 177 p = msg_print(sp, cmd, &nf); 178 len = strlen(p); 179 msgq(sp, M_ERR, "%.*s%s: received signal: %s%s", 180 (int)MIN(len, 20), p, len > 20 ? " ..." : "", 181 sigmsg(WTERMSIG(pstat)), 182 WCOREDUMP(pstat) ? "; core dumped" : ""); 183 if (nf) 184 FREE_SPACE(sp, p, 0); 185 return (1); 186 } 187 188 if (WIFEXITED(pstat) && WEXITSTATUS(pstat)) { 189 /* 190 * Remain silent for "normal" errors when doing shell file 191 * name expansions, they almost certainly indicate nothing 192 * more than a failure to match. 193 * 194 * Remain silent for vi read filter errors. It's historic 195 * practice. 196 */ 197 if (!silent) { 198 for (; cmdskip(*cmd); ++cmd); 199 p = msg_print(sp, cmd, &nf); 200 len = strlen(p); 201 msgq(sp, M_ERR, "%.*s%s: exited with status %d", 202 (int)MIN(len, 20), p, len > 20 ? " ..." : "", 203 WEXITSTATUS(pstat)); 204 if (nf) 205 FREE_SPACE(sp, p, 0); 206 } 207 return (1); 208 } 209 return (0); 210 } 211 212 /* 213 * sigmsg -- 214 * Return a pointer to a message describing a signal. 215 */ 216 static const char * 217 sigmsg(int signo) 218 { 219 static char buf[40]; 220 char *message; 221 222 /* POSIX.1-2008 leaves strsignal(3)'s return value unspecified. */ 223 if ((message = strsignal(signo)) != NULL) 224 return message; 225 (void)snprintf(buf, sizeof(buf), "Unknown signal: %d", signo); 226 return (buf); 227 } 228