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