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