2 * Copyright (c) 2008, 2009 Nicira Networks.
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30 #include "dynamic-string.h"
31 #include "fatal-signal.h"
32 #include "openflow/nicira-ext.h"
34 #include "openflow/openflow.h"
35 #include "poll-loop.h"
37 #include "socket-util.h"
41 #define THIS_MODULE VLM_executer
44 #define MAX_CHILDREN 8
47 /* Information about child process. */
48 char *name; /* argv[0] passed to child. */
49 pid_t pid; /* Child's process ID. */
51 /* For sending a reply to the controller when the child dies. */
53 uint32_t xid; /* Transaction ID used by controller. */
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. */
65 char *command_acl; /* Command white/blacklist, as shell globs. */
66 char *command_dir; /* Directory that contains commands. */
69 struct child children[MAX_CHILDREN];
73 /* File descriptors for waking up when a child dies. */
74 static int signal_fds[2];
76 /* File descriptor for /dev/null. */
77 static int null_fd = -1;
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,
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
88 executer_is_permitted(const char *acl_, const char *cmd)
90 char *acl, *save_ptr, *pattern;
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 "
101 /* Check 'cmd' against 'acl'. */
104 allowed = denied = false;
105 while ((pattern = strsep(&save_ptr, ",")) != NULL && !denied) {
106 if (pattern[0] != '!' && !fnmatch(pattern, cmd, 0)) {
108 } else if (pattern[0] == '!' && !fnmatch(pattern + 1, cmd, 0)) {
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);
123 VLOG_WARN("denying command execution: \"%s\" is blacklisted", cmd);
125 return allowed && !denied;
129 executer_handle_request(struct executer *e, struct rconn *rconn,
130 struct nicira_header *request)
134 char *exec_file = NULL;
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");
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);
156 /* Count arguments. */
158 for (i = 0; i <= args_size; i++) {
159 argc += args[i] == '\0';
162 /* Set argv[*] to point to each argument. */
163 argv = xmalloc((argc + 1) * sizeof *argv);
165 for (i = 1; i < argc; i++) {
166 argv[i] = strchr(argv[i - 1], '\0') + 1;
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");
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");
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");
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)");
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. */
209 dup2(output_fds[1], 1);
211 max_fds = get_max_fds();
212 for (i = 3; i < max_fds; i++) {
215 if (chdir(e->command_dir)) {
216 printf("could not change directory to \"%s\": %s",
217 e->command_dir, strerror(errno));
220 execv(argv[0], argv);
221 printf("failed to start \"%s\": %s\n", argv[0], strerror(errno));
223 } else if (pid > 0) {
224 /* Running in parent. */
227 VLOG_INFO("started \"%s\" subprocess", argv[0]);
228 send_child_status(rconn, request->header.xid, NXT_STATUS_STARTED,
230 child = &e->children[e->n_children++];
231 child->name = xstrdup(argv[0]);
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]);
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]);
256 send_child_status(struct rconn *rconn, uint32_t xid, uint32_t status,
257 const void *data, size_t size)
260 struct nx_command_reply *r;
261 struct ofpbuf *buffer;
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);
276 send_child_message(struct rconn *rconn, uint32_t xid, uint32_t status,
279 send_child_status(rconn, xid, status, message, strlen(message));
282 /* 'child' died with 'status' as its return code. Deal with it. */
284 child_terminated(struct child *child, int status)
289 /* Log how it terminated. */
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));
298 ds_put_format(&ds, "by signal %d", WTERMSIG(status));
300 ds_put_format(&ds, " (%s)", name);
303 if (WCOREDUMP(status)) {
304 ds_put_cstr(&ds, " (core dumped)");
306 VLOG_INFO("child process \"%s\" with pid %ld terminated %s",
307 child->name, (long int) child->pid, ds_cstr(&ds));
310 /* Send a status message back to the controller that requested the
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;
317 ofp_status = NXT_STATUS_UNKNOWN;
319 if (WCOREDUMP(status)) {
320 ofp_status |= NXT_STATUS_COREDUMP;
322 send_child_status(child->rconn, child->xid, ofp_status,
323 child->output, child->output_size);
326 /* Read output from 'child' and append it to its output buffer. */
328 poll_child(struct child *child)
332 if (child->output_fd < 0) {
337 n = read(child->output_fd, child->output + child->output_size,
338 MAX_OUTPUT - child->output_size);
339 } while (n < 0 && errno == EINTR);
341 child->output_size += n;
342 if (child->output_size < MAX_OUTPUT) {
345 } else if (n < 0 && errno == EAGAIN) {
348 close(child->output_fd);
349 child->output_fd = -1;
353 executer_run(struct executer *e)
355 char buffer[MAX_CHILDREN];
358 if (!e->n_children) {
362 /* Read output from children. */
363 for (i = 0; i < e->n_children; i++) {
364 struct child *child = &e->children[i];
368 /* If SIGCHLD was received, reap dead children. */
369 if (read(signal_fds[0], buffer, sizeof buffer) <= 0) {
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) {
380 } else if (pid <= 0) {
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) {
389 child_terminated(child, status);
392 *child = e->children[--e->n_children];
396 VLOG_WARN("child with unknown pid %ld terminated", (long int) pid);
403 executer_wait(struct executer *e)
408 /* Wake up on SIGCHLD. */
409 poll_fd_wait(signal_fds[0], POLLIN);
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);
422 executer_rconn_closing(struct executer *e, struct rconn *rconn)
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
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;
438 sigchld_handler(int signr UNUSED)
440 write(signal_fds[1], "", 1);
444 executer_create(const char *command_acl, const char *command_dir,
445 struct executer **executerp)
452 /* Create pipe for notifying us that SIGCHLD was invoked. */
453 if (pipe(signal_fds)) {
454 VLOG_ERR("pipe failed: %s", strerror(errno));
457 set_nonblocking(signal_fds[0]);
458 set_nonblocking(signal_fds[1]);
460 /* Open /dev/null. */
461 null_fd = open("/dev/null", O_RDWR);
464 VLOG_ERR("could not open /dev/null: %s", strerror(error));
465 close(signal_fds[0]);
466 close(signal_fds[1]);
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));
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));
492 executer_destroy(struct executer *e)
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];
503 kill(child->pid, SIGHUP);
504 /* We don't own child->rconn. */
513 executer_set_acl(struct executer *e, const char *acl, const char *dir)
515 free(e->command_acl);
516 e->command_acl = xstrdup(acl);
517 free(e->command_dir);
518 e->command_dir = xstrdup(dir);