1 /* Copyright (c) 2008 The Board of Trustees of The Leland Stanford
4 * We are making the OpenFlow specification and associated documentation
5 * (Software) available for public use and benefit with the expectation
6 * that others will use, modify and enhance the Software and contribute
7 * those enhancements back to the community. However, since we would
8 * like to make the Software available for broadest use, with as few
9 * restrictions as possible permission is hereby granted, free of
10 * charge, to any person obtaining a copy of this Software to deal in
11 * the Software under the copyrights without restriction, including
12 * without limitation the rights to use, copy, modify, merge, publish,
13 * distribute, sublicense, and/or sell copies of the Software, and to
14 * permit persons to whom the Software is furnished to do so, subject to
15 * the following conditions:
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 * The name and trademarks of copyright holder(s) may NOT be used in
30 * advertising or publicity pertaining to the Software or any
31 * derivatives without specific, written prior permission.
46 #include "dynamic-string.h"
47 #include "fatal-signal.h"
48 #include "openflow/nicira-ext.h"
50 #include "openflow/openflow.h"
51 #include "poll-loop.h"
54 #include "socket-util.h"
58 #define THIS_MODULE VLM_executer
61 #define MAX_CHILDREN 8
64 /* Information about child process. */
65 char *name; /* argv[0] passed to child. */
66 pid_t pid; /* Child's process ID. */
68 /* For sending a reply to the controller when the child dies. */
70 uint32_t xid; /* Transaction ID used by controller. */
72 /* We read up to MAX_OUTPUT bytes of output and send them back to the
73 * controller when the child dies. */
74 #define MAX_OUTPUT 4096
75 int output_fd; /* FD from which to read child's output. */
76 uint8_t *output; /* Output data. */
77 size_t output_size; /* Number of bytes of output data so far. */
81 const struct settings *s;
84 struct child children[MAX_CHILDREN];
87 /* File descriptors. */
88 int wait_fd; /* Pipe FD for wakeup when on SIGCHLD. */
89 int null_fd; /* FD for /dev/null. */
92 static void send_child_status(struct relay *, uint32_t xid, uint32_t status,
93 const void *data, size_t size);
94 static void send_child_message(struct relay *, uint32_t xid, uint32_t status,
97 /* Returns true if 'cmd' is allowed by 'acl', which is a command-separated
98 * access control list in the format described for --command-acl in
101 executer_is_permitted(const char *acl_, const char *cmd)
103 char *acl, *save_ptr, *pattern;
104 bool allowed, denied;
106 /* Verify that 'cmd' consists only of alphanumerics plus _ or -. */
107 if (cmd[strspn(cmd, "abcdefghijklmnopqrstuvwxyz"
108 "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-")] != '\0') {
109 VLOG_WARN("rejecting command name \"%s\" that contain forbidden "
114 /* Check 'cmd' against 'acl'. */
117 allowed = denied = false;
118 while ((pattern = strsep(&save_ptr, ",")) != NULL && !denied) {
119 if (pattern[0] != '!' && !fnmatch(pattern, cmd, 0)) {
121 } else if (pattern[0] == '!' && !fnmatch(pattern + 1, cmd, 0)) {
127 /* Check the command white/blacklisted state. */
128 if (allowed && !denied) {
129 VLOG_WARN("permitting command execution: \"%s\" is whitelisted", cmd);
130 } else if (allowed && denied) {
131 VLOG_WARN("denying command execution: \"%s\" is both blacklisted "
132 "and whitelisted", cmd);
133 } else if (!allowed) {
134 VLOG_WARN("denying command execution: \"%s\" is not whitelisted", cmd);
136 VLOG_WARN("denying command execution: \"%s\" is blacklisted", cmd);
138 return allowed && !denied;
142 executer_remote_packet_cb(struct relay *r, void *e_)
144 struct executer *e = e_;
145 struct ofpbuf *msg = r->halves[HALF_REMOTE].rxbuf;
146 struct nicira_header *request;
149 char *exec_file = NULL;
158 /* Check for NXT_COMMAND_REQUEST vendor extension. */
159 if (msg->size < sizeof(struct nicira_header)) {
163 if (request->header.type != OFPT_VENDOR
164 || request->vendor != htonl(NX_VENDOR_ID)
165 || request->subtype != htonl(NXT_COMMAND_REQUEST)) {
169 /* Verify limit on children not exceeded.
170 * XXX should probably kill children when the connection drops? */
171 if (e->n_children >= MAX_CHILDREN) {
172 send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
173 "too many child processes");
174 VLOG_WARN("limit of %d child processes reached, dropping request",
179 /* Copy argument buffer, adding a null terminator at the end. Now every
180 * argument is null-terminated, instead of being merely null-delimited. */
181 args_size = msg->size - sizeof *request;
182 args = xmemdup0((const void *) (request + 1), args_size);
184 /* Count arguments. */
186 for (i = 0; i <= args_size; i++) {
187 argc += args[i] == '\0';
190 /* Set argv[*] to point to each argument. */
191 argv = xmalloc((argc + 1) * sizeof *argv);
193 for (i = 1; i < argc; i++) {
194 argv[i] = strchr(argv[i - 1], '\0') + 1;
198 /* Check permissions. */
199 if (!executer_is_permitted(e->s->command_acl, argv[0])) {
200 send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
201 "command not allowed");
205 /* Find the executable. */
206 exec_file = xasprintf("%s/%s", e->s->command_dir, argv[0]);
207 if (stat(exec_file, &s)) {
208 VLOG_WARN("failed to stat \"%s\": %s", exec_file, strerror(errno));
209 send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
210 "command not allowed");
213 if (!S_ISREG(s.st_mode)) {
214 VLOG_WARN("\"%s\" is not a regular file", exec_file);
215 send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
216 "command not allowed");
221 /* Arrange to capture output. */
222 if (pipe(output_fds)) {
223 VLOG_WARN("pipe failed: %s", strerror(errno));
224 send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
225 "internal error (pipe)");
232 * XXX should run in new process group so that we can signal all
233 * subprocesses at once? Would also want to catch fatal signals and
234 * kill them at the same time though. */
237 dup2(output_fds[1], 1);
239 max_fds = get_max_fds();
240 for (i = 3; i < max_fds; i++) {
243 if (chdir(e->s->command_dir)) {
244 printf("could not change directory to \"%s\": %s",
245 e->s->command_dir, strerror(errno));
248 execv(argv[0], argv);
249 printf("failed to start \"%s\": %s\n", argv[0], strerror(errno));
251 } else if (pid > 0) {
252 /* Running in parent. */
255 VLOG_WARN("started \"%s\" subprocess", argv[0]);
256 send_child_status(r, request->header.xid, NXT_STATUS_STARTED, NULL, 0);
257 child = &e->children[e->n_children++];
258 child->name = xstrdup(argv[0]);
261 child->xid = request->header.xid;
262 child->output_fd = output_fds[0];
263 child->output = xmalloc(MAX_OUTPUT);
264 child->output_size = 0;
265 set_nonblocking(output_fds[0]);
266 close(output_fds[1]);
268 VLOG_WARN("fork failed: %s", strerror(errno));
269 send_child_message(r, request->header.xid, NXT_STATUS_ERROR,
270 "internal error (fork)");
271 close(output_fds[0]);
272 close(output_fds[1]);
283 send_child_status(struct relay *relay, uint32_t xid, uint32_t status,
284 const void *data, size_t size)
287 struct nx_command_reply *r;
288 struct ofpbuf *buffer;
290 r = make_openflow_xid(sizeof *r, OFPT_VENDOR, xid, &buffer);
291 r->nxh.vendor = htonl(NX_VENDOR_ID);
292 r->nxh.subtype = htonl(NXT_COMMAND_REPLY);
293 r->status = htonl(status);
294 ofpbuf_put(buffer, data, size);
295 update_openflow_length(buffer);
296 if (rconn_send(relay->halves[HALF_REMOTE].rconn, buffer, NULL)) {
297 ofpbuf_delete(buffer);
303 send_child_message(struct relay *relay, uint32_t xid, uint32_t status,
306 send_child_status(relay, xid, status, message, strlen(message));
309 /* 'child' died with 'status' as its return code. Deal with it. */
311 child_terminated(struct child *child, int status)
316 /* Log how it terminated. */
318 if (WIFEXITED(status)) {
319 ds_put_format(&ds, "normally with status %d", WEXITSTATUS(status));
320 } else if (WIFSIGNALED(status)) {
321 #ifdef HAVE_STRSIGNAL
322 ds_put_format(&ds, "by signal %s", strsignal(WTERMSIG(status)));
324 ds_put_format(&ds, "by signal %d", WTERMSIG(status));
327 if (WCOREDUMP(status)) {
328 ds_put_cstr(&ds, " (core dumped)");
330 VLOG_WARN("child process \"%s\" with pid %ld terminated %s",
331 child->name, (long int) child->pid, ds_cstr(&ds));
334 /* Send a status message back to the controller that requested the
336 if (WIFEXITED(status)) {
337 ofp_status = WEXITSTATUS(status) | NXT_STATUS_EXITED;
338 } else if (WIFSIGNALED(status)) {
339 ofp_status = WTERMSIG(status) | NXT_STATUS_SIGNALED;
341 ofp_status = NXT_STATUS_UNKNOWN;
343 if (WCOREDUMP(status)) {
344 ofp_status |= NXT_STATUS_COREDUMP;
346 send_child_status(child->relay, child->xid, ofp_status,
347 child->output, child->output_size);
350 /* Read output from 'child' and append it to its output buffer. */
352 poll_child(struct child *child)
356 if (child->output_fd < 0) {
361 n = read(child->output_fd, child->output + child->output_size,
362 MAX_OUTPUT - child->output_size);
363 } while (n < 0 && errno == EINTR);
365 child->output_size += n;
366 if (child->output_size < MAX_OUTPUT) {
369 } else if (n < 0 && errno == EAGAIN) {
372 close(child->output_fd);
373 child->output_fd = -1;
377 executer_periodic_cb(void *e_)
379 struct executer *e = e_;
380 char buffer[MAX_CHILDREN];
383 if (!e->n_children) {
387 /* Read output from children. */
388 for (i = 0; i < e->n_children; i++) {
389 struct child *child = &e->children[i];
393 /* If SIGCHLD was received, reap dead children. */
394 if (read(e->wait_fd, buffer, sizeof buffer) <= 0) {
401 /* Get dead child in 'pid' and its return code in 'status'. */
402 pid = waitpid(WAIT_ANY, &status, WNOHANG);
403 if (pid < 0 && errno == EINTR) {
405 } else if (pid <= 0) {
409 /* Find child with given 'pid' and drop it from the list. */
410 for (i = 0; i < e->n_children; i++) {
411 struct child *child = &e->children[i];
412 if (child->pid == pid) {
414 child_terminated(child, status);
417 *child = e->children[--e->n_children];
421 VLOG_WARN("child with unknown pid %ld terminated", (long int) pid);
428 executer_wait_cb(void *e_)
430 struct executer *e = e_;
434 /* Wake up on SIGCHLD. */
435 poll_fd_wait(e->wait_fd, POLLIN);
437 /* Wake up when we get output from a child. */
438 for (i = 0; i < e->n_children; i++) {
439 struct child *child = &e->children[i];
440 if (child->output_fd >= 0) {
441 poll_fd_wait(e->wait_fd, POLLIN);
448 executer_closing_cb(struct relay *r, void *e_)
450 struct executer *e = e_;
453 /* If any of our children was connected to 'r', then disconnect it so we
454 * don't try to reference a dead connection when the process terminates
456 * XXX kill the children started by 'r'? */
457 for (i = 0; i < e->n_children; i++) {
458 if (e->children[i].relay == r) {
459 e->children[i].relay = NULL;
467 sigchld_handler(int signr UNUSED)
469 write(child_fd, "", 1);
472 static struct hook_class executer_hook_class = {
473 NULL, /* local_packet_cb */
474 executer_remote_packet_cb, /* remote_packet_cb */
475 executer_periodic_cb, /* periodic_cb */
476 executer_wait_cb, /* wait_cb */
477 executer_closing_cb, /* closing_cb */
481 executer_start(struct secchan *secchan, const struct settings *settings)
487 /* Create pipe for notifying us that SIGCHLD was invoked. */
489 ofp_fatal(errno, "pipe failed");
491 set_nonblocking(fds[0]);
492 set_nonblocking(fds[1]);
495 /* Open /dev/null. */
496 null_fd = open("/dev/null", O_RDWR);
498 ofp_fatal(errno, "could not open /dev/null");
501 /* Set up signal handler. */
502 memset(&sa, 0, sizeof sa);
503 sa.sa_handler = sigchld_handler;
504 sigemptyset(&sa.sa_mask);
505 sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
506 if (sigaction(SIGCHLD, &sa, NULL)) {
507 ofp_fatal(errno, "sigaction(SIGCHLD) faile");
511 e = xcalloc(1, sizeof *e);
515 e->null_fd = null_fd;
516 add_hook(secchan, &executer_hook_class, e);