void service_start(struct service *svc, const char *dynamic_args) { struct stat s; pid_t pid; int needs_console; int n; #ifdef HAVE_SELINUX char *scon = NULL; int rc; #endif /* 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->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; } needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0; if (needs_console && (!have_console)) { ERROR("service '%s' requires console\n", svc->name); svc->flags |= SVC_DISABLED; return; } if (stat(svc->args[0], &s) != 0) { ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], 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; } #ifdef HAVE_SELINUX if (is_selinux_enabled() > 0) { char *mycon = NULL, *fcon = NULL; INFO("computing context for service '%s'\n", svc->args[0]); 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); freecon(mycon); return; } rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon); freecon(mycon); freecon(fcon); if (rc < 0) { ERROR("could not get context while starting '%s'\n", svc->name); return; } } #endif NOTICE("starting '%s'\n", svc->name); pid = fork(); if (pid == 0) { struct socketinfo *si; struct svcenvinfo *ei; char tmp[32]; int fd, sz; umask(077); #ifdef __arm__ /* * b/7188322 - Temporarily revert to the compat memory layout * to avoid breaking third party apps. * * THIS WILL GO AWAY IN A FUTURE ANDROID RELEASE. * * http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=7dbaa466 * changes the kernel mapping from bottom up to top-down. * This breaks some programs which improperly embed * an out of date copy of Android's linker. */ int current = personality(0xffffFFFF); personality(current | ADDR_COMPAT_LAYOUT); #endif if (properties_inited()) { get_property_workspace(&fd, &sz); sprintf(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); #ifdef HAVE_SELINUX setsockcreatecon(scon); #endif 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); if (s >= 0) { publish_socket(si->name, s); } } #ifdef HAVE_SELINUX freecon(scon); scon = NULL; setsockcreatecon(NULL); #endif 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 0 for (n = 0; svc->args[n]; n++) { INFO("args[%d] = '%s'\n", n, svc->args[n]); } for (n = 0; ENV[n]; n++) { INFO("env[%d] = '%s'\n", n, ENV[n]); } #endif 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); } } #ifdef HAVE_SELINUX if (svc->seclabel) { if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) { ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno)); _exit(127); } } #endif 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] = '\0'; execve(svc->args[0], (char**) arg_ptrs, (char**) ENV); } _exit(127); } #ifdef HAVE_SELINUX freecon(scon); #endif 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 (properties_inited()) notify_service_state(svc->name, "running"); }
void service_start(struct service *svc, const char *dynamic_args) { struct stat s; pid_t pid; int needs_console; int n; /* 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->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; } needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0; if (needs_console && (!have_console)) { ERROR("service '%s' requires console\n", svc->name); svc->flags |= SVC_DISABLED; return; } if (stat(svc->args[0], &s) != 0) { ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], 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; } NOTICE("starting '%s'\n", svc->name); pid = fork(); if (pid == 0) { struct socketinfo *si; struct svcenvinfo *ei; char tmp[32]; int fd, sz; if (properties_inited()) { get_property_workspace(&fd, &sz); sprintf(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); if (s >= 0) { publish_socket(si->name, s); } } 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 0 for (n = 0; svc->args[n]; n++) { INFO("args[%d] = '%s'\n", n, svc->args[n]); } for (n = 0; ENV[n]; n++) { INFO("env[%d] = '%s'\n", n, ENV[n]); } #endif 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 (!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] = '\0'; execve(svc->args[0], (char**) arg_ptrs, (char**) ENV); } _exit(127); } 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 (properties_inited()) notify_service_state(svc->name, "running"); }