Annotation of src/usr.bin/make/cmd_exec.c, Revision 1.13
1.13 ! espie 1: /* $OpenBSD: cmd_exec.c,v 1.12 2023/08/31 06:53:28 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 "defines.h"
34: #include "cmd_exec.h"
35: #include "buf.h"
36: #include "memory.h"
37: #include "pathnames.h"
1.11 espie 38: #include "job.h"
1.12 espie 39: #include "str.h"
40:
41: /* The following array is used to make a fast determination of which
42: * characters are interpreted specially by the shell. If a command
43: * contains any of these characters, it is executed by the shell, not
44: * directly by us. */
45: static char meta[256];
46:
47: void
48: CmdExec_Init(void)
49: {
50: char *p;
51:
52: for (p = "#=|^(){};&<>*?[]:$`\\\n~"; *p != '\0'; p++)
53: meta[(unsigned char) *p] = 1;
54: /* The null character serves as a sentinel in the string. */
55: meta[0] = 1;
56: }
57:
58: static char **
59: recheck_command_for_shell(char **av)
60: {
61: char *runsh[] = {
62: "!", "alias", "cd", "eval", "exit", "read", "set", "ulimit",
63: "unalias", "unset", "wait", "umask", NULL
64: };
65:
66: char **p;
67:
68: /* optimization: if exec cmd, we avoid the intermediate shell */
69: if (strcmp(av[0], "exec") == 0)
70: av++;
71:
72: if (!av[0])
73: return NULL;
74:
75: for (p = runsh; *p; p++)
76: if (strcmp(av[0], *p) == 0)
77: return NULL;
78:
79: return av;
80: }
81:
82: void
83: run_command(const char *cmd, bool errCheck)
84: {
85: const char *p;
86: char *shargv[4];
87: char **todo;
88:
89: shargv[0] = _PATH_BSHELL;
90:
91: shargv[1] = errCheck ? "-ec" : "-c";
92: shargv[2] = (char *)cmd;
93: shargv[3] = NULL;
94:
95: todo = shargv;
96:
97:
98: /* Search for meta characters in the command. If there are no meta
99: * characters, there's no need to execute a shell to execute the
100: * command. */
101: for (p = cmd; !meta[(unsigned char)*p]; p++)
102: continue;
103: if (*p == '\0') {
104: char *bp;
105: char **av;
106: int argc;
107: /* No meta-characters, so probably no need to exec a shell.
108: * Break the command into words to form an argument vector
109: * we can execute. */
110: av = brk_string(cmd, &argc, &bp);
111: av = recheck_command_for_shell(av);
112: if (av != NULL)
113: todo = av;
114: }
115: execvp(todo[0], todo);
116: if (errno == ENOENT)
117: fprintf(stderr, "%s: not found\n", todo[0]);
118: else
119: perror(todo[0]);
120: _exit(1);
121: }
1.1 espie 122:
123: char *
1.5 espie 124: Cmd_Exec(const char *cmd, char **err)
1.1 espie 125: {
1.6 espie 126: int fds[2]; /* Pipe streams */
127: pid_t cpid; /* Child PID */
128: char *result; /* Result */
129: int status; /* Command exit status */
130: BUFFER buf; /* Buffer to store the result. */
131: char *cp; /* Pointer into result. */
132: ssize_t cc; /* Characters read from pipe. */
133: size_t length; /* Total length of result. */
134:
135:
136: *err = NULL;
137:
138: /* Open a pipe for retrieving shell's output. */
139: if (pipe(fds) == -1) {
140: *err = "Couldn't create pipe for \"%s\"";
141: goto bad;
1.3 millert 142: }
1.1 espie 143:
1.6 espie 144: /* Fork */
145: switch (cpid = fork()) {
146: case 0:
1.11 espie 147: reset_signal_mask();
1.6 espie 148: /* Close input side of pipe */
149: (void)close(fds[0]);
150:
151: /* Duplicate the output stream to the shell's output, then
152: * shut the extra thing down. Note we don't fetch the error
153: * stream: user can use redirection to grab it as this goes
154: * through /bin/sh.
155: */
156: if (fds[1] != 1) {
157: (void)dup2(fds[1], 1);
158: (void)close(fds[1]);
159: }
160:
1.12 espie 161: run_command(cmd, false);
1.6 espie 162: /*NOTREACHED*/
163:
164: case -1:
165: *err = "Couldn't exec \"%s\"";
166: goto bad;
167:
168: default:
169: /* No need for the writing half. */
170: (void)close(fds[1]);
171:
172: Buf_Init(&buf, MAKE_BSIZE);
173:
174: do {
175: char grab[BUFSIZ];
176:
177: cc = read(fds[0], grab, sizeof(grab));
178: if (cc > 0)
179: Buf_AddChars(&buf, cc, grab);
180: } while (cc > 0 || (cc == -1 && errno == EINTR));
181:
182: /* Close the input side of the pipe. */
183: (void)close(fds[0]);
184:
185: /* Wait for the child to exit. */
1.9 espie 186: while (waitpid(cpid, &status, 0) == -1 && errno == EINTR)
1.6 espie 187: continue;
188:
189: if (cc == -1)
190: *err = "Couldn't read shell's output for \"%s\"";
191:
192: if (status)
193: *err = "\"%s\" returned non-zero status";
194:
195: length = Buf_Size(&buf);
196: result = Buf_Retrieve(&buf);
197:
198: /* The result is null terminated, Convert newlines to spaces. */
199: cp = result + length - 1;
200:
201: if (cp >= result && *cp == '\n')
202: /* A final newline is just stripped. */
203: *cp-- = '\0';
204:
205: while (cp >= result) {
206: if (*cp == '\n')
207: *cp = ' ';
208: cp--;
209: }
210: break;
1.1 espie 211: }
1.6 espie 212: return result;
213: bad:
214: return estrdup("");
1.1 espie 215: }
216: