Rename "secchan" to "ofproto" (library) and "ovs-openflowd" (program).
[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];
75
76 /* File descriptor for /dev/null. */
77 static int null_fd = -1;
78
79 static void send_child_status(struct rconn *, uint32_t xid, uint32_t status,
80                               const void *data, size_t size);
81 static void send_child_message(struct rconn *, uint32_t xid, uint32_t status,
82                                const char *message);
83
84 /* Returns true if 'cmd' is allowed by 'acl', which is a command-separated
85  * access control list in the format described for --command-acl in
86  * ovs-openflowd(8). */
87 static bool
88 executer_is_permitted(const char *acl_, const char *cmd)
89 {
90     char *acl, *save_ptr, *pattern;
91     bool allowed, denied;
92
93     /* Verify that 'cmd' consists only of alphanumerics plus _ or -. */
94     if (cmd[strspn(cmd, "abcdefghijklmnopqrstuvwxyz"
95                    "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-")] != '\0') {
96         VLOG_WARN("rejecting command name \"%s\" that contain forbidden "
97                   "characters", cmd);
98         return false;
99     }
100
101     /* Check 'cmd' against 'acl'. */
102     acl = xstrdup(acl_);
103     save_ptr = acl;
104     allowed = denied = false;
105     while ((pattern = strsep(&save_ptr, ",")) != NULL && !denied) {
106         if (pattern[0] != '!' && !fnmatch(pattern, cmd, 0)) {
107             allowed = true;
108         } else if (pattern[0] == '!' && !fnmatch(pattern + 1, cmd, 0)) {
109             denied = true;
110         }
111     }
112     free(acl);
113
114     /* Check the command white/blacklisted state. */
115     if (allowed && !denied) {
116         VLOG_INFO("permitting command execution: \"%s\" is whitelisted", cmd);
117     } else if (allowed && denied) {
118         VLOG_WARN("denying command execution: \"%s\" is both blacklisted "
119                   "and whitelisted", cmd);
120     } else if (!allowed) {
121         VLOG_WARN("denying command execution: \"%s\" is not whitelisted", cmd);
122     } else if (denied) {
123         VLOG_WARN("denying command execution: \"%s\" is blacklisted", cmd);
124     }
125     return allowed && !denied;
126 }
127
128 int
129 executer_handle_request(struct executer *e, struct rconn *rconn,
130                         struct nicira_header *request)
131 {
132     char **argv;
133     char *args;
134     char *exec_file = NULL;
135     int max_fds;
136     struct stat s;
137     size_t args_size;
138     size_t argc;
139     size_t i;
140     pid_t pid;
141     int output_fds[2];
142
143     /* Verify limit on children not exceeded.
144      * XXX should probably kill children when the connection drops? */
145     if (e->n_children >= MAX_CHILDREN) {
146         send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
147                            "too many child processes");
148         return 0;
149     }
150
151     /* Copy argument buffer, adding a null terminator at the end.  Now every
152      * argument is null-terminated, instead of being merely null-delimited. */
153     args_size = ntohs(request->header.length) - sizeof *request;
154     args = xmemdup0((const void *) (request + 1), args_size);
155
156     /* Count arguments. */
157     argc = 0;
158     for (i = 0; i <= args_size; i++) {
159         argc += args[i] == '\0';
160     }
161
162     /* Set argv[*] to point to each argument. */
163     argv = xmalloc((argc + 1) * sizeof *argv);
164     argv[0] = args;
165     for (i = 1; i < argc; i++) {
166         argv[i] = strchr(argv[i - 1], '\0') + 1;
167     }
168     argv[argc] = NULL;
169
170     /* Check permissions. */
171     if (!executer_is_permitted(e->command_acl, argv[0])) {
172         send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
173                            "command not allowed");
174         goto done;
175     }
176
177     /* Find the executable. */
178     exec_file = xasprintf("%s/%s", e->command_dir, argv[0]);
179     if (stat(exec_file, &s)) {
180         VLOG_WARN("failed to stat \"%s\": %s", exec_file, strerror(errno));
181         send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
182                            "command not allowed");
183         goto done;
184     }
185     if (!S_ISREG(s.st_mode)) {
186         VLOG_WARN("\"%s\" is not a regular file", exec_file);
187         send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
188                            "command not allowed");
189         goto done;
190     }
191     argv[0] = exec_file;
192
193     /* Arrange to capture output. */
194     if (pipe(output_fds)) {
195         VLOG_WARN("pipe failed: %s", strerror(errno));
196         send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
197                            "internal error (pipe)");
198         goto done;
199     }
200
201     pid = fork();
202     if (!pid) {
203         /* Running in child.
204          * XXX should run in new process group so that we can signal all
205          * subprocesses at once?  Would also want to catch fatal signals and
206          * kill them at the same time though. */
207         fatal_signal_fork();
208         dup2(null_fd, 0);
209         dup2(output_fds[1], 1);
210         dup2(null_fd, 2);
211         max_fds = get_max_fds();
212         for (i = 3; i < max_fds; i++) {
213             close(i);
214         }
215         if (chdir(e->command_dir)) {
216             printf("could not change directory to \"%s\": %s",
217                    e->command_dir, strerror(errno));
218             exit(EXIT_FAILURE);
219         }
220         execv(argv[0], argv);
221         printf("failed to start \"%s\": %s\n", argv[0], strerror(errno));
222         exit(EXIT_FAILURE);
223     } else if (pid > 0) {
224         /* Running in parent. */
225         struct child *child;
226
227         VLOG_INFO("started \"%s\" subprocess", argv[0]);
228         send_child_status(rconn, request->header.xid, NXT_STATUS_STARTED,
229                           NULL, 0);
230         child = &e->children[e->n_children++];
231         child->name = xstrdup(argv[0]);
232         child->pid = pid;
233         child->rconn = rconn;
234         child->xid = request->header.xid;
235         child->output_fd = output_fds[0];
236         child->output = xmalloc(MAX_OUTPUT);
237         child->output_size = 0;
238         set_nonblocking(output_fds[0]);
239         close(output_fds[1]);
240     } else {
241         VLOG_WARN("fork failed: %s", strerror(errno));
242         send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
243                            "internal error (fork)");
244         close(output_fds[0]);
245         close(output_fds[1]);
246     }
247
248 done:
249     free(exec_file);
250     free(args);
251     free(argv);
252     return 0;
253 }
254
255 static void
256 send_child_status(struct rconn *rconn, uint32_t xid, uint32_t status,
257                   const void *data, size_t size)
258 {
259     if (rconn) {
260         struct nx_command_reply *r;
261         struct ofpbuf *buffer;
262
263         r = make_openflow_xid(sizeof *r, OFPT_VENDOR, xid, &buffer);
264         r->nxh.vendor = htonl(NX_VENDOR_ID);
265         r->nxh.subtype = htonl(NXT_COMMAND_REPLY);
266         r->status = htonl(status);
267         ofpbuf_put(buffer, data, size);
268         update_openflow_length(buffer);
269         if (rconn_send(rconn, buffer, NULL)) {
270             ofpbuf_delete(buffer);
271         }
272     }
273 }
274
275 static void
276 send_child_message(struct rconn *rconn, uint32_t xid, uint32_t status,
277                    const char *message)
278 {
279     send_child_status(rconn, xid, status, message, strlen(message));
280 }
281
282 /* 'child' died with 'status' as its return code.  Deal with it. */
283 static void
284 child_terminated(struct child *child, int status)
285 {
286     struct ds ds;
287     uint32_t ofp_status;
288
289     /* Log how it terminated. */
290     ds_init(&ds);
291     if (WIFEXITED(status)) {
292         ds_put_format(&ds, "normally with status %d", WEXITSTATUS(status));
293     } else if (WIFSIGNALED(status)) {
294         const char *name = NULL;
295 #ifdef HAVE_STRSIGNAL
296         name = strsignal(WTERMSIG(status));
297 #endif
298         ds_put_format(&ds, "by signal %d", WTERMSIG(status));
299         if (name) {
300             ds_put_format(&ds, " (%s)", name);
301         }
302     }
303     if (WCOREDUMP(status)) {
304         ds_put_cstr(&ds, " (core dumped)");
305     }
306     VLOG_INFO("child process \"%s\" with pid %ld terminated %s",
307               child->name, (long int) child->pid, ds_cstr(&ds));
308     ds_destroy(&ds);
309
310     /* Send a status message back to the controller that requested the
311      * command. */
312     if (WIFEXITED(status)) {
313         ofp_status = WEXITSTATUS(status) | NXT_STATUS_EXITED;
314     } else if (WIFSIGNALED(status)) {
315         ofp_status = WTERMSIG(status) | NXT_STATUS_SIGNALED;
316     } else {
317         ofp_status = NXT_STATUS_UNKNOWN;
318     }
319     if (WCOREDUMP(status)) {
320         ofp_status |= NXT_STATUS_COREDUMP;
321     }
322     send_child_status(child->rconn, child->xid, ofp_status,
323                       child->output, child->output_size);
324 }
325
326 /* Read output from 'child' and append it to its output buffer. */
327 static void
328 poll_child(struct child *child)
329 {
330     ssize_t n;
331
332     if (child->output_fd < 0) {
333         return;
334     }
335
336     do {
337         n = read(child->output_fd, child->output + child->output_size,
338                  MAX_OUTPUT - child->output_size);
339     } while (n < 0 && errno == EINTR);
340     if (n > 0) {
341         child->output_size += n;
342         if (child->output_size < MAX_OUTPUT) {
343             return;
344         }
345     } else if (n < 0 && errno == EAGAIN) {
346         return;
347     }
348     close(child->output_fd);
349     child->output_fd = -1;
350 }
351
352 void
353 executer_run(struct executer *e)
354 {
355     char buffer[MAX_CHILDREN];
356     size_t i;
357
358     if (!e->n_children) {
359         return;
360     }
361
362     /* Read output from children. */
363     for (i = 0; i < e->n_children; i++) {
364         struct child *child = &e->children[i];
365         poll_child(child);
366     }
367
368     /* If SIGCHLD was received, reap dead children. */
369     if (read(signal_fds[0], buffer, sizeof buffer) <= 0) {
370         return;
371     }
372     for (;;) {
373         int status;
374         pid_t pid;
375
376         /* Get dead child in 'pid' and its return code in 'status'. */
377         pid = waitpid(WAIT_ANY, &status, WNOHANG);
378         if (pid < 0 && errno == EINTR) {
379             continue;
380         } else if (pid <= 0) {
381             return;
382         }
383
384         /* Find child with given 'pid' and drop it from the list. */
385         for (i = 0; i < e->n_children; i++) {
386             struct child *child = &e->children[i];
387             if (child->pid == pid) {
388                 poll_child(child);
389                 child_terminated(child, status);
390                 free(child->name);
391                 free(child->output);
392                 *child = e->children[--e->n_children];
393                 goto found;
394             }
395         }
396         VLOG_WARN("child with unknown pid %ld terminated", (long int) pid);
397     found:;
398     }
399
400 }
401
402 void
403 executer_wait(struct executer *e)
404 {
405     if (e->n_children) {
406         size_t i;
407
408         /* Wake up on SIGCHLD. */
409         poll_fd_wait(signal_fds[0], POLLIN);
410
411         /* Wake up when we get output from a child. */
412         for (i = 0; i < e->n_children; i++) {
413             struct child *child = &e->children[i];
414             if (child->output_fd >= 0) {
415                 poll_fd_wait(child->output_fd, POLLIN);
416             }
417         }
418     }
419 }
420
421 void
422 executer_rconn_closing(struct executer *e, struct rconn *rconn)
423 {
424     size_t i;
425
426     /* If any of our children was connected to 'r', then disconnect it so we
427      * don't try to reference a dead connection when the process terminates
428      * later.
429      * XXX kill the children started by 'r'? */
430     for (i = 0; i < e->n_children; i++) {
431         if (e->children[i].rconn == rconn) {
432             e->children[i].rconn = NULL;
433         }
434     }
435 }
436
437 static void
438 sigchld_handler(int signr UNUSED)
439 {
440     write(signal_fds[1], "", 1);
441 }
442
443 int
444 executer_create(const char *command_acl, const char *command_dir,
445                 struct executer **executerp)
446 {
447     struct executer *e;
448     struct sigaction sa;
449
450     *executerp = NULL;
451     if (null_fd == -1) {
452         /* Create pipe for notifying us that SIGCHLD was invoked. */
453         if (pipe(signal_fds)) {
454             VLOG_ERR("pipe failed: %s", strerror(errno));
455             return errno;
456         }
457         set_nonblocking(signal_fds[0]);
458         set_nonblocking(signal_fds[1]);
459
460         /* Open /dev/null. */
461         null_fd = open("/dev/null", O_RDWR);
462         if (null_fd < 0) {
463             int error = errno;
464             VLOG_ERR("could not open /dev/null: %s", strerror(error));
465             close(signal_fds[0]);
466             close(signal_fds[1]);
467             return error;
468         }
469     }
470
471     /* Set up signal handler. */
472     memset(&sa, 0, sizeof sa);
473     sa.sa_handler = sigchld_handler;
474     sigemptyset(&sa.sa_mask);
475     sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
476     if (sigaction(SIGCHLD, &sa, NULL)) {
477         VLOG_ERR("sigaction(SIGCHLD) failed: %s", strerror(errno));
478         return errno;
479     }
480
481     e = xcalloc(1, sizeof *e);
482     e->command_acl = xstrdup(command_acl);
483     e->command_dir = (command_dir
484                       ? xstrdup(command_dir)
485                       : xasprintf("%s/commands", ovs_pkgdatadir));
486     e->n_children = 0;
487     *executerp = e;
488     return 0;
489 }
490
491 void
492 executer_destroy(struct executer *e)
493 {
494     if (e) {
495         size_t i;
496
497         free(e->command_acl);
498         free(e->command_dir);
499         for (i = 0; i < e->n_children; i++) {
500             struct child *child = &e->children[i];
501
502             free(child->name);
503             kill(child->pid, SIGHUP);
504             /* We don't own child->rconn. */
505             free(child->output);
506             free(child);
507         }
508         free(e);
509     }
510 }
511
512 void
513 executer_set_acl(struct executer *e, const char *acl, const char *dir)
514 {
515     free(e->command_acl);
516     e->command_acl = xstrdup(acl);
517     free(e->command_dir);
518     e->command_dir = xstrdup(dir);
519 }