init: Move signal handling to signal_handler.c

Change-Id: I3a24afa28a1cd279c749d6f384f687b8de56067e
diff --git a/init/Android.mk b/init/Android.mk
index e107bbb..3233883 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -11,7 +11,8 @@
 	util.c \
 	parser.c \
 	logo.c \
-	keychords.c
+	keychords.c \
+	signal_handler.c
 
 ifeq ($(strip $(INIT_BOOTCHART)),true)
 LOCAL_SRC_FILES += bootchart.c
diff --git a/init/init.c b/init/init.c
index 008c513..b76fa66 100755
--- a/init/init.c
+++ b/init/init.c
@@ -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 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 @@
     }
 }
 
-#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 @@
                            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 @@
 
 int main(int argc, char **argv)
 {
-    int signal_recv_fd = -1;
     int fd_count;
     int s[2];
     int fd;
@@ -630,12 +542,6 @@
     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 @@
          */
     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 @@
     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 @@
             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;
         }
 
diff --git a/init/init.h b/init/init.h
index 8210238..4d0593d 100644
--- a/init/init.h
+++ b/init/init.h
@@ -157,6 +157,8 @@
 
 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);
diff --git a/init/signal_handler.c b/init/signal_handler.c
new file mode 100644
index 0000000..56352f1
--- /dev/null
+++ b/init/signal_handler.c
@@ -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;
+}
diff --git a/init/signal_handler.h b/init/signal_handler.h
new file mode 100644
index 0000000..b092ccb
--- /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