add inittab support
authorJohn Crispin <blogic@openwrt.org>
Fri, 8 Mar 2013 22:51:51 +0000 (23:51 +0100)
committerJohn Crispin <blogic@openwrt.org>
Wed, 13 Mar 2013 17:58:13 +0000 (18:58 +0100)
inittab.c [new file with mode: 0644]

diff --git a/inittab.c b/inittab.c
new file mode 100644 (file)
index 0000000..32be11f
--- /dev/null
+++ b/inittab.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
+ * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <regex.h>
+
+#include <libubox/utils.h>
+#include <libubox/list.h>
+
+#include "procd.h"
+
+#define TAG_ID         0
+#define TAG_RUNLVL     1
+#define TAG_ACTION     2
+#define TAG_PROCESS    3
+
+#define MAX_ARGS       8
+
+struct init_action;
+const char *console;
+
+struct init_handler {
+       const char *name;
+       void (*cb) (struct init_action *a);
+       int atomic;
+};
+
+struct init_action {
+       struct list_head list;
+
+       char *id;
+       char *argv[MAX_ARGS];
+       char *line;
+
+       struct init_handler *handler;
+       struct uloop_process proc;
+
+       int pending;
+       int respawn;
+       struct uloop_timeout tout;
+};
+
+static const char *tab = "/etc/inittab";
+static char *ask = "/sbin/askfirst";
+
+static struct init_action *pending;
+
+static LIST_HEAD(actions);
+
+static void fork_script(struct init_action *a)
+{
+       a->proc.pid = fork();
+       if (!a->proc.pid) {
+               execvp(a->argv[0], a->argv);
+               ERROR("Failed to execute %s\n", a->argv[0]);
+               exit(-1);
+       }
+
+       if (a->proc.pid > 0) {
+               DEBUG(2, "Launched new %s action, pid=%d\n",
+                                       a->handler->name,
+                                       (int) a->proc.pid);
+               uloop_process_add(&a->proc);
+       }
+}
+
+static void child_exit(struct uloop_process *proc, int ret)
+{
+       struct init_action *a = container_of(proc, struct init_action, proc);
+
+       DEBUG(2, "pid:%d\n", proc->pid);
+       if (a->tout.cb) {
+               uloop_timeout_set(&a->tout, a->respawn);
+       } else {
+               a->pending = 0;
+               pending = NULL;
+               procd_state_next();
+       }
+}
+
+static void respawn(struct uloop_timeout *tout)
+{
+       struct init_action *a = container_of(tout, struct init_action, tout);
+       fork_script(a);
+}
+
+static void runscript(struct init_action *a)
+{
+       a->proc.cb = child_exit;
+       fork_script(a);
+}
+
+static void askfirst(struct init_action *a)
+{
+       struct stat s;
+       int i;
+
+       chdir("/dev");
+       i = stat(a->id, &s);
+       chdir("/");
+       if (i || (console && !strcmp(console, a->id))) {
+               DEBUG(2, "Skipping %s\n", a->id);
+               return;
+       }
+
+       a->tout.cb = respawn;
+       for (i = MAX_ARGS - 2; i >= 2; i--)
+               a->argv[i] = a->argv[i - 2];
+       a->argv[0] = ask;
+       a->argv[1] = a->id;
+       a->respawn = 500;
+
+       a->proc.cb = child_exit;
+       fork_script(a);
+}
+
+static void askconsole(struct init_action *a)
+{
+       struct stat s;
+       char line[256], *tty;
+       int i, r, fd = open("/proc/cmdline", O_RDONLY);
+       regex_t pat_cmdline;
+       regmatch_t matches[2];
+
+       if (!fd)
+               return;
+
+       r = read(fd, line, sizeof(line) - 1);
+       line[r] = '\0';
+       close(fd);
+
+       regcomp(&pat_cmdline, "console=([a-zA-Z0-9]*)", REG_EXTENDED);
+       if (regexec(&pat_cmdline, line, 2, matches, 0))
+               goto err_out;
+       line[matches[1].rm_eo] = '\0';
+       tty = &line[matches[1].rm_so];
+
+       chdir("/dev");
+       i = stat(tty, &s);
+       chdir("/");
+       if (i) {
+               DEBUG(2, "skipping %s\n", tty);
+               goto err_out;
+       }
+       console = strdup(tty);
+
+       a->tout.cb = respawn;
+       for (i = MAX_ARGS - 2; i >= 2; i--)
+               a->argv[i] = a->argv[i - 2];
+       a->argv[0] = ask;
+       a->argv[1] = strdup(tty);
+       a->respawn = 500;
+
+       a->proc.cb = child_exit;
+       fork_script(a);
+err_out:
+       regfree(&pat_cmdline);
+}
+
+static struct init_handler handlers[] = {
+       {
+               .name = "sysinit",
+               .cb = runscript,
+       }, {
+               .name = "shutdown",
+               .cb = runscript,
+       }, {
+               .name = "askfirst",
+               .cb = askfirst,
+               .atomic = 1,
+       }, {
+               .name = "askconsole",
+               .cb = askconsole,
+               .atomic = 1,
+       }
+};
+
+static int add_action(struct init_action *a, const char *name)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(handlers); i++)
+               if (!strcmp(handlers[i].name, name)) {
+                       a->handler = &handlers[i];
+                       list_add_tail(&a->list, &actions);
+                       return 0;
+               }
+       ERROR("Unknown init handler %s\n", name);
+       return -1;
+}
+
+void procd_inittab_run(const char *handler)
+{
+       struct init_action *a;
+
+       list_for_each_entry(a, &actions, list)
+               if (!strcmp(a->handler->name, handler)) {
+                       if (a->handler->atomic) {
+                               a->handler->cb(a);
+                               continue;
+                       }
+                       if (pending || a->pending)
+                               break;
+                       a->pending = 1;
+                       pending = a;
+                       a->handler->cb(a);
+               }
+}
+
+void procd_inittab(void)
+{
+#define LINE_LEN       128
+       FILE *fp = fopen(tab, "r");
+       struct init_action *a;
+       regex_t pat_inittab;
+       regmatch_t matches[5];
+       char *line;
+
+       if (!fp) {
+               ERROR("Failed to open %s\n", tab);
+               return;
+       }
+
+       regcomp(&pat_inittab, "([a-zA-Z0-9]*):([a-zA-Z0-9]*):([a-zA-Z0-9]*):([a-zA-Z0-9/[.-.]. ]*)", REG_EXTENDED);
+       line = malloc(LINE_LEN);
+       a = malloc(sizeof(struct init_action));
+       memset(a, 0, sizeof(struct init_action));
+
+       while (fgets(line, LINE_LEN, fp)) {
+               char *tags[TAG_PROCESS + 1];
+               char *tok;
+               int i;
+
+               if (*line == '#')
+                       continue;
+
+               if (regexec(&pat_inittab, line, 5, matches, 0))
+                       continue;
+
+               DEBUG(2, "Parsing inittab - %s", line);
+
+               for (i = TAG_ID; i <= TAG_PROCESS; i++) {
+                       line[matches[i].rm_eo] = '\0';
+                       tags[i] = &line[matches[i + 1].rm_so];
+               };
+
+               tok = strtok(tags[TAG_PROCESS], " ");
+               for (i = 0; i < (MAX_ARGS - i - 1) && tok; i++) {
+                       a->argv[i] = tok;
+                       tok = strtok(NULL, " ");
+               }
+               a->argv[i] = NULL;
+               a->id = tags[TAG_ID];
+               a->line = line;
+
+               if (add_action(a, tags[TAG_ACTION]))
+                       continue;
+               line = malloc(LINE_LEN);
+               a = malloc(sizeof(struct init_action));
+               memset(a, 0, sizeof(struct init_action));
+       }
+
+       fclose(fp);
+       free(line);
+       free(a);
+       regfree(&pat_inittab);
+}