Refactor tty handling to use service backend enabling conditions etc.

Initial refactor of the tty implementation to use the service/run/task
general backend.  This enables all the features of services also for
ttys, except logging because it makes no sense.

Work in progress:

 - plugins/tty.c does not work anymore, could possibly be removed in
   favor of usinga (a new) condition instead (if-tty-exists)
 - fallback tty does not work anymore, should we remove it, or can we
   handle it as an optional built-in with (a new) condition?
 - @console does not work anymore, needs to generate N cloned services

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2021-04-14 16:29:19 +02:00
parent 3cf5d36972
commit 30020263ba
12 changed files with 285 additions and 497 deletions
+2 -3
View File
@@ -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;
+24 -51
View File
@@ -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)
+4 -3
View File
@@ -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);
}
/*
+2
View File
@@ -30,6 +30,7 @@
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
@@ -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;
+1 -1
View File
@@ -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);
+90 -21
View File
@@ -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);
}
/**
+6 -10
View File
@@ -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;
+27 -4
View File
@@ -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;
}
+29 -12
View File
@@ -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;
+79 -352
View File
@@ -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
+17 -38
View File
@@ -25,51 +25,30 @@
#ifndef FINIT_TTY_H_
#define FINIT_TTY_H_
#include <limits.h>
#include <sys/resource.h>
#include <lite/queue.h> /* BSD sys/queue.h API */
#define TTY_MAX_ARGS 16
#include <stdio.h>
#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_ */