Create Service and ServiceManager classes

Change-Id: I363a5e4751ad83d2f4096882a6fbbeddca03acfe
diff --git a/init/init.cpp b/init/init.cpp
index ac4db7c..c94a6fe 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -32,7 +32,6 @@
 #include <sys/types.h>
 #include <sys/un.h>
 #include <sys/wait.h>
-#include <termios.h>
 #include <unistd.h>
 
 #include <mtd/mtd-user.h>
@@ -54,16 +53,17 @@
 #include <memory>
 
 #include "action.h"
+#include "bootchart.h"
 #include "devices.h"
 #include "init.h"
+#include "init_parser.h"
+#include "keychords.h"
 #include "log.h"
 #include "property_service.h"
-#include "bootchart.h"
+#include "service.h"
 #include "signal_handler.h"
-#include "keychords.h"
-#include "init_parser.h"
-#include "util.h"
 #include "ueventd.h"
+#include "util.h"
 #include "watchdogd.h"
 
 struct selabel_handle *sehandle;
@@ -73,11 +73,11 @@
 
 static char qemu[32];
 
-static int have_console;
-static std::string console_name = "/dev/console";
+int have_console;
+std::string console_name = "/dev/console";
 static time_t process_needs_restart;
 
-static const char *ENV[32];
+const char *ENV[32];
 
 bool waiting_for_exec = false;
 
@@ -92,27 +92,6 @@
     }
 }
 
-void service::NotifyStateChange(const char* new_state) {
-    if (!properties_initialized()) {
-        // 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)
 {
@@ -145,398 +124,76 @@
     return -1;
 }
 
-void zap_stdio(void)
-{
-    int fd;
-    fd = open("/dev/null", O_RDWR);
-    dup2(fd, 0);
-    dup2(fd, 1);
-    dup2(fd, 2);
-    close(fd);
-}
-
-static void open_console()
-{
-    int fd;
-    if ((fd = open(console_name.c_str(), O_RDWR)) < 0) {
-        fd = open("/dev/null", O_RDWR);
-    }
-    ioctl(fd, TIOCSCTTY, 0);
-    dup2(fd, 0);
-    dup2(fd, 1);
-    dup2(fd, 2);
-    close(fd);
-}
-
-static void publish_socket(const char *name, int fd)
-{
-    char key[64] = ANDROID_SOCKET_ENV_PREFIX;
-    char val[64];
-
-    strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
-            name,
-            sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
-    snprintf(val, sizeof(val), "%d", fd);
-    add_environment(key, val);
-
-    /* make sure we don't close-on-exec */
-    fcntl(fd, F_SETFD, 0);
-}
-
-void service_start(struct service *svc, const char *dynamic_args)
-{
-    // 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.
-    if (svc->flags & SVC_RUNNING) {
-        return;
-    }
-
-    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 sb;
-    if (stat(svc->args[0], &sb) == -1) {
-        ERROR("cannot find '%s' (%s), disabling '%s'\n", svc->args[0], strerror(errno), svc->name);
-        svc->flags |= SVC_DISABLED;
-        return;
-    }
-
-    if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
-        ERROR("service '%s' must be one-shot to use dynamic args, disabling\n", svc->args[0]);
-        svc->flags |= SVC_DISABLED;
-        return;
-    }
-
-    char* scon = NULL;
-    if (svc->seclabel) {
-        scon = strdup(svc->seclabel);
-        if (!scon) {
-            ERROR("Out of memory while starting '%s'\n", svc->name);
-            return;
-        }
-    } else {
-        char *mycon = NULL, *fcon = NULL;
-
-        INFO("computing context for service '%s'\n", svc->args[0]);
-        int rc = getcon(&mycon);
-        if (rc < 0) {
-            ERROR("could not get context while starting '%s'\n", svc->name);
-            return;
-        }
-
-        rc = getfilecon(svc->args[0], &fcon);
-        if (rc < 0) {
-            ERROR("could not get context while starting '%s'\n", svc->name);
-            free(mycon);
-            return;
-        }
-
-        rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
-        if (rc == 0 && !strcmp(scon, mycon)) {
-            ERROR("Service %s does not have a SELinux domain defined.\n", svc->name);
-            free(mycon);
-            free(fcon);
-            free(scon);
-            return;
-        }
-        free(mycon);
-        free(fcon);
-        if (rc < 0) {
-            ERROR("could not get context while starting '%s'\n", svc->name);
-            return;
-        }
-    }
-
-    NOTICE("Starting service '%s'...\n", svc->name);
-
-    pid_t pid = fork();
-    if (pid == 0) {
-        struct socketinfo *si;
-        struct svcenvinfo *ei;
-        char tmp[32];
-        int fd, sz;
-
-        umask(077);
-        if (properties_initialized()) {
-            get_property_workspace(&fd, &sz);
-            snprintf(tmp, sizeof(tmp), "%d,%d", dup(fd), sz);
-            add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
-        }
-
-        for (ei = svc->envvars; ei; ei = ei->next)
-            add_environment(ei->name, ei->value);
-
-        for (si = svc->sockets; si; si = si->next) {
-            int socket_type = (
-                    !strcmp(si->type, "stream") ? SOCK_STREAM :
-                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
-            int s = create_socket(si->name, socket_type,
-                                  si->perm, si->uid, si->gid, si->socketcon ?: scon);
-            if (s >= 0) {
-                publish_socket(si->name, s);
-            }
-        }
-
-        free(scon);
-        scon = NULL;
-
-        if (svc->writepid_files_) {
-            std::string pid_str = android::base::StringPrintf("%d", pid);
-            for (auto& file : *svc->writepid_files_) {
-                if (!android::base::WriteStringToFile(pid_str, file)) {
-                    ERROR("couldn't write %s to %s: %s\n",
-                          pid_str.c_str(), file.c_str(), strerror(errno));
-                }
-            }
-        }
-
-        if (svc->ioprio_class != IoSchedClass_NONE) {
-            if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
-                ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
-                      getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
-            }
-        }
-
-        if (needs_console) {
-            setsid();
-            open_console();
-        } else {
-            zap_stdio();
-        }
-
-        if (false) {
-            for (size_t n = 0; svc->args[n]; n++) {
-                INFO("args[%zu] = '%s'\n", n, svc->args[n]);
-            }
-            for (size_t n = 0; ENV[n]; n++) {
-                INFO("env[%zu] = '%s'\n", n, ENV[n]);
-            }
-        }
-
-        setpgid(0, getpid());
-
-        // As requested, set our gid, supplemental gids, and uid.
-        if (svc->gid) {
-            if (setgid(svc->gid) != 0) {
-                ERROR("setgid failed: %s\n", strerror(errno));
-                _exit(127);
-            }
-        }
-        if (svc->nr_supp_gids) {
-            if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
-                ERROR("setgroups failed: %s\n", strerror(errno));
-                _exit(127);
-            }
-        }
-        if (svc->uid) {
-            if (setuid(svc->uid) != 0) {
-                ERROR("setuid failed: %s\n", strerror(errno));
-                _exit(127);
-            }
-        }
-        if (svc->seclabel) {
-            if (setexeccon(svc->seclabel) < 0) {
-                ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
-                _exit(127);
-            }
-        }
-
-        if (!dynamic_args) {
-            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
-                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
-            }
-        } else {
-            char *arg_ptrs[INIT_PARSER_MAXARGS+1];
-            int arg_idx = svc->nargs;
-            char *tmp = strdup(dynamic_args);
-            char *next = tmp;
-            char *bword;
-
-            /* Copy the static arguments */
-            memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));
-
-            while((bword = strsep(&next, " "))) {
-                arg_ptrs[arg_idx++] = bword;
-                if (arg_idx == INIT_PARSER_MAXARGS)
-                    break;
-            }
-            arg_ptrs[arg_idx] = NULL;
-            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
-        }
-        _exit(127);
-    }
-
-    free(scon);
-
-    if (pid < 0) {
-        ERROR("failed to start '%s'\n", svc->name);
-        svc->pid = 0;
-        return;
-    }
-
-    svc->time_started = gettime();
-    svc->pid = pid;
-    svc->flags |= SVC_RUNNING;
-
-    if ((svc->flags & SVC_EXEC) != 0) {
-        INFO("SVC_EXEC pid %d (uid %d gid %d+%zu context %s) started; waiting...\n",
-             svc->pid, svc->uid, svc->gid, svc->nr_supp_gids,
-             svc->seclabel ? : "default");
-        waiting_for_exec = true;
-    }
-
-    svc->NotifyStateChange("running");
-}
-
-/* The how field should be either SVC_DISABLED, SVC_RESET, or SVC_RESTART */
-static void service_stop_or_reset(struct service *svc, int how)
-{
-    /* The service is still SVC_RUNNING until its process exits, but if it has
-     * already exited it shoudn't attempt a restart yet. */
-    svc->flags &= ~(SVC_RESTARTING | SVC_DISABLED_START);
-
-    if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
-        /* Hrm, an illegal flag.  Default to SVC_DISABLED */
-        how = SVC_DISABLED;
-    }
-        /* if the service has not yet started, prevent
-         * it from auto-starting with its class
-         */
-    if (how == SVC_RESET) {
-        svc->flags |= (svc->flags & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;
-    } else {
-        svc->flags |= how;
-    }
-
-    if (svc->pid) {
-        NOTICE("Service '%s' is being killed...\n", svc->name);
-        kill(-svc->pid, SIGKILL);
-        svc->NotifyStateChange("stopping");
-    } else {
-        svc->NotifyStateChange("stopped");
-    }
-}
-
-void service_reset(struct service *svc)
-{
-    service_stop_or_reset(svc, SVC_RESET);
-}
-
-void service_stop(struct service *svc)
-{
-    service_stop_or_reset(svc, SVC_DISABLED);
-}
-
-void service_restart(struct service *svc)
-{
-    if (svc->flags & SVC_RUNNING) {
-        /* Stop, wait, then start the service. */
-        service_stop_or_reset(svc, SVC_RESTART);
-    } else if (!(svc->flags & SVC_RESTARTING)) {
-        /* Just start the service since it's not running. */
-        service_start(svc, NULL);
-    } /* else: Service is restarting anyways. */
-}
-
 void property_changed(const char *name, const char *value)
 {
     if (property_triggers_enabled)
         ActionManager::GetInstance().QueuePropertyTrigger(name, value);
 }
 
-static void restart_service_if_needed(struct service *svc)
-{
-    time_t next_start_time = svc->time_started + 5;
-
-    if (next_start_time <= gettime()) {
-        svc->flags &= (~SVC_RESTARTING);
-        service_start(svc, NULL);
-        return;
-    }
-
-    if ((next_start_time < process_needs_restart) ||
-        (process_needs_restart == 0)) {
-        process_needs_restart = next_start_time;
-    }
-}
-
 static void restart_processes()
 {
     process_needs_restart = 0;
-    service_for_each_flags(SVC_RESTARTING,
-                           restart_service_if_needed);
+    ServiceManager::GetInstance().
+        ForEachServiceWithFlags(SVC_RESTARTING, [] (Service* s) {
+                s->RestartIfNeeded(process_needs_restart);
+            });
 }
 
-static void msg_start(const char *name)
+static void msg_start(const std::string& name)
 {
-    struct service *svc = NULL;
-    char *tmp = NULL;
-    char *args = NULL;
+    Service* svc = nullptr;
+    std::vector<std::string> vargs;
 
-    if (!strchr(name, ':'))
-        svc = service_find_by_name(name);
-    else {
-        tmp = strdup(name);
-        if (tmp) {
-            args = strchr(tmp, ':');
-            *args = '\0';
-            args++;
+    size_t colon_pos = name.find(':');
+    if (colon_pos == std::string::npos) {
+        svc = ServiceManager::GetInstance().FindServiceByName(name);
+    } else {
+        std::string service_name(name.substr(0, colon_pos));
+        std::string args(name.substr(colon_pos + 1));
+        vargs = android::base::Split(args, " ");
 
-            svc = service_find_by_name(tmp);
-        }
+        svc = ServiceManager::GetInstance().FindServiceByName(service_name);
     }
 
     if (svc) {
-        service_start(svc, args);
+        svc->Start(vargs);
     } else {
-        ERROR("no such service '%s'\n", name);
+        ERROR("no such service '%s'\n", name.c_str());
     }
-    if (tmp)
-        free(tmp);
 }
 
-static void msg_stop(const char *name)
+static void msg_stop(const std::string& name)
 {
-    struct service *svc = service_find_by_name(name);
+    Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
 
     if (svc) {
-        service_stop(svc);
+        svc->Stop();
     } else {
-        ERROR("no such service '%s'\n", name);
+        ERROR("no such service '%s'\n", name.c_str());
     }
 }
 
-static void msg_restart(const char *name)
+static void msg_restart(const std::string& name)
 {
-    struct service *svc = service_find_by_name(name);
+    Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
 
     if (svc) {
-        service_restart(svc);
+        svc->Restart();
     } else {
-        ERROR("no such service '%s'\n", name);
+        ERROR("no such service '%s'\n", name.c_str());
     }
 }
 
-void handle_control_message(const char *msg, const char *arg)
+void handle_control_message(const std::string& msg, const std::string& arg)
 {
-    if (!strcmp(msg,"start")) {
+    if (msg == "start") {
         msg_start(arg);
-    } else if (!strcmp(msg,"stop")) {
+    } else if (msg == "stop") {
         msg_stop(arg);
-    } else if (!strcmp(msg,"restart")) {
+    } else if (msg == "restart") {
         msg_restart(arg);
     } else {
-        ERROR("unknown control msg '%s'\n", msg);
+        ERROR("unknown control msg '%s'\n", msg.c_str());
     }
 }