mirror of
https://github.com/troglobit/finit.git
synced 2026-10-13 10:14:47 +00:00
Add basic support for runlevels and bugfix FIFO I/O plugins
Like most other init implementations do, when entering runlevel 1, finit creates the file /etc/nologin to prevent users from logging in during single-user mode, this file is then removed upon leaving runlevel 1. Also, bugfix how FIFO I/O plugins are handled in finit. A FIFO will need to be reopened by the server side when the client closes the pipe. Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
+26
@@ -60,6 +60,31 @@ runs a directory of scripts. This replaces the earlier
|
||||
``/usr/sbin/services.sh`` support of the original finit_.
|
||||
|
||||
|
||||
Runlevels
|
||||
---------
|
||||
|
||||
Very simple support for runlevels_ exist. Currently the system will
|
||||
always start in runlevel 3 and all services belonging to that runlevel
|
||||
will be started at boot.
|
||||
|
||||
To specify an allowed set of runlevels for a service, add [NNN] to its
|
||||
service stanza in finit.conf, like this:
|
||||
|
||||
service [345] /sbin/klogd -n -x -- Kernel log daemon
|
||||
|
||||
Meaning that klogd is allowed to run in runlevels 3, 4 and 5, only.
|
||||
|
||||
service [!06] /sbin/klogd -n -x -- Kernel log daemon
|
||||
|
||||
Meaning all runlevels *but* 0 and 6.
|
||||
|
||||
Existing finit.conf files that lack runlevel setting will get a default
|
||||
runlevel assigned, [345].
|
||||
|
||||
Switching between runlevels can be done by calling init with a single
|
||||
argument, e.g., 'init 5' switches to runlevel 5.
|
||||
|
||||
|
||||
Rebooting and Halting
|
||||
---------------------
|
||||
|
||||
@@ -191,5 +216,6 @@ Regards
|
||||
.. _fastinit: http://wiki.eeeuser.com/boot_process:the_boot_process
|
||||
.. _`SysV init`: http://savannah.nongnu.org/projects/sysvinit
|
||||
.. _upstart: http://upstart.ubuntu.com/
|
||||
.. _runlevels: http://en.wikipedia.org/wiki/Runlevel
|
||||
.. _openrc: http://www.gentoo.org/proj/en/base/openrc/
|
||||
.. _`finit-1.3.tar.xz`: http://github.com/downloads/troglobit/finit/finit-1.3.tar.xz
|
||||
|
||||
@@ -35,14 +35,16 @@
|
||||
#include "sig.h"
|
||||
#include "lite.h"
|
||||
|
||||
int debug = 0;
|
||||
int verbose = 1;
|
||||
char *sdown = NULL;
|
||||
char *network = NULL;
|
||||
char *username = NULL;
|
||||
char *hostname = NULL;
|
||||
char *rcsd = NULL;
|
||||
char *console = NULL;
|
||||
int debug = 0;
|
||||
int verbose = 1;
|
||||
int runlevel = 3;
|
||||
int prevlevel = 1;
|
||||
char *sdown = NULL;
|
||||
char *network = NULL;
|
||||
char *username = NULL;
|
||||
char *hostname = NULL;
|
||||
char *rcsd = NULL;
|
||||
char *console = NULL;
|
||||
|
||||
static void parse_kernel_cmdline(void)
|
||||
{
|
||||
@@ -63,6 +65,37 @@ static void parse_kernel_cmdline(void)
|
||||
}
|
||||
}
|
||||
|
||||
static int client(int argc, char *argv[])
|
||||
{
|
||||
int fd;
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_RUNLVL,
|
||||
};
|
||||
|
||||
if (!fexist(FINIT_FIFO)) {
|
||||
fprintf(stderr, "/sbin/init does not support %s!\n", FINIT_FIFO);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Missing argument.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fd = open(FINIT_FIFO, O_WRONLY);
|
||||
if (-1 == fd) {
|
||||
perror("Failed opening " FINIT_FIFO);
|
||||
return 1;
|
||||
}
|
||||
|
||||
rq.runlevel = (int)argv[1][0];
|
||||
write(fd, &rq, sizeof(rq));
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run_loop(void)
|
||||
{
|
||||
while (1) {
|
||||
@@ -82,8 +115,11 @@ static void banner(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
int main(int UNUSED(args), char* UNUSED(argv[]))
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (getpid() != 1)
|
||||
return client(argc, argv);
|
||||
|
||||
/*
|
||||
* Initial setup of signals, ignore all until we're up.
|
||||
*/
|
||||
|
||||
@@ -62,6 +62,19 @@
|
||||
#define USERNAME_SIZE 16
|
||||
#define HOSTNAME_SIZE 32
|
||||
|
||||
#define INIT_MAGIC 0x03091969
|
||||
#define INIT_CMD_RUNLVL 1
|
||||
|
||||
struct init_request {
|
||||
int magic; /* Magic number */
|
||||
int cmd; /* What kind of request */
|
||||
int runlevel; /* Runlevel to change to */
|
||||
int sleeptime; /* Time between TERM and KILL */
|
||||
char data[368];
|
||||
};
|
||||
|
||||
extern int runlevel;
|
||||
extern int prevlevel;
|
||||
extern char *sdown;
|
||||
extern char *network;
|
||||
extern char *hostname;
|
||||
|
||||
@@ -189,15 +189,20 @@ void plugin_run_hooks(hook_point_t no)
|
||||
}
|
||||
|
||||
/* Generic libev I/O callback, looks up correct plugin and calls its callback */
|
||||
static void generic_io_cb(int fd, int events)
|
||||
static void generic_io_cb(struct pollfd *io)
|
||||
{
|
||||
plugin_t *p, *tmp;
|
||||
|
||||
/* Find matching plugin, pick first matching fd */
|
||||
PLUGIN_ITERATOR(p, tmp) {
|
||||
if (is_io_plugin(p) && p->io.fd == fd) {
|
||||
if (is_io_plugin(p) && p->io.fd == io->fd) {
|
||||
_d("Calling I/O %s from runloop...", basename(p->name));
|
||||
p->io.cb(p->io.arg, fd, events);
|
||||
p->io.cb(p->io.arg, io->fd, io->events);
|
||||
|
||||
/* Update fd, may be changed by plugin callback, e.g., if FIFO */
|
||||
io->fd = p->io.fd;
|
||||
io->events = p->io.flags;
|
||||
io->revents = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -221,7 +226,7 @@ void plugin_monitor(void)
|
||||
/* Traverse all I/O fds and run callbacks */
|
||||
for (i = 0; i < num_fds; i++) {
|
||||
if (fds[i].revents)
|
||||
generic_io_cb(fds[i].fd, fds[i].revents);
|
||||
generic_io_cb(&fds[i]);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
+51
-33
@@ -32,78 +32,96 @@
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "sig.h"
|
||||
#include "finit.h"
|
||||
|
||||
#define INIT_MAGIC 0x03091969
|
||||
#define INIT_CMD_RUNLVL 1
|
||||
static void parse(void *arg, int fd, int events);
|
||||
|
||||
struct init_request {
|
||||
int magic; /* Magic number */
|
||||
int cmd; /* What kind of request */
|
||||
int runlevel; /* Runlevel to change to */
|
||||
int sleeptime; /* Time between TERM and KILL */
|
||||
char data[368];
|
||||
static plugin_t plugin = {
|
||||
.io = {
|
||||
.cb = parse,
|
||||
.flags = PLUGIN_IO_READ,
|
||||
},
|
||||
};
|
||||
|
||||
static void fifo_open(void)
|
||||
{
|
||||
plugin.io.fd = open(FINIT_FIFO, O_RDONLY | O_NONBLOCK);
|
||||
if (-1 == plugin.io.fd) {
|
||||
_e("Failed opening %s FIFO, error %d: %s", FINIT_FIFO, errno, strerror(errno));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Standard reboot/shutdown utilities talk to init using /dev/initctl.
|
||||
* We should check if the fifo was recreated and reopen it.
|
||||
*/
|
||||
static void read_initctl(void *UNUSED(arg), int fd, int UNUSED(events))
|
||||
static void parse(void *UNUSED(arg), int fd, int UNUSED(events))
|
||||
{
|
||||
struct init_request request;
|
||||
struct init_request rq;
|
||||
|
||||
_d("Receiving request on %s...", FINIT_FIFO);
|
||||
while (1) {
|
||||
ssize_t len = read(fd, &request, sizeof(request));
|
||||
ssize_t len = read(fd, &rq, sizeof(rq));
|
||||
|
||||
if (-1 == len) {
|
||||
_d("Error %d", errno);
|
||||
break; /* Probably EAGAIN */
|
||||
if (len <= 0) {
|
||||
if (-1 == len) {
|
||||
if (EINTR == errno)
|
||||
continue;
|
||||
|
||||
if (EAGAIN == errno)
|
||||
break;
|
||||
|
||||
_e("Failed reading initctl request, error %d: %s", errno, strerror(errno));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (request.magic != INIT_MAGIC)
|
||||
continue;
|
||||
if (rq.magic != INIT_MAGIC || len != sizeof(rq)) {
|
||||
_e("Invalid initctl request.");
|
||||
break;
|
||||
}
|
||||
|
||||
_d("Magic OK...");
|
||||
if (request.cmd == INIT_CMD_RUNLVL) {
|
||||
switch (request.runlevel) {
|
||||
if (rq.cmd == INIT_CMD_RUNLVL) {
|
||||
switch (rq.runlevel) {
|
||||
case '0':
|
||||
_d("Halting system (SIGUSR2)");
|
||||
do_shutdown(SIGUSR2);
|
||||
break;
|
||||
|
||||
case 's':
|
||||
case 'S':
|
||||
rq.runlevel = '1';
|
||||
/* Fall through to regular processing */
|
||||
case '1'...'5':
|
||||
_d("Setting new runlevel %c ...", rq.runlevel);
|
||||
svc_runlevel(rq.runlevel - '0');
|
||||
break;
|
||||
|
||||
case '6':
|
||||
_d("Rebooting system (SIGUSR1)");
|
||||
do_shutdown(SIGUSR1);
|
||||
break;
|
||||
|
||||
default:
|
||||
_d("Unsupported runlevel: %d", request.runlevel);
|
||||
_d("Unsupported runlevel: %d", rq.runlevel);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
_d("Unsupported cmd: %d", request.cmd);
|
||||
_d("Unsupported cmd: %d", rq.cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static plugin_t plugin = {
|
||||
.io = {
|
||||
.cb = read_initctl,
|
||||
.flags = PLUGIN_IO_READ,
|
||||
},
|
||||
};
|
||||
close(fd);
|
||||
fifo_open();
|
||||
}
|
||||
|
||||
PLUGIN_INIT(plugin_init)
|
||||
{
|
||||
_d("Setting up %s", FINIT_FIFO);
|
||||
mkfifo(FINIT_FIFO, 0600);
|
||||
|
||||
plugin.io.fd = open(FINIT_FIFO, O_RDONLY | O_NONBLOCK);
|
||||
if (-1 == plugin.io.fd) {
|
||||
_e("Failed opening %s FIFO, error %d: %s", FINIT_FIFO, errno, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
fifo_open();
|
||||
plugin_register(&plugin);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,10 @@
|
||||
#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. */
|
||||
@@ -121,6 +125,44 @@ svc_t *svc_iterator(int restart)
|
||||
return &services[i++];
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_runlevel - Change to a new runlevel
|
||||
* @newlevel: New runlevel to activate
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
if (runlevel == newlevel)
|
||||
return;
|
||||
|
||||
if (newlevel > 5 || newlevel < 1)
|
||||
return;
|
||||
|
||||
prevlevel = runlevel;
|
||||
runlevel = newlevel;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
int run = svc_enabled(svc, 0, NULL);
|
||||
|
||||
if (svc->pid) {
|
||||
if (run == SVC_STOP)
|
||||
svc_stop(svc);
|
||||
} else {
|
||||
if (run == SVC_START)
|
||||
svc_start(svc);
|
||||
}
|
||||
}
|
||||
|
||||
if (runlevel == 1)
|
||||
touch("/etc/nologin"); /* Disable login in single-user mode */
|
||||
else
|
||||
remove("/etc/nologin");
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_register - Register a non-backgrounding service to be monitored
|
||||
* @line: A complete command line with -- separated description text
|
||||
@@ -129,18 +171,18 @@ svc_t *svc_iterator(int restart)
|
||||
* Actually, the @line can optionally start with a username, denoted by
|
||||
* an @ character. Like this:
|
||||
*
|
||||
* "service [@username] /path/to/daemon -- Description text"
|
||||
* "service @username [!0-6] /path/to/daemon -- Description text"
|
||||
*
|
||||
* The [] brackets means the argument is optional. If the username is
|
||||
* left out the daemon is started as root.
|
||||
* If the username is left out the daemon is started as root. The []
|
||||
* brackets are there to denote the allowed runlevels.
|
||||
*
|
||||
* Returns:
|
||||
* POSIX OK(0) on success, or non-zero errno exit status on failure.
|
||||
*/
|
||||
int svc_register(char *line, char *username)
|
||||
{
|
||||
int i = 0;
|
||||
char *cmd, *desc;
|
||||
int i = 0, not = 0;
|
||||
char *cmd, *desc, *runlevels = NULL;
|
||||
svc_t *svc;
|
||||
|
||||
if (!line) {
|
||||
@@ -159,8 +201,13 @@ int svc_register(char *line, char *username)
|
||||
return errno = ENOENT;
|
||||
}
|
||||
|
||||
if (cmd[0] == '@') { /* @username */
|
||||
username = &cmd[1];
|
||||
while (cmd) {
|
||||
if (cmd[0] == '@') /* @username */
|
||||
username = &cmd[1];
|
||||
else if (cmd[0] == '[') /* [runlevels] */
|
||||
runlevels = &cmd[0];
|
||||
else
|
||||
break;
|
||||
|
||||
/* Check if valid command follows... */
|
||||
cmd = strtok(NULL, " ");
|
||||
@@ -170,7 +217,7 @@ int svc_register(char *line, char *username)
|
||||
|
||||
svc = svc_new();
|
||||
if (!svc) {
|
||||
_e("Out of memorym, cannot register service %s", cmd);
|
||||
_e("Out of memory, cannot register service %s", cmd);
|
||||
return errno = ENOMEM;
|
||||
}
|
||||
|
||||
@@ -185,6 +232,34 @@ int svc_register(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 = 0x7E;
|
||||
continue;
|
||||
}
|
||||
if ('s' == lvl || 'S' == lvl)
|
||||
lvl = '1';
|
||||
|
||||
level = lvl - '0';
|
||||
if (level > 6 || level < 0)
|
||||
continue;
|
||||
|
||||
if (not)
|
||||
CLRBIT(svc->runlevels, level);
|
||||
else
|
||||
SETBIT(svc->runlevels, level);
|
||||
}
|
||||
_d("Service %s runlevel 0x%2x", svc->cmd, svc->runlevels);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -210,6 +285,9 @@ svc_cmd_t svc_enabled(svc_t *svc, int event, void *arg)
|
||||
return SVC_STOP;
|
||||
}
|
||||
|
||||
if (!ISSET(svc->runlevels, runlevel))
|
||||
return SVC_STOP;
|
||||
|
||||
/* Is there a service plugin registered? */
|
||||
if (svc->cb)
|
||||
return svc->cb(svc, event, arg);
|
||||
@@ -333,7 +411,7 @@ int svc_start(svc_t *svc)
|
||||
int fd;
|
||||
char buf[CMD_SIZE] = "";
|
||||
|
||||
fd = open (CONSOLE, O_WRONLY | O_APPEND);
|
||||
fd = open(CONSOLE, O_WRONLY | O_APPEND);
|
||||
if (-1 != fd) {
|
||||
dup2(STDOUT_FILENO, fd);
|
||||
dup2(STDERR_FILENO, fd);
|
||||
@@ -389,10 +467,12 @@ int svc_stop(svc_t *svc)
|
||||
return 1;
|
||||
}
|
||||
|
||||
print_desc("Stopping ", svc->desc);
|
||||
if (runlevel != 1)
|
||||
print_desc("Stopping ", svc->desc);
|
||||
_d("Sending SIGTERM to pid:%d name:'%s'", svc->pid, pid_get_name(svc->pid, NULL, 0));
|
||||
res = kill(svc->pid, SIGTERM);
|
||||
print_result(res);
|
||||
if (runlevel != 1)
|
||||
print_result(res);
|
||||
svc->pid = 0;
|
||||
svc->stat_restart_counter = 0;
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ typedef enum {
|
||||
typedef struct svc {
|
||||
pid_t pid;
|
||||
int reload;
|
||||
int runlevels;
|
||||
unsigned int stat_restart_counter; /* Incremented for each restart by service monitor. */
|
||||
char cmd[MAX_ARG_LEN];
|
||||
char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN];
|
||||
@@ -87,6 +88,7 @@ static inline svc_t *finit_svc_connect(void)
|
||||
svc_t *svc_new (void);
|
||||
svc_t *svc_find (char *name);
|
||||
svc_t *svc_iterator (int restart);
|
||||
void svc_runlevel (int newlevel);
|
||||
|
||||
int svc_register (char *line, char *username);
|
||||
int svc_id_by_name (char *name);
|
||||
|
||||
Reference in New Issue
Block a user