Rename "secchan" to "ofproto" (library) and "ovs-openflowd" (program).
[sliver-openvswitch.git] / secchan / executer.c
diff --git a/secchan/executer.c b/secchan/executer.c
deleted file mode 100644 (file)
index 210d7cb..0000000
+++ /dev/null
@@ -1,519 +0,0 @@
-/*
- * Copyright (c) 2008, 2009 Nicira Networks.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at:
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <config.h>
-#include "executer.h"
-#include <errno.h>
-#include <fcntl.h>
-#include <fnmatch.h>
-#include <poll.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <string.h>
-#include <unistd.h>
-#include "dirs.h"
-#include "dynamic-string.h"
-#include "fatal-signal.h"
-#include "openflow/nicira-ext.h"
-#include "ofpbuf.h"
-#include "openflow/openflow.h"
-#include "poll-loop.h"
-#include "rconn.h"
-#include "socket-util.h"
-#include "util.h"
-#include "vconn.h"
-
-#define THIS_MODULE VLM_executer
-#include "vlog.h"
-
-#define MAX_CHILDREN 8
-
-struct child {
-    /* Information about child process. */
-    char *name;                 /* argv[0] passed to child. */
-    pid_t pid;                  /* Child's process ID. */
-
-    /* For sending a reply to the controller when the child dies. */
-    struct rconn *rconn;
-    uint32_t xid;               /* Transaction ID used by controller. */
-
-    /* We read up to MAX_OUTPUT bytes of output and send them back to the
-     * controller when the child dies. */
-#define MAX_OUTPUT 4096
-    int output_fd;              /* FD from which to read child's output. */
-    uint8_t *output;            /* Output data. */
-    size_t output_size;         /* Number of bytes of output data so far. */
-};
-
-struct executer {
-    /* Settings. */
-    char *command_acl;          /* Command white/blacklist, as shell globs. */
-    char *command_dir;          /* Directory that contains commands. */
-
-    /* Children. */
-    struct child children[MAX_CHILDREN];
-    size_t n_children;
-};
-
-/* File descriptors for waking up when a child dies. */
-static int signal_fds[2];
-
-/* File descriptor for /dev/null. */
-static int null_fd = -1;
-
-static void send_child_status(struct rconn *, uint32_t xid, uint32_t status,
-                              const void *data, size_t size);
-static void send_child_message(struct rconn *, uint32_t xid, uint32_t status,
-                               const char *message);
-
-/* Returns true if 'cmd' is allowed by 'acl', which is a command-separated
- * access control list in the format described for --command-acl in
- * secchan(8). */
-static bool
-executer_is_permitted(const char *acl_, const char *cmd)
-{
-    char *acl, *save_ptr, *pattern;
-    bool allowed, denied;
-
-    /* Verify that 'cmd' consists only of alphanumerics plus _ or -. */
-    if (cmd[strspn(cmd, "abcdefghijklmnopqrstuvwxyz"
-                   "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-")] != '\0') {
-        VLOG_WARN("rejecting command name \"%s\" that contain forbidden "
-                  "characters", cmd);
-        return false;
-    }
-
-    /* Check 'cmd' against 'acl'. */
-    acl = xstrdup(acl_);
-    save_ptr = acl;
-    allowed = denied = false;
-    while ((pattern = strsep(&save_ptr, ",")) != NULL && !denied) {
-        if (pattern[0] != '!' && !fnmatch(pattern, cmd, 0)) {
-            allowed = true;
-        } else if (pattern[0] == '!' && !fnmatch(pattern + 1, cmd, 0)) {
-            denied = true;
-        }
-    }
-    free(acl);
-
-    /* Check the command white/blacklisted state. */
-    if (allowed && !denied) {
-        VLOG_INFO("permitting command execution: \"%s\" is whitelisted", cmd);
-    } else if (allowed && denied) {
-        VLOG_WARN("denying command execution: \"%s\" is both blacklisted "
-                  "and whitelisted", cmd);
-    } else if (!allowed) {
-        VLOG_WARN("denying command execution: \"%s\" is not whitelisted", cmd);
-    } else if (denied) {
-        VLOG_WARN("denying command execution: \"%s\" is blacklisted", cmd);
-    }
-    return allowed && !denied;
-}
-
-int
-executer_handle_request(struct executer *e, struct rconn *rconn,
-                        struct nicira_header *request)
-{
-    char **argv;
-    char *args;
-    char *exec_file = NULL;
-    int max_fds;
-    struct stat s;
-    size_t args_size;
-    size_t argc;
-    size_t i;
-    pid_t pid;
-    int output_fds[2];
-
-    /* Verify limit on children not exceeded.
-     * XXX should probably kill children when the connection drops? */
-    if (e->n_children >= MAX_CHILDREN) {
-        send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
-                           "too many child processes");
-        return 0;
-    }
-
-    /* Copy argument buffer, adding a null terminator at the end.  Now every
-     * argument is null-terminated, instead of being merely null-delimited. */
-    args_size = ntohs(request->header.length) - sizeof *request;
-    args = xmemdup0((const void *) (request + 1), args_size);
-
-    /* Count arguments. */
-    argc = 0;
-    for (i = 0; i <= args_size; i++) {
-        argc += args[i] == '\0';
-    }
-
-    /* Set argv[*] to point to each argument. */
-    argv = xmalloc((argc + 1) * sizeof *argv);
-    argv[0] = args;
-    for (i = 1; i < argc; i++) {
-        argv[i] = strchr(argv[i - 1], '\0') + 1;
-    }
-    argv[argc] = NULL;
-
-    /* Check permissions. */
-    if (!executer_is_permitted(e->command_acl, argv[0])) {
-        send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
-                           "command not allowed");
-        goto done;
-    }
-
-    /* Find the executable. */
-    exec_file = xasprintf("%s/%s", e->command_dir, argv[0]);
-    if (stat(exec_file, &s)) {
-        VLOG_WARN("failed to stat \"%s\": %s", exec_file, strerror(errno));
-        send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
-                           "command not allowed");
-        goto done;
-    }
-    if (!S_ISREG(s.st_mode)) {
-        VLOG_WARN("\"%s\" is not a regular file", exec_file);
-        send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
-                           "command not allowed");
-        goto done;
-    }
-    argv[0] = exec_file;
-
-    /* Arrange to capture output. */
-    if (pipe(output_fds)) {
-        VLOG_WARN("pipe failed: %s", strerror(errno));
-        send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
-                           "internal error (pipe)");
-        goto done;
-    }
-
-    pid = fork();
-    if (!pid) {
-        /* Running in child.
-         * XXX should run in new process group so that we can signal all
-         * subprocesses at once?  Would also want to catch fatal signals and
-         * kill them at the same time though. */
-        fatal_signal_fork();
-        dup2(null_fd, 0);
-        dup2(output_fds[1], 1);
-        dup2(null_fd, 2);
-        max_fds = get_max_fds();
-        for (i = 3; i < max_fds; i++) {
-            close(i);
-        }
-        if (chdir(e->command_dir)) {
-            printf("could not change directory to \"%s\": %s",
-                   e->command_dir, strerror(errno));
-            exit(EXIT_FAILURE);
-        }
-        execv(argv[0], argv);
-        printf("failed to start \"%s\": %s\n", argv[0], strerror(errno));
-        exit(EXIT_FAILURE);
-    } else if (pid > 0) {
-        /* Running in parent. */
-        struct child *child;
-
-        VLOG_INFO("started \"%s\" subprocess", argv[0]);
-        send_child_status(rconn, request->header.xid, NXT_STATUS_STARTED,
-                          NULL, 0);
-        child = &e->children[e->n_children++];
-        child->name = xstrdup(argv[0]);
-        child->pid = pid;
-        child->rconn = rconn;
-        child->xid = request->header.xid;
-        child->output_fd = output_fds[0];
-        child->output = xmalloc(MAX_OUTPUT);
-        child->output_size = 0;
-        set_nonblocking(output_fds[0]);
-        close(output_fds[1]);
-    } else {
-        VLOG_WARN("fork failed: %s", strerror(errno));
-        send_child_message(rconn, request->header.xid, NXT_STATUS_ERROR,
-                           "internal error (fork)");
-        close(output_fds[0]);
-        close(output_fds[1]);
-    }
-
-done:
-    free(exec_file);
-    free(args);
-    free(argv);
-    return 0;
-}
-
-static void
-send_child_status(struct rconn *rconn, uint32_t xid, uint32_t status,
-                  const void *data, size_t size)
-{
-    if (rconn) {
-        struct nx_command_reply *r;
-        struct ofpbuf *buffer;
-
-        r = make_openflow_xid(sizeof *r, OFPT_VENDOR, xid, &buffer);
-        r->nxh.vendor = htonl(NX_VENDOR_ID);
-        r->nxh.subtype = htonl(NXT_COMMAND_REPLY);
-        r->status = htonl(status);
-        ofpbuf_put(buffer, data, size);
-        update_openflow_length(buffer);
-        if (rconn_send(rconn, buffer, NULL)) {
-            ofpbuf_delete(buffer);
-        }
-    }
-}
-
-static void
-send_child_message(struct rconn *rconn, uint32_t xid, uint32_t status,
-                   const char *message)
-{
-    send_child_status(rconn, xid, status, message, strlen(message));
-}
-
-/* 'child' died with 'status' as its return code.  Deal with it. */
-static void
-child_terminated(struct child *child, int status)
-{
-    struct ds ds;
-    uint32_t ofp_status;
-
-    /* Log how it terminated. */
-    ds_init(&ds);
-    if (WIFEXITED(status)) {
-        ds_put_format(&ds, "normally with status %d", WEXITSTATUS(status));
-    } else if (WIFSIGNALED(status)) {
-        const char *name = NULL;
-#ifdef HAVE_STRSIGNAL
-        name = strsignal(WTERMSIG(status));
-#endif
-        ds_put_format(&ds, "by signal %d", WTERMSIG(status));
-        if (name) {
-            ds_put_format(&ds, " (%s)", name);
-        }
-    }
-    if (WCOREDUMP(status)) {
-        ds_put_cstr(&ds, " (core dumped)");
-    }
-    VLOG_INFO("child process \"%s\" with pid %ld terminated %s",
-              child->name, (long int) child->pid, ds_cstr(&ds));
-    ds_destroy(&ds);
-
-    /* Send a status message back to the controller that requested the
-     * command. */
-    if (WIFEXITED(status)) {
-        ofp_status = WEXITSTATUS(status) | NXT_STATUS_EXITED;
-    } else if (WIFSIGNALED(status)) {
-        ofp_status = WTERMSIG(status) | NXT_STATUS_SIGNALED;
-    } else {
-        ofp_status = NXT_STATUS_UNKNOWN;
-    }
-    if (WCOREDUMP(status)) {
-        ofp_status |= NXT_STATUS_COREDUMP;
-    }
-    send_child_status(child->rconn, child->xid, ofp_status,
-                      child->output, child->output_size);
-}
-
-/* Read output from 'child' and append it to its output buffer. */
-static void
-poll_child(struct child *child)
-{
-    ssize_t n;
-
-    if (child->output_fd < 0) {
-        return;
-    }
-
-    do {
-        n = read(child->output_fd, child->output + child->output_size,
-                 MAX_OUTPUT - child->output_size);
-    } while (n < 0 && errno == EINTR);
-    if (n > 0) {
-        child->output_size += n;
-        if (child->output_size < MAX_OUTPUT) {
-            return;
-        }
-    } else if (n < 0 && errno == EAGAIN) {
-        return;
-    }
-    close(child->output_fd);
-    child->output_fd = -1;
-}
-
-void
-executer_run(struct executer *e)
-{
-    char buffer[MAX_CHILDREN];
-    size_t i;
-
-    if (!e->n_children) {
-        return;
-    }
-
-    /* Read output from children. */
-    for (i = 0; i < e->n_children; i++) {
-        struct child *child = &e->children[i];
-        poll_child(child);
-    }
-
-    /* If SIGCHLD was received, reap dead children. */
-    if (read(signal_fds[0], buffer, sizeof buffer) <= 0) {
-        return;
-    }
-    for (;;) {
-        int status;
-        pid_t pid;
-
-        /* Get dead child in 'pid' and its return code in 'status'. */
-        pid = waitpid(WAIT_ANY, &status, WNOHANG);
-        if (pid < 0 && errno == EINTR) {
-            continue;
-        } else if (pid <= 0) {
-            return;
-        }
-
-        /* Find child with given 'pid' and drop it from the list. */
-        for (i = 0; i < e->n_children; i++) {
-            struct child *child = &e->children[i];
-            if (child->pid == pid) {
-                poll_child(child);
-                child_terminated(child, status);
-                free(child->name);
-                free(child->output);
-                *child = e->children[--e->n_children];
-                goto found;
-            }
-        }
-        VLOG_WARN("child with unknown pid %ld terminated", (long int) pid);
-    found:;
-    }
-
-}
-
-void
-executer_wait(struct executer *e)
-{
-    if (e->n_children) {
-        size_t i;
-
-        /* Wake up on SIGCHLD. */
-        poll_fd_wait(signal_fds[0], POLLIN);
-
-        /* Wake up when we get output from a child. */
-        for (i = 0; i < e->n_children; i++) {
-            struct child *child = &e->children[i];
-            if (child->output_fd >= 0) {
-                poll_fd_wait(child->output_fd, POLLIN);
-            }
-        }
-    }
-}
-
-void
-executer_rconn_closing(struct executer *e, struct rconn *rconn)
-{
-    size_t i;
-
-    /* If any of our children was connected to 'r', then disconnect it so we
-     * don't try to reference a dead connection when the process terminates
-     * later.
-     * XXX kill the children started by 'r'? */
-    for (i = 0; i < e->n_children; i++) {
-        if (e->children[i].rconn == rconn) {
-            e->children[i].rconn = NULL;
-        }
-    }
-}
-
-static void
-sigchld_handler(int signr UNUSED)
-{
-    write(signal_fds[1], "", 1);
-}
-
-int
-executer_create(const char *command_acl, const char *command_dir,
-                struct executer **executerp)
-{
-    struct executer *e;
-    struct sigaction sa;
-
-    *executerp = NULL;
-    if (null_fd == -1) {
-        /* Create pipe for notifying us that SIGCHLD was invoked. */
-        if (pipe(signal_fds)) {
-            VLOG_ERR("pipe failed: %s", strerror(errno));
-            return errno;
-        }
-        set_nonblocking(signal_fds[0]);
-        set_nonblocking(signal_fds[1]);
-
-        /* Open /dev/null. */
-        null_fd = open("/dev/null", O_RDWR);
-        if (null_fd < 0) {
-            int error = errno;
-            VLOG_ERR("could not open /dev/null: %s", strerror(error));
-            close(signal_fds[0]);
-            close(signal_fds[1]);
-            return error;
-        }
-    }
-
-    /* Set up signal handler. */
-    memset(&sa, 0, sizeof sa);
-    sa.sa_handler = sigchld_handler;
-    sigemptyset(&sa.sa_mask);
-    sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
-    if (sigaction(SIGCHLD, &sa, NULL)) {
-        VLOG_ERR("sigaction(SIGCHLD) failed: %s", strerror(errno));
-        return errno;
-    }
-
-    e = xcalloc(1, sizeof *e);
-    e->command_acl = xstrdup(command_acl);
-    e->command_dir = (command_dir
-                      ? xstrdup(command_dir)
-                      : xasprintf("%s/commands", ovs_pkgdatadir));
-    e->n_children = 0;
-    *executerp = e;
-    return 0;
-}
-
-void
-executer_destroy(struct executer *e)
-{
-    if (e) {
-        size_t i;
-
-        free(e->command_acl);
-        free(e->command_dir);
-        for (i = 0; i < e->n_children; i++) {
-            struct child *child = &e->children[i];
-
-            free(child->name);
-            kill(child->pid, SIGHUP);
-            /* We don't own child->rconn. */
-            free(child->output);
-            free(child);
-        }
-        free(e);
-    }
-}
-
-void
-executer_set_acl(struct executer *e, const char *acl, const char *dir)
-{
-    free(e->command_acl);
-    e->command_acl = xstrdup(acl);
-    free(e->command_dir);
-    e->command_dir = xstrdup(dir);
-}