diff --git a/plugins/tty.c b/plugins/tty.c index 8f9a5526..b43b1af7 100644 --- a/plugins/tty.c +++ b/plugins/tty.c @@ -55,17 +55,19 @@ static void setup(void) static void do_tty(char *tty, size_t len, int creat) { +#if 0 char name[len + 6]; - struct tty *entry; + svc_t *entry; snprintf(name, sizeof(name), "/dev/%s", tty); - entry = tty_find(name); + entry = svc_find_by_tty(name); if (entry && tty_enabled(entry)) { if (creat) tty_start(entry); else if (entry->pid) tty_stop(entry); } +#endif } static void watcher(void *arg, int fd, int events) diff --git a/src/conf.c b/src/conf.c index 5add0fcf..805732ea 100644 --- a/src/conf.c +++ b/src/conf.c @@ -592,7 +592,7 @@ static void parse_dynamic(char *line, struct rlimit rlimit[], char *file) /* Regular or serial TTYs to run getty */ if (MATCH_CMD(line, "tty ", x)) { - tty_register(strip_line(x), rlimit, file); + service_register(SVC_TYPE_TTY, strip_line(x), rlimit, file); return; } } @@ -664,7 +664,6 @@ int conf_reload(void) /* Mark and sweep */ cgroup_mark_all(); svc_mark_dynamic(); - tty_mark(); /* * Reset global rlimit to bootstrap values from conf_init(). @@ -678,7 +677,7 @@ int conf_reload(void) /* If rescue.conf is missing, fall back to a root shell */ rc = parse_conf(RESCUE_CONF, 0); if (rc) - tty_register(line, global_rlimit, NULL); + service_register(SVC_TYPE_TTY, line, global_rlimit, NULL); print(rc, "Entering rescue mode"); goto done; diff --git a/src/exec.c b/src/exec.c index 45f1e3fc..c5c54fff 100644 --- a/src/exec.c +++ b/src/exec.c @@ -382,74 +382,47 @@ static int activate_console(int noclear, int nowait) */ pid_t run_getty(char *tty, char *baud, char *term, int noclear, int nowait, struct rlimit rlimit[]) { - pid_t pid; + speed_t speed; + int rc = 1; - pid = fork(); - if (!pid) { - speed_t speed; - int rc = 1; - - sched_yield(); - - speed = stty_parse_speed(baud); - prepare_tty(tty, speed, "tty", rlimit); - if (activate_console(noclear, nowait)) { - logit(LOG_INFO, "Starting built-in getty on %s, speed %u", tty, speed); - rc = getty(tty, speed, term, NULL); - } - - _exit(rc); + speed = stty_parse_speed(baud); + prepare_tty(tty, speed, "tty", rlimit); + if (activate_console(noclear, nowait)) { + logit(LOG_INFO, "Starting built-in getty on %s, speed %u", tty, speed); + rc = getty(tty, speed, term, NULL); } - cgroup_user("getty", pid); - - return pid; + return rc; } +/* + * Start external getty as defined by user in .conf file + */ pid_t run_getty2(char *tty, char *cmd, char *args[], int noclear, int nowait, struct rlimit rlimit[]) { - pid_t pid; + int rc = 1; - pid = fork(); - if (!pid) { - int rc = 1; - - sched_yield(); - - /* Dunno speed, tell stty() to not mess with it */ - prepare_tty(tty, B0, "getty", rlimit); - if (activate_console(noclear, nowait)) { - logit(LOG_INFO, "Starting external getty on %s, speed %u", tty, B0); - rc = execv(cmd, args); - } - - vhangup(); - _exit(rc); + /* Dunno speed, tell stty() to not mess with it */ + prepare_tty(tty, B0, "getty", rlimit); + if (activate_console(noclear, nowait)) { + logit(LOG_INFO, "Starting external getty on %s, speed %u", tty, B0); + rc = execv(cmd, args); } - cgroup_user("getty", pid); + vhangup(); - return pid; + return rc; } pid_t run_sh(char *tty, int noclear, int nowait, struct rlimit rlimit[]) { - pid_t pid; + int rc = 1; - pid = fork(); - if (!pid) { - int rc = 1; + prepare_tty(tty, B0, "finit-sh", rlimit); + if (activate_console(noclear, nowait)) + rc = sh(tty); - prepare_tty(tty, B0, "finit-sh", rlimit); - if (activate_console(noclear, nowait)) - rc = sh(tty); - - _exit(rc); - } - - cgroup_user("root", pid); - - return pid; + return rc; } int run_parts(char *dir, char *cmd) diff --git a/src/finit.c b/src/finit.c index 23d607cc..9e470c2c 100644 --- a/src/finit.c +++ b/src/finit.c @@ -59,6 +59,7 @@ int prevlevel = -1; int debug = 0; /* debug mode from kernel cmdline */ int rescue = 0; /* rescue mode from kernel cmdline */ int single = 0; /* single user mode from kernel cmdline */ +int bootstrap = 1; /* set while bootrapping (for TTYs) */ char *sdown = NULL; char *network = NULL; char *hostname = NULL; @@ -310,9 +311,9 @@ static void finalize(void *unused) /* Disable progress output at normal runtime */ enable_progress(0); - /* Delayed start of TTYs at bootstrap */ - _d("Launching all getty services ..."); - tty_runlevel(); + /* System bootrapped, launch TTYs et al */ + bootstrap = 0; + service_step_all(SVC_TYPE_RESPAWN); } /* diff --git a/src/finit.h b/src/finit.h index 4cfb403b..4e2c9837 100644 --- a/src/finit.h +++ b/src/finit.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -105,6 +106,7 @@ extern int prevlevel; extern int debug; extern int rescue; extern int single; +extern int bootstrap; extern char *rcsd; extern char *sdown; extern char *network; diff --git a/src/initctl.c b/src/initctl.c index 03d784ce..674aaf18 100644 --- a/src/initctl.c +++ b/src/initctl.c @@ -626,7 +626,7 @@ static char *status(svc_t *svc, int full) { static char buf[64]; const char *color; - char ok[64] = {0}; + char ok[32] = {0}; char *s; s = svc_status(svc); diff --git a/src/service.c b/src/service.c index 58acd141..55a94823 100644 --- a/src/service.c +++ b/src/service.c @@ -538,7 +538,8 @@ static int service_start(svc_t *svc) } args[i] = NULL; - redirect(svc); + if (!svc_is_tty(svc)) + redirect(svc); sig_unblock(); /* @@ -556,6 +557,8 @@ static int service_start(svc_t *svc) if (svc_is_runtask(svc)) status = exec_runtask(args[0], &args[1]); + else if (svc_is_tty(svc)) + status = tty_exec(svc); else status = execvp(args[0], &args[1]); @@ -573,7 +576,10 @@ static int service_start(svc_t *svc) _d("Starting %s %s", svc->cmd, buf); } - cgroup_service(group_name(svc, grnam, sizeof(grnam)), pid, &svc->cgroup); + if (svc_is_tty(svc)) + cgroup_user("getty", pid); + else + cgroup_service(group_name(svc, grnam, sizeof(grnam)), pid, &svc->cgroup); logit(LOG_CONSOLE | LOG_NOTICE, "Starting %s[%d]", svc_ident(svc, NULL, 0), pid); @@ -649,6 +655,9 @@ static void service_cleanup(svc_t *svc) basename(svc->cmd), fn); /* No longer running, update books. */ + if (svc_is_tty(svc) && svc->pid > 1) + utmp_set_dead(svc->pid); /* Set DEAD_PROCESS UTMP entry */ + svc->oldpid = svc->pid; svc->start_time = svc->pid = 0; } @@ -925,13 +934,10 @@ static void parse_name(svc_t *svc, char *arg) } strlcpy(svc->name, name, sizeof(svc->name)); - - /* Warn if svc generates same condition as an existing service */ - svc_validate(svc); } -/** - * parse_cmdline_args - Update the command line args in the svc struct +/* + * Update the command line args in the svc struct * * strtok internal pointer must be positioned at first command line arg * when this function is called. @@ -1071,6 +1077,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) char *username = NULL, *log = NULL, *pid = NULL; char *name = NULL, *halt = NULL, *delay = NULL; char *id = NULL, *env = NULL, *cgroup = NULL; + struct tty tty = { 0 }; int levels = 0; int manual = 0; char *line; @@ -1103,7 +1110,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) } } - cmd = strtok(line, " "); + cmd = strtok(line, " \t"); if (!cmd) { incomplete: _e("Incomplete service '%s', cannot register", cfg); @@ -1111,6 +1118,9 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) } while (cmd) { + if (type == SVC_TYPE_TTY && cmd[0] == '@') + break; /* @console */ + if (cmd[0] == '@') /* @username[:group] */ username = &cmd[1]; else if (cmd[0] == '[') /* [runlevels] */ @@ -1155,7 +1165,33 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) if (!id) id = ""; - svc = svc_find(cmd, id); + if (type == SVC_TYPE_TTY) { + size_t i, len = 0; + + if (tty_parse_args(cmd, &tty)) + return errno; + + len += tty.num + 5; + for (i = 0; i < tty.num; i++) + len += strlen(tty.args[i]); + + line = alloca(len); + if (!line) + return errno; + + snprintf(line, len, "%s ", tty.cmd ?: "tty"); + for (i = 0; i < tty.num; i++) { + strlcat(line, tty.args[i], len); + strlcat(line, " ", len); + } + cmd = strtok(line, " \t"); + if (!cmd) + return errno; + + svc = svc_find_by_tty(tty.dev); + } else + svc = svc_find(cmd, id); + if (!svc) { _d("Creating new svc for %s id #%s type %d", cmd, id, type); svc = svc_new(cmd, id, type); @@ -1197,7 +1233,43 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) conf_parse_cond(svc, cond); - parse_name(svc, name); + if (type == SVC_TYPE_TTY) { + char *ptr; + + if (tty.dev) + strlcpy(svc->dev, tty.dev, sizeof(svc->dev)); + if (tty.baud) + strlcpy(svc->baud, tty.baud, sizeof(svc->baud)); + if (tty.term) + strlcpy(svc->term, tty.term, sizeof(svc->term)); + svc->noclear = tty.noclear; + svc->nowait = tty.nowait; + svc->nologin = tty.nologin; + + /* TTYs cannot be redirected */ + log = NULL; + + /* Create name:id tuple for identiy, e.g., tty:S0 */ + ptr = strrchr(svc->dev, '/'); + if (ptr) { + ptr++; + if (!strncmp(ptr, "tty", 3)) + ptr += 3; + } else + ptr = svc->dev; + if (!id || id[0] == 0) + id = ptr; + strlcpy(svc->name, "tty", sizeof(svc->name)); + strlcpy(svc->id, id, sizeof(svc->id)); + } else + parse_name(svc, name); + + /* + * Warn if svc generates same condition (based on name:id) + * as an existing service. + */ + svc_validate(svc); + if (halt) parse_sighalt(svc, halt); if (delay) @@ -1206,6 +1278,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file) parse_log(svc, log); if (desc) strlcpy(svc->desc, desc, sizeof(svc->desc)); + else if (type == SVC_TYPE_TTY) + snprintf(svc->desc, sizeof(svc->desc), "Getty on %s", svc->dev); if (env) parse_env(svc, env); if (file) @@ -1258,9 +1332,6 @@ void service_monitor(pid_t lost, int status) if (fexist(SYNC_SHUTDOWN) || lost <= 1) return; - if (tty_respawn(lost)) - return; - svc = svc_find_by_pid(lost); if (!svc) { _d("collected unknown PID %d", lost); @@ -1276,7 +1347,7 @@ void service_monitor(pid_t lost, int status) return; /* Try removing PID file (in case service does not clean up after itself) */ - if (svc_is_daemon(svc)) { + if (svc_is_daemon(svc) || svc_is_tty(svc)) { service_cleanup(svc); } else if (svc_is_runtask(svc)) { /* run/task should run at least once per runlevel */ @@ -1376,7 +1447,7 @@ static void svc_set_state(svc_t *svc, svc_state_t new) *state = new; /* if PID isn't collected within SVC_TERM_TIMEOUT msec, kill it! */ - if ((*state == SVC_STOPPING_STATE)) { + if (*state == SVC_STOPPING_STATE) { _d("%s is stopping, wait %d sec before sending SIGKILL ...", svc->cmd, svc->killdelay / 1000); service_timeout_cancel(svc); @@ -1421,15 +1492,13 @@ restart: if (!svc->pid) { char cond[MAX_COND_LEN]; - /* PID was collected normally, no need to kill it */ - _d("%s: stopped normally, no need to send SIGKILL :)", svc->cmd); + _d("%s: stopped, cleaning up timers and conditions ...", svc->cmd); service_timeout_cancel(svc); - - _d("%s: clearing pid condition ...", svc->name); cond_clear(mkcond(svc, cond, sizeof(cond))); switch (svc->type) { case SVC_TYPE_SERVICE: + case SVC_TYPE_TTY: svc_set_state(svc, SVC_HALTED_STATE); break; @@ -1477,7 +1546,7 @@ restart: } if (!svc->pid) { - if (svc_is_daemon(svc)) { + if (svc_is_daemon(svc) || svc_is_tty(svc)) { svc_restarting(svc); svc_set_state(svc, SVC_HALTED_STATE); @@ -1596,7 +1665,7 @@ void service_step_all(int types) void service_worker(void *unused) { - service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_RUNTASK); + service_step_all(SVC_TYPE_RESPAWN | SVC_TYPE_RUNTASK); } /** diff --git a/src/sm.c b/src/sm.c index 8b8bf4d9..6a48158c 100644 --- a/src/sm.c +++ b/src/sm.c @@ -206,10 +206,6 @@ restart: break; } - /* No TTYs run at bootstrap, they have a delayed start. */ - if (prevlevel > 0) - tty_runlevel(); - sm->state = SM_RUNNING_STATE; break; @@ -224,8 +220,7 @@ restart: _d("Stopping services not allowed after reconf ..."); sm->in_teardown = 1; cond_reload(); - service_step_all(SVC_TYPE_SERVICE); - tty_reload(NULL); + service_step_all(SVC_TYPE_ANY); sm->state = SM_RELOAD_WAIT_STATE; break; @@ -242,16 +237,17 @@ restart: } sm->in_teardown = 0; - /* Cleanup stale services */ - svc_clean_dynamic(service_unregister); _d("Starting services after reconf ..."); - service_step_all(SVC_TYPE_SERVICE); + service_step_all(SVC_TYPE_ANY); + + /* Cleanup stale services */ + svc_clean_dynamic(service_unregister); _d("Calling reconf hooks ..."); plugin_run_hooks(HOOK_SVC_RECONF); - service_step_all(SVC_TYPE_SERVICE); + service_step_all(SVC_TYPE_ANY); _d("Reconfiguration done"); sm->state = SM_RUNNING_STATE; diff --git a/src/svc.c b/src/svc.c index fa63cfa7..f6d98a17 100644 --- a/src/svc.c +++ b/src/svc.c @@ -114,7 +114,7 @@ svc_t *svc_new(char *cmd, char *id, int type) /* Find first job n:o if registering multiple instances */ for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { - if (!strcmp(svc->cmd, cmd)) { + if (cmd && !strcmp(svc->cmd, cmd)) { job = svc->job; break; } @@ -130,13 +130,17 @@ svc_t *svc_new(char *cmd, char *id, int type) svc->job = job; if (id && id[0]) strlcpy(svc->id, id, sizeof(svc->id)); - strlcpy(svc->cmd, cmd, sizeof(svc->cmd)); + if (cmd) + strlcpy(svc->cmd, cmd, sizeof(svc->cmd)); /* Default description, if missing */ strlcpy(svc->desc, svc->name, sizeof(svc->desc)); /* Default HALT signal to send */ - svc->sighalt = SIGTERM; + if (svc_is_tty(svc)) + svc->sighalt = SIGHUP; + else + svc->sighalt = SIGTERM; /* Default delay between SIGTERM and SIGKILL */ svc->killdelay = SVC_TERM_TIMEOUT; @@ -327,7 +331,7 @@ svc_t *svc_stop_completed(void) svc_t *svc, *iter = NULL; for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { - if (svc->state == SVC_STOPPING_STATE) + if (svc->state == SVC_STOPPING_STATE && svc->pid > 1) return svc; } @@ -422,6 +426,22 @@ svc_t *svc_find_by_nameid(char *name, char *id) return NULL; } + +svc_t *svc_find_by_tty(char *dev) +{ + svc_t *svc, *iter = NULL; + + for (svc = svc_iterator(&iter, 1); svc; svc = svc_iterator(&iter, 0)) { + if (!svc_is_tty(svc)) + continue; + + if (!strcmp(dev, svc->name)) + return svc; + } + + return NULL; +} + /** * svc_find_by_plidfile - Find a service by its PID file * @fn: PID file, can be absolute path or relative to /run @@ -581,6 +601,9 @@ int svc_enabled(svc_t *svc) if (svc_is_missing(svc)) return 0; + if (svc_is_tty(svc) && bootstrap) + return 0; + return 1; } diff --git a/src/svc.h b/src/svc.h index de16bb49..a09f0905 100644 --- a/src/svc.h +++ b/src/svc.h @@ -42,10 +42,12 @@ typedef enum { SVC_TYPE_SERVICE = 1, /* Monitored, will be respawned */ SVC_TYPE_TASK = 2, /* One-shot, runs in parallell */ SVC_TYPE_RUN = 4, /* Like task, but wait for completion */ + SVC_TYPE_TTY = 8, /* Like service, but dedicated to TTYs */ SVC_TYPE_SYSV = 32, /* SysV style init.d script w/ start/stop */ } svc_type_t; #define SVC_TYPE_ANY (-1) +#define SVC_TYPE_RESPAWN (SVC_TYPE_SERVICE | SVC_TYPE_TTY) #define SVC_TYPE_RUNTASK (SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SYSV) typedef enum { @@ -125,15 +127,27 @@ typedef struct svc { char once; /* run/task, (at least) once per runlevel */ const char restart_cnt; /* Incremented for each restart by service monitor. */ - /* Set for services we need to redirect stdout/stderr to syslog */ - struct { - char enabled; - char null; - char console; - char file[64]; - char prio[20]; - char ident[20]; - } log; + union { + /* services we redirect stdout/stderr to syslog (not TTYs!) */ + struct { + char enabled; + char null; + char console; + char file[64]; + char prio[20]; + char ident[20]; + } log; + + /* Only for TTY type services */ + struct { + char dev[32]; + char baud[10]; + char term[10]; + int noclear; + int nowait; + int nologin; + }; + }; /* Identity */ char username[MAX_USER_LEN]; @@ -165,6 +179,7 @@ svc_t *svc_find (char *cmd, char *id); svc_t *svc_find_by_pid (pid_t pid); svc_t *svc_find_by_jobid (int job, char *id); svc_t *svc_find_by_nameid (char *name, char *id); +svc_t *svc_find_by_tty (char *dev); svc_t *svc_find_by_pidfile (char *fn); svc_t *svc_iterator (svc_t **iter, int first); @@ -189,9 +204,10 @@ int svc_is_unique (svc_t *svc); int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, char *)); -static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; } -static inline int svc_is_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV == svc->type; } -static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type); } +static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; } +static inline int svc_is_sysv (svc_t *svc) { return svc && SVC_TYPE_SYSV == svc->type; } +static inline int svc_is_tty (svc_t *svc) { return svc && SVC_TYPE_TTY == svc->type; } +static inline int svc_is_runtask (svc_t *svc) { return svc && (SVC_TYPE_RUNTASK & svc->type);} static inline int svc_is_forking (svc_t *svc) { return (svc_is_daemon(svc) || svc_is_sysv(svc)) && svc->pidfile[0] == '!'; } static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); } @@ -229,6 +245,7 @@ static inline int svc_has_cond(svc_t *svc) case SVC_TYPE_SERVICE: case SVC_TYPE_TASK: case SVC_TYPE_RUN: + case SVC_TYPE_TTY: case SVC_TYPE_SYSV: return 1; diff --git a/src/tty.c b/src/tty.c index 2e0a8845..7c0cf015 100644 --- a/src/tty.c +++ b/src/tty.c @@ -42,17 +42,16 @@ #ifdef FALLBACK_SHELL static pid_t fallback = 0; #endif -static LIST_HEAD(, tty) tty_list = LIST_HEAD_INITIALIZER(); -static char *canonicalize(char *tty) +char *tty_canonicalize(char *dev) { - struct stat st; static char path[80]; + struct stat st; - if (!tty) + if (!dev) return NULL; - strlcpy(path, tty, sizeof(path)); + strlcpy(path, dev, sizeof(path)); if (stat(path, &st)) { if (!strncmp(path, _PATH_DEV, strlen(_PATH_DEV))) { unavailable: @@ -60,7 +59,7 @@ static char *canonicalize(char *tty) return path; } - snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty); + snprintf(path, sizeof(path), "%s%s", _PATH_DEV, dev); if (stat(path, &st)) goto unavailable; } @@ -71,37 +70,40 @@ static char *canonicalize(char *tty) return path; } -void tty_mark(void) +/* + * The @console syntax is a wildcard to match the system console(s) given + * on the kernel cmdline. As such it can match multiple, or even none. + */ +int tty_isatcon(char *dev) { - struct tty *tty; - - LIST_FOREACH(tty, &tty_list, link) - tty->dirty = -1; + return dev && !strcmp(dev, "@console"); } -void tty_sweep(void) +int tty_atcon(char *buf, size_t len) { - struct tty *tty, *tmp; + FILE *fp; - LIST_FOREACH_SAFE(tty, &tty_list, link, tmp) { - if (!tty->dirty) - continue; - - _d("TTY %s dirty, stopping ...", tty->name); - tty_stop(tty); - - if (tty->dirty == -1) { - _d("TTY %s removed, cleaning up.", tty->name); - tty_unregister(tty); - } + fp = fopen("/sys/class/tty/console/active", "r"); + if (!fp) { + _e("Cannot find system console, is sysfs not mounted?"); + errno = ENOENT; + return -1; } + + if (!fgets(buf, len, fp)) { + fclose(fp); + return -1; + } + + chomp(buf); + fclose(fp); + + return 0; } /** - * tty_register - Register a getty on a device - * @line: Configuration, text after initial "tty" - * @rlimit: Limits for this service/run/task, may be global limits - * @file: The file name TTY was loaded from + * tty_parse_args - Parse cmdline args for a tty + * @cmd: command or tty * * A Finit tty line can use the internal getty implementation or an * external one, like the BusyBox getty for instance. This function @@ -119,66 +121,52 @@ void tty_sweep(void) * Different getty implementations prefer the TTY device argument in * different order, so take care to investigate this first. */ -int tty_register(char *line, struct rlimit rlimit[], char *file) +int tty_parse_args(char *cmd, struct tty *tty) { - struct tty *entry; - size_t i, num = 0; - char *tok, *cmd = NULL, *args[TTY_MAX_ARGS], buf[256]; - char *dev = NULL, *baud = NULL; - char *runlevels = NULL, *term = NULL; - int insert = 0, noclear = 0, nowait = 0, nologin = 0, atcon = 0; + char *dev = NULL; + size_t i; - if (!line) { - _e("Missing argument"); - return errno = EINVAL; - } - - /* - * Split line in separate arguments. For an external getty - * this is used with execv(), for the built-in it simplifies - * further translation. - */ - tok = strtok(line, " \t"); - while (tok && num < NELEMS(args)) { - if (!strcmp(tok, "noclear")) - noclear = 1; - else if (!strcmp(tok, "nowait")) - nowait = 1; - else if (!strcmp(tok, "nologin")) - nologin = 1; + do { + _d("token %s", cmd); + if (!strcmp(cmd, "noclear")) + tty->noclear = 1; + else if (!strcmp(cmd, "nowait")) + tty->nowait = 1; + else if (!strcmp(cmd, "nologin")) + tty->nologin = 1; else - args[num++] = tok; + tty->args[tty->num++] = cmd; - tok = strtok(NULL, " \t"); - } + cmd = strtok(NULL, " \t"); + } while (cmd && tty->num < NELEMS(tty->args)); /* Iterate over all args */ - for (i = 0; i < num; i++) { + for (i = 0; i < tty->num; i++) { + _d("Checking arg %s for dev and cmd ...", tty->args[i]); /* * First, figure out if built-in or external getty * tty [12345] /dev/ttyAMA0 115200 noclear vt220 # built-in * tty [12345] /sbin/getty -L 115200 @console vt100 noclear # external */ - if ((!cmd && !dev) || (cmd && !dev)) { - if (args[i][0] == '[') - runlevels = line; - if (!strcmp(args[i], "@console")) - dev = args[i]; - if (!strncmp(args[i], "/dev", 4)) - dev = args[i]; - if (!strncmp(args[i], "tty", 3) || !strcmp(args[i], "console")) - dev = args[i]; - if (!access(args[i], X_OK)) - cmd = args[i]; + if ((!tty->cmd && !dev) || (tty->cmd && !dev)) { + if (!strcmp(tty->args[i], "@console")) + dev = tty->args[i]; + if (!strncmp(tty->args[i], "/dev", 4)) + dev = tty->args[i]; + if (!strncmp(tty->args[i], "tty", 3) || !strcmp(tty->args[i], "console")) + dev = tty->args[i]; + if (!access(tty->args[i], X_OK)) + tty->cmd = tty->args[i]; /* The first arg must be one of the above */ continue; } /* Built-in getty args */ - if (!cmd && dev) { - if (isdigit(args[i][0])) { - baud = args[i]; + if (!tty->cmd && dev) { + _d("Found dev %s for built-in getty", dev); + if (isdigit(tty->args[i][0])) { + tty->baud = tty->args[i]; continue; } @@ -186,189 +174,25 @@ int tty_register(char *line, struct rlimit rlimit[], char *file) * Last arg, if not anything else, is the value * to be used for the TERM environment variable. */ - if (i + 1 == num) - term = args[i]; + if (i + 1 == tty->num) + tty->term = tty->args[i]; } } - if (!dev) { - error: + if (!tty_isatcon(dev)) + tty->dev = tty_canonicalize(dev); + + if (!tty->dev) { _e("Incomplete or non-existing TTY device given, cannot register."); return errno = EINVAL; } - /* Auto-detect serial console, for embedded devices mostly */ - if (!strcmp(dev, "@console")) { - FILE *fp; - - fp = fopen("/sys/class/tty/console/active", "r"); - if (!fp) { - _e("Cannot find system console, is sysfs not mounted?"); - return errno = ENOENT; - } - - atcon = 1; - if (fgets(buf, sizeof(buf), fp)) - dev = strtok(chomp(buf), " \t"); - - fclose(fp); - } - -again: - /* Ensure all getty (built-in + external) are registered with absolute path */ - dev = canonicalize(dev); - if (!dev) - goto error; - - entry = tty_find(dev); - if (!entry) { - entry = calloc(1, sizeof(*entry)); - if (!entry) - return errno = ENOMEM; - insert = 1; - } else { - if (entry->cmd) { - free(entry->cmd); - entry->cmd = NULL; - for (i = 0; i < TTY_MAX_ARGS; i++) { - if (entry->args[i]) - free(entry->args[i]); - entry->args[i] = NULL; - } - } - insert = 0; - } - - strlcpy(entry->name, dev, sizeof(entry->name)); - strlcpy(entry->baud, baud ? baud : "", sizeof(entry->baud)); - strlcpy(entry->term, term ? term : "", sizeof(entry->term)); - entry->noclear = noclear; - entry->nowait = nowait; - entry->nologin = nologin; - entry->runlevels = conf_parse_runlevels(runlevels); - - /* External getty */ - if (cmd) { - int j = 0; - - tok = strrchr(cmd, '/'); - if (!tok) - tok = cmd; - else - tok++; - entry->cmd = strdup(cmd); - args[1] = strdup(tok); - - for (i = 1; i < num; i++) { - char *arg = args[i]; - - /* Replace @console with actual device */ - if (arg && !strcmp(arg, "@console")) - arg = dev; - if (arg) - entry->args[j++] = strdup(arg); - } - entry->args[++j] = NULL; - } - - _d("Registering %s getty on TTY %s at %s baud with term %s on runlevels %s", - cmd ? "external" : "built-in", dev, baud ?: "NULL", term ?: "N/A", runlevels ?: "[2-5]"); - - if (insert) - LIST_INSERT_HEAD(&tty_list, entry, link); - - /* Register configured limits */ - memcpy(entry->rlimit, rlimit, sizeof(entry->rlimit)); - - if (file && conf_changed(file)) - entry->dirty = 1; /* Modified, restart */ - else - entry->dirty = 0; /* Not modified */ - _d("TTY %s is %sdirty", dev, entry->dirty ? "" : "NOT "); - - if (atcon) { - dev = strtok(NULL, " \t"); - if (dev) { - _d("Found another @console: %s", dev); - goto again; - } - } + _d("Registering %s getty on TTY %s at %s baud with term %s", tty->cmd ? "external" : "built-in", + tty->dev, tty->baud ?: "NULL", tty->term ?: "N/A"); return 0; } -int tty_unregister(struct tty *tty) -{ - if (!tty) { - _e("Missing argument"); - return errno = EINVAL; - } - - LIST_REMOVE(tty, link); - - if (tty->cmd) { - int i; - - free(tty->cmd); - for (i = 0; i < TTY_MAX_ARGS; i++) { - if (tty->args[i]) - free(tty->args[i]); - tty->args[i] = NULL; - } - } - free(tty); - - return 0; -} - -struct tty *tty_find(char *dev) -{ - struct tty *entry; - - LIST_FOREACH(entry, &tty_list, link) { - if (!strcmp(dev, entry->name)) - return entry; - } - - return NULL; -} - -size_t tty_num(void) -{ - size_t num = 0; - struct tty *entry; - - LIST_FOREACH(entry, &tty_list, link) - num++; - - return num; -} - -size_t tty_num_active(void) -{ - size_t num = 0; - struct tty *entry; - - LIST_FOREACH(entry, &tty_list, link) { - if (entry->pid) - num++; - } - - return num; -} - -struct tty *tty_find_by_pid(pid_t pid) -{ - struct tty *entry; - - LIST_FOREACH(entry, &tty_list, link) { - if (entry->pid == pid) - return entry; - } - - return NULL; -} - static int tty_exist(char *dev) { int fd, result; @@ -385,65 +209,35 @@ static int tty_exist(char *dev) return result; } -void tty_start(struct tty *tty) +int tty_exec(svc_t *tty) { char *dev; + int rc; - if (tty->pid) { - _d("%s: TTY already active", tty->name); - return; - } - - dev = canonicalize(tty->name); + dev = tty_canonicalize(tty->dev); if (!dev) { - _d("%s: Cannot find TTY device: %s", tty->name, strerror(errno)); - return; + _d("%s: Cannot find TTY device: %s", tty->dev, strerror(errno)); + return EX_CONFIG; } if (tty_exist(dev)) { _d("%s: Not a valid TTY: %s", dev, strerror(errno)); - return; + return EX_OSFILE; } if (tty->nologin) { _d("%s: Starting /bin/sh ...", dev); - tty->pid = run_sh(dev, tty->noclear, tty->nowait, tty->rlimit); - return; + return run_sh(dev, tty->noclear, tty->nowait, tty->rlimit); } _d("%s: Starting %sgetty ...", dev, !tty->cmd ? "built-in " : ""); - if (!tty->cmd) - tty->pid = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit); + if (!strcmp(tty->cmd, "tty")) + rc = run_getty(dev, tty->baud, tty->term, tty->noclear, tty->nowait, tty->rlimit); else - tty->pid = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, tty->rlimit); -} +// rc = run_getty2(dev, tty->cmd, tty->args, tty->noclear, tty->nowait, tty->rlimit); + rc = -1; -void tty_stop(struct tty *tty) -{ - if (!tty->pid) - return; - - /* - * XXX: TTY handling should be refactored to regular services, - * XXX: that way we could rely on the state machine to properly - * XXX: send SIGTERM, wait for max 2 sec to collect PID before - * XXX: sending SIGKILL. - */ - _d("Stopping TTY %s", tty->name); - kill(tty->pid, SIGKILL); - waitpid(tty->pid, NULL, 0); - tty->pid = 0; -} - -int tty_enabled(struct tty *tty) -{ - if (!tty) - return 0; - - if (ISSET(tty->runlevels, runlevel)) - return 1; - - return 0; + return rc; } /* @@ -484,73 +278,6 @@ int tty_fallback(pid_t lost) return 0; } -static void tty_action(struct tty *tty) -{ - if (!tty_enabled(tty)) - tty_stop(tty); - else - tty_start(tty); -} - -/* - * TTY monitor, called by service_monitor() - */ -int tty_respawn(pid_t pid) -{ - struct tty *tty = tty_find_by_pid(pid); - - if (!tty) - return tty_fallback(pid); - - /* Set DEAD_PROCESS UTMP entry */ - utmp_set_dead(pid); - - /* Clear PID to be able to respawn it. */ - tty->pid = 0; - tty_action(tty); - - return 1; -} - -/* - * Called after reload of /etc/finit.d/, stop/start TTYs - */ -void tty_reload(char *dev) -{ - struct tty *tty; - - if (dev) { - tty = tty_find(dev); - if (!tty) { - logit(LOG_WARNING, "No TTY registered for %s", dev); - return; - } - - tty_action(tty); - tty->dirty = 0; - return; - } - - tty_sweep(); - - LIST_FOREACH(tty, &tty_list, link) { - tty_action(tty); - tty->dirty = 0; - } -} - -/* Start all TTYs that exist in the system and are allowed at this runlevel */ -void tty_runlevel(void) -{ - struct tty *tty; - - LIST_FOREACH(tty, &tty_list, link) - tty_action(tty); - - /* Start fallback shell if enabled && no TTYs */ - tty_fallback(tty_num_active() > 0 ? 1 : 0); -} - /** * Local Variables: * indent-tabs-mode: t diff --git a/src/tty.h b/src/tty.h index febca073..15cde538 100644 --- a/src/tty.h +++ b/src/tty.h @@ -25,51 +25,30 @@ #ifndef FINIT_TTY_H_ #define FINIT_TTY_H_ -#include -#include -#include /* BSD sys/queue.h API */ - -#define TTY_MAX_ARGS 16 +#include +#include "svc.h" struct tty { - LIST_ENTRY(tty) link; + char *cmd; + char *args[MAX_NUM_SVC_ARGS]; + size_t num; - char name[42]; - char baud[10]; - char term[10]; - int noclear; - int nowait; - int nologin; - int runlevels; - - char *cmd; /* NULL when running built-in getty */ - char *args[TTY_MAX_ARGS]; - - int pid; - - /* Limits and scoping */ - struct rlimit rlimit[RLIMIT_NLIMITS]; - - /* Set if modified => reloaded, or -1 when marked for removal */ - int dirty; + char *dev; + char *baud; + char *term; + int noclear; + int nowait; + int nologin; }; -void tty_mark (void); -void tty_sweep (void); +char *tty_canonicalize (char *dev); -int tty_register (char *line, struct rlimit rlimit[], char *file); -int tty_unregister (struct tty *tty); +int tty_isatcon (char *dev); +int tty_atcon (char *buf, size_t len); -struct tty *tty_find (char *dev); -size_t tty_num (void); -size_t tty_num_active (void); -struct tty *tty_find_by_pid (pid_t pid); -void tty_start (struct tty *tty); -void tty_stop (struct tty *tty); -int tty_enabled (struct tty *tty); -int tty_respawn (pid_t pid); -void tty_reload (char *dev); -void tty_runlevel (void); +int tty_parse_args (char *cmdline, struct tty *tty); + +int tty_exec (svc_t *tty); #endif /* FINIT_TTY_H_ */