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

Commit 47145db4 authored by Dan Albert's avatar Dan Albert
Browse files

resolved conflicts for merge of 5ebc2f5b to master

Change-Id: I9fdb437051e2f1c9afef13101ae40f881c4c6f19
parents bdd7cbd8 5ebc2f5b
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -75,6 +75,7 @@ include $(BUILD_EXECUTABLE)
include $(CLEAR_VARS)
include $(CLEAR_VARS)
LOCAL_MODULE := init_tests
LOCAL_MODULE := init_tests
LOCAL_SRC_FILES := \
LOCAL_SRC_FILES := \
    init_parser_test.cpp \
    util_test.cpp \
    util_test.cpp \


LOCAL_SHARED_LIBRARIES += \
LOCAL_SHARED_LIBRARIES += \
+8 −3
Original line number Original line Diff line number Diff line
@@ -172,11 +172,16 @@ int do_enable(int nargs, char **args)
    return 0;
    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;
        return -1;
    }
    }
    service_start(svc, NULL);
    return 0;
}


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


#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
#include <string.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/mount.h>
#include <sys/stat.h>
#include <sys/poll.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/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/un.h>
#include <dirent.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>


#include <memory>
#include <memory>


#include <mtd/mtd-user.h>

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


#include <libgen.h>

#include <cutils/list.h>
#include <cutils/android_reboot.h>
#include <cutils/android_reboot.h>
#include <cutils/sockets.h>
#include <cutils/iosched_policy.h>
#include <cutils/fs.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 <private/android_filesystem_config.h>
#include <termios.h>
#include <utils/file.h>
#include <utils/file.h>
#include <utils/stringprintf.h>
#include <utils/stringprintf.h>


@@ -75,22 +75,35 @@ static char qemu[32];
static struct action *cur_action = NULL;
static struct action *cur_action = NULL;
static struct command *cur_command = 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 int have_console;
static char console_name[PROP_VALUE_MAX] = "/dev/console";
static char console_name[PROP_VALUE_MAX] = "/dev/console";
static time_t process_needs_restart;
static time_t process_needs_restart;


static const char *ENV[32];
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 */
/* add_environment - add "key=value" to the current environment */
int add_environment(const char *key, const char *val)
int add_environment(const char *key, const char *val)
{
{
@@ -163,35 +176,26 @@ static void publish_socket(const char *name, int fd)


void service_start(struct service *svc, const char *dynamic_args)
void service_start(struct service *svc, const char *dynamic_args)
{
{
    struct stat s;
    // Starting a service removes it from the disabled or reset state and
    pid_t pid;
    // immediately takes it out of the restarting state if it was in there.
    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
         */
    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
    svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
    svc->time_started = 0;
    svc->time_started = 0;


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


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


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


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


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


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


    pid = fork();
    pid_t pid = fork();

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


        setpgid(0, getpid());
        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 (svc->gid) {
            if (setgid(svc->gid) != 0) {
            if (setgid(svc->gid) != 0) {
                ERROR("setgid failed: %s\n", strerror(errno));
                ERROR("setgid failed: %s\n", strerror(errno));
@@ -364,8 +368,13 @@ void service_start(struct service *svc, const char *dynamic_args)
    svc->pid = pid;
    svc->pid = pid;
    svc->flags |= SVC_RUNNING;
    svc->flags |= SVC_RUNNING;


    if (properties_inited())
    if ((svc->flags & SVC_EXEC) != 0) {
        notify_service_state(svc->name, "running");
        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 */
/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */
@@ -391,9 +400,9 @@ static void service_stop_or_reset(struct service *svc, int how)
    if (svc->pid) {
    if (svc->pid) {
        NOTICE("service '%s' is being killed\n", svc->name);
        NOTICE("service '%s' is being killed\n", svc->name);
        kill(-svc->pid, SIGKILL);
        kill(-svc->pid, SIGKILL);
        notify_service_state(svc->name, "stopping");
        svc->NotifyStateChange("stopping");
    } else {
    } else {
        notify_service_state(svc->name, "stopped");
        svc->NotifyStateChange("stopped");
    }
    }
}
}


@@ -993,28 +1002,18 @@ static void selinux_initialize(void)
    security_setenforce(is_enforcing);
    security_setenforce(is_enforcing);
}
}


int main(int argc, char **argv)
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;

    if (!strcmp(basename(argv[0]), "ueventd"))
    if (!strcmp(basename(argv[0]), "ueventd"))
        return ueventd_main(argc, argv);
        return ueventd_main(argc, argv);


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


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


        /* Get the basic filesystem setup we need put
    // Get the basic filesystem setup we need put together in the initramdisk
         * together in the initramdisk on / and then we'll
    // on / and then we'll let the rc file figure out the rest.
         * let the rc file figure out the rest.
         */
    mkdir("/dev", 0755);
    mkdir("/dev", 0755);
    mkdir("/proc", 0755);
    mkdir("/proc", 0755);
    mkdir("/sys", 0755);
    mkdir("/sys", 0755);
@@ -1026,15 +1025,13 @@ int main(int argc, char **argv)
    mount("proc", "/proc", "proc", 0, NULL);
    mount("proc", "/proc", "proc", 0, NULL);
    mount("sysfs", "/sys", "sysfs", 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));
    close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));


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


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

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


    selinux_initialize();
    selinux_initialize();
    /* These directories were necessarily created before initial policy load

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


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

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


@@ -1081,50 +1075,58 @@ int main(int argc, char **argv)
    queue_builtin_action(keychord_init_action, "keychord_init");
    queue_builtin_action(keychord_init_action, "keychord_init");
    queue_builtin_action(console_init_action, "console_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);
    action_for_each_trigger("init", action_add_queue_tail);


    /* Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
    // Repeat mix_hwrng_into_linux_rng in case /dev/hw_random or /dev/random
     * wasn't ready immediately after wait_for_coldboot_done
    // 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(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
    queue_builtin_action(property_service_init_action, "property_service_init");
    queue_builtin_action(property_service_init_action, "property_service_init");
    queue_builtin_action(signal_init_action, "signal_init");
    queue_builtin_action(signal_init_action, "signal_init");


    /* Don't mount filesystems or start core system services if in charger mode. */
    // Don't mount filesystems or start core system services in charger mode.
    if (is_charger) {
    if (strcmp(bootmode, "charger") == 0) {
        action_for_each_trigger("charger", action_add_queue_tail);
        action_for_each_trigger("charger", action_add_queue_tail);
    } else {
    } else {
        action_for_each_trigger("late-init", action_add_queue_tail);
        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");
    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 (;;) {
    for (;;) {
        if (!waiting_for_exec) {
            execute_one_command();
            execute_one_command();
            restart_processes();
            restart_processes();
        }


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


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


#include <sys/types.h>

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


#include <sys/stat.h>

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

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


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

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


#define SVC_DISABLED    0x01  /* do not autostart with class */
#define SVC_DISABLED       0x001  // do not autostart with class
#define SVC_ONESHOT     0x02  /* do not restart on exit */
#define SVC_ONESHOT        0x002  // do not restart on exit
#define SVC_RUNNING     0x04  /* currently active */
#define SVC_RUNNING        0x004  // currently active
#define SVC_RESTARTING  0x08  /* waiting to restart */
#define SVC_RESTARTING     0x008  // waiting to restart
#define SVC_CONSOLE     0x10  /* requires console */
#define SVC_CONSOLE        0x010  // requires console
#define SVC_CRITICAL    0x20  /* will reboot into recovery if keeps crashing */
#define SVC_CRITICAL       0x020  // will reboot into recovery if keeps crashing
#define SVC_RESET       0x40  /* Use when stopping a process, but not disabling
#define SVC_RESET          0x040  // Use when stopping a process, but not disabling so it can be restarted with its class.
                                 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_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_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_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 NR_SVC_SUPP_GIDS 12    /* twelve supplementary groups */


#define COMMAND_RETRY_TIMEOUT 5
#define COMMAND_RETRY_TIMEOUT 5


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

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


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


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


    char *seclabel;
    const char* seclabel;


    struct socketinfo *sockets;
    struct socketinfo *sockets;
    struct svcenvinfo *envvars;
    struct svcenvinfo *envvars;
@@ -131,7 +129,13 @@ struct service {
    char *args[1];
    char *args[1];
}; /*     ^-------'args' MUST be at the end of this struct! */
}; /*     ^-------'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_name(const char *name);
struct service *service_find_by_pid(pid_t pid);
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 service_start(struct service *svc, const char *dynamic_args);
void property_changed(const char *name, const char *value);
void property_changed(const char *name, const char *value);


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

extern int selinux_reload_policy(void);
void zap_stdio(void);
void zap_stdio(void);


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


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


int init_parse_config_file(const char* path) {
int init_parse_config_file(const char* path) {
    INFO("Parsing %s...", path);
    INFO("Parsing %s...\n", path);
    std::string data;
    std::string data;
    if (!read_file(path, &data)) {
    if (!read_file(path, &data)) {
        return -1;
        return -1;
@@ -663,6 +664,65 @@ int action_queue_empty()
    return list_empty(&action_queue);
    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)
static void *parse_service(struct parse_state *state, int nargs, char **args)
{
{
    if (nargs < 3) {
    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");
        parse_error(state, "out of memory\n");
        return 0;
        return 0;
    }
    }
    svc->name = args[1];
    svc->name = strdup(args[1]);
    svc->classname = "default";
    svc->classname = "default";
    memcpy(svc->args, args + 2, sizeof(char*) * nargs);
    memcpy(svc->args, args + 2, sizeof(char*) * nargs);
    trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
    trigger* cur_trigger = (trigger*) calloc(1, sizeof(*cur_trigger));
Loading