xref: /openbsd/usr.bin/make/cmd_exec.c (revision d485f761)
1 /*	$OpenPackages$ */
2 /*	$OpenBSD: cmd_exec.c,v 1.1 2001/05/23 12:34:40 espie Exp $ */
3 /*
4  * Copyright (c) 2001 Marc Espie.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
16  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OPENBSD
19  * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <sys/types.h>
29 #include <sys/wait.h>
30 #include <errno.h>
31 #include <stdio.h>
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"
39 
40 char *
41 Cmd_Exec(cmd, err)
42     const char	*cmd;
43     char	**err;
44 {
45     char	*args[4];	/* Args for invoking the shell */
46     int 	fds[2]; 	/* Pipe streams */
47     pid_t 	cpid;		/* Child PID */
48     pid_t 	pid;		/* PID from wait() */
49     char	*result;	/* Result */
50     int 	status; 	/* Command exit status */
51     BUFFER	buf;		/* Buffer to store the result. */
52     char	*cp;		/* Pointer into result. */
53     ssize_t	cc;		/* Characters read from pipe. */
54     size_t	length;		/* Total length of result. */
55 
56 
57     *err = NULL;
58 
59     /* Set up arguments for the shell. */
60     args[0] = "sh";
61     args[1] = "-c";
62     args[2] = (char *)cmd;
63     args[3] = NULL;
64 
65     /* Open a pipe for retrieving shell's output. */
66     if (pipe(fds) == -1) {
67 	*err = "Couldn't create pipe for \"%s\"";
68 	goto bad;
69     }
70 
71     /* Fork */
72     switch (cpid = vfork()) {
73     case 0:
74 	/* Close input side of pipe */
75 	(void)close(fds[0]);
76 
77 	/* Duplicate the output stream to the shell's output, then
78 	 * shut the extra thing down. Note we don't fetch the error
79 	 * stream: user can use redirection to grab it as this goes
80 	 * through /bin/sh.
81 	 */
82 	(void)dup2(fds[1], 1);
83 	if (fds[1] != 1)
84 	    (void)close(fds[1]);
85 
86 	(void)execv(_PATH_BSHELL, args);
87 	_exit(1);
88 	/*NOTREACHED*/
89 
90     case -1:
91 	*err = "Couldn't exec \"%s\"";
92 	goto bad;
93 
94     default:
95 	/* No need for the writing half. */
96 	(void)close(fds[1]);
97 
98 	Buf_Init(&buf, MAKE_BSIZE);
99 
100 	do {
101 	    char   grab[BUFSIZ];
102 
103 	    cc = read(fds[0], grab, sizeof(grab));
104 	    if (cc > 0)
105 		Buf_AddChars(&buf, cc, grab);
106 	}
107 	while (cc > 0 || (cc == -1 && errno == EINTR));
108 
109 	/* Close the input side of the pipe.  */
110 	(void)close(fds[0]);
111 
112 	/* Wait for the child to exit.  */
113 	while ((pid = wait(&status)) != cpid && pid >= 0)
114 	    continue;
115 
116 	if (cc == -1)
117 	    *err = "Couldn't read shell's output for \"%s\"";
118 
119 	if (status)
120 	    *err = "\"%s\" returned non-zero status";
121 
122 	length = Buf_Size(&buf);
123 	result = Buf_Retrieve(&buf);
124 
125 	/* The result is null terminated, Convert newlines to spaces. */
126 	cp = result + length - 1;
127 
128 	if (*cp == '\n')
129 	    /* A final newline is just stripped.  */
130 	    *cp-- = '\0';
131 
132 	while (cp >= result) {
133 	    if (*cp == '\n')
134 		*cp = ' ';
135 	    cp--;
136 	}
137 	break;
138     }
139     return result;
140 bad:
141     return estrdup("");
142 }
143 
144