Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a4d98484 authored by Elliott Hughes's avatar Elliott Hughes Committed by Gerrit Code Review
Browse files

Merge "Implement exec."

parents 9fabbbfb 8d82ea05
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@ include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_MODULE := init_tests
LOCAL_SRC_FILES := \
    init_parser_test.cpp \
    util_test.cpp \

LOCAL_SHARED_LIBRARIES += \
+8 −3
Original line number Diff line number Diff line
@@ -172,11 +172,16 @@ int do_enable(int nargs, char **args)
    return 0;
}

int do_exec(int nargs, char **args)
{
int do_exec(int nargs, char** args) {
    service* svc = make_exec_oneshot_service(nargs, args);
    if (svc == NULL) {
        return -1;
    }
    service_start(svc, NULL);
    return 0;
}

// TODO: remove execonce when exec is available.
int do_execonce(int nargs, char **args)
{
    pid_t child;
+94 −92
Original line number Diff line number Diff line
@@ -14,37 +14,37 @@
 * limitations under the License.
 */

#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/poll.h>
#include <errno.h>
#include <stdarg.h>
#include <mtd/mtd-user.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>

#include <mtd/mtd-user.h>

#include <selinux/selinux.h>
#include <selinux/label.h>
#include <selinux/android.h>

#include <libgen.h>

#include <cutils/list.h>
#include <cutils/android_reboot.h>
#include <cutils/sockets.h>
#include <cutils/iosched_policy.h>
#include <cutils/fs.h>
#include <cutils/iosched_policy.h>
#include <cutils/list.h>
#include <cutils/sockets.h>
#include <private/android_filesystem_config.h>
#include <termios.h>

#include "devices.h"
#include "init.h"
@@ -72,22 +72,35 @@ static char qemu[32];
static struct action *cur_action = NULL;
static struct command *cur_command = NULL;

void notify_service_state(const char *name, const char *state)
{
    char pname[PROP_NAME_MAX];
    int len = strlen(name);
    if ((len + 10) > PROP_NAME_MAX)
        return;
    snprintf(pname, sizeof(pname), "init.svc.%s", name);
    property_set(pname, state);
}

static int have_console;
static char console_name[PROP_VALUE_MAX] = "/dev/console";
static time_t process_needs_restart;

static const char *ENV[32];

bool waiting_for_exec = false;

void service::NotifyStateChange(const char* new_state) {
    if (!properties_inited()) {
        // If properties aren't available yet, we can't set them.
        return;
    }

    if ((flags & SVC_EXEC) != 0) {
        // 'exec' commands don't have properties tracking their state.
        return;
    }

    char prop_name[PROP_NAME_MAX];
    if (snprintf(prop_name, sizeof(prop_name), "init.svc.%s", name) >= PROP_NAME_MAX) {
        // If the property name would be too long, we can't set it.
        ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n", name, new_state);
        return;
    }

    property_set(prop_name, new_state);
}

/* add_environment - add "key=value" to the current environment */
int add_environment(const char *key, const char *val)
{
@@ -160,35 +173,26 @@ static void publish_socket(const char *name, int fd)

void service_start(struct service *svc, const char *dynamic_args)
{
    struct stat s;
    pid_t pid;
    int needs_console;
    char *scon = NULL;
    int rc;

        /* starting a service removes it from the disabled or reset
         * state and immediately takes it out of the restarting
         * state if it was in there
         */
    // Starting a service removes it from the disabled or reset state and
    // immediately takes it out of the restarting state if it was in there.
    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
    svc->time_started = 0;

        /* running processes require no additional work -- if
         * they're in the process of exiting, we've ensured
         * that they will immediately restart on exit, unless
         * they are ONESHOT
         */
    // Running processes require no additional work --- if they're in the
    // process of exiting, we've ensured that they will immediately restart
    // on exit, unless they are ONESHOT.
    if (svc->flags & SVC_RUNNING) {
        return;
    }

    needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;
    if (needs_console && (!have_console)) {
    bool needs_console = (svc->flags & SVC_CONSOLE);
    if (needs_console && !have_console) {
        ERROR("service '%s' requires console\n", svc->name);
        svc->flags |= SVC_DISABLED;
        return;
    }

    struct stat s;
    if (stat(svc->args[0], &s) != 0) {
        ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
        svc->flags |= SVC_DISABLED;
@@ -202,6 +206,7 @@ void service_start(struct service *svc, const char *dynamic_args)
        return;
    }

    char* scon = NULL;
    if (is_selinux_enabled() > 0) {
        if (svc->seclabel) {
            scon = strdup(svc->seclabel);
@@ -213,7 +218,7 @@ void service_start(struct service *svc, const char *dynamic_args)
            char *mycon = NULL, *fcon = NULL;

            INFO("computing context for service '%s'\n", svc->args[0]);
            rc = getcon(&mycon);
            int rc = getcon(&mycon);
            if (rc < 0) {
                ERROR("could not get context while starting '%s'\n", svc->name);
                return;
@@ -241,8 +246,7 @@ void service_start(struct service *svc, const char *dynamic_args)

    NOTICE("starting '%s'\n", svc->name);

    pid = fork();

    pid_t pid = fork();
    if (pid == 0) {
        struct socketinfo *si;
        struct svcenvinfo *ei;
@@ -298,7 +302,7 @@ void service_start(struct service *svc, const char *dynamic_args)

        setpgid(0, getpid());

    /* as requested, set our gid, supplemental gids, and uid */
        // As requested, set our gid, supplemental gids, and uid.
        if (svc->gid) {
            if (setgid(svc->gid) != 0) {
                ERROR("setgid failed: %s\n", strerror(errno));
@@ -361,8 +365,13 @@ void service_start(struct service *svc, const char *dynamic_args)
    svc->pid = pid;
    svc->flags |= SVC_RUNNING;

    if (properties_inited())
        notify_service_state(svc->name, "running");
    if ((svc->flags & SVC_EXEC) != 0) {
        INFO("SVC_EXEC pid %d (uid %d gid %d+%d context %s) started; waiting...\n",
             svc->pid, svc->uid, svc->gid, svc->nr_supp_gids, svc->seclabel);
        waiting_for_exec = true;
    }

    svc->NotifyStateChange("running");
}

/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */
@@ -388,9 +397,9 @@ static void service_stop_or_reset(struct service *svc, int how)
    if (svc->pid) {
        NOTICE("service '%s' is being killed\n", svc->name);
        kill(-svc->pid, SIGKILL);
        notify_service_state(svc->name, "stopping");
        svc->NotifyStateChange("stopping");
    } else {
        notify_service_state(svc->name, "stopped");
        svc->NotifyStateChange("stopped");
    }
}

@@ -969,28 +978,18 @@ static void selinux_initialize(void)
    security_setenforce(is_enforcing);
}

int main(int argc, char **argv)
{
    size_t fd_count = 0;
    struct pollfd ufds[4];
    int property_set_fd_init = 0;
    int signal_fd_init = 0;
    int keychord_fd_init = 0;
    bool is_charger = false;

int main(int argc, char** argv) {
    if (!strcmp(basename(argv[0]), "ueventd"))
        return ueventd_main(argc, argv);

    if (!strcmp(basename(argv[0]), "watchdogd"))
        return watchdogd_main(argc, argv);

    /* clear the umask */
    // Clear the umask.
    umask(0);

        /* Get the basic filesystem setup we need put
         * together in the initramdisk on / and then we'll
         * let the rc file figure out the rest.
         */
    // Get the basic filesystem setup we need put together in the initramdisk
    // on / and then we'll let the rc file figure out the rest.
    mkdir("/dev", 0755);
    mkdir("/proc", 0755);
    mkdir("/sys", 0755);
@@ -1002,15 +1001,13 @@ int main(int argc, char **argv)
    mount("proc", "/proc", "proc", 0, NULL);
    mount("sysfs", "/sys", "sysfs", 0, NULL);

        /* indicate that booting is in progress to background fw loaders, etc */
    // Indicate that booting is in progress to background fw loaders, etc.
    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));

        /* We must have some place other than / to create the
         * device nodes for kmsg and null, otherwise we won't
         * be able to remount / read-only later on.
         * Now that tmpfs is mounted on /dev, we can actually
         * talk to the outside world.
         */
    // We must have some place other than / to create the device nodes for
    // kmsg and null, otherwise we won't be able to remount / read-only
    // later on. Now that tmpfs is mounted on /dev, we can actually talk
    // to the outside world.
    open_devnull_stdio();
    klog_init();
    property_init();
@@ -1019,25 +1016,22 @@ int main(int argc, char **argv)

    process_kernel_cmdline();

    union selinux_callback cb;
    selinux_callback cb;
    cb.func_log = log_callback;
    selinux_set_callback(SELINUX_CB_LOG, cb);

    cb.func_audit = audit_callback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);

    selinux_initialize();
    /* These directories were necessarily created before initial policy load
     * and therefore need their security context restored to the proper value.
     * This must happen before /dev is populated by ueventd.
     */

    // These directories were necessarily created before initial policy load
    // and therefore need their security context restored to the proper value.
    // This must happen before /dev is populated by ueventd.
    restorecon("/dev");
    restorecon("/dev/socket");
    restorecon("/dev/__properties__");
    restorecon_recursive("/sys");

    is_charger = !strcmp(bootmode, "charger");

    INFO("property init\n");
    property_load_boot_defaults();

@@ -1050,50 +1044,58 @@ int main(int argc, char **argv)
    queue_builtin_action(keychord_init_action, "keychord_init");
    queue_builtin_action(console_init_action, "console_init");

    /* execute all the boot actions to get us started */
    // Execute all the boot actions to get us started.
    action_for_each_trigger("init", action_add_queue_tail);

    /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
     * wasn't ready immediately after wait_for_coldboot_done
     */
    // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
    // wasn't ready immediately after wait_for_coldboot_done
    queue_builtin_action(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    queue_builtin_action(property_service_init_action, "property_service_init");
    queue_builtin_action(signal_init_action, "signal_init");

    /* Don't mount filesystems or start core system services if in charger mode. */
    if (is_charger) {
    // Don't mount filesystems or start core system services in charger mode.
    if (strcmp(bootmode, "charger") == 0) {
        action_for_each_trigger("charger", action_add_queue_tail);
    } else {
        action_for_each_trigger("late-init", action_add_queue_tail);
    }

    /* run all property triggers based on current state of the properties */
    // Run all property triggers based on current state of the properties.
    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");

    // TODO: why do we only initialize ufds after execute_one_command and restart_processes?
    size_t fd_count = 0;
    struct pollfd ufds[3];
    bool property_set_fd_init = false;
    bool signal_fd_init = false;
    bool keychord_fd_init = false;

    for (;;) {
        if (!waiting_for_exec) {
            execute_one_command();
            restart_processes();
        }

        if (!property_set_fd_init && get_property_set_fd() > 0) {
            ufds[fd_count].fd = get_property_set_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            property_set_fd_init = 1;
            property_set_fd_init = true;
        }
        if (!signal_fd_init && get_signal_fd() > 0) {
            ufds[fd_count].fd = get_signal_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            signal_fd_init = 1;
            signal_fd_init = true;
        }
        if (!keychord_fd_init && get_keychord_fd() > 0) {
            ufds[fd_count].fd = get_keychord_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            keychord_fd_init = 1;
            keychord_fd_init = true;
        }

        int timeout = -1;
+28 −25
Original line number Diff line number Diff line
@@ -17,13 +17,11 @@
#ifndef _INIT_INIT_H
#define _INIT_INIT_H

#include <sys/types.h>

#include <cutils/list.h>
#include <cutils/iosched_policy.h>

#include <sys/stat.h>

void handle_control_message(const char *msg, const char *arg);

struct command
{
        /* list of commands in an action */
@@ -59,8 +57,6 @@ struct action {
    struct command *current;
};

void build_triggers_string(char *name_str, int length, struct action *cur_action);

struct socketinfo {
    struct socketinfo *next;
    const char *name;
@@ -77,27 +73,29 @@ struct svcenvinfo {
    const char *value;
};

#define SVC_DISABLED    0x01  /* do not autostart with class */
#define SVC_ONESHOT     0x02  /* do not restart on exit */
#define SVC_RUNNING     0x04  /* currently active */
#define SVC_RESTARTING  0x08  /* waiting to restart */
#define SVC_CONSOLE     0x10  /* requires console */
#define SVC_CRITICAL    0x20  /* will reboot into recovery if keeps crashing */
#define SVC_RESET       0x40  /* Use when stopping a process, but not disabling
                                 so it can be restarted with its class */
#define SVC_RC_DISABLED 0x80  /* Remember if the disabled flag was set in the rc script */
#define SVC_RESTART     0x100 /* Use to safely restart (stop, wait, start) a service */
#define SVC_DISABLED_START 0x200 /* a start was requested but it was disabled at the time */
#define SVC_DISABLED       0x001  // do not autostart with class
#define SVC_ONESHOT        0x002  // do not restart on exit
#define SVC_RUNNING        0x004  // currently active
#define SVC_RESTARTING     0x008  // waiting to restart
#define SVC_CONSOLE        0x010  // requires console
#define SVC_CRITICAL       0x020  // will reboot into recovery if keeps crashing
#define SVC_RESET          0x040  // Use when stopping a process, but not disabling so it can be restarted with its class.
#define SVC_RC_DISABLED    0x080  // Remember if the disabled flag was set in the rc script.
#define SVC_RESTART        0x100  // Use to safely restart (stop, wait, start) a service.
#define SVC_DISABLED_START 0x200  // A start was requested but it was disabled at the time.
#define SVC_EXEC           0x400  // This synthetic service corresponds to an 'exec'.

#define NR_SVC_SUPP_GIDS 12    /* twelve supplementary groups */

#define COMMAND_RETRY_TIMEOUT 5

struct service {
    void NotifyStateChange(const char* new_state);

        /* list of all services */
    struct listnode slist;

    const char *name;
    char *name;
    const char *classname;

    unsigned flags;
@@ -111,7 +109,7 @@ struct service {
    gid_t supp_gids[NR_SVC_SUPP_GIDS];
    size_t nr_supp_gids;

    char *seclabel;
    const char* seclabel;

    struct socketinfo *sockets;
    struct svcenvinfo *envvars;
@@ -131,7 +129,13 @@ struct service {
    char *args[1];
}; /*     ^-------'args' MUST be at the end of this struct! */

void notify_service_state(const char *name, const char *state);
extern bool waiting_for_exec;
extern struct selabel_handle *sehandle;
extern struct selabel_handle *sehandle_prop;

void build_triggers_string(char *name_str, int length, struct action *cur_action);

void handle_control_message(const char *msg, const char *arg);

struct service *service_find_by_name(const char *name);
struct service *service_find_by_pid(pid_t pid);
@@ -147,9 +151,8 @@ void service_restart(struct service *svc);
void service_start(struct service *svc, const char *dynamic_args);
void property_changed(const char *name, const char *value);

extern struct selabel_handle *sehandle;
extern struct selabel_handle *sehandle_prop;
extern int selinux_reload_policy(void);
int selinux_reload_policy(void);

void zap_stdio(void);

#endif	/* _INIT_INIT_H */
+62 −2
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
 * limitations under the License.
 */

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -440,7 +441,7 @@ parser_done:
}

int init_parse_config_file(const char* path) {
    INFO("Parsing %s...", path);
    INFO("Parsing %s...\n", path);
    std::string data;
    if (!read_file(path, &data)) {
        return -1;
@@ -663,6 +664,65 @@ int action_queue_empty()
    return list_empty(&action_queue);
}

service* make_exec_oneshot_service(int nargs, char** args) {
    // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
    int command_arg = 1;
    for (int i = 1; i < nargs; ++i) {
        if (strcmp(args[i], "--") == 0) {
            command_arg = i + 1;
            break;
        }
    }
    if (command_arg > 4 + NR_SVC_SUPP_GIDS) {
        ERROR("exec called with too many supplementary group ids\n");
        return NULL;
    }

    int argc = nargs - command_arg;
    char** argv = (args + command_arg);
    if (argc < 1) {
        ERROR("exec called without command\n");
        return NULL;
    }

    service* svc = (service*) calloc(1, sizeof(*svc) + sizeof(char*) * argc);
    if (svc == NULL) {
        ERROR("Couldn't allocate service for exec of '%s': %s", argv[0], strerror(errno));
        return NULL;
    }

    if (command_arg > 2) {
        svc->seclabel = args[1];
    }
    if (command_arg > 3) {
        svc->uid = decode_uid(args[2]);
    }
    if (command_arg > 4) {
        svc->gid = decode_uid(args[3]);
        svc->nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;
        for (size_t i = 0; i < svc->nr_supp_gids; ++i) {
            svc->supp_gids[i] = decode_uid(args[4 + i]);
        }
    }

    static int exec_count; // Every service needs a unique name.
    char* name = NULL;
    asprintf(&name, "exec %d (%s)", exec_count++, argv[0]);
    if (name == NULL) {
        ERROR("Couldn't allocate name for exec service '%s'\n", argv[0]);
        free(svc);
        return NULL;
    }
    svc->name = name;
    svc->classname = "default";
    svc->flags = SVC_EXEC | SVC_ONESHOT;
    svc->nargs = argc;
    memcpy(svc->args, argv, sizeof(char*) * svc->nargs);
    svc->args[argc] = NULL;
    list_add_tail(&service_list, &svc->slist);
    return svc;
}

static void *parse_service(struct parse_state *state, int nargs, char **args)
{
    if (nargs < 3) {
@@ -686,7 +746,7 @@ static void *parse_service(struct parse_state *state, int nargs, char **args)
        parse_error(state, "out of memory\n");
        return 0;
    }
    svc->name = args[1];
    svc->name = strdup(args[1]);
    svc->classname = "default";
    memcpy(svc->args, args + 2, sizeof(char*) * nargs);
    trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
Loading