📄 init.c
字号:
/* * Copyright (C) 2008 The Android Open Source Project * * 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 <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 <time.h>#include <errno.h>#include <stdarg.h>#include <mtd/mtd-user.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/un.h>#include <sys/reboot.h>#include <cutils/sockets.h>#include <termios.h>#include <linux/kd.h>#include <sys/system_properties.h>#include "devices.h"#include "init.h"#include "property_service.h"#ifndef BOOTCHART# define BOOTCHART 0#endifstatic int property_triggers_enabled = 0;#if BOOTCHARTstatic int bootchart_count;extern int bootchart_init(void);extern int bootchart_step(void);extern void bootchart_finish(void);# define BOOTCHART_POLLING_MS 200 /* polling period in ms */# define BOOTCHART_MAX_TIME_MS (2*60*1000) /* max polling time from boot */# define BOOTCHART_MAX_COUNT (BOOTCHART_MAX_TIME_MS/BOOTCHART_POLLING_MS)#endifstatic char console[32];static char serialno[32];static char bootmode[32];static char baseband[32];static char carrier[32];static char bootloader[32];static char hardware[32];static unsigned revision = 0;static char qemu[32];static void drain_action_queue(void);static 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 = "/dev/console";static time_t process_needs_restart;static const char *ENV[32];/* add_environment - add "key=value" to the current environment */int add_environment(const char *key, const char *val){ int n; for (n = 0; n < 31; n++) { if (!ENV[n]) { size_t len = strlen(key) + strlen(val) + 2; char *entry = malloc(len); snprintf(entry, len, "%s=%s", key, val); ENV[n] = entry; return 0; } } return 1;}static 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, O_RDWR)) < 0) { fd = open("/dev/null", O_RDWR); } dup2(fd, 0); dup2(fd, 1); dup2(fd, 2); close(fd);}/* * gettime() - returns the time in seconds of the system's monotonic clock or * zero on error. */static time_t gettime(void){ struct timespec ts; int ret; ret = clock_gettime(CLOCK_MONOTONIC, &ts); if (ret < 0) { ERROR("clock_gettime(CLOCK_MONOTONIC) failed: %s\n", strerror(errno)); return 0; } return ts.tv_sec;}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){ struct stat s; pid_t pid; int needs_console; int n; /* starting a service removes it from the disabled * state and immediately takes it out of the restarting * state if it was in there */ svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING)); 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; } NOTICE("starting '%s'\n", svc->name); pid = fork(); if (pid == 0) { struct socketinfo *si; struct svcenvinfo *ei; char tmp[32]; int fd, sz; 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 s = create_socket(si->name, !strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_STREAM, si->perm, si->uid, si->gid); if (s >= 0) { publish_socket(si->name, s); } } 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) { setgid(svc->gid); } if (svc->nr_supp_gids) { setgroups(svc->nr_supp_gids, svc->supp_gids); } if (svc->uid) { setuid(svc->uid); } execve(svc->args[0], (char**) svc->args, (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; notify_service_state(svc->name, "running");}void service_stop(struct service *svc){ /* we are no longer running, nor should we * attempt to restart */ svc->flags &= (~(SVC_RUNNING|SVC_RESTARTING)); /* if the service has not yet started, prevent * it from auto-starting with its class */ svc->flags |= SVC_DISABLED; if (svc->pid) { NOTICE("service '%s' is being killed\n", svc->name); kill(-svc->pid, SIGTERM); notify_service_state(svc->name, "stopping"); } else { notify_service_state(svc->name, "stopped"); }}void property_changed(const char *name, const char *value){ if (property_triggers_enabled) { queue_property_triggers(name, value); drain_action_queue(); }}#define CRITICAL_CRASH_THRESHOLD 4 /* if we crash >4 times ... */#define CRITICAL_CRASH_WINDOW (4*60) /* ... in 4 minutes, goto recovery*/static int wait_for_one_process(int block){ pid_t pid; int status; struct service *svc; struct socketinfo *si; time_t now; struct listnode *node; struct command *cmd; while ( (pid = waitpid(-1, &status, block ? 0 : WNOHANG)) == -1 && errno == EINTR ); if (pid <= 0) return -1; INFO("waitpid returned pid %d, status = %08x\n", pid, status); svc = service_find_by_pid(pid); if (!svc) { ERROR("untracked pid %d exited\n", pid); return 0; } NOTICE("process '%s', pid %d exited\n", svc->name, pid); if (!(svc->flags & SVC_ONESHOT)) { kill(-pid, SIGKILL); NOTICE("process '%s' killing any children in process group\n", svc->name); } /* remove any sockets we may have created */ for (si = svc->sockets; si; si = si->next) { char tmp[128]; snprintf(tmp, sizeof(tmp), ANDROID_SOCKET_DIR"/%s", si->name); unlink(tmp); } svc->pid = 0; svc->flags &= (~SVC_RUNNING); /* oneshot processes go into the disabled state on exit */ if (svc->flags & SVC_ONESHOT) { svc->flags |= SVC_DISABLED; } /* disabled processes do not get restarted automatically */ if (svc->flags & SVC_DISABLED) { notify_service_state(svc->name, "stopped"); return 0; } now = gettime(); if (svc->flags & SVC_CRITICAL) { if (svc->time_crashed + CRITICAL_CRASH_WINDOW >= now) { if (++svc->nr_crashed > CRITICAL_CRASH_THRESHOLD) { ERROR("critical process '%s' exited %d times in %d minutes; " "rebooting into recovery mode\n", svc->name, CRITICAL_CRASH_THRESHOLD, CRITICAL_CRASH_WINDOW / 60); sync(); __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, "recovery"); return 0; } } else { svc->time_crashed = now; svc->nr_crashed = 1; } } /* Execute all onrestart commands for this service. */ list_for_each(node, &svc->onrestart.commands) { cmd = node_to_item(node, struct command, clist); cmd->func(cmd->nargs, cmd->args); } svc->flags |= SVC_RESTARTING; notify_service_state(svc->name, "restarting"); return 0;}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); 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);}static int signal_fd = -1;static void sigchld_handler(int s){ write(signal_fd, &s, 1);}static void msg_start(const char *name){ struct service *svc = service_find_by_name(name); if (svc) { service_start(svc); } else { ERROR("no such service '%s'\n", name); }}static void msg_stop(const char *name){ struct service *svc = service_find_by_name(name); if (svc) { service_stop(svc); } else { ERROR("no such service '%s'\n"); }}void handle_control_message(const char *msg, const char *arg){ if (!strcmp(msg,"start")) { msg_start(arg); } else if (!strcmp(msg,"stop")) { msg_stop(arg); } else { ERROR("unknown control msg '%s'\n", msg); }}#define MAX_MTD_PARTITIONS 16static struct {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -