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