s4-dns: use a loadparm list for samba_runcmd() commands
[ira/wip.git] / lib / util / util_runcmd.c
1 /*
2    Unix SMB/CIFS mplementation.
3
4    run a child command
5
6    Copyright (C) Andrew Tridgell 2010
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21 */
22
23 /*
24   this runs a child command with stdout and stderr going to the Samba
25   log
26  */
27
28 #include "includes.h"
29 #include "system/filesys.h"
30 #include "lib/tevent/tevent.h"
31 #include "libcli/composite/composite.h"
32
33 struct samba_runcmd {
34         int stdout_log_level;
35         int stderr_log_level;
36         struct tevent_fd *fde_stdout;
37         struct tevent_fd *fde_stderr;
38         int fd_stdout, fd_stderr;
39         char *arg0;
40         pid_t pid;
41         char buf[1024];
42         uint16_t buf_used;
43 };
44
45 /*
46   called when a command times out
47  */
48 static void runcmd_timeout(struct tevent_context *ev,
49                            struct tevent_timer *te,
50                            struct timeval current_time,
51                            void *private_data)
52 {
53         struct composite_context *c = talloc_get_type_abort(private_data, struct composite_context);
54         struct samba_runcmd *r = talloc_get_type_abort(c->private_data, struct samba_runcmd);
55         kill(r->pid, SIGKILL);
56         waitpid(r->pid, NULL, 0);
57         talloc_free(r->fde_stderr);
58         talloc_free(r->fde_stdout);
59         composite_error(c, NT_STATUS_IO_TIMEOUT);
60 }
61
62 /*
63   handle stdout/stderr from the child
64  */
65 static void runcmd_io_handler(struct tevent_context *ev,
66                               struct tevent_fd *fde,
67                               uint16_t flags,
68                               void *private_data)
69 {
70         struct composite_context *c = talloc_get_type_abort(private_data, struct composite_context);
71         struct samba_runcmd *r = talloc_get_type_abort(c->private_data, struct samba_runcmd);
72         int level;
73         char *p;
74         int n, fd;
75
76         if (fde == r->fde_stdout) {
77                 level = r->stdout_log_level;
78                 fd = r->fd_stdout;
79         } else {
80                 level = r->stderr_log_level;
81                 fd = r->fd_stderr;
82         }
83
84         if (!(flags & TEVENT_FD_READ)) {
85                 return;
86         }
87
88         n = read(fd, &r->buf[r->buf_used],
89                  sizeof(r->buf) - r->buf_used);
90         if (n > 0) {
91                 r->buf_used += n;
92         } else if (n == 0) {
93                 if (fde == r->fde_stdout) {
94                         talloc_free(fde);
95                         r->fde_stdout = NULL;
96                 }
97                 if (fde == r->fde_stderr) {
98                         talloc_free(fde);
99                         r->fde_stderr = NULL;
100                 }
101                 if (r->fde_stdout == NULL &&
102                     r->fde_stderr == NULL) {
103                         int status;
104                         /* the child has closed both stdout and
105                          * stderr, assume its dead */
106                         pid_t pid = waitpid(r->pid, &status, 0);
107                         if (pid != r->pid) {
108                                 DEBUG(0,("Error in waitpid() for child %s\n", r->arg0));
109                                 composite_error(c, map_nt_error_from_unix(errno));
110                                 return;
111                         }
112                         status = WEXITSTATUS(status);
113                         DEBUG(3,("Child %s exited with status %d\n", r->arg0, status));
114                         if (status == 0) {
115                                 composite_done(c);
116                         } else {
117                                 composite_error(c, map_nt_error_from_unix(status));
118                         }
119                         return;
120                 }
121                 return;
122         }
123
124         while (r->buf_used > 0 &&
125                (p = memchr(r->buf, '\n', r->buf_used)) != NULL) {
126                 int n1 = (p - r->buf)+1;
127                 int n2 = n1 - 1;
128                 /* swallow \r from child processes */
129                 if (n2 > 0 && r->buf[n2-1] == '\r') {
130                         n2--;
131                 }
132                 DEBUG(level,("%s: %*.*s\n", r->arg0, n2, n2, r->buf));
133                 memmove(r->buf, p+1, sizeof(r->buf) - n1);
134                 r->buf_used -= n1;
135         }
136
137         /* the buffer could have completely filled - unfortunately we have
138            no choice but to dump it out straight away */
139         if (r->buf_used == sizeof(r->buf)) {
140                 DEBUG(level,("%s: %*.*s\n", r->arg0, r->buf_used, r->buf_used, r->buf));
141                 r->buf_used = 0;
142         }
143 }
144
145
146 /*
147   run a command as a child process, with a timeout.
148
149   any stdout/stderr from the child will appear in the Samba logs with
150   the specified log levels
151  */
152 struct composite_context *samba_runcmd(struct tevent_context *ev,
153                                        TALLOC_CTX *mem_ctx,
154                                        struct timeval timeout,
155                                        int stdout_log_level,
156                                        int stderr_log_level,
157                                        const char **argv0, ...)
158 {
159         struct samba_runcmd *r;
160         int p1[2], p2[2];
161         char **argv;
162         int ret;
163         va_list ap;
164         struct composite_context *c;
165
166         c = composite_create(mem_ctx, ev);
167         if (c == NULL) return NULL;
168
169         r = talloc_zero(c, struct samba_runcmd);
170         if (composite_nomem(r, c)) return c;
171
172         c->private_data = r;
173
174         r->stdout_log_level = stdout_log_level;
175         r->stderr_log_level = stderr_log_level;
176
177         r->arg0 = talloc_strdup(r, argv0[0]);
178         if (composite_nomem(r->arg0, c)) return c;
179
180         if (pipe(p1) != 0) {
181                 composite_error(c, map_nt_error_from_unix(errno));
182                 return c;
183         }
184         if (pipe(p2) != 0) {
185                 composite_error(c, map_nt_error_from_unix(errno));
186                 close(p1[0]);
187                 close(p1[1]);
188                 return c;
189         }
190
191         r->pid = fork();
192         if (r->pid == (pid_t)-1) {
193                 composite_error(c, map_nt_error_from_unix(errno));
194                 close(p1[0]);
195                 close(p1[1]);
196                 close(p2[0]);
197                 close(p2[1]);
198                 return c;
199         }
200
201         if (r->pid != 0) {
202                 /* the parent */
203                 close(p1[1]);
204                 close(p2[1]);
205                 r->fd_stdout = p1[0];
206                 r->fd_stderr = p2[0];
207                 set_blocking(r->fd_stdout, false);
208                 set_blocking(r->fd_stderr, false);
209                 r->fde_stdout = tevent_add_fd(ev, r, r->fd_stdout, TEVENT_FD_READ, runcmd_io_handler, c);
210                 tevent_fd_set_auto_close(r->fde_stdout);
211                 r->fde_stderr = tevent_add_fd(ev, r, r->fd_stderr, TEVENT_FD_READ, runcmd_io_handler, c);
212                 tevent_fd_set_auto_close(r->fde_stderr);
213                 if (!timeval_is_zero(&timeout)) {
214                         tevent_add_timer(ev, r, timeout, runcmd_timeout, c);
215                 }
216                 return c;
217         }
218
219         /* the child */
220         close(p1[0]);
221         close(p2[0]);
222         close(0);
223         close(1);
224         close(2);
225
226         /* setup for logging to go to the parents debug log */
227         open("/dev/null", O_RDONLY); /* for stdin */
228         dup2(p1[1], 1);
229         dup2(p2[1], 2);
230
231         argv = str_list_copy(r, argv0);
232         if (!argv) {
233                 fprintf(stderr, "Out of memory in child\n");
234                 _exit(255);
235         }
236
237         va_start(ap, argv0);
238         while (1) {
239                 char *arg = va_arg(ap, char *);
240                 if (arg == NULL) break;
241                 argv = discard_const_p(char *, str_list_add((const char **)argv, arg));
242                 if (!argv) {
243                         fprintf(stderr, "Out of memory in child\n");
244                         _exit(255);
245                 }
246         }
247         va_end(ap);
248
249         ret = execv(r->arg0, argv);
250         fprintf(stderr, "Failed to exec child - %s\n", strerror(errno));
251         _exit(255);
252         return NULL;
253 }