mirror of
https://github.com/troglobit/finit.git
synced 2026-10-11 17:02:42 +07:00
Refactor TTY code to honor runlevels and use I/O plugin to watch /dev
Add support for runlevels to tty command and support for reading baud rate and TERM variable from finit.conf. The tty-watcher thread has been converted to a standard I/O plugin. Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
@@ -34,8 +34,7 @@ ARCHIVE = $(PKG).tar.xz
|
||||
EXEC = finit reboot
|
||||
HEADERS = plugin.h svc.h helpers.h queue.h
|
||||
DISTFILES = LICENSE README ChangeLog finit.conf services
|
||||
OBJS = finit.o conf.o helpers.o sig.o svc.o plugin.o
|
||||
OBJS += tty.o
|
||||
OBJS = finit.o conf.o helpers.o sig.o svc.o plugin.o tty.o
|
||||
SRCS = $(OBJS:.o=.c)
|
||||
DEPS = $(addprefix .,$(SRCS:.c=.d))
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "finit.h"
|
||||
#include "svc.h"
|
||||
#include "tty.h"
|
||||
#include "lite.h"
|
||||
#include "helpers.h"
|
||||
|
||||
@@ -49,6 +50,42 @@ static char *strip_line(char *line)
|
||||
return line;
|
||||
}
|
||||
|
||||
/* Convert optional "[!123456789S]" string into a bitmask */
|
||||
int parse_runlevels(char *runlevels)
|
||||
{
|
||||
int i, not = 0, bitmask = 0;
|
||||
|
||||
if (!runlevels)
|
||||
runlevels = "[234]";
|
||||
i = 1;
|
||||
while (i) {
|
||||
int level;
|
||||
char lvl = runlevels[i++];
|
||||
|
||||
if (']' == lvl || 0 == lvl)
|
||||
break;
|
||||
if ('!' == lvl) {
|
||||
not = 1;
|
||||
bitmask = 0x3FE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('s' == lvl || 'S' == lvl)
|
||||
lvl = ':'; /* RUNLEVEL_BOOT */
|
||||
|
||||
level = lvl - '0';
|
||||
if (level > RUNLEVEL_BOOT || level < 0)
|
||||
continue;
|
||||
|
||||
if (not)
|
||||
CLRBIT(bitmask, level);
|
||||
else
|
||||
SETBIT(bitmask, level);
|
||||
}
|
||||
|
||||
return bitmask;
|
||||
}
|
||||
|
||||
void parse_finit_conf(char *file)
|
||||
{
|
||||
FILE *fp;
|
||||
@@ -67,9 +104,8 @@ void parse_finit_conf(char *file)
|
||||
while (!feof(fp)) {
|
||||
if (!fgets(line, sizeof(line), fp))
|
||||
continue;
|
||||
|
||||
_d("conf: %s", line);
|
||||
chomp(line);
|
||||
_d("conf: %s", line);
|
||||
|
||||
/* Skip comments. */
|
||||
if (MATCH_CMD(line, "#", x))
|
||||
@@ -179,9 +215,7 @@ void parse_finit_conf(char *file)
|
||||
continue;
|
||||
}
|
||||
if (MATCH_CMD(line, "tty ", x)) {
|
||||
char *tty = strdup(strip_line(x));
|
||||
int baud = 115200; /* XXX - Read from config file */
|
||||
tty_add(tty, baud);
|
||||
tty_register(strip_line(x));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "plugin.h"
|
||||
#include "svc.h"
|
||||
#include "sig.h"
|
||||
#include "tty.h"
|
||||
#include "lite.h"
|
||||
|
||||
int debug = 0;
|
||||
@@ -273,7 +274,7 @@ int main(int argc, char* argv[])
|
||||
plugin_run_hooks(HOOK_SYSTEM_UP);
|
||||
|
||||
/* Start GETTY on console(s) */
|
||||
tty_start();
|
||||
tty_runlevel(runlevel);
|
||||
|
||||
/*
|
||||
* Enter main loop to monior /dev/initctl and services
|
||||
|
||||
@@ -37,12 +37,16 @@
|
||||
#if defined EMBEDDED_SYSTEM
|
||||
# define CONSOLE "/dev/console"
|
||||
# define SETUP_DEVFS "/sbin/mdev -s"
|
||||
# define GETTY "/sbin/getty -L 115200 %s vt100"
|
||||
# define GETTY "/sbin/getty -L"
|
||||
# define GETTY_BUSYBOX
|
||||
# define BAUDRATE 115200
|
||||
# define RUNLEVEL 2
|
||||
#else /* Debian/Ubuntu based distributions */
|
||||
# define CONSOLE "/dev/tty1"
|
||||
# define SETUP_DEVFS "/sbin/udevd --daemon"
|
||||
# define GETTY "/sbin/getty -8 38400 %s"
|
||||
# define GETTY "/sbin/getty -8"
|
||||
# define GETTY_AGETTY
|
||||
# define BAUDRATE 38400
|
||||
# define RUNLEVEL 2
|
||||
# define RANDOMSEED "/var/lib/urandom/random-seed"
|
||||
# define REMOUNT_ROOTFS_RW
|
||||
@@ -84,11 +88,8 @@ extern char *console;
|
||||
extern char *__progname;
|
||||
|
||||
/* conf.c */
|
||||
void parse_finit_conf(char *file);
|
||||
|
||||
/* tty.c */
|
||||
void tty_start(void);
|
||||
int tty_add(char *tty, int baud);
|
||||
int parse_runlevels (char *runlevels);
|
||||
void parse_finit_conf (char *file);
|
||||
|
||||
#endif /* FINIT_H_ */
|
||||
|
||||
|
||||
@@ -505,7 +505,7 @@ int run_interactive(char *cmd, char *fmt, ...)
|
||||
return status;
|
||||
}
|
||||
|
||||
pid_t run_getty(char *cmd, int console)
|
||||
pid_t run_getty(char *cmd, char *args[], int console)
|
||||
{
|
||||
pid_t pid = fork();
|
||||
|
||||
@@ -557,7 +557,7 @@ pid_t run_getty(char *cmd, int console)
|
||||
if (fexist(SYNC_STOPPED))
|
||||
continue;
|
||||
|
||||
run(cmd);
|
||||
execv(cmd, args);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
@@ -657,6 +657,13 @@ void chomp(char *str)
|
||||
*x = 0;
|
||||
}
|
||||
|
||||
/* Signal safe sleep ... we get a lot of SIGCHLD at reboot */
|
||||
void do_sleep(unsigned int sec)
|
||||
{
|
||||
while ((sec = sleep(sec)))
|
||||
;
|
||||
}
|
||||
|
||||
void set_hostname(char *hostname)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
@@ -74,6 +74,10 @@
|
||||
#define _e(fmt, args...) do { fprintf(stderr, "finit:%s:%s() - " fmt "\n", __FILE__, __func__, ##args); } while (0)
|
||||
#define _pe(fmt, args...) do { fprintf(stderr, "finit:%s:%s() - " fmt ". Error %d: %s\n", __FILE__, __func__, ##args, errno, strerror(errno)); } while (0)
|
||||
|
||||
#define ISSET(a,i) ((a & (1 << (i)) ? 1 : 0))
|
||||
#define SETBIT(a,i) (a |= (1 << (i)))
|
||||
#define CLRBIT(a,i) (a &= ~(1 << (i)))
|
||||
|
||||
extern int debug;
|
||||
extern int verbose;
|
||||
|
||||
@@ -93,6 +97,7 @@ void print_desc (char *action, char *desc);
|
||||
int print_result (int fail);
|
||||
int start_process (char *cmd, char *args[], int console);
|
||||
void chomp (char *str);
|
||||
void do_sleep (unsigned int sec);
|
||||
int getuser (char *username);
|
||||
int getgroup (char *group);
|
||||
void set_hostname (char *hostname);
|
||||
@@ -100,7 +105,7 @@ void set_hostname (char *hostname);
|
||||
int complete (char *cmd, int pid);
|
||||
int run (char *cmd);
|
||||
int run_interactive (char *cmd, char *fmt, ...);
|
||||
pid_t run_getty (char *cmd, int console);
|
||||
pid_t run_getty (char *cmd, char *args[], int console);
|
||||
int run_parts (char *dir, char *cmd);
|
||||
|
||||
#endif /* FINIT_HELPERS_H_ */
|
||||
|
||||
@@ -124,9 +124,9 @@ int plugin_unregister(plugin_t *plugin)
|
||||
}
|
||||
|
||||
|
||||
// _d("Comparing %s against plugin %s", str, p->name);
|
||||
#define SEARCH_PLUGIN(str) \
|
||||
PLUGIN_ITERATOR(p, tmp) { \
|
||||
_d("Comparing %s against plugin %s", str, p->name); \
|
||||
if (!strcmp(p->name, str)) \
|
||||
return p; \
|
||||
}
|
||||
@@ -305,9 +305,9 @@ static void check_plugin_depends(plugin_t *plugin)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PLUGIN_DEP_MAX && plugin->depends[i]; i++) {
|
||||
_d("Plugin %s depends on %s ...", plugin->name, plugin->depends[i]);
|
||||
// _d("Plugin %s depends on %s ...", plugin->name, plugin->depends[i]);
|
||||
if (plugin_find(plugin->depends[i])) {
|
||||
_d("OK plugin %s was already loaded.", plugin->depends[i]);
|
||||
// _d("OK plugin %s was already loaded.", plugin->depends[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ CPPFLAGS += -I..
|
||||
LDFLAGS := $(filter-out -rdynamic, $(LDFLAGS))
|
||||
LDFLAGS += -shared
|
||||
PLUGINS ?= initctl.so alsa-utils.so bootmisc.so dbus.so hwclock.so \
|
||||
resolvconf.so urandom.so x11-common.so
|
||||
resolvconf.so urandom.so x11-common.so tty.so
|
||||
DEPS = $(addprefix .,$(PLUGINS:.so=.d))
|
||||
|
||||
include ../common.mk
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/* Optional TTY Watcher, used to catch new TTYs that are discovered, e.g., USB
|
||||
*
|
||||
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
|
||||
* Copyright (c) 2013 Joachim Nilsson <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h> /* O_RDONLY et al */
|
||||
#include <unistd.h> /* read() */
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "tty.h"
|
||||
|
||||
static void watcher(void *arg, int fd, int events);
|
||||
|
||||
static plugin_t plugin = {
|
||||
.io = {
|
||||
.cb = watcher,
|
||||
.flags = PLUGIN_IO_READ,
|
||||
},
|
||||
};
|
||||
|
||||
static void setup(void)
|
||||
{
|
||||
plugin.io.fd = inotify_init1(IN_NONBLOCK);
|
||||
if (-1 == plugin.io.fd || inotify_add_watch(plugin.io.fd, "/dev", IN_CREATE | IN_DELETE) < 0)
|
||||
_e("Failed starting TTY watcher: %s", strerror(errno));
|
||||
}
|
||||
|
||||
static void watcher(void *UNUSED(arg), int fd, int UNUSED(events))
|
||||
{
|
||||
int len = 0;
|
||||
char buf[EVENT_SIZE], name[42];
|
||||
tty_node_t *entry;
|
||||
struct inotify_event *notified = (struct inotify_event *)buf;
|
||||
|
||||
while ((len = read(fd, buf, sizeof(buf)))) {
|
||||
if (-1 == len) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
|
||||
snprintf(name, sizeof(name), "/dev/%s", notified->name);
|
||||
entry = tty_find(name);
|
||||
if (entry && tty_enabled(&entry->data, runlevel)) {
|
||||
if (notified->mask & IN_CREATE)
|
||||
tty_start(&entry->data);
|
||||
else if (entry->data.pid)
|
||||
tty_stop(&entry->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
{
|
||||
setup();
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
PLUGIN_EXIT(plugin_exit)
|
||||
{
|
||||
plugin_unregister(&plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -49,13 +49,6 @@
|
||||
|
||||
static int stopped = 0;
|
||||
|
||||
/* Signal safe sleep ... we get a lot of SIGCHLD at reboot */
|
||||
void do_sleep(unsigned int sec)
|
||||
{
|
||||
while ((sec = sleep(sec)))
|
||||
;
|
||||
}
|
||||
|
||||
void do_shutdown (int sig)
|
||||
{
|
||||
touch(SYNC_SHUTDOWN);
|
||||
|
||||
@@ -29,13 +29,10 @@
|
||||
#include "helpers.h"
|
||||
#include "private.h"
|
||||
#include "sig.h"
|
||||
#include "tty.h"
|
||||
#include "lite.h"
|
||||
#include "svc.h"
|
||||
|
||||
#define ISSET(a,i) ((a & (1 << (i)) ? 1 : 0))
|
||||
#define SETBIT(a,i) (a |= (1 << (i)))
|
||||
#define CLRBIT(a,i) (a &= ~(1 << (i)))
|
||||
|
||||
/* The registered services are kept in shared memory for easy read-only access
|
||||
* by 3rd party APIs. Mostly because of the ability to, from the outside, query
|
||||
* liveness state of all daemons that should run. */
|
||||
@@ -151,14 +148,14 @@ void svc_bootstrap(void)
|
||||
* Stops all services not in @newlevel and starts, or lets continue to run,
|
||||
* those in @newlevel. Also updates @prevlevel and active @runlevel.
|
||||
*/
|
||||
void svc_runlevel (int newlevel)
|
||||
void svc_runlevel(int newlevel)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
if (runlevel == newlevel)
|
||||
return;
|
||||
|
||||
if (newlevel > 5 || newlevel < 1)
|
||||
if (newlevel < 0 || newlevel > 9)
|
||||
return;
|
||||
|
||||
prevlevel = runlevel;
|
||||
@@ -176,10 +173,22 @@ void svc_runlevel (int newlevel)
|
||||
}
|
||||
}
|
||||
|
||||
if (0 == runlevel) {
|
||||
do_shutdown(SIGUSR2);
|
||||
return;
|
||||
}
|
||||
if (6 == runlevel) {
|
||||
do_shutdown(SIGUSR1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (runlevel == 1)
|
||||
touch("/etc/nologin"); /* Disable login in single-user mode */
|
||||
else
|
||||
remove("/etc/nologin");
|
||||
|
||||
if (RUNLEVEL_BOOT != prevlevel)
|
||||
tty_runlevel(runlevel);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,7 +223,7 @@ void svc_runlevel (int newlevel)
|
||||
*/
|
||||
int svc_register(int type, char *line, char *username)
|
||||
{
|
||||
int i = 0, not = 0;
|
||||
int i = 0;
|
||||
char *cmd, *desc, *runlevels = NULL;
|
||||
svc_t *svc;
|
||||
|
||||
@@ -269,34 +278,7 @@ int svc_register(int type, char *line, char *username)
|
||||
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
||||
svc->args[i][0] = 0;
|
||||
|
||||
if (!runlevels)
|
||||
runlevels = "[234]";
|
||||
i = 1;
|
||||
while (i) {
|
||||
int level;
|
||||
char lvl = runlevels[i++];
|
||||
|
||||
if (']' == lvl || 0 == lvl)
|
||||
break;
|
||||
if ('!' == lvl) {
|
||||
not = 1;
|
||||
svc->runlevels = 0x3FE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ('s' == lvl || 'S' == lvl)
|
||||
lvl = ':'; /* RUNLEVEL_BOOT */
|
||||
|
||||
level = lvl - '0';
|
||||
if (level > RUNLEVEL_BOOT || level < 0)
|
||||
continue;
|
||||
|
||||
if (not)
|
||||
CLRBIT(svc->runlevels, level);
|
||||
else
|
||||
SETBIT(svc->runlevels, level);
|
||||
}
|
||||
|
||||
svc->runlevels = parse_runlevels(runlevels);
|
||||
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
|
||||
|
||||
return 0;
|
||||
@@ -358,6 +340,9 @@ void svc_monitor(void)
|
||||
if (is_norespawn())
|
||||
return;
|
||||
|
||||
if (tty_respawn(lost))
|
||||
return;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (lost != svc->pid)
|
||||
continue;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* TTY handling in finit
|
||||
/* Finit TTY handling
|
||||
*
|
||||
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
|
||||
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
|
||||
* Copyright (c) 2013 Joachim Nilsson <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -21,119 +22,181 @@
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <sys/inotify.h>
|
||||
#include <signal.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "queue.h"
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "lite.h"
|
||||
#include "tty.h"
|
||||
|
||||
typedef struct finit_tty_t {
|
||||
char *name;
|
||||
int baud;
|
||||
|
||||
int pid; /* Runtime information*/
|
||||
} finit_tty_t;
|
||||
|
||||
typedef struct node {
|
||||
LIST_ENTRY(node) link;
|
||||
finit_tty_t data;
|
||||
} node_t;
|
||||
LIST_HEAD(, tty_node) tty_list = LIST_HEAD_INITIALIZER();
|
||||
|
||||
|
||||
/* We need to make room for the filename also, which is sent after inotify_event. */
|
||||
#define EVENT_SIZE ((sizeof(struct inotify_event) + FILENAME_MAX))
|
||||
|
||||
LIST_HEAD(, node) node_list = LIST_HEAD_INITIALIZER();
|
||||
int tty_add(char *tty, int baud)
|
||||
/* tty [!1-9,S] <DEV> [BAUD[,BAUD,...]] [TERM] */
|
||||
int tty_register(char *line)
|
||||
{
|
||||
node_t *entry;
|
||||
tty_node_t *entry;
|
||||
int insert = 0, baud = 0;
|
||||
char *cmd, *dev;
|
||||
char *runlevels = NULL, *term = NULL;
|
||||
|
||||
if (!tty)
|
||||
if (!line) {
|
||||
_e("Invalid input argument.");
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
entry = malloc(sizeof(*entry));
|
||||
if (!entry)
|
||||
return errno = ENOMEM;
|
||||
cmd = strtok(line, " ");
|
||||
if (!cmd) {
|
||||
incomplete:
|
||||
_e("Incomplete tty, cannot register.");
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
entry->data.name = tty;
|
||||
if (cmd[0] == '[') { /* [runlevels] */
|
||||
runlevels = &cmd[0];
|
||||
dev = strtok(NULL, " ");
|
||||
if (!dev)
|
||||
goto incomplete;
|
||||
} else {
|
||||
dev = cmd;
|
||||
}
|
||||
|
||||
cmd = strtok(NULL, " ");
|
||||
if (cmd) {
|
||||
baud = strtonum(cmd, 50, 4000000, NULL);
|
||||
term = strtok(NULL, " ");
|
||||
}
|
||||
|
||||
entry = tty_find(dev);
|
||||
if (!entry) {
|
||||
insert = 1;
|
||||
entry = malloc(sizeof(*entry));
|
||||
if (!entry)
|
||||
return errno = ENOMEM;
|
||||
}
|
||||
|
||||
entry->data.name = strdup(dev);
|
||||
if (!baud)
|
||||
baud = BAUDRATE;
|
||||
entry->data.baud = baud;
|
||||
entry->data.term = term ? strdup(term) : NULL;
|
||||
entry->data.runlevels = parse_runlevels(runlevels);
|
||||
_d("Registering tty %s at %d baud with term=%s on runlevels %s", dev, baud, term ?: "N/A", runlevels);
|
||||
|
||||
LIST_INSERT_HEAD(&node_list, entry, link);
|
||||
if (insert)
|
||||
LIST_INSERT_HEAD(&tty_list, entry, link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tty_startstop_one(finit_tty_t *tty, uint32_t mask)
|
||||
tty_node_t *tty_find(char *dev)
|
||||
{
|
||||
int is_console = 0;
|
||||
char cmd[strlen(GETTY) + FILENAME_MAX];
|
||||
tty_node_t *entry;
|
||||
|
||||
LIST_FOREACH(entry, &tty_list, link) {
|
||||
if (!strcmp(dev, entry->data.name))
|
||||
return entry;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tty_node_t *tty_find_by_pid(pid_t pid)
|
||||
{
|
||||
tty_node_t *entry;
|
||||
|
||||
LIST_FOREACH(entry, &tty_list, link) {
|
||||
if (entry->data.pid == pid)
|
||||
return entry;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void tty_start(finit_tty_t *tty)
|
||||
{
|
||||
int i = 0, is_console = 0;
|
||||
char *cmd, *arg;
|
||||
char line[FILENAME_MAX];
|
||||
char *args[42];
|
||||
|
||||
if (tty->pid)
|
||||
return;
|
||||
|
||||
if (console && !strcmp(tty->name, console))
|
||||
is_console = 1;
|
||||
|
||||
if (mask & IN_CREATE) {
|
||||
_d("Starting %s as %s", tty->name, is_console ? "console" : "TTY");
|
||||
snprintf(cmd, sizeof(cmd), GETTY, tty->name);
|
||||
tty->pid = run_getty(cmd, is_console);
|
||||
}
|
||||
#if defined(GETTY_AGETTY)
|
||||
snprintf(line, sizeof(line), "%s %s %d %s", GETTY, tty->name, tty->baud, tty->term ?: "");
|
||||
#elif defined(GETTY_BUSYBOX)
|
||||
snprintf(line, sizeof(line), "%s %d %s %s", GETTY, tty->baud, tty->name, tty->term ?: "");
|
||||
#else
|
||||
#error No GETTY defined!
|
||||
#endif
|
||||
cmd = strtok(line, " ");
|
||||
args[i++] = basename(cmd);
|
||||
while ((arg = strtok(NULL, " ")))
|
||||
args[i++] = arg;
|
||||
args[i] = NULL;
|
||||
|
||||
/* Kill the spawned child, and recollect it. */
|
||||
if ((mask & IN_DELETE) && tty->pid) {
|
||||
_d("Stopping %s", tty->name);
|
||||
kill(tty->pid, SIGKILL);
|
||||
waitpid(tty->pid, NULL, 0);
|
||||
tty->pid = 0;
|
||||
}
|
||||
_d("Starting %s: %s on %s", is_console ? "console" : "TTY", cmd, tty->name);
|
||||
tty->pid = run_getty(cmd, args, is_console);
|
||||
}
|
||||
|
||||
static void tty_watcher(node_t *entry)
|
||||
void tty_stop(finit_tty_t *tty)
|
||||
{
|
||||
int fd = inotify_init();
|
||||
if (!tty->pid)
|
||||
return;
|
||||
|
||||
prctl(PR_SET_NAME, "tty-watcher", 0, 0, 0);
|
||||
|
||||
if (-1 == fd || inotify_add_watch(fd, "/dev", IN_CREATE | IN_DELETE) < 0) {
|
||||
fprintf(stderr, "finit: Failed starting TTY watcher: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int len = 0;
|
||||
char buf[EVENT_SIZE];
|
||||
struct inotify_event *notified = (struct inotify_event *)buf;
|
||||
|
||||
while ((len = read(fd, buf, EVENT_SIZE))) {
|
||||
if (len == -1) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
continue;
|
||||
}
|
||||
|
||||
LIST_FOREACH(entry, &node_list, link) {
|
||||
if (!strcmp(notified->name, entry->data.name))
|
||||
tty_startstop_one(&entry->data, notified->mask);
|
||||
}
|
||||
}
|
||||
}
|
||||
_d("Stopping TTY %s", tty->name);
|
||||
kill(tty->pid, SIGTERM);
|
||||
do_sleep(2);
|
||||
kill(tty->pid, SIGKILL);
|
||||
waitpid(tty->pid, NULL, 0);
|
||||
tty->pid = 0;
|
||||
}
|
||||
|
||||
void tty_start(void)
|
||||
int tty_enabled(finit_tty_t *tty, int runlevel)
|
||||
{
|
||||
node_t *entry;
|
||||
if (!tty)
|
||||
return 0;
|
||||
|
||||
/* Start all TTYs that already exist in the system */
|
||||
LIST_FOREACH(entry, &node_list, link) {
|
||||
chdir("/dev");
|
||||
if (fexist (entry->data.name))
|
||||
tty_startstop_one(&entry->data, IN_CREATE);
|
||||
if (!ISSET(tty->runlevels, runlevel))
|
||||
return 0;
|
||||
else if (fexist(tty->name))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TTY monitor, called by svc_monitor() */
|
||||
int tty_respawn(pid_t pid)
|
||||
{
|
||||
tty_node_t *entry = tty_find_by_pid(pid);
|
||||
|
||||
if (!entry)
|
||||
return 0;
|
||||
|
||||
if (!tty_enabled(&entry->data, runlevel))
|
||||
tty_stop(&entry->data);
|
||||
else
|
||||
tty_start(&entry->data);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Start all TTYs that exist in the system and are allowed at this runlevel */
|
||||
void tty_runlevel(int runlevel)
|
||||
{
|
||||
tty_node_t *entry;
|
||||
|
||||
LIST_FOREACH(entry, &tty_list, link) {
|
||||
if (!tty_enabled(&entry->data, runlevel))
|
||||
tty_stop(&entry->data);
|
||||
else
|
||||
tty_start(&entry->data);
|
||||
}
|
||||
|
||||
/* Start a watcher to catch new TTYs that is discovered, e.g., USB */
|
||||
if (!fork())
|
||||
tty_watcher(entry);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/* Finit TTY handling
|
||||
*
|
||||
* Copyright (c) 2013 Mattias Walström <lazzer@gmail.com>
|
||||
* Copyright (c) 2013 Joachim Nilsson <troglobit@gmail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef FINIT_TTY_H_
|
||||
#define FINIT_TTY_H_
|
||||
|
||||
#include "queue.h" /* BSD sys/queue.h API */
|
||||
|
||||
#define EVENT_SIZE ((sizeof(struct inotify_event) + 20))
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int baud;
|
||||
char *term;
|
||||
int runlevels;
|
||||
|
||||
int pid;
|
||||
} finit_tty_t;
|
||||
|
||||
typedef struct tty_node {
|
||||
LIST_ENTRY(tty_node) link;
|
||||
finit_tty_t data;
|
||||
} tty_node_t;
|
||||
|
||||
//extern LIST_HEAD(, tty_node) tty_list;
|
||||
|
||||
int tty_register (char *line);
|
||||
tty_node_t *tty_find (char *dev);
|
||||
tty_node_t *tty_find_by_pid (pid_t pid);
|
||||
void tty_start (finit_tty_t *tty);
|
||||
void tty_stop (finit_tty_t *tty);
|
||||
int tty_enabled (finit_tty_t *tty, int runlevel);
|
||||
int tty_respawn (pid_t pid);
|
||||
void tty_runlevel (int runlevel);
|
||||
|
||||
#endif /* FINIT_TTY_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* version-control: t
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
Reference in New Issue
Block a user