Merge citrix branch into master.
[sliver-openvswitch.git] / ofproto / executer.c
1 /*
2  * Copyright (c) 2008, 2009 Nicira Networks.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <config.h>
18 #include "executer.h"
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <fnmatch.h>
22 #include <poll.h>
23 #include <signal.h>
24 #include <stdlib.h>
25 #include <sys/stat.h>
26 #include <sys/wait.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include "dirs.h"
30 #include "dynamic-string.h"
31 #include "fatal-signal.h"
32 #include "openflow/nicira-ext.h"
33 #include "ofpbuf.h"
34 #include "openflow/openflow.h"
35 #include "poll-loop.h"
36 #include "rconn.h"
37 #include "socket-util.h"
38 #include "util.h"
39 #include "vconn.h"
40
41 #define THIS_MODULE VLM_executer
42 #include "vlog.h"
43
44 #define MAX_CHILDREN 8
45
46 struct child {
47     /* Information about child process. */
48     char *name;                 /* argv[0] passed to child. */
49     pid_t pid;                  /* Child's process ID. */
50
51     /* For sending a reply to the controller when the child dies. */
52     struct rconn *rconn;
53     uint32_t xid;               /* Transaction ID used by controller. */
54
55     /* We read up to MAX_OUTPUT bytes of output and send them back to the
56      * controller when the child dies. */
57 #define MAX_OUTPUT 4096
58     int output_fd;              /* FD from which to read child's output. */
59     uint8_t *output;            /* Output data. */
60     size_t output_size;         /* Number of bytes of output data so far. */
61 };
62
63 struct executer {
64     /* Settings. */
65     char *command_acl;          /* Command white/blacklist, as shell globs. */
66     char *command_dir;          /* Directory that contains commands. */
67
68     /* Children. */
69     struct child children[MAX_CHILDREN];
70     size_t n_children;
71 };
72
73 /* File descriptors for waking up when a child dies. */
74 static int signal_fds[2] = {-1, -1};
75
76 static void send_child_status(struct rconn *, uint32_t xid, uint32_t status,
77                               const void *data, size_t size);
78 static void send_child_message(struct rconn *, uint32_t xid, uint32_t status,
79                                const char *message);
80
81 /* Returns true if 'cmd' is allowed by 'acl', which is a command-separated
82  * access control list in the format described for --command-acl in
83  * ovs-openflowd(8). */
84 static bool
85 executer_is_permitted(const char *acl_, const char *cmd)
86 {
87     char *acl, *save_ptr, *pattern;
88     bool allowed, denied;
89
90     /* Verify that 'cmd' consists only of alphanumerics plus _ or -. */
91     if (cmd[strspn(cmd, "abcdefghijklmnopqrstuvwxyz"
92                    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-")] != '\0') {
93         VLOG_WARN("rejecting command name \"%s\" that contain forbidden "
94                   "characters", cmd);
95         return false;
96     }
97
98     /* Check 'cmd' against 'acl'. */
99     acl = xstrdup(acl_);
100     save_ptr = acl;
101     allowed = denied = false;
102     while ((pattern = strsep(&save_ptr, ",")) != NULL && !denied) {
103         if (pattern[0] != '!' && !fnmatch(pattern, cmd, 0)) {
104             allowed = true;
105         } else if (pattern[0] == '!' && !fnmatch(pattern + 1, cmd, 0)) {
106             denied = true;
107         }
108     }
109     free(acl);
110
111     /* Check the command white/blacklisted state. */
112     if (allowed && !denied) {
113         VLOG_INFO("permitting command execution: \"%s\" is whitelisted", cmd);
114     } else if (allowed && denied) {
115         VLOG_WARN("denying command execution: \"%s\" is both blacklisted "
116                   "and whitelisted", cmd);
117     } else if (!allowed) {
118         VLOG_WARN("denying command execution: \"%s\" is not whitelisted", cmd);
119     } else if (denied) {
120         VLOG_WARN("denying command execution: \"%s\" is blacklisted", cmd);
121     }
122     return allowed && !denied;
123 }
124
125 int
126 executer_handle_request(struct executer *e, struct rconn *rconn,
127                         struct nicira_header *request)
128 {
129     char **argv;
130     char *args;
131     char *exec_file = NULL;
132     int max_fds;
133     struct stat s;
134     size_t args_size;
135     size_t argc;
136     size_t i;
137     pid_t pid;
138     int output_fds[2];
139
140     /* Verify limit on children not exceeded.
141      * XXX should probably kill children when the connection drops? */
142     if (e->n_children >= MAX_CHILDREN) {
143         send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
144                            "too many child processes");
145         return 0;
146     }
147
148     /* Copy argument buffer, adding a null terminator at the end.  Now every
149      * argument is null-terminated, instead of being merely null-delimited. */
150     args_size = ntohs(request->header.length) - sizeof *request;
151     args = xmemdup0((const void *) (request + 1), args_size);
152
153     /* Count arguments. */
154     argc = 0;
155     for (i = 0; i <= args_size; i++) {
156         argc += args[i] == '\0';
157     }
158
159     /* Set argv[*] to point to each argument. */
160     argv = xmalloc((argc + 1) * sizeof *argv);
161     argv[0] = args;
162     for (i = 1; i < argc; i++) {
163         argv[i] = strchr(argv[i - 1], '\0') + 1;
164     }
165     argv[argc] = NULL;
166
167     /* Check permissions. */
168     if (!executer_is_permitted(e->command_acl, argv[0])) {
169         send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
170                            "command not allowed");
171         goto done;
172     }
173
174     /* Find the executable. */
175     exec_file = xasprintf("%s/%s", e->command_dir, argv[0]);
176     if (stat(exec_file, &s)) {
177         VLOG_WARN("failed to stat \"%s\": %s", exec_file, strerror(errno));
178         send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
179                            "command not allowed");
180         goto done;
181     }
182     if (!S_ISREG(s.st_mode)) {
183         VLOG_WARN("\"%s\" is not a regular file", exec_file);
184         send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
185                            "command not allowed");
186         goto done;
187     }
188     argv[0] = exec_file;
189
190     /* Arrange to capture output. */
191     if (pipe(output_fds)) {
192         VLOG_WARN("pipe failed: %s", strerror(errno));
193         send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
194                            "internal error (pipe)");
195         goto done;
196     }
197
198     pid = fork();
199     if (!pid) {
200         /* Running in child.
201          * XXX should run in new process group so that we can signal all
202          * subprocesses at once?  Would also want to catch fatal signals and
203          * kill them at the same time though. */
204         fatal_signal_fork();
205         dup2(get_null_fd(), 0);
206         dup2(output_fds[1], 1);
207         dup2(get_null_fd(), 2);
208         max_fds = get_max_fds();
209         for (i = 3; i < max_fds; i++) {
210             close(i);
211         }
212         if (chdir(e->command_dir)) {
213             printf("could not change directory to \"%s\": %s",
214                    e->command_dir, strerror(errno));
215             exit(EXIT_FAILURE);
216         }
217         execv(argv[0], argv);
218         printf("failed to start \"%s\": %s\n", argv[0], strerror(errno));
219         exit(EXIT_FAILURE);
220     } else if (pid > 0) {
221         /* Running in parent. */
222         struct child *child;
223
224         VLOG_INFO("started \"%s\" subprocess", argv[0]);
225         send_child_status(rconn, request->header.xid, NXT_STATUS_STARTED,
226                           NULL, 0);
227         child = &e->children[e->n_children++];
228         child->name = xstrdup(argv[0]);
229         child->pid = pid;
230         child->rconn = rconn;
231         child->xid = request->header.xid;
232         child->output_fd = output_fds[0];
233         child->output = xmalloc(MAX_OUTPUT);
234         child->output_size = 0;
235         set_nonblocking(output_fds[0]);
236         close(output_fds[1]);
237     } else {
238         VLOG_WARN("fork failed: %s", strerror(errno));
239         send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
240                            "internal error (fork)");
241         close(output_fds[0]);
242         close(output_fds[1]);
243     }
244
245 done:
246     free(exec_file);
247     free(args);
248     free(argv);
249     return 0;
250 }
251
252 static void
253 send_child_status(struct rconn *rconn, uint32_t xid, uint32_t status,
254                   const void *data, size_t size)
255 {
256     if (rconn) {
257         struct nx_command_reply *r;
258         struct ofpbuf *buffer;
259
260         r = make_openflow_xid(sizeof *r, OFPT_VENDOR, xid, &buffer);
261         r->nxh.vendor = htonl(NX_VENDOR_ID);
262         r->nxh.subtype = htonl(NXT_COMMAND_REPLY);
263         r->status = htonl(status);
264         ofpbuf_put(buffer, data, size);
265         update_openflow_length(buffer);
266         if (rconn_send(rconn, buffer, NULL)) {
267             ofpbuf_delete(buffer);
268         }
269     }
270 }
271
272 static void
273 send_child_message(struct rconn *rconn, uint32_t xid, uint32_t status,
274                    const char *message)
275 {
276     send_child_status(rconn, xid, status, message, strlen(message));
277 }
278
279 /* 'child' died with 'status' as its return code.  Deal with it. */
280 static void
281 child_terminated(struct child *child, int status)
282 {
283     struct ds ds;
284     uint32_t ofp_status;
285
286     /* Log how it terminated. */
287     ds_init(&ds);
288     if (WIFEXITED(status)) {
289         ds_put_format(&ds, "normally with status %d", WEXITSTATUS(status));
290     } else if (WIFSIGNALED(status)) {
291         const char *name = NULL;
292 #ifdef HAVE_STRSIGNAL
293         name = strsignal(WTERMSIG(status));
294 #endif
295         ds_put_format(&ds, "by signal %d", WTERMSIG(status));
296         if (name) {
297             ds_put_format(&ds, " (%s)", name);
298         }
299     }
300     if (WCOREDUMP(status)) {
301         ds_put_cstr(&ds, " (core dumped)");
302     }
303     VLOG_INFO("child process \"%s\" with pid %ld terminated %s",
304               child->name, (long int) child->pid, ds_cstr(&ds));
305     ds_destroy(&ds);
306
307     /* Send a status message back to the controller that requested the
308      * command. */
309     if (WIFEXITED(status)) {
310         ofp_status = WEXITSTATUS(status) | NXT_STATUS_EXITED;
311     } else if (WIFSIGNALED(status)) {
312         ofp_status = WTERMSIG(status) | NXT_STATUS_SIGNALED;
313     } else {
314         ofp_status = NXT_STATUS_UNKNOWN;
315     }
316     if (WCOREDUMP(status)) {
317         ofp_status |= NXT_STATUS_COREDUMP;
318     }
319     send_child_status(child->rconn, child->xid, ofp_status,
320                       child->output, child->output_size);
321 }
322
323 /* Read output from 'child' and append it to its output buffer. */
324 static void
325 poll_child(struct child *child)
326 {
327     ssize_t n;
328
329     if (child->output_fd < 0) {
330         return;
331     }
332
333     do {
334         n = read(child->output_fd, child->output + child->output_size,
335                  MAX_OUTPUT - child->output_size);
336     } while (n < 0 && errno == EINTR);
337     if (n > 0) {
338         child->output_size += n;
339         if (child->output_size < MAX_OUTPUT) {
340             return;
341         }
342     } else if (n < 0 && errno == EAGAIN) {
343         return;
344     }
345     close(child->output_fd);
346     child->output_fd = -1;
347 }
348
349 void
350 executer_run(struct executer *e)
351 {
352     char buffer[MAX_CHILDREN];
353     size_t i;
354
355     if (!e->n_children) {
356         return;
357     }
358
359     /* Read output from children. */
360     for (i = 0; i < e->n_children; i++) {
361         struct child *child = &e->children[i];
362         poll_child(child);
363     }
364
365     /* If SIGCHLD was received, reap dead children. */
366     if (read(signal_fds[0], buffer, sizeof buffer) <= 0) {
367         return;
368     }
369     for (;;) {
370         int status;
371         pid_t pid;
372
373         /* Get dead child in 'pid' and its return code in 'status'. */
374         pid = waitpid(WAIT_ANY, &status, WNOHANG);
375         if (pid < 0 && errno == EINTR) {
376             continue;
377         } else if (pid <= 0) {
378             return;
379         }
380
381         /* Find child with given 'pid' and drop it from the list. */
382         for (i = 0; i < e->n_children; i++) {
383             struct child *child = &e->children[i];
384             if (child->pid == pid) {
385                 poll_child(child);
386                 child_terminated(child, status);
387                 free(child->name);
388                 free(child->output);
389                 *child = e->children[--e->n_children];
390                 goto found;
391             }
392         }
393         VLOG_WARN("child with unknown pid %ld terminated", (long int) pid);
394     found:;
395     }
396
397 }
398
399 void
400 executer_wait(struct executer *e)
401 {
402     if (e->n_children) {
403         size_t i;
404
405         /* Wake up on SIGCHLD. */
406         poll_fd_wait(signal_fds[0], POLLIN);
407
408         /* Wake up when we get output from a child. */
409         for (i = 0; i < e->n_children; i++) {
410             struct child *child = &e->children[i];
411             if (child->output_fd >= 0) {
412                 poll_fd_wait(child->output_fd, POLLIN);
413             }
414         }
415     }
416 }
417
418 void
419 executer_rconn_closing(struct executer *e, struct rconn *rconn)
420 {
421     size_t i;
422
423     /* If any of our children was connected to 'r', then disconnect it so we
424      * don't try to reference a dead connection when the process terminates
425      * later.
426      * XXX kill the children started by 'r'? */
427     for (i = 0; i < e->n_children; i++) {
428         if (e->children[i].rconn == rconn) {
429             e->children[i].rconn = NULL;
430         }
431     }
432 }
433
434 static void
435 sigchld_handler(int signr UNUSED)
436 {
437     write(signal_fds[1], "", 1);
438 }
439
440 int
441 executer_create(const char *command_acl, const char *command_dir,
442                 struct executer **executerp)
443 {
444     struct executer *e;
445     struct sigaction sa;
446
447     *executerp = NULL;
448     if (signal_fds[0] == -1) {
449         /* Make sure we can get a fd for /dev/null. */
450         int null_fd = get_null_fd();
451         if (null_fd < 0) {
452             return -null_fd;
453         }
454
455         /* Create pipe for notifying us that SIGCHLD was invoked. */
456         if (pipe(signal_fds)) {
457             VLOG_ERR("pipe failed: %s", strerror(errno));
458             return errno;
459         }
460         set_nonblocking(signal_fds[0]);
461         set_nonblocking(signal_fds[1]);
462     }
463
464     /* Set up signal handler. */
465     memset(&sa, 0, sizeof sa);
466     sa.sa_handler = sigchld_handler;
467     sigemptyset(&sa.sa_mask);
468     sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
469     if (sigaction(SIGCHLD, &sa, NULL)) {
470         VLOG_ERR("sigaction(SIGCHLD) failed: %s", strerror(errno));
471         return errno;
472     }
473
474     e = xcalloc(1, sizeof *e);
475     e->command_acl = xstrdup(command_acl);
476     e->command_dir = (command_dir
477                       ? xstrdup(command_dir)
478                       : xasprintf("%s/commands", ovs_pkgdatadir));
479     e->n_children = 0;
480     *executerp = e;
481     return 0;
482 }
483
484 void
485 executer_destroy(struct executer *e)
486 {
487     if (e) {
488         size_t i;
489
490         free(e->command_acl);
491         free(e->command_dir);
492         for (i = 0; i < e->n_children; i++) {
493             struct child *child = &e->children[i];
494
495             free(child->name);
496             kill(child->pid, SIGHUP);
497             /* We don't own child->rconn. */
498             free(child->output);
499             free(child);
500         }
501         free(e);
502     }
503 }
504
505 void
506 executer_set_acl(struct executer *e, const char *acl, const char *dir)
507 {
508     free(e->command_acl);
509     e->command_acl = xstrdup(acl);
510     free(e->command_dir);
511     e->command_dir = xstrdup(dir);
512 }