1 /* $OpenBSD: apply.c,v 1.27 2015/10/10 17:48:34 deraadt Exp $ */ 2 /* $NetBSD: apply.c,v 1.3 1995/03/25 03:38:23 glass Exp $ */ 3 4 /*- 5 * Copyright (c) 1994 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Jan-Simon Pendry. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/wait.h> 37 38 #include <ctype.h> 39 #include <err.h> 40 #include <paths.h> 41 #include <signal.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <unistd.h> 46 47 __dead void usage(void); 48 static int mysystem(const char *); 49 50 int 51 main(int argc, char *argv[]) 52 { 53 int ch, clen, debug, i, l, magic, n, nargs, rval; 54 char *c, *c2, *cmd, *p, *q; 55 size_t len; 56 57 if (pledge("stdio proc exec", NULL) == -1) 58 err(1, "pledge"); 59 60 debug = 0; 61 magic = '%'; /* Default magic char is `%'. */ 62 nargs = -1; 63 while ((ch = getopt(argc, argv, "a:d0123456789")) != -1) 64 switch (ch) { 65 case 'a': 66 if (optarg[1] != '\0') 67 errx(1, 68 "illegal magic character specification."); 69 magic = optarg[0]; 70 break; 71 case 'd': 72 debug = 1; 73 break; 74 case '0': case '1': case '2': case '3': case '4': 75 case '5': case '6': case '7': case '8': case '9': 76 if (nargs != -1) 77 errx(1, 78 "only one -# argument may be specified."); 79 nargs = ch - '0'; 80 break; 81 default: 82 usage(); 83 } 84 argc -= optind; 85 argv += optind; 86 87 if (argc < 2) 88 usage(); 89 90 /* 91 * The command to run is argv[0], and the args are argv[1..]. 92 * Look for %digit references in the command, remembering the 93 * largest one. 94 */ 95 for (n = 0, p = argv[0]; *p != '\0'; ++p) 96 if (p[0] == magic && 97 isdigit((unsigned char)p[1]) && p[1] != '0') { 98 ++p; 99 if (p[0] - '0' > n) 100 n = p[0] - '0'; 101 } 102 103 /* 104 * If there were any %digit references, then use those, otherwise 105 * build a new command string with sufficient %digit references at 106 * the end to consume (nargs) arguments each time round the loop. 107 * Allocate enough space to hold the maximum command. 108 */ 109 if (n == 0) { 110 len = sizeof("exec ") - 1 + 111 strlen(argv[0]) + 9 * (sizeof(" %1") - 1) + 1; 112 if ((cmd = malloc(len)) == NULL) 113 err(1, NULL); 114 115 /* If nargs not set, default to a single argument. */ 116 if (nargs == -1) 117 nargs = 1; 118 119 l = snprintf(cmd, len, "exec %s", argv[0]); 120 if (l >= len || l == -1) 121 errx(1, "error building exec string"); 122 len -= l; 123 p = cmd + l; 124 125 for (i = 1; i <= nargs; i++) { 126 l = snprintf(p, len, " %c%d", magic, i); 127 if (l >= len || l == -1) 128 errx(1, "error numbering arguments"); 129 len -= l; 130 p += l; 131 } 132 133 /* 134 * If nargs set to the special value 0, eat a single 135 * argument for each command execution. 136 */ 137 if (nargs == 0) 138 nargs = 1; 139 } else { 140 if (asprintf(&cmd, "exec %s", argv[0]) == -1) 141 err(1, NULL); 142 nargs = n; 143 } 144 145 /* 146 * Grab some space in which to build the command. Allocate 147 * as necessary later, but no reason to build it up slowly 148 * for the normal case. 149 */ 150 if ((c = malloc(clen = 1024)) == NULL) 151 err(1, NULL); 152 153 /* 154 * (argc) and (argv) are still offset by one to make it simpler to 155 * expand %digit references. At the end of the loop check for (argc) 156 * equals 1 means that all the (argv) has been consumed. 157 */ 158 for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) { 159 /* 160 * Find a max value for the command length, and ensure 161 * there's enough space to build it. 162 */ 163 for (l = strlen(cmd), i = 0; i < nargs; i++) 164 l += strlen(argv[i+1]); 165 if (l > clen) { 166 if ((c2 = realloc(c, l)) == NULL) 167 err(1, NULL); 168 c = c2; 169 clen = l; 170 } 171 172 /* Expand command argv references. */ 173 for (p = cmd, q = c; *p != '\0'; ++p) 174 if (p[0] == magic && 175 isdigit((unsigned char)p[1]) && p[1] != '0') { 176 strlcpy(q, argv[(++p)[0] - '0'], c + clen - q); 177 q += strlen(q); 178 } else 179 *q++ = *p; 180 181 /* Terminate the command string. */ 182 *q = '\0'; 183 184 /* Run the command. */ 185 if (debug) 186 (void)printf("%s\n", c); 187 else if (mysystem(c)) 188 rval = 1; 189 } 190 191 if (argc != 1) 192 errx(1, "expecting additional argument%s after \"%s\"", 193 (nargs - argc) ? "s" : "", argv[argc - 1]); 194 exit(rval); 195 } 196 197 /* 198 * mysystem -- 199 * Private version of system(3). Use the user's SHELL environment 200 * variable as the shell to execute. 201 */ 202 static int 203 mysystem(const char *command) 204 { 205 static const char *name, *shell; 206 pid_t pid; 207 int pstat; 208 sigset_t mask, omask; 209 sig_t intsave, quitsave; 210 211 if (shell == NULL) { 212 if ((shell = getenv("SHELL")) == NULL) 213 shell = _PATH_BSHELL; 214 if ((name = strrchr(shell, '/')) == NULL) 215 name = shell; 216 else 217 ++name; 218 } 219 if (!command) /* just checking... */ 220 return(1); 221 222 sigemptyset(&mask); 223 sigaddset(&mask, SIGCHLD); 224 sigprocmask(SIG_BLOCK, &mask, &omask); 225 switch(pid = fork()) { 226 case -1: /* error */ 227 err(1, "fork"); 228 case 0: /* child */ 229 sigprocmask(SIG_SETMASK, &omask, NULL); 230 execl(shell, name, "-c", command, (char *)NULL); 231 err(1, "%s", shell); 232 } 233 intsave = signal(SIGINT, SIG_IGN); 234 quitsave = signal(SIGQUIT, SIG_IGN); 235 pid = waitpid(pid, &pstat, 0); 236 sigprocmask(SIG_SETMASK, &omask, NULL); 237 (void)signal(SIGINT, intsave); 238 (void)signal(SIGQUIT, quitsave); 239 return(pid == -1 ? -1 : pstat); 240 } 241 242 __dead void 243 usage(void) 244 { 245 (void)fprintf(stderr, 246 "usage: apply [-#] [-d] [-a magic] command argument ...\n"); 247 exit(1); 248 } 249