diff options
Diffstat (limited to 'init/signal_handler.c')
-rw-r--r-- | init/signal_handler.c | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/init/signal_handler.c b/init/signal_handler.c new file mode 100644 index 0000000..3e5d136 --- /dev/null +++ b/init/signal_handler.c @@ -0,0 +1,159 @@ +/* + * 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 <stdio.h> +#include <errno.h> +#include <signal.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <cutils/sockets.h> +#include <sys/reboot.h> + +#include "init.h" +#include "list.h" +#include "util.h" +#include "log.h" + +static int signal_fd = -1; +static int signal_recv_fd = -1; + +static void sigchld_handler(int s) +{ + write(signal_fd, &s, 1); +} + +#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)) + ; +} + +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); + } + + handle_signal(); +} + +int get_signal_fd() +{ + return signal_recv_fd; +} |