Search for location of waf script
[Samba.git] / lib / util / sys_popen.c
blob709f1822f332109934fe0985abd626abe12b55d6
1 /*
2 * Unix SMB/CIFS implementation.
3 * Samba system utilities
4 * Copyright (C) Jeremy Allison 2000
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "replace.h"
21 #include "system/wait.h"
22 #include "system/filesys.h"
23 #include <talloc.h>
24 #include "lib/util/sys_popen.h"
25 #include "lib/util/debug.h"
27 /**************************************************************************
28 Extract a command into an arg list.
29 ****************************************************************************/
31 static char **extract_args(TALLOC_CTX *mem_ctx, const char *command)
33 char *trunc_cmd;
34 char *saveptr;
35 char *ptr;
36 int argcl;
37 char **argl = NULL;
38 int i;
40 if (!(trunc_cmd = talloc_strdup(mem_ctx, command))) {
41 DEBUG(0, ("talloc failed\n"));
42 goto nomem;
45 if(!(ptr = strtok_r(trunc_cmd, " \t", &saveptr))) {
46 TALLOC_FREE(trunc_cmd);
47 errno = EINVAL;
48 return NULL;
52 * Count the args.
55 for( argcl = 1; ptr; ptr = strtok_r(NULL, " \t", &saveptr))
56 argcl++;
58 TALLOC_FREE(trunc_cmd);
60 if (!(argl = talloc_array(mem_ctx, char *, argcl + 1))) {
61 goto nomem;
65 * Now do the extraction.
68 if (!(trunc_cmd = talloc_strdup(mem_ctx, command))) {
69 goto nomem;
72 ptr = strtok_r(trunc_cmd, " \t", &saveptr);
73 i = 0;
75 if (!(argl[i++] = talloc_strdup(argl, ptr))) {
76 goto nomem;
79 while((ptr = strtok_r(NULL, " \t", &saveptr)) != NULL) {
81 if (!(argl[i++] = talloc_strdup(argl, ptr))) {
82 goto nomem;
86 argl[i++] = NULL;
87 TALLOC_FREE(trunc_cmd);
88 return argl;
90 nomem:
91 DEBUG(0, ("talloc failed\n"));
92 TALLOC_FREE(trunc_cmd);
93 TALLOC_FREE(argl);
94 errno = ENOMEM;
95 return NULL;
98 /**************************************************************************
99 Wrapper for popen. Safer as it doesn't search a path.
100 Modified from the glibc sources.
101 modified by tridge to return a file descriptor. We must kick our FILE* habit
102 ****************************************************************************/
104 typedef struct _popen_list
106 int fd;
107 pid_t child_pid;
108 struct _popen_list *next;
109 } popen_list;
111 static popen_list *popen_chain;
113 int sys_popen(const char *command)
115 int parent_end, child_end;
116 int pipe_fds[2];
117 popen_list *entry = NULL;
118 char **argl = NULL;
119 int ret;
121 if (!*command) {
122 errno = EINVAL;
123 return -1;
126 ret = pipe(pipe_fds);
127 if (ret < 0) {
128 DEBUG(0, ("sys_popen: error opening pipe: %s\n",
129 strerror(errno)));
130 return -1;
133 parent_end = pipe_fds[0];
134 child_end = pipe_fds[1];
136 entry = talloc_zero(NULL, popen_list);
137 if (entry == NULL) {
138 DEBUG(0, ("sys_popen: malloc failed\n"));
139 goto err_exit;
143 * Extract the command and args into a NULL terminated array.
146 argl = extract_args(NULL, command);
147 if (argl == NULL) {
148 DEBUG(0, ("sys_popen: extract_args() failed: %s\n", strerror(errno)));
149 goto err_exit;
152 entry->child_pid = fork();
154 if (entry->child_pid == -1) {
155 DEBUG(0, ("sys_popen: fork failed: %s\n", strerror(errno)));
156 goto err_exit;
159 if (entry->child_pid == 0) {
162 * Child !
165 int child_std_end = STDOUT_FILENO;
166 popen_list *p;
168 close(parent_end);
169 if (child_end != child_std_end) {
170 dup2 (child_end, child_std_end);
171 close (child_end);
175 * POSIX.2: "popen() shall ensure that any streams from previous
176 * popen() calls that remain open in the parent process are closed
177 * in the new child process."
180 for (p = popen_chain; p; p = p->next)
181 close(p->fd);
183 ret = execv(argl[0], argl);
184 if (ret == -1) {
185 DEBUG(0, ("sys_popen: ERROR executing command "
186 "'%s': %s\n", command, strerror(errno)));
188 _exit (127);
192 * Parent.
195 close (child_end);
196 TALLOC_FREE(argl);
198 /* Link into popen_chain. */
199 entry->next = popen_chain;
200 popen_chain = entry;
201 entry->fd = parent_end;
203 return entry->fd;
205 err_exit:
207 TALLOC_FREE(entry);
208 TALLOC_FREE(argl);
209 close(pipe_fds[0]);
210 close(pipe_fds[1]);
211 return -1;
214 /**************************************************************************
215 Wrapper for pclose. Modified from the glibc sources.
216 ****************************************************************************/
218 int sys_pclose(int fd)
220 int wstatus;
221 popen_list **ptr = &popen_chain;
222 popen_list *entry = NULL;
223 pid_t wait_pid;
224 int status = -1;
226 /* Unlink from popen_chain. */
227 for ( ; *ptr != NULL; ptr = &(*ptr)->next) {
228 if ((*ptr)->fd == fd) {
229 entry = *ptr;
230 *ptr = (*ptr)->next;
231 status = 0;
232 break;
236 if (status < 0 || close(entry->fd) < 0)
237 return -1;
240 * As Samba is catching and eating child process
241 * exits we don't really care about the child exit
242 * code, a -1 with errno = ECHILD will do fine for us.
245 do {
246 wait_pid = waitpid (entry->child_pid, &wstatus, 0);
247 } while (wait_pid == -1 && errno == EINTR);
249 TALLOC_FREE(entry);
251 if (wait_pid == -1)
252 return -1;
253 return wstatus;