diff --git a/init/Android.mk b/init/Android.mk index d3766d455..323388319 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -10,7 +10,9 @@ LOCAL_SRC_FILES:= \ property_service.c \ util.c \ parser.c \ - logo.c + logo.c \ + keychords.c \ + signal_handler.c ifeq ($(strip $(INIT_BOOTCHART)),true) LOCAL_SRC_FILES += bootchart.c diff --git a/init/builtins.c b/init/builtins.c index b4af700bd..76969547a 100644 --- a/init/builtins.c +++ b/init/builtins.c @@ -35,6 +35,8 @@ #include "keywords.h" #include "property_service.h" #include "devices.h" +#include "parser.h" +#include "util.h" #include diff --git a/init/devices.c b/init/devices.c index bde906bc6..a9ed141a4 100644 --- a/init/devices.c +++ b/init/devices.c @@ -40,6 +40,8 @@ #define FIRMWARE_DIR "/etc/firmware" #define MAX_QEMU_PERM 6 +static int device_fd = -1; + struct uevent { const char *action; const char *path; @@ -569,12 +571,12 @@ static void handle_firmware_event(struct uevent *uevent) } #define UEVENT_MSG_LEN 1024 -void handle_device_fd(int fd) +void handle_device_fd() { char msg[UEVENT_MSG_LEN+2]; int n; - while((n = recv(fd, msg, UEVENT_MSG_LEN, 0)) > 0) { + while((n = recv(device_fd, msg, UEVENT_MSG_LEN, 0)) > 0) { struct uevent uevent; if(n == UEVENT_MSG_LEN) /* overflow -- discard */ @@ -599,7 +601,7 @@ void handle_device_fd(int fd) ** socket's buffer. */ -static void do_coldboot(int event_fd, DIR *d) +static void do_coldboot(DIR *d) { struct dirent *de; int dfd, fd; @@ -610,7 +612,7 @@ static void do_coldboot(int event_fd, DIR *d) if(fd >= 0) { write(fd, "add\n", 4); close(fd); - handle_device_fd(event_fd); + handle_device_fd(); } while((de = readdir(d))) { @@ -627,40 +629,42 @@ static void do_coldboot(int event_fd, DIR *d) if(d2 == 0) close(fd); else { - do_coldboot(event_fd, d2); + do_coldboot(d2); closedir(d2); } } } -static void coldboot(int event_fd, const char *path) +static void coldboot(const char *path) { DIR *d = opendir(path); if(d) { - do_coldboot(event_fd, d); + do_coldboot(d); closedir(d); } } -int device_init(void) +void device_init(void) { suseconds_t t0, t1; - int fd; - fd = open_uevent_socket(); - if(fd < 0) - return -1; + device_fd = open_uevent_socket(); + if(device_fd < 0) + return; - fcntl(fd, F_SETFD, FD_CLOEXEC); - fcntl(fd, F_SETFL, O_NONBLOCK); + fcntl(device_fd, F_SETFD, FD_CLOEXEC); + fcntl(device_fd, F_SETFL, O_NONBLOCK); t0 = get_usecs(); - coldboot(fd, "/sys/class"); - coldboot(fd, "/sys/block"); - coldboot(fd, "/sys/devices"); + coldboot("/sys/class"); + coldboot("/sys/block"); + coldboot("/sys/devices"); t1 = get_usecs(); log_event_print("coldboot %ld uS\n", ((long) (t1 - t0))); - - return fd; +} + +int get_device_fd() +{ + return device_fd; } diff --git a/init/devices.h b/init/devices.h index b484da45e..e14f4c8a1 100644 --- a/init/devices.h +++ b/init/devices.h @@ -17,11 +17,11 @@ #ifndef _INIT_DEVICES_H #define _INIT_DEVICES_H -extern void handle_device_fd(int fd); -extern int device_init(void); +extern void handle_device_fd(); +extern void device_init(void); extern void qemu_init(void); extern void qemu_cmdline(const char* name, const char *value); extern int add_devperms_partners(const char *name, mode_t perm, unsigned int uid, unsigned int gid, unsigned short prefix); - +int get_device_fd(); #endif /* _INIT_DEVICES_H */ diff --git a/init/init.c b/init/init.c index 65d3d845d..3d619b904 100755 --- a/init/init.c +++ b/init/init.c @@ -25,20 +25,16 @@ #include #include #include -#include #include #include #include #include #include #include -#include #include #include #include -#include -#include #include @@ -46,6 +42,10 @@ #include "init.h" #include "property_service.h" #include "bootchart.h" +#include "signal_handler.h" +#include "keychords.h" +#include "parser.h" +#include "util.h" static int property_triggers_enabled = 0; @@ -62,11 +62,8 @@ static char bootloader[32]; static char hardware[32]; static unsigned revision = 0; static char qemu[32]; -static struct input_keychord *keychords = 0; -static int keychords_count = 0; -static int keychords_length = 0; -static void notify_service_state(const char *name, const char *state) +void notify_service_state(const char *name, const char *state) { char pname[PROP_NAME_MAX]; int len = strlen(name); @@ -122,24 +119,6 @@ static void open_console() 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; @@ -328,86 +307,6 @@ void property_changed(const char *name, const char *value) } } -#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; - } - } - - svc->flags |= SVC_RESTARTING; - - /* 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); - } - 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; @@ -431,13 +330,6 @@ static void restart_processes() 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; @@ -486,78 +378,6 @@ void handle_control_message(const char *msg, const char *arg) } } -#define MAX_MTD_PARTITIONS 16 - -static struct { - char name[16]; - int number; -} mtd_part_map[MAX_MTD_PARTITIONS]; - -static int mtd_part_count = -1; - -static void find_mtd_partitions(void) -{ - int fd; - char buf[1024]; - char *pmtdbufp; - ssize_t pmtdsize; - int r; - - fd = open("/proc/mtd", O_RDONLY); - if (fd < 0) - return; - - buf[sizeof(buf) - 1] = '\0'; - pmtdsize = read(fd, buf, sizeof(buf) - 1); - pmtdbufp = buf; - while (pmtdsize > 0) { - int mtdnum, mtdsize, mtderasesize; - char mtdname[16]; - mtdname[0] = '\0'; - mtdnum = -1; - r = sscanf(pmtdbufp, "mtd%d: %x %x %15s", - &mtdnum, &mtdsize, &mtderasesize, mtdname); - if ((r == 4) && (mtdname[0] == '"')) { - char *x = strchr(mtdname + 1, '"'); - if (x) { - *x = 0; - } - INFO("mtd partition %d, %s\n", mtdnum, mtdname + 1); - if (mtd_part_count < MAX_MTD_PARTITIONS) { - strcpy(mtd_part_map[mtd_part_count].name, mtdname + 1); - mtd_part_map[mtd_part_count].number = mtdnum; - mtd_part_count++; - } else { - ERROR("too many mtd partitions\n"); - } - } - while (pmtdsize > 0 && *pmtdbufp != '\n') { - pmtdbufp++; - pmtdsize--; - } - if (pmtdsize > 0) { - pmtdbufp++; - pmtdsize--; - } - } - close(fd); -} - -int mtd_name_to_number(const char *name) -{ - int n; - if (mtd_part_count < 0) { - mtd_part_count = 0; - find_mtd_partitions(); - } - for (n = 0; n < mtd_part_count; n++) { - if (!strcmp(name, mtd_part_map[n].name)) { - return mtd_part_map[n].number; - } - } - return -1; -} - static void import_kernel_nv(char *name, int in_qemu) { char *value = strchr(name, '='); @@ -712,103 +532,8 @@ void open_devnull_stdio(void) exit(1); } -void add_service_keycodes(struct service *svc) -{ - struct input_keychord *keychord; - int i, size; - - if (svc->keycodes) { - /* add a new keychord to the list */ - size = sizeof(*keychord) + svc->nkeycodes * sizeof(keychord->keycodes[0]); - keychords = realloc(keychords, keychords_length + size); - if (!keychords) { - ERROR("could not allocate keychords\n"); - keychords_length = 0; - keychords_count = 0; - return; - } - - keychord = (struct input_keychord *)((char *)keychords + keychords_length); - keychord->version = KEYCHORD_VERSION; - keychord->id = keychords_count + 1; - keychord->count = svc->nkeycodes; - svc->keychord_id = keychord->id; - - for (i = 0; i < svc->nkeycodes; i++) { - keychord->keycodes[i] = svc->keycodes[i]; - } - keychords_count++; - keychords_length += size; - } -} - -int open_keychord() -{ - int fd, ret; - - service_for_each(add_service_keycodes); - - /* nothing to do if no services require keychords */ - if (!keychords) - return -1; - - fd = open("/dev/keychord", O_RDWR); - if (fd < 0) { - ERROR("could not open /dev/keychord\n"); - return fd; - } - fcntl(fd, F_SETFD, FD_CLOEXEC); - - ret = write(fd, keychords, keychords_length); - if (ret != keychords_length) { - ERROR("could not configure /dev/keychord %d (%d)\n", ret, errno); - close(fd); - fd = -1; - } - - free(keychords); - keychords = 0; - - return fd; -} - -void handle_keychord(int fd) -{ - struct service *svc; - char* debuggable; - char* adb_enabled; - int ret; - __u16 id; - - // only handle keychords if ro.debuggable is set or adb is enabled. - // the logic here is that bugreports should be enabled in userdebug or eng builds - // and on user builds for users that are developers. - debuggable = property_get("ro.debuggable"); - adb_enabled = property_get("init.svc.adbd"); - if ((debuggable && !strcmp(debuggable, "1")) || - (adb_enabled && !strcmp(adb_enabled, "running"))) { - ret = read(fd, &id, sizeof(id)); - if (ret != sizeof(id)) { - ERROR("could not read keychord id\n"); - return; - } - - svc = service_find_by_keychord(id); - if (svc) { - INFO("starting service %s from keychord\n", svc->name); - service_start(svc, NULL); - } else { - ERROR("service for keychord %d not found\n", id); - } - } -} - int main(int argc, char **argv) { - int device_fd = -1; - int property_set_fd = -1; - int signal_recv_fd = -1; - int keychord_fd = -1; int fd_count; int s[2]; int fd; @@ -818,12 +543,6 @@ int main(int argc, char **argv) char *tmpdev; char* debuggable; - act.sa_handler = sigchld_handler; - act.sa_flags = SA_NOCLDSTOP; - act.sa_mask = 0; - act.sa_restorer = NULL; - sigaction(SIGCHLD, &act, 0); - /* clear the umask */ umask(0); @@ -866,12 +585,12 @@ int main(int argc, char **argv) drain_action_queue(); INFO("device init\n"); - device_fd = device_init(); + device_init(); property_init(); // only listen for keychords if ro.debuggable is true - keychord_fd = open_keychord(); + keychord_init(); if (console[0]) { snprintf(tmp, sizeof(tmp), "/dev/%s", console); @@ -939,22 +658,14 @@ int main(int argc, char **argv) * after the ro.foo properties are set above so * that /data/local.prop cannot interfere with them. */ - property_set_fd = start_property_service(); + start_property_service(); - /* create a signalling mechanism for the sigchld handler */ - if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) { - signal_fd = s[0]; - signal_recv_fd = s[1]; - fcntl(s[0], F_SETFD, FD_CLOEXEC); - fcntl(s[0], F_SETFL, O_NONBLOCK); - fcntl(s[1], F_SETFD, FD_CLOEXEC); - fcntl(s[1], F_SETFL, O_NONBLOCK); - } + signal_init(); /* make sure we actually have all the pieces we need */ - if ((device_fd < 0) || - (property_set_fd < 0) || - (signal_recv_fd < 0)) { + if ((get_device_fd() < 0) || + (get_property_set_fd() < 0) || + (get_signal_fd() < 0)) { ERROR("init startup failure\n"); return 1; } @@ -971,16 +682,16 @@ int main(int argc, char **argv) /* enable property triggers */ property_triggers_enabled = 1; - ufds[0].fd = device_fd; + ufds[0].fd = get_device_fd(); ufds[0].events = POLLIN; - ufds[1].fd = property_set_fd; + ufds[1].fd = get_property_set_fd(); ufds[1].events = POLLIN; - ufds[2].fd = signal_recv_fd; + ufds[2].fd = get_signal_fd(); ufds[2].events = POLLIN; fd_count = 3; - if (keychord_fd > 0) { - ufds[3].fd = keychord_fd; + if (get_keychord_fd() > 0) { + ufds[3].fd = get_keychord_fd(); ufds[3].events = POLLIN; fd_count++; } else { @@ -1029,20 +740,17 @@ int main(int argc, char **argv) continue; if (ufds[2].revents == POLLIN) { - /* we got a SIGCHLD - reap and restart as needed */ - read(signal_recv_fd, tmp, sizeof(tmp)); - while (!wait_for_one_process(0)) - ; + handle_signal(); continue; } if (ufds[0].revents == POLLIN) - handle_device_fd(device_fd); + handle_device_fd(); if (ufds[1].revents == POLLIN) - handle_property_set_fd(property_set_fd); + handle_property_set_fd(); if (ufds[3].revents == POLLIN) - handle_keychord(keychord_fd); + handle_keychord(); } return 0; diff --git a/init/init.h b/init/init.h index f92a4d7ab..1ebdbac61 100644 --- a/init/init.h +++ b/init/init.h @@ -17,15 +17,8 @@ #ifndef _INIT_INIT_H #define _INIT_INIT_H -int mtd_name_to_number(const char *name); - void handle_control_message(const char *msg, const char *arg); -int create_socket(const char *name, int type, mode_t perm, - uid_t uid, gid_t gid); - -void *read_file(const char *fn, unsigned *_sz); - void log_init(void); void log_set_level(int level); void log_close(void); @@ -39,8 +32,6 @@ void log_write(int level, const char *fmt, ...) #define LOG_DEFAULT_LEVEL 3 /* messages <= this level are logged */ #define LOG_UEVENTS 0 /* log uevent messages if 1. verbose */ -unsigned int decode_uid(const char *s); - struct listnode { struct listnode *next; @@ -156,6 +147,8 @@ struct service { int parse_config_file(const char *fn); +void notify_service_state(const char *name, const char *state); + struct service *service_find_by_name(const char *name); struct service *service_find_by_pid(pid_t pid); struct service *service_find_by_keychord(int keychord_id); @@ -168,14 +161,6 @@ void service_stop(struct service *svc); void service_start(struct service *svc, const char *dynamic_args); void property_changed(const char *name, const char *value); -void drain_action_queue(void); -struct action *action_remove_queue_head(void); -void action_add_queue_tail(struct action *act); -void action_for_each_trigger(const char *trigger, - void (*func)(struct action *act)); -void queue_property_triggers(const char *name, const char *value); -void queue_all_property_triggers(); - #define INIT_IMAGE_FILE "/initlogo.rle" int load_565rle_image( char *file_name ); diff --git a/init/keychords.c b/init/keychords.c new file mode 100644 index 000000000..bc286dd18 --- /dev/null +++ b/init/keychords.c @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2010 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 +#include +#include +#include +#include +#include + +#include "init.h" +#include "property_service.h" + +static struct input_keychord *keychords = 0; +static int keychords_count = 0; +static int keychords_length = 0; +static int keychord_fd = -1; + +void add_service_keycodes(struct service *svc) +{ + struct input_keychord *keychord; + int i, size; + + if (svc->keycodes) { + /* add a new keychord to the list */ + size = sizeof(*keychord) + svc->nkeycodes * sizeof(keychord->keycodes[0]); + keychords = realloc(keychords, keychords_length + size); + if (!keychords) { + ERROR("could not allocate keychords\n"); + keychords_length = 0; + keychords_count = 0; + return; + } + + keychord = (struct input_keychord *)((char *)keychords + keychords_length); + keychord->version = KEYCHORD_VERSION; + keychord->id = keychords_count + 1; + keychord->count = svc->nkeycodes; + svc->keychord_id = keychord->id; + + for (i = 0; i < svc->nkeycodes; i++) { + keychord->keycodes[i] = svc->keycodes[i]; + } + keychords_count++; + keychords_length += size; + } +} + +void keychord_init() +{ + int fd, ret; + + service_for_each(add_service_keycodes); + + /* nothing to do if no services require keychords */ + if (!keychords) + return; + + fd = open("/dev/keychord", O_RDWR); + if (fd < 0) { + ERROR("could not open /dev/keychord\n"); + return; + } + fcntl(fd, F_SETFD, FD_CLOEXEC); + + ret = write(fd, keychords, keychords_length); + if (ret != keychords_length) { + ERROR("could not configure /dev/keychord %d (%d)\n", ret, errno); + close(fd); + fd = -1; + } + + free(keychords); + keychords = 0; + + keychord_fd = fd; +} + +void handle_keychord() +{ + struct service *svc; + const char* debuggable; + const char* adb_enabled; + int ret; + __u16 id; + + // only handle keychords if ro.debuggable is set or adb is enabled. + // the logic here is that bugreports should be enabled in userdebug or eng builds + // and on user builds for users that are developers. + debuggable = property_get("ro.debuggable"); + adb_enabled = property_get("init.svc.adbd"); + if ((debuggable && !strcmp(debuggable, "1")) || + (adb_enabled && !strcmp(adb_enabled, "running"))) { + ret = read(keychord_fd, &id, sizeof(id)); + if (ret != sizeof(id)) { + ERROR("could not read keychord id\n"); + return; + } + + svc = service_find_by_keychord(id); + if (svc) { + INFO("starting service %s from keychord\n", svc->name); + service_start(svc, NULL); + } else { + ERROR("service for keychord %d not found\n", id); + } + } +} + +int get_keychord_fd() +{ + return keychord_fd; +} diff --git a/init/keychords.h b/init/keychords.h new file mode 100644 index 000000000..070b85891 --- /dev/null +++ b/init/keychords.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef _INIT_KEYCHORDS_H_ +#define _INIT_KEYCHORDS_H_ + +struct service; + +void add_service_keycodes(struct service *svc); +void keychord_init(void); +void handle_keychord(void); +int get_keychord_fd(void); + +#endif diff --git a/init/parser.c b/init/parser.c index 7da0d1944..9424e7ed2 100644 --- a/init/parser.c +++ b/init/parser.c @@ -9,6 +9,8 @@ #include "init.h" #include "property_service.h" +#include "parser.h" +#include "util.h" #include diff --git a/init/parser.h b/init/parser.h new file mode 100644 index 000000000..cec848db6 --- /dev/null +++ b/init/parser.h @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef PARSER_H_ +#define PARSER_H_ + +struct action; + +void drain_action_queue(void); +struct action *action_remove_queue_head(void); +void action_add_queue_tail(struct action *act); +void action_for_each_trigger(const char *trigger, + void (*func)(struct action *act)); +int action_queue_empty(void); +void queue_property_triggers(const char *name, const char *value); +void queue_all_property_triggers(); + +#endif /* PARSER_H_ */ diff --git a/init/property_service.c b/init/property_service.c index d2505dd13..b1c65527a 100644 --- a/init/property_service.c +++ b/init/property_service.c @@ -43,11 +43,14 @@ #include "property_service.h" #include "init.h" +#include "util.h" #define PERSISTENT_PROPERTY_DIR "/data/property" static int persistent_properties_loaded = 0; +static int property_set_fd = -1; + /* White list of permissions for setting property services. */ struct { const char *prefix; @@ -187,7 +190,7 @@ static int property_write(prop_info *pi, const char *value) * * Returns 1 if uid allowed, 0 otherwise. */ -static int check_control_perms(const char *name, int uid, int gid) { +static int check_control_perms(const char *name, unsigned int uid, unsigned int gid) { int i; if (uid == AID_SYSTEM || uid == AID_ROOT) return 1; @@ -208,7 +211,7 @@ static int check_control_perms(const char *name, int uid, int gid) { * Checks permissions for setting system properties. * Returns 1 if uid allowed, 0 otherwise. */ -static int check_perms(const char *name, unsigned int uid, int gid) +static int check_perms(const char *name, unsigned int uid, unsigned int gid) { int i; if (uid == 0) @@ -344,7 +347,7 @@ static int property_list(void (*propfn)(const char *key, const char *value, void return 0; } -void handle_property_set_fd(int fd) +void handle_property_set_fd() { prop_msg msg; int s; @@ -355,7 +358,7 @@ void handle_property_set_fd(int fd) socklen_t addr_size = sizeof(addr); socklen_t cr_size = sizeof(cr); - if ((s = accept(fd, (struct sockaddr *) &addr, &addr_size)) < 0) { + if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) { return; } @@ -493,7 +496,7 @@ void property_init(void) load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT); } -int start_property_service(void) +void start_property_service(void) { int fd; @@ -504,10 +507,15 @@ int start_property_service(void) load_persistent_properties(); fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0); - if(fd < 0) return -1; + if(fd < 0) return; fcntl(fd, F_SETFD, FD_CLOEXEC); fcntl(fd, F_SETFL, O_NONBLOCK); listen(fd, 8); - return fd; + property_set_fd = fd; +} + +int get_property_set_fd() +{ + return property_set_fd; } diff --git a/init/property_service.h b/init/property_service.h index d12f1f38b..5bfa46c95 100644 --- a/init/property_service.h +++ b/init/property_service.h @@ -17,12 +17,12 @@ #ifndef _INIT_PROPERTY_H #define _INIT_PROPERTY_H -extern void handle_property_fd(int fd); -extern void handle_property_set_fd(int fd); +extern void handle_property_set_fd(void); extern void property_init(void); -extern int start_property_service(void); +extern void start_property_service(void); void get_property_workspace(int *fd, int *sz); extern const char* property_get(const char *name); extern int property_set(const char *name, const char *value); +int get_property_set_fd(void); #endif /* _INIT_PROPERTY_H */ diff --git a/init/signal_handler.c b/init/signal_handler.c new file mode 100644 index 000000000..b16eef104 --- /dev/null +++ b/init/signal_handler.c @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2010 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "init.h" +#include "util.h" + +static int signal_fd = -1; +static int signal_recv_fd = -1; + +static void sigchld_handler(int s) +{ + write(signal_fd, &s, 1); +} + +void signal_init(void) +{ + int s[2]; + + struct sigaction act; + + act.sa_handler = sigchld_handler; + act.sa_flags = SA_NOCLDSTOP; + act.sa_mask = 0; + act.sa_restorer = NULL; + sigaction(SIGCHLD, &act, 0); + + /* create a signalling mechanism for the sigchld handler */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) { + signal_fd = s[0]; + signal_recv_fd = s[1]; + fcntl(s[0], F_SETFD, FD_CLOEXEC); + fcntl(s[0], F_SETFL, O_NONBLOCK); + fcntl(s[1], F_SETFD, FD_CLOEXEC); + fcntl(s[1], F_SETFL, O_NONBLOCK); + } +} + +#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; + } + } + + svc->flags |= SVC_RESTARTING; + + /* 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); + } + notify_service_state(svc->name, "restarting"); + return 0; +} + +void handle_signal(void) +{ + char tmp[32]; + + /* we got a SIGCHLD - reap and restart as needed */ + read(signal_recv_fd, tmp, sizeof(tmp)); + while (!wait_for_one_process(0)) + ; +} + +int get_signal_fd() +{ + return signal_recv_fd; +} diff --git a/init/signal_handler.h b/init/signal_handler.h new file mode 100644 index 000000000..b092ccb6e --- /dev/null +++ b/init/signal_handler.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef _INIT_SIGNAL_HANDLER_H_ +#define _INIT_SIGNAL_HANDLER_H_ + +void signal_init(void); +void handle_signal(void); +int get_signal_fd(void); + +#endif diff --git a/init/util.c b/init/util.c index 0b7667dae..6f9a12e9e 100644 --- a/init/util.c +++ b/init/util.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -209,3 +210,92 @@ void list_remove(struct listnode *item) item->prev->next = item->next; } +#define MAX_MTD_PARTITIONS 16 + +static struct { + char name[16]; + int number; +} mtd_part_map[MAX_MTD_PARTITIONS]; + +static int mtd_part_count = -1; + +static void find_mtd_partitions(void) +{ + int fd; + char buf[1024]; + char *pmtdbufp; + ssize_t pmtdsize; + int r; + + fd = open("/proc/mtd", O_RDONLY); + if (fd < 0) + return; + + buf[sizeof(buf) - 1] = '\0'; + pmtdsize = read(fd, buf, sizeof(buf) - 1); + pmtdbufp = buf; + while (pmtdsize > 0) { + int mtdnum, mtdsize, mtderasesize; + char mtdname[16]; + mtdname[0] = '\0'; + mtdnum = -1; + r = sscanf(pmtdbufp, "mtd%d: %x %x %15s", + &mtdnum, &mtdsize, &mtderasesize, mtdname); + if ((r == 4) && (mtdname[0] == '"')) { + char *x = strchr(mtdname + 1, '"'); + if (x) { + *x = 0; + } + INFO("mtd partition %d, %s\n", mtdnum, mtdname + 1); + if (mtd_part_count < MAX_MTD_PARTITIONS) { + strcpy(mtd_part_map[mtd_part_count].name, mtdname + 1); + mtd_part_map[mtd_part_count].number = mtdnum; + mtd_part_count++; + } else { + ERROR("too many mtd partitions\n"); + } + } + while (pmtdsize > 0 && *pmtdbufp != '\n') { + pmtdbufp++; + pmtdsize--; + } + if (pmtdsize > 0) { + pmtdbufp++; + pmtdsize--; + } + } + close(fd); +} + +int mtd_name_to_number(const char *name) +{ + int n; + if (mtd_part_count < 0) { + mtd_part_count = 0; + find_mtd_partitions(); + } + for (n = 0; n < mtd_part_count; n++) { + if (!strcmp(name, mtd_part_map[n].name)) { + return mtd_part_map[n].number; + } + } + return -1; +} + +/* + * gettime() - returns the time in seconds of the system's monotonic clock or + * zero on error. + */ +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; +} diff --git a/init/util.h b/init/util.h new file mode 100644 index 000000000..4f473eccf --- /dev/null +++ b/init/util.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef _INIT_UTIL_H_ +#define _INIT_UTIL_H_ + +int mtd_name_to_number(const char *name); +int create_socket(const char *name, int type, mode_t perm, + uid_t uid, gid_t gid); +void *read_file(const char *fn, unsigned *_sz); +time_t gettime(void); +unsigned int decode_uid(const char *s); + +#endif