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