2 * Copyright (c) 2008, 2009 Nicira Networks.
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:
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
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] = {-1, -1};
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,
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). */
85 executer_is_permitted(const char *acl_, const char *cmd)
87 char *acl, *save_ptr, *pattern;
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 "
98 /* Check 'cmd' against 'acl'. */
101 allowed = denied = false;
102 while ((pattern = strsep(&save_ptr, ",")) != NULL && !denied) {
103 if (pattern[0] != '!' && !fnmatch(pattern, cmd, 0)) {
105 } else if (pattern[0] == '!' && !fnmatch(pattern + 1, cmd, 0)) {
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);
120 VLOG_WARN("denying command execution: \"%s\" is blacklisted", cmd);
122 return allowed && !denied;
126 executer_handle_request(struct executer *e, struct rconn *rconn,
127 struct nicira_header *request)
131 char *exec_file = NULL;
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");
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);
153 /* Count arguments. */
155 for (i = 0; i <= args_size; i++) {
156 argc += args[i] == '\0';
159 /* Set argv[*] to point to each argument. */
160 argv = xmalloc((argc + 1) * sizeof *argv);
162 for (i = 1; i < argc; i++) {
163 argv[i] = strchr(argv[i - 1], '\0') + 1;
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");
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");
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");
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)");
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. */
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++) {
212 if (chdir(e->command_dir)) {
213 printf("could not change directory to \"%s\": %s",
214 e->command_dir, strerror(errno));
217 execv(argv[0], argv);
218 printf("failed to start \"%s\": %s\n", argv[0], strerror(errno));
220 } else if (pid > 0) {
221 /* Running in parent. */
224 VLOG_INFO("started \"%s\" subprocess", argv[0]);
225 send_child_status(rconn, request->header.xid, NXT_STATUS_STARTED,
227 child = &e->children[e->n_children++];
228 child->name = xstrdup(argv[0]);
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]);
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]);
253 send_child_status(struct rconn *rconn, uint32_t xid, uint32_t status,
254 const void *data, size_t size)
257 struct nx_command_reply *r;
258 struct ofpbuf *buffer;
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);
273 send_child_message(struct rconn *rconn, uint32_t xid, uint32_t status,
276 send_child_status(rconn, xid, status, message, strlen(message));
279 /* 'child' died with 'status' as its return code. Deal with it. */
281 child_terminated(struct child *child, int status)
286 /* Log how it terminated. */
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));
295 ds_put_format(&ds, "by signal %d", WTERMSIG(status));
297 ds_put_format(&ds, " (%s)", name);
300 if (WCOREDUMP(status)) {
301 ds_put_cstr(&ds, " (core dumped)");
303 VLOG_INFO("child process \"%s\" with pid %ld terminated %s",
304 child->name, (long int) child->pid, ds_cstr(&ds));
307 /* Send a status message back to the controller that requested the
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;
314 ofp_status = NXT_STATUS_UNKNOWN;
316 if (WCOREDUMP(status)) {
317 ofp_status |= NXT_STATUS_COREDUMP;
319 send_child_status(child->rconn, child->xid, ofp_status,
320 child->output, child->output_size);
323 /* Read output from 'child' and append it to its output buffer. */
325 poll_child(struct child *child)
329 if (child->output_fd < 0) {
334 n = read(child->output_fd, child->output + child->output_size,
335 MAX_OUTPUT - child->output_size);
336 } while (n < 0 && errno == EINTR);
338 child->output_size += n;
339 if (child->output_size < MAX_OUTPUT) {
342 } else if (n < 0 && errno == EAGAIN) {
345 close(child->output_fd);
346 child->output_fd = -1;
350 executer_run(struct executer *e)
352 char buffer[MAX_CHILDREN];
355 if (!e->n_children) {
359 /* Read output from children. */
360 for (i = 0; i < e->n_children; i++) {
361 struct child *child = &e->children[i];
365 /* If SIGCHLD was received, reap dead children. */
366 if (read(signal_fds[0], buffer, sizeof buffer) <= 0) {
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) {
377 } else if (pid <= 0) {
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) {
386 child_terminated(child, status);
389 *child = e->children[--e->n_children];
393 VLOG_WARN("child with unknown pid %ld terminated", (long int) pid);
400 executer_wait(struct executer *e)
405 /* Wake up on SIGCHLD. */
406 poll_fd_wait(signal_fds[0], POLLIN);
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);
419 executer_rconn_closing(struct executer *e, struct rconn *rconn)
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
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;
435 sigchld_handler(int signr UNUSED)
437 ignore(write(signal_fds[1], "", 1));
441 executer_create(const char *command_acl, const char *command_dir,
442 struct executer **executerp)
448 if (signal_fds[0] == -1) {
449 /* Make sure we can get a fd for /dev/null. */
450 int null_fd = get_null_fd();
455 /* Create pipe for notifying us that SIGCHLD was invoked. */
456 if (pipe(signal_fds)) {
457 VLOG_ERR("pipe failed: %s", strerror(errno));
460 set_nonblocking(signal_fds[0]);
461 set_nonblocking(signal_fds[1]);
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));
474 e = xzalloc(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));
485 executer_destroy(struct executer *e)
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];
496 kill(child->pid, SIGHUP);
497 /* We don't own child->rconn. */
506 executer_set_acl(struct executer *e, const char *acl, const char *dir)
508 free(e->command_acl);
509 e->command_acl = xstrdup(acl);
510 free(e->command_dir);
511 e->command_dir = xstrdup(dir);