lib: Fix CID 1445648 Null pointer dereferences
[metze/samba/wip.git] / lib / util / sys_popen.c
1 /*
2  * Unix SMB/CIFS implementation.
3  *  Samba system utilities
4  * Copyright (C) Jeremy Allison  2000
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
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"
26
27 /**************************************************************************
28  Wrapper for popen. Safer as it doesn't search a path.
29  Modified from the glibc sources.
30  modified by tridge to return a file descriptor. We must kick our FILE* habit
31 ****************************************************************************/
32
33 typedef struct _popen_list
34 {
35         int fd;
36         pid_t child_pid;
37         struct _popen_list *next;
38 } popen_list;
39
40 static popen_list *popen_chain;
41
42 int sys_popenv(char * const argl[])
43 {
44         int parent_end, child_end;
45         int pipe_fds[2];
46         popen_list *entry = NULL;
47         const char *command = NULL;
48         int ret;
49
50         if (argl == NULL) {
51                 errno = EINVAL;
52                 return -1;
53         }
54         command = argl[0];
55
56         if (!*command) {
57                 errno = EINVAL;
58                 return -1;
59         }
60
61         ret = pipe(pipe_fds);
62         if (ret < 0) {
63                 DBG_ERR("error opening pipe: %s\n",
64                           strerror(errno));
65                 return -1;
66         }
67
68         parent_end = pipe_fds[0];
69         child_end = pipe_fds[1];
70
71         entry = talloc_zero(NULL, popen_list);
72         if (entry == NULL) {
73                 DBG_ERR("talloc failed\n");
74                 goto err_exit;
75         }
76
77         entry->child_pid = fork();
78
79         if (entry->child_pid == -1) {
80                 DBG_ERR("fork failed: %s\n", strerror(errno));
81                 goto err_exit;
82         }
83
84         if (entry->child_pid == 0) {
85
86                 /*
87                  * Child !
88                  */
89
90                 int child_std_end = STDOUT_FILENO;
91                 popen_list *p;
92
93                 close(parent_end);
94                 if (child_end != child_std_end) {
95                         dup2 (child_end, child_std_end);
96                         close (child_end);
97                 }
98
99                 /*
100                  * POSIX.2:  "popen() shall ensure that any streams from previous
101                  * popen() calls that remain open in the parent process are closed
102                  * in the new child process."
103                  */
104
105                 for (p = popen_chain; p; p = p->next)
106                         close(p->fd);
107
108                 ret = execv(argl[0], argl);
109                 if (ret == -1) {
110                         DBG_ERR("ERROR executing command "
111                           "'%s': %s\n", command, strerror(errno));
112                 }
113                 _exit (127);
114         }
115
116         /*
117          * Parent.
118          */
119
120         close (child_end);
121
122         /* Link into popen_chain. */
123         entry->next = popen_chain;
124         popen_chain = entry;
125         entry->fd = parent_end;
126
127         return entry->fd;
128
129 err_exit:
130
131         TALLOC_FREE(entry);
132         close(pipe_fds[0]);
133         close(pipe_fds[1]);
134         return -1;
135 }
136
137 /**************************************************************************
138  Wrapper for pclose. Modified from the glibc sources.
139 ****************************************************************************/
140
141 int sys_pclose(int fd)
142 {
143         int wstatus;
144         popen_list **ptr = &popen_chain;
145         popen_list *entry = NULL;
146         pid_t wait_pid;
147         int status = -1;
148
149         /* Unlink from popen_chain. */
150         for ( ; *ptr != NULL; ptr = &(*ptr)->next) {
151                 if ((*ptr)->fd == fd) {
152                         entry = *ptr;
153                         *ptr = (*ptr)->next;
154                         status = 0;
155                         break;
156                 }
157         }
158
159         if (status < 0 || close(entry->fd) < 0)
160                 return -1;
161
162         /*
163          * As Samba is catching and eating child process
164          * exits we don't really care about the child exit
165          * code, a -1 with errno = ECHILD will do fine for us.
166          */
167
168         do {
169                 wait_pid = waitpid (entry->child_pid, &wstatus, 0);
170         } while (wait_pid == -1 && errno == EINTR);
171
172         TALLOC_FREE(entry);
173
174         if (wait_pid == -1)
175                 return -1;
176         return wstatus;
177 }