init: Move signal handling to signal_handler.c

Change-Id: I3a24afa28a1cd279c749d6f384f687b8de56067e
This commit is contained in:
Colin Cross 2010-04-13 19:48:59 -07:00
parent 504bc5175a
commit 9c5366ba55
5 changed files with 188 additions and 112 deletions

View File

@ -11,7 +11,8 @@ LOCAL_SRC_FILES:= \
util.c \
parser.c \
logo.c \
keychords.c
keychords.c \
signal_handler.c
ifeq ($(strip $(INIT_BOOTCHART)),true)
LOCAL_SRC_FILES += bootchart.c

View File

@ -31,7 +31,6 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/reboot.h>
#include <cutils/sockets.h>
#include <cutils/iosched_policy.h>
@ -43,6 +42,7 @@
#include "init.h"
#include "property_service.h"
#include "bootchart.h"
#include "signal_handler.h"
#include "keychords.h"
#include "parser.h"
@ -62,7 +62,7 @@ static char hardware[32];
static unsigned revision = 0;
static char qemu[32];
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);
@ -306,86 +306,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;
@ -409,13 +329,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;
@ -620,7 +533,6 @@ void open_devnull_stdio(void)
int main(int argc, char **argv)
{
int signal_recv_fd = -1;
int fd_count;
int s[2];
int fd;
@ -630,12 +542,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);
@ -753,20 +659,12 @@ int main(int argc, char **argv)
*/
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 ((get_device_fd() < 0) ||
(get_property_set_fd() < 0) ||
(signal_recv_fd < 0)) {
(get_signal_fd() < 0)) {
ERROR("init startup failure\n");
return 1;
}
@ -787,7 +685,7 @@ int main(int argc, char **argv)
ufds[0].events = POLLIN;
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;
@ -841,10 +739,7 @@ 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;
}

View File

@ -157,6 +157,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);

154
init/signal_handler.c Normal file
View File

@ -0,0 +1,154 @@
/*
* 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"
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;
}

24
init/signal_handler.h Normal file
View File

@ -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