From: John Crispin Date: Fri, 8 Mar 2013 22:51:51 +0000 (+0100) Subject: add inittab support X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fprocd.git;a=commitdiff_plain;h=271e7eca16ed6074f66026c7bd9383c060098af4 add inittab support --- diff --git a/inittab.c b/inittab.c new file mode 100644 index 0000000..32be11f --- /dev/null +++ b/inittab.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2013 Felix Fietkau + * Copyright (C) 2013 John Crispin + * + * 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 +#include + +#include +#include +#include +#include +#include + +#include +#include + +#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); +}