Annotation of src/usr.bin/make/cmd_exec.c, Revision 1.12
1.12 ! espie 1: /* $OpenBSD: cmd_exec.c,v 1.11 2020/01/16 16:07:18 espie Exp $ */
1.1 espie 2: /*
3: * Copyright (c) 2001 Marc Espie.
4: *
5: * Redistribution and use in source and binary forms, with or without
6: * modification, are permitted provided that the following conditions
7: * are met:
8: * 1. Redistributions of source code must retain the above copyright
9: * notice, this list of conditions and the following disclaimer.
10: * 2. Redistributions in binary form must reproduce the above copyright
11: * notice, this list of conditions and the following disclaimer in the
12: * documentation and/or other materials provided with the distribution.
13: *
14: * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
15: * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
18: * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25: */
26:
27: #include <sys/types.h>
28: #include <sys/wait.h>
29: #include <errno.h>
30: #include <stdio.h>
1.12 ! espie 31: #include <string.h>
1.1 espie 32: #include <unistd.h>
33: #include "config.h"
34: #include "defines.h"
35: #include "cmd_exec.h"
36: #include "buf.h"
37: #include "memory.h"
38: #include "pathnames.h"
1.11 espie 39: #include "job.h"
1.12 ! espie 40: #include "str.h"
! 41:
! 42: /* The following array is used to make a fast determination of which
! 43: * characters are interpreted specially by the shell. If a command
! 44: * contains any of these characters, it is executed by the shell, not
! 45: * directly by us. */
! 46: static char meta[256];
! 47:
! 48: void
! 49: CmdExec_Init(void)
! 50: {
! 51: char *p;
! 52:
! 53: for (p = "#=|^(){};&<>*?[]:$`\\\n~"; *p != '\0'; p++)
! 54: meta[(unsigned char) *p] = 1;
! 55: /* The null character serves as a sentinel in the string. */
! 56: meta[0] = 1;
! 57: }
! 58:
! 59: static char **
! 60: recheck_command_for_shell(char **av)
! 61: {
! 62: char *runsh[] = {
! 63: "!", "alias", "cd", "eval", "exit", "read", "set", "ulimit",
! 64: "unalias", "unset", "wait", "umask", NULL
! 65: };
! 66:
! 67: char **p;
! 68:
! 69: /* optimization: if exec cmd, we avoid the intermediate shell */
! 70: if (strcmp(av[0], "exec") == 0)
! 71: av++;
! 72:
! 73: if (!av[0])
! 74: return NULL;
! 75:
! 76: for (p = runsh; *p; p++)
! 77: if (strcmp(av[0], *p) == 0)
! 78: return NULL;
! 79:
! 80: return av;
! 81: }
! 82:
! 83: void
! 84: run_command(const char *cmd, bool errCheck)
! 85: {
! 86: const char *p;
! 87: char *shargv[4];
! 88: char **todo;
! 89:
! 90: shargv[0] = _PATH_BSHELL;
! 91:
! 92: shargv[1] = errCheck ? "-ec" : "-c";
! 93: shargv[2] = (char *)cmd;
! 94: shargv[3] = NULL;
! 95:
! 96: todo = shargv;
! 97:
! 98:
! 99: /* Search for meta characters in the command. If there are no meta
! 100: * characters, there's no need to execute a shell to execute the
! 101: * command. */
! 102: for (p = cmd; !meta[(unsigned char)*p]; p++)
! 103: continue;
! 104: if (*p == '\0') {
! 105: char *bp;
! 106: char **av;
! 107: int argc;
! 108: /* No meta-characters, so probably no need to exec a shell.
! 109: * Break the command into words to form an argument vector
! 110: * we can execute. */
! 111: av = brk_string(cmd, &argc, &bp);
! 112: av = recheck_command_for_shell(av);
! 113: if (av != NULL)
! 114: todo = av;
! 115: }
! 116: execvp(todo[0], todo);
! 117: if (errno == ENOENT)
! 118: fprintf(stderr, "%s: not found\n", todo[0]);
! 119: else
! 120: perror(todo[0]);
! 121: _exit(1);
! 122: }
1.1 espie 123:
124: char *
1.5 espie 125: Cmd_Exec(const char *cmd, char **err)
1.1 espie 126: {
1.6 espie 127: int fds[2]; /* Pipe streams */
128: pid_t cpid; /* Child PID */
129: char *result; /* Result */
130: int status; /* Command exit status */
131: BUFFER buf; /* Buffer to store the result. */
132: char *cp; /* Pointer into result. */
133: ssize_t cc; /* Characters read from pipe. */
134: size_t length; /* Total length of result. */
135:
136:
137: *err = NULL;
138:
139: /* Open a pipe for retrieving shell's output. */
140: if (pipe(fds) == -1) {
141: *err = "Couldn't create pipe for \"%s\"";
142: goto bad;
1.3 millert 143: }
1.1 espie 144:
1.6 espie 145: /* Fork */
146: switch (cpid = fork()) {
147: case 0:
1.11 espie 148: reset_signal_mask();
1.6 espie 149: /* Close input side of pipe */
150: (void)close(fds[0]);
151:
152: /* Duplicate the output stream to the shell's output, then
153: * shut the extra thing down. Note we don't fetch the error
154: * stream: user can use redirection to grab it as this goes
155: * through /bin/sh.
156: */
157: if (fds[1] != 1) {
158: (void)dup2(fds[1], 1);
159: (void)close(fds[1]);
160: }
161:
1.12 ! espie 162: run_command(cmd, false);
1.6 espie 163: /*NOTREACHED*/
164:
165: case -1:
166: *err = "Couldn't exec \"%s\"";
167: goto bad;
168:
169: default:
170: /* No need for the writing half. */
171: (void)close(fds[1]);
172:
173: Buf_Init(&buf, MAKE_BSIZE);
174:
175: do {
176: char grab[BUFSIZ];
177:
178: cc = read(fds[0], grab, sizeof(grab));
179: if (cc > 0)
180: Buf_AddChars(&buf, cc, grab);
181: } while (cc > 0 || (cc == -1 && errno == EINTR));
182:
183: /* Close the input side of the pipe. */
184: (void)close(fds[0]);
185:
186: /* Wait for the child to exit. */
1.9 espie 187: while (waitpid(cpid, &status, 0) == -1 && errno == EINTR)
1.6 espie 188: continue;
189:
190: if (cc == -1)
191: *err = "Couldn't read shell's output for \"%s\"";
192:
193: if (status)
194: *err = "\"%s\" returned non-zero status";
195:
196: length = Buf_Size(&buf);
197: result = Buf_Retrieve(&buf);
198:
199: /* The result is null terminated, Convert newlines to spaces. */
200: cp = result + length - 1;
201:
202: if (cp >= result && *cp == '\n')
203: /* A final newline is just stripped. */
204: *cp-- = '\0';
205:
206: while (cp >= result) {
207: if (*cp == '\n')
208: *cp = ' ';
209: cp--;
210: }
211: break;
1.1 espie 212: }
1.6 espie 213: return result;
214: bad:
215: return estrdup("");
1.1 espie 216: }
217: