mirror of
https://github.com/troglobit/finit.git
synced 2026-11-14 19:03:16 +00: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:
1 parent
f97aab4699
commit
0f60aa2289
13 files changed
+391
-144
No files matched your search
@@ -34,8 +34,7 @@ ARCHIVE = $(PKG).tar.xz
|
|||||||
EXEC = finit reboot
|
EXEC = finit reboot
|
||||||
HEADERS = plugin.h svc.h helpers.h queue.h
|
HEADERS = plugin.h svc.h helpers.h queue.h
|
||||||
DISTFILES = LICENSE README ChangeLog finit.conf services
|
DISTFILES = LICENSE README ChangeLog finit.conf services
|
||||||
OBJS = finit.o conf.o helpers.o sig.o svc.o plugin.o
|
OBJS = finit.o conf.o helpers.o sig.o svc.o plugin.o tty.o
|
||||||
OBJS += tty.o
|
|
||||||
SRCS = $(OBJS:.o=.c)
|
SRCS = $(OBJS:.o=.c)
|
||||||
DEPS = $(addprefix .,$(SRCS:.c=.d))
|
DEPS = $(addprefix .,$(SRCS:.c=.d))
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
|
#include "tty.h"
|
||||||
#include "lite.h"
|
#include "lite.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
|
||||||
@@ -49,6 +50,42 @@ static char *strip_line(char *line)
|
|||||||
return 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)
|
void parse_finit_conf(char *file)
|
||||||
{
|
{
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
@@ -67,9 +104,8 @@ void parse_finit_conf(char *file)
|
|||||||
while (!feof(fp)) {
|
while (!feof(fp)) {
|
||||||
if (!fgets(line, sizeof(line), fp))
|
if (!fgets(line, sizeof(line), fp))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
_d("conf: %s", line);
|
|
||||||
chomp(line);
|
chomp(line);
|
||||||
|
_d("conf: %s", line);
|
||||||
|
|
||||||
/* Skip comments. */
|
/* Skip comments. */
|
||||||
if (MATCH_CMD(line, "#", x))
|
if (MATCH_CMD(line, "#", x))
|
||||||
@@ -179,9 +215,7 @@ void parse_finit_conf(char *file)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (MATCH_CMD(line, "tty ", x)) {
|
if (MATCH_CMD(line, "tty ", x)) {
|
||||||
char *tty = strdup(strip_line(x));
|
tty_register(strip_line(x));
|
||||||
int baud = 115200; /* XXX - Read from config file */
|
|
||||||
tty_add(tty, baud);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@
|
|||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
#include "svc.h"
|
#include "svc.h"
|
||||||
#include "sig.h"
|
#include "sig.h"
|
||||||
|
#include "tty.h"
|
||||||
#include "lite.h"
|
#include "lite.h"
|
||||||
|
|
||||||
int debug = 0;
|
int debug = 0;
|
||||||
@@ -273,7 +274,7 @@ int main(int argc, char* argv[])
|
|||||||
plugin_run_hooks(HOOK_SYSTEM_UP);
|
plugin_run_hooks(HOOK_SYSTEM_UP);
|
||||||
|
|
||||||
/* Start GETTY on console(s) */
|
/* Start GETTY on console(s) */
|
||||||
tty_start();
|
tty_runlevel(runlevel);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enter main loop to monior /dev/initctl and services
|
* Enter main loop to monior /dev/initctl and services
|
||||||
|
|||||||
@@ -37,12 +37,16 @@
|
|||||||
#if defined EMBEDDED_SYSTEM
|
#if defined EMBEDDED_SYSTEM
|
||||||
# define CONSOLE "/dev/console"
|
# define CONSOLE "/dev/console"
|
||||||
# define SETUP_DEVFS "/sbin/mdev -s"
|
# 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
|
# define RUNLEVEL 2
|
||||||
#else /* Debian/Ubuntu based distributions */
|
#else /* Debian/Ubuntu based distributions */
|
||||||
# define CONSOLE "/dev/tty1"
|
# define CONSOLE "/dev/tty1"
|
||||||
# define SETUP_DEVFS "/sbin/udevd --daemon"
|
# 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 RUNLEVEL 2
|
||||||
# define RANDOMSEED "/var/lib/urandom/random-seed"
|
# define RANDOMSEED "/var/lib/urandom/random-seed"
|
||||||
# define REMOUNT_ROOTFS_RW
|
# define REMOUNT_ROOTFS_RW
|
||||||
@@ -84,11 +88,8 @@ extern char *console;
|
|||||||
extern char *__progname;
|
extern char *__progname;
|
||||||
|
|
||||||
/* conf.c */
|
/* conf.c */
|
||||||
void parse_finit_conf(char *file);
|
int parse_runlevels (char *runlevels);
|
||||||
|
void parse_finit_conf (char *file);
|
||||||
/* tty.c */
|
|
||||||
void tty_start(void);
|
|
||||||
int tty_add(char *tty, int baud);
|
|
||||||
|
|
||||||
#endif /* FINIT_H_ */
|
#endif /* FINIT_H_ */
|
||||||
|
|
||||||
|
|||||||
@@ -505,7 +505,7 @@ int run_interactive(char *cmd, char *fmt, ...)
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
pid_t run_getty(char *cmd, int console)
|
pid_t run_getty(char *cmd, char *args[], int console)
|
||||||
{
|
{
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
|
|
||||||
@@ -557,7 +557,7 @@ pid_t run_getty(char *cmd, int console)
|
|||||||
if (fexist(SYNC_STOPPED))
|
if (fexist(SYNC_STOPPED))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
run(cmd);
|
execv(cmd, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit(0);
|
exit(0);
|
||||||
@@ -657,6 +657,13 @@ void chomp(char *str)
|
|||||||
*x = 0;
|
*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)
|
void set_hostname(char *hostname)
|
||||||
{
|
{
|
||||||
FILE *fp;
|
FILE *fp;
|
||||||
|
|||||||
@@ -74,6 +74,10 @@
|
|||||||
#define _e(fmt, args...) do { fprintf(stderr, "finit:%s:%s() - " fmt "\n", __FILE__, __func__, ##args); } while (0)
|
#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 _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 debug;
|
||||||
extern int verbose;
|
extern int verbose;
|
||||||
|
|
||||||
@@ -93,6 +97,7 @@ void print_desc (char *action, char *desc);
|
|||||||
int print_result (int fail);
|
int print_result (int fail);
|
||||||
int start_process (char *cmd, char *args[], int console);
|
int start_process (char *cmd, char *args[], int console);
|
||||||
void chomp (char *str);
|
void chomp (char *str);
|
||||||
|
void do_sleep (unsigned int sec);
|
||||||
int getuser (char *username);
|
int getuser (char *username);
|
||||||
int getgroup (char *group);
|
int getgroup (char *group);
|
||||||
void set_hostname (char *hostname);
|
void set_hostname (char *hostname);
|
||||||
@@ -100,7 +105,7 @@ void set_hostname (char *hostname);
|
|||||||
int complete (char *cmd, int pid);
|
int complete (char *cmd, int pid);
|
||||||
int run (char *cmd);
|
int run (char *cmd);
|
||||||
int run_interactive (char *cmd, char *fmt, ...);
|
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);
|
int run_parts (char *dir, char *cmd);
|
||||||
|
|
||||||
#endif /* FINIT_HELPERS_H_ */
|
#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) \
|
#define SEARCH_PLUGIN(str) \
|
||||||
PLUGIN_ITERATOR(p, tmp) { \
|
PLUGIN_ITERATOR(p, tmp) { \
|
||||||
_d("Comparing %s against plugin %s", str, p->name); \
|
|
||||||
if (!strcmp(p->name, str)) \
|
if (!strcmp(p->name, str)) \
|
||||||
return p; \
|
return p; \
|
||||||
}
|
}
|
||||||
@@ -305,9 +305,9 @@ static void check_plugin_depends(plugin_t *plugin)
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < PLUGIN_DEP_MAX && plugin->depends[i]; 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])) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ CPPFLAGS += -I..
|
|||||||
LDFLAGS := $(filter-out -rdynamic, $(LDFLAGS))
|
LDFLAGS := $(filter-out -rdynamic, $(LDFLAGS))
|
||||||
LDFLAGS += -shared
|
LDFLAGS += -shared
|
||||||
PLUGINS ?= initctl.so alsa-utils.so bootmisc.so dbus.so hwclock.so \
|
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))
|
DEPS = $(addprefix .,$(PLUGINS:.so=.d))
|
||||||
|
|
||||||
include ../common.mk
|
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;
|
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)
|
void do_shutdown (int sig)
|
||||||
{
|
{
|
||||||
touch(SYNC_SHUTDOWN);
|
touch(SYNC_SHUTDOWN);
|
||||||
|
|||||||
@@ -29,13 +29,10 @@
|
|||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "private.h"
|
#include "private.h"
|
||||||
#include "sig.h"
|
#include "sig.h"
|
||||||
|
#include "tty.h"
|
||||||
#include "lite.h"
|
#include "lite.h"
|
||||||
#include "svc.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
|
/* 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
|
* by 3rd party APIs. Mostly because of the ability to, from the outside, query
|
||||||
* liveness state of all daemons that should run. */
|
* 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,
|
* Stops all services not in @newlevel and starts, or lets continue to run,
|
||||||
* those in @newlevel. Also updates @prevlevel and active @runlevel.
|
* those in @newlevel. Also updates @prevlevel and active @runlevel.
|
||||||
*/
|
*/
|
||||||
void svc_runlevel (int newlevel)
|
void svc_runlevel(int newlevel)
|
||||||
{
|
{
|
||||||
svc_t *svc;
|
svc_t *svc;
|
||||||
|
|
||||||
if (runlevel == newlevel)
|
if (runlevel == newlevel)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (newlevel > 5 || newlevel < 1)
|
if (newlevel < 0 || newlevel > 9)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
prevlevel = runlevel;
|
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)
|
if (runlevel == 1)
|
||||||
touch("/etc/nologin"); /* Disable login in single-user mode */
|
touch("/etc/nologin"); /* Disable login in single-user mode */
|
||||||
else
|
else
|
||||||
remove("/etc/nologin");
|
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 svc_register(int type, char *line, char *username)
|
||||||
{
|
{
|
||||||
int i = 0, not = 0;
|
int i = 0;
|
||||||
char *cmd, *desc, *runlevels = NULL;
|
char *cmd, *desc, *runlevels = NULL;
|
||||||
svc_t *svc;
|
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]));
|
strlcpy(svc->args[i++], cmd, sizeof(svc->args[0]));
|
||||||
svc->args[i][0] = 0;
|
svc->args[i][0] = 0;
|
||||||
|
|
||||||
if (!runlevels)
|
svc->runlevels = parse_runlevels(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);
|
|
||||||
}
|
|
||||||
|
|
||||||
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
|
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -358,6 +340,9 @@ void svc_monitor(void)
|
|||||||
if (is_norespawn())
|
if (is_norespawn())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (tty_respawn(lost))
|
||||||
|
return;
|
||||||
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||||
if (lost != svc->pid)
|
if (lost != svc->pid)
|
||||||
continue;
|
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
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -21,119 +22,181 @@
|
|||||||
* THE SOFTWARE.
|
* THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/inotify.h>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sys/prctl.h>
|
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
|
||||||
#include "queue.h"
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "lite.h"
|
#include "lite.h"
|
||||||
|
#include "tty.h"
|
||||||
|
|
||||||
typedef struct finit_tty_t {
|
LIST_HEAD(, tty_node) tty_list = LIST_HEAD_INITIALIZER();
|
||||||
char *name;
|
|
||||||
int baud;
|
|
||||||
|
|
||||||
int pid; /* Runtime information*/
|
|
||||||
} finit_tty_t;
|
|
||||||
|
|
||||||
typedef struct node {
|
|
||||||
LIST_ENTRY(node) link;
|
|
||||||
finit_tty_t data;
|
|
||||||
} node_t;
|
|
||||||
|
|
||||||
|
|
||||||
/* We need to make room for the filename also, which is sent after inotify_event. */
|
/* tty [!1-9,S] <DEV> [BAUD[,BAUD,...]] [TERM] */
|
||||||
#define EVENT_SIZE ((sizeof(struct inotify_event) + FILENAME_MAX))
|
int tty_register(char *line)
|
||||||
|
|
||||||
LIST_HEAD(, node) node_list = LIST_HEAD_INITIALIZER();
|
|
||||||
int tty_add(char *tty, int baud)
|
|
||||||
{
|
{
|
||||||
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;
|
return errno = EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
entry = malloc(sizeof(*entry));
|
cmd = strtok(line, " ");
|
||||||
if (!entry)
|
if (!cmd) {
|
||||||
return errno = ENOMEM;
|
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.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;
|
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;
|
tty_node_t *entry;
|
||||||
char cmd[strlen(GETTY) + FILENAME_MAX];
|
|
||||||
|
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))
|
if (console && !strcmp(tty->name, console))
|
||||||
is_console = 1;
|
is_console = 1;
|
||||||
|
|
||||||
if (mask & IN_CREATE) {
|
#if defined(GETTY_AGETTY)
|
||||||
_d("Starting %s as %s", tty->name, is_console ? "console" : "TTY");
|
snprintf(line, sizeof(line), "%s %s %d %s", GETTY, tty->name, tty->baud, tty->term ?: "");
|
||||||
snprintf(cmd, sizeof(cmd), GETTY, tty->name);
|
#elif defined(GETTY_BUSYBOX)
|
||||||
tty->pid = run_getty(cmd, is_console);
|
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. */
|
_d("Starting %s: %s on %s", is_console ? "console" : "TTY", cmd, tty->name);
|
||||||
if ((mask & IN_DELETE) && tty->pid) {
|
tty->pid = run_getty(cmd, args, is_console);
|
||||||
_d("Stopping %s", tty->name);
|
|
||||||
kill(tty->pid, SIGKILL);
|
|
||||||
waitpid(tty->pid, NULL, 0);
|
|
||||||
tty->pid = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
_d("Stopping TTY %s", tty->name);
|
||||||
|
kill(tty->pid, SIGTERM);
|
||||||
if (-1 == fd || inotify_add_watch(fd, "/dev", IN_CREATE | IN_DELETE) < 0) {
|
do_sleep(2);
|
||||||
fprintf(stderr, "finit: Failed starting TTY watcher: %s\n", strerror(errno));
|
kill(tty->pid, SIGKILL);
|
||||||
exit(1);
|
waitpid(tty->pid, NULL, 0);
|
||||||
}
|
tty->pid = 0;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 */
|
if (!ISSET(tty->runlevels, runlevel))
|
||||||
LIST_FOREACH(entry, &node_list, link) {
|
return 0;
|
||||||
chdir("/dev");
|
else if (fexist(tty->name))
|
||||||
if (fexist (entry->data.name))
|
return 1;
|
||||||
tty_startstop_one(&entry->data, IN_CREATE);
|
|
||||||
|
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