mirror of
https://github.com/troglobit/finit.git
synced 2026-10-18 10:59:34 +00:00
Relocate source files to an src/ subdirectory
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
1 parent
044ded3045
commit
fff68b7b06
61 files changed
+155
-154
No files matched your search
@@ -0,0 +1,95 @@
|
||||
AM_CPPFLAGS = -U_FORTIFY_SOURCE
|
||||
AM_CPPFLAGS += -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE
|
||||
if STATIC
|
||||
AM_CPPFLAGS += -DENABLE_STATIC
|
||||
AM_LDFLAGS = -static-libtool-libs
|
||||
else
|
||||
AM_CPPFLAGS += -DPLUGIN_PATH=\"$(pkglibdir)\"
|
||||
AM_LDFLAGS = -export-dynamic
|
||||
endif
|
||||
|
||||
sbin_PROGRAMS = finit initctl reboot
|
||||
finit_SOURCES = api.c \
|
||||
cond.c cond-w.c cond.h \
|
||||
client.c \
|
||||
conf.c conf.h \
|
||||
exec.c finit.c finit.h \
|
||||
getty.c \
|
||||
helpers.c helpers.h \
|
||||
mdadm.c mount.c pid.c \
|
||||
plugin.c plugin.h private.h \
|
||||
service.c service.h \
|
||||
sig.c sig.h \
|
||||
sm.c sm.h \
|
||||
svc.c svc.h \
|
||||
tty.c tty.h \
|
||||
util.c util.h \
|
||||
utmp-api.c utmp-api.h
|
||||
pkginclude_HEADERS = cond.h finit.h helpers.h inetd.h plugin.h svc.h
|
||||
if INETD
|
||||
finit_SOURCES += inetd.c inetd.h
|
||||
endif
|
||||
if WATCHDOGD
|
||||
finit_SOURCES += watchdog.c watchdog.h
|
||||
endif
|
||||
|
||||
finit_CFLAGS = -W -Wall -Wextra -std=gnu99 $(lite_CFLAGS) $(uev_CFLAGS)
|
||||
finit_LDADD = $(lite_LIBS) $(uev_LIBS)
|
||||
if STATIC
|
||||
finit_LDADD += plugins/libplug.la
|
||||
else
|
||||
finit_LDADD += -ldl
|
||||
endif
|
||||
|
||||
initctl_SOURCES = initctl.c svc.c svc.h helpers.c helpers.h cond.c cond.h \
|
||||
util.c util.h utmp-api.c utmp-api.h
|
||||
initctl_CFLAGS = $(lite_CFLAGS)
|
||||
initctl_LDADD = $(lite_LIBS)
|
||||
|
||||
reboot_SOURCES = reboot.c util.c util.h
|
||||
|
||||
INIT_LNKS = telinit init
|
||||
REBOOT_LNKS = shutdown halt poweroff suspend
|
||||
|
||||
# Hook in install to add finit and reboot symlink(s)
|
||||
install-exec-hook:
|
||||
@for file in $(INIT_LNKS); do \
|
||||
lnk=$(DESTDIR)$(sbindir)/$$file; \
|
||||
if [ -e $$lnk ]; then \
|
||||
echo "Skipping $$lnk exists already."; \
|
||||
else \
|
||||
$(LN_S) finit $$lnk; \
|
||||
fi; \
|
||||
done
|
||||
@if [ -L $(DESTDIR)$(sbindir)/reboot ]; then \
|
||||
echo "Non-finit reboot, skipping install."; \
|
||||
else \
|
||||
for file in $(REBOOT_LNKS); do \
|
||||
link=$(DESTDIR)$(sbindir)/$$file; \
|
||||
if [ -e $$link ]; then \
|
||||
echo "$$link exists, skipping.";\
|
||||
else \
|
||||
$(LN_S) reboot $$link; \
|
||||
fi; \
|
||||
done; \
|
||||
fi
|
||||
|
||||
uninstall-hook:
|
||||
@for file in $(INIT_LNKS); do \
|
||||
lnk=`readlink $(DESTDIR)$(sbindir)/$$file`; \
|
||||
if [ "$$lnk" != "finit" ]; then \
|
||||
echo "Skipping non-finit $$file."; \
|
||||
continue; \
|
||||
fi; \
|
||||
$(RM) $(DESTDIR)$(sbindir)/$$file; \
|
||||
done
|
||||
@if [ -L $(DESTDIR)$(sbindir)/reboot ]; then \
|
||||
echo "Non-finit reboot, skipping uninstall."; \
|
||||
else \
|
||||
for file in $(REBOOT_LNKS); do \
|
||||
if [ "`readlink $$file`" != "reboot" ]; \
|
||||
then continue; \
|
||||
fi; \
|
||||
$(RM) $(DESTDIR)$(sbindir)/$$file; \
|
||||
done; \
|
||||
fi
|
||||
@@ -0,0 +1,348 @@
|
||||
/* External client API, using UNIX domain socket.
|
||||
*
|
||||
* Copyright (c) 2015 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>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <lite/lite.h>
|
||||
#include <uev/uev.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "sig.h"
|
||||
#include "service.h"
|
||||
#include "util.h"
|
||||
|
||||
uev_t api_watcher;
|
||||
|
||||
static int call(int (*action)(svc_t *), char *buf, size_t len)
|
||||
{
|
||||
return svc_parse_jobstr(buf, len, action, NULL);
|
||||
}
|
||||
|
||||
static int service_block(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
svc_block(svc);
|
||||
service_step(svc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_unblock(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
svc_unblock(svc);
|
||||
service_step(svc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int service_restart(svc_t *svc)
|
||||
{
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
svc_mark_dirty(svc);
|
||||
service_step(svc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_start (char *buf, size_t len) { return call(service_unblock, buf, len); }
|
||||
static int do_pause (char *buf, size_t len) { return call(service_block, buf, len); }
|
||||
static int do_restart(char *buf, size_t len) { return call(service_restart, buf, len); }
|
||||
|
||||
#ifdef INETD_ENABLED
|
||||
static int do_query_inetd(char *buf, size_t len)
|
||||
{
|
||||
int id = 1;
|
||||
char *ptr, *input = sanitize(buf, len);
|
||||
svc_t *svc;
|
||||
|
||||
if (!input)
|
||||
return -1;
|
||||
|
||||
ptr = strchr(input, ':');
|
||||
if (ptr) {
|
||||
*ptr++ = 0;
|
||||
id = atonum(ptr);
|
||||
}
|
||||
|
||||
svc = svc_find_by_jobid(atonum(input), id);
|
||||
if (!svc || !svc_is_inetd(svc)) {
|
||||
_e("Cannot %s svc %s ...", !svc ? "find" : "query, not an inetd", input);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return inetd_filter_str(&svc->inetd, buf, len);
|
||||
}
|
||||
#endif /* INETD_ENABLED */
|
||||
|
||||
typedef struct {
|
||||
char *event;
|
||||
void (*cb)(void);
|
||||
} ev_t;
|
||||
|
||||
ev_t ev_list[] = {
|
||||
{ "RELOAD", service_reload_dynamic },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static int do_handle_event(char *event)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; ev_list[i].event; i++) {
|
||||
ev_t *e = &ev_list[i];
|
||||
size_t len = MAX(strlen(e->event), strlen(event));
|
||||
|
||||
if (!strncasecmp(e->event, event, len)) {
|
||||
e->cb();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (event[0] == '-')
|
||||
cond_clear(&event[1]);
|
||||
else if (event[0] == '+')
|
||||
cond_set(&event[1]);
|
||||
else
|
||||
cond_set(event);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_handle_emit(char *buf, size_t len)
|
||||
{
|
||||
int result = 0;
|
||||
char *input, *event, *pos;
|
||||
|
||||
input = sanitize(buf, len);
|
||||
if (!input)
|
||||
return -1;
|
||||
|
||||
event = strtok_r(input, " ", &pos);
|
||||
while (event) {
|
||||
result += do_handle_event(event);
|
||||
event = strtok_r(NULL, " ", &pos);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* In contrast to the SysV compat handling in plugins/initctl.c, when
|
||||
* `initctl runlevel 0` is issued we default to POWERDOWN the system
|
||||
* instead of just halting.
|
||||
*/
|
||||
static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
int sd, lvl;
|
||||
struct init_request rq;
|
||||
|
||||
sd = accept(w->fd, NULL, NULL);
|
||||
if (sd < 0) {
|
||||
_pe("Failed serving API request");
|
||||
return;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
int result = 0;
|
||||
ssize_t len;
|
||||
|
||||
len = read(sd, &rq, sizeof(rq));
|
||||
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 (rq.magic != INIT_MAGIC || len != sizeof(rq)) {
|
||||
_e("Invalid initctl request");
|
||||
break;
|
||||
}
|
||||
|
||||
switch (rq.cmd) {
|
||||
case INIT_CMD_RUNLVL:
|
||||
switch (rq.runlevel) {
|
||||
case 's':
|
||||
case 'S':
|
||||
rq.runlevel = '1'; /* Single user mode */
|
||||
/* Fall through to regular processing */
|
||||
|
||||
case '0'...'9':
|
||||
_d("Setting new runlevel %c", rq.runlevel);
|
||||
lvl = rq.runlevel - '0';
|
||||
if (lvl == 0)
|
||||
halt = SHUT_OFF;
|
||||
if (lvl == 6)
|
||||
halt = SHUT_REBOOT;
|
||||
service_runlevel(lvl);
|
||||
break;
|
||||
|
||||
default:
|
||||
_d("Unsupported runlevel: %d", rq.runlevel);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case INIT_CMD_DEBUG:
|
||||
debug = !debug;
|
||||
if (debug)
|
||||
silent = 0;
|
||||
else
|
||||
silent = quiet ? 1 : SILENT_MODE;
|
||||
logit(LOG_INFO, "Debug mode %s", debug ? "enabled" : "disabled");
|
||||
break;
|
||||
|
||||
case INIT_CMD_RELOAD: /* 'init q' and 'initctl reload' */
|
||||
service_reload_dynamic();
|
||||
break;
|
||||
|
||||
case INIT_CMD_START_SVC:
|
||||
result = do_start(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_STOP_SVC:
|
||||
result = do_pause(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_RESTART_SVC:
|
||||
result = do_restart(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
#ifdef INETD_ENABLED
|
||||
case INIT_CMD_QUERY_INETD:
|
||||
result = do_query_inetd(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
#endif
|
||||
|
||||
case INIT_CMD_EMIT:
|
||||
result = do_handle_emit(rq.data, sizeof(rq.data));
|
||||
break;
|
||||
|
||||
case INIT_CMD_GET_RUNLEVEL:
|
||||
rq.runlevel = runlevel;
|
||||
break;
|
||||
|
||||
case INIT_CMD_ACK:
|
||||
_d("Client failed reading ACK");
|
||||
goto leave;
|
||||
|
||||
case INIT_CMD_WDOG_HELLO:
|
||||
if (rq.runlevel <= 0) {
|
||||
result = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (wdogpid > 0 && wdogpid != rq.runlevel) {
|
||||
kill(wdogpid, SIGTERM);
|
||||
do_sleep(1);
|
||||
}
|
||||
wdogpid = rq.runlevel;
|
||||
break;
|
||||
|
||||
default:
|
||||
_d("Unsupported cmd: %d", rq.cmd);
|
||||
break;
|
||||
}
|
||||
|
||||
if (result)
|
||||
rq.cmd = INIT_CMD_NACK;
|
||||
else
|
||||
rq.cmd = INIT_CMD_ACK;
|
||||
len = write(sd, &rq, sizeof(rq));
|
||||
if (len != sizeof(rq))
|
||||
_d("Failed sending ACK/NACK back to client");
|
||||
}
|
||||
|
||||
leave:
|
||||
close(sd);
|
||||
}
|
||||
|
||||
int api_init(uev_ctx_t *ctx)
|
||||
{
|
||||
int sd;
|
||||
mode_t oldmask;
|
||||
struct sockaddr_un sun = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
|
||||
sd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
if (-1 == sd) {
|
||||
_pe("Failed starting external API socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
erase(INIT_SOCKET);
|
||||
oldmask = umask(0077);
|
||||
if (-1 == bind(sd, (struct sockaddr*)&sun, sizeof(sun)))
|
||||
goto error;
|
||||
|
||||
if (-1 == listen(sd, 10))
|
||||
goto error;
|
||||
|
||||
umask(oldmask);
|
||||
if (!uev_io_init(ctx, &api_watcher, cb, NULL, sd, UEV_READ))
|
||||
return 0;
|
||||
|
||||
error:
|
||||
_pe("Failed intializing API socket");
|
||||
umask(oldmask);
|
||||
close(sd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int api_exit(void)
|
||||
{
|
||||
return shutdown(api_watcher.fd, SHUT_RDWR) ||
|
||||
close(api_watcher.fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
/* Simple telinit client
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2015 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 "config.h" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "util.h"
|
||||
|
||||
static int do_send(struct init_request *rq)
|
||||
{
|
||||
static int fd = -1;
|
||||
int result = 255;
|
||||
ssize_t len;
|
||||
|
||||
if (fd == -1) {
|
||||
if (!fexist(FINIT_FIFO)) {
|
||||
fprintf(stderr, "%s does not support %s!\n", prognm, FINIT_FIFO);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fd = open(FINIT_FIFO, O_RDWR);
|
||||
if (-1 == fd) {
|
||||
perror("Failed opening " FINIT_FIFO);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
len = write(fd, rq, sizeof(*rq));
|
||||
if (sizeof(*rq) == len)
|
||||
result = 0;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int do_reload(void)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_RELOAD,
|
||||
};
|
||||
|
||||
return do_send(&rq);
|
||||
}
|
||||
|
||||
static int set_env(int runlevel)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_SETENV,
|
||||
};
|
||||
|
||||
if (runlevel == 0) {
|
||||
strlcpy(rq.data, "INIT_HALT=POWERDOWN", sizeof(rq.data));
|
||||
return do_send(&rq);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_runlevel(int runlevel)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_RUNLVL,
|
||||
.runlevel = runlevel,
|
||||
};
|
||||
|
||||
return set_env(runlevel) || do_send(&rq);
|
||||
}
|
||||
|
||||
/* telinit q | telinit <0-9> */
|
||||
static int usage(int rc)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [OPTIONS] [q | Q | 0-9]\n\n"
|
||||
"Options:\n"
|
||||
" -h, --help This help text\n"
|
||||
" -V, --version Show Finit version\n\n"
|
||||
"Commands:\n"
|
||||
" 0 Power-off the system, same as initctl poweroff\n"
|
||||
" 6 Reboot the system, same as initctl reboot\n"
|
||||
" 2, 3, 4, 5 Change runlevel. Starts services in new runlevel, stops any\n"
|
||||
" services in prev. runlevel that are not allowed in new.\n"
|
||||
" q, Q Reload *.conf in /etc/finit.d/, same as initctl reload or\n"
|
||||
" sending SIGHUP to PID 1\n"
|
||||
" 1, s, S Enter system rescue mode, runlevel 1\n"
|
||||
"\n", prognm);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int client(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
struct option long_options[] = {
|
||||
{"help", 0, NULL, 'h'},
|
||||
{"version", 0, NULL, 'v'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
silent = 1;
|
||||
progname(argv[0]);
|
||||
while ((c = getopt_long(argc, argv, "h?v", long_options, NULL)) != EOF) {
|
||||
switch(c) {
|
||||
case 'v':
|
||||
return puts("v" VERSION) == EOF;
|
||||
|
||||
case 'h':
|
||||
case '?':
|
||||
return usage(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind < argc) {
|
||||
int req = (int)argv[optind][0];
|
||||
|
||||
if (isdigit(req))
|
||||
return set_runlevel(req);
|
||||
|
||||
if (req == 'q' || req == 'Q')
|
||||
return do_reload();
|
||||
|
||||
if (req == 's' || req == 'S')
|
||||
return set_runlevel(1);
|
||||
}
|
||||
|
||||
return usage(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
/* Condition engine (write)
|
||||
*
|
||||
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.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 <ftw.h>
|
||||
#include <libgen.h>
|
||||
#include <lite/lite.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "service.h"
|
||||
|
||||
int cond_set_path(const char *path, enum cond_state new)
|
||||
{
|
||||
char buf[MAX_ARG_LEN], *dir;
|
||||
enum cond_state old;
|
||||
|
||||
old = cond_get_path(path);
|
||||
|
||||
switch (new) {
|
||||
case COND_ON:
|
||||
strlcpy(buf, path, sizeof(buf));
|
||||
dir = dirname(buf);
|
||||
if (!dir) {
|
||||
_e("Invalid path '%s' for condition", path);
|
||||
return 0;
|
||||
}
|
||||
if (makepath(dir) && errno != EEXIST) {
|
||||
_pe("Failed creating dir '%s' for condition '%s'", dir, path);
|
||||
return 0;
|
||||
}
|
||||
touch(path);
|
||||
utimensat(0, path, NULL, 0);
|
||||
break;
|
||||
|
||||
case COND_OFF:
|
||||
unlink(path);
|
||||
break;
|
||||
|
||||
default:
|
||||
_e("Invalid condition state");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return new != old;
|
||||
}
|
||||
|
||||
/* Has condition in configuration and cond is allowed? */
|
||||
static int svc_has_cond(svc_t *svc)
|
||||
{
|
||||
if (!svc->cond[0])
|
||||
return 0;
|
||||
|
||||
switch (svc->type) {
|
||||
case SVC_TYPE_SERVICE:
|
||||
case SVC_TYPE_TASK:
|
||||
case SVC_TYPE_RUN:
|
||||
return 1;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cond_update(const char *name)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
_d("%s", name);
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (!svc_has_cond(svc) || !cond_affects(name, svc->cond))
|
||||
continue;
|
||||
|
||||
_d("%s: match <%s> %s", name ?: "nil", svc->cond, svc->cmd);
|
||||
service_step(svc);
|
||||
}
|
||||
}
|
||||
|
||||
void cond_set(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
if (!cond_set_path(cond_path(name), COND_ON))
|
||||
return;
|
||||
|
||||
cond_update(name);
|
||||
}
|
||||
|
||||
void cond_clear(const char *name)
|
||||
{
|
||||
_d("%s", name);
|
||||
if (!cond_set_path(cond_path(name), COND_OFF))
|
||||
return;
|
||||
|
||||
cond_update(name);
|
||||
}
|
||||
|
||||
void cond_reload(void)
|
||||
{
|
||||
_d("");
|
||||
cond_set_path(COND_RECONF, COND_ON);
|
||||
|
||||
cond_update(NULL);
|
||||
}
|
||||
|
||||
|
||||
static int reassert(const char *fpath, const struct stat *UNUSED(sb), int tflg, struct FTW *ftw)
|
||||
{
|
||||
char *nm;
|
||||
|
||||
if (ftw->level == 0)
|
||||
return 1;
|
||||
|
||||
if (tflg != FTW_F)
|
||||
return 0;
|
||||
|
||||
nm = (char *)fpath + sizeof(COND_PATH);
|
||||
_d("Reasserting %s => %s", fpath, nm);
|
||||
cond_set(nm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* Used only by netlink plugin atm.
|
||||
* type: is a one of svc/, net/, etc.
|
||||
*/
|
||||
void cond_reassert(const char *type)
|
||||
{
|
||||
_d("%s", type);
|
||||
nftw(cond_path(type), reassert, 20, FTW_DEPTH);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
/* Condition engine (read)
|
||||
*
|
||||
* Copyright (c) 2015-2016 Tobias Waldekranz <tobias@waldekranz.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 <lite/lite.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "service.h"
|
||||
|
||||
static inline int timespec_newer(const struct timespec *a,
|
||||
const struct timespec *b)
|
||||
{
|
||||
if (a->tv_sec != b->tv_sec)
|
||||
return a->tv_sec > b->tv_sec;
|
||||
|
||||
return a->tv_nsec > b->tv_nsec;
|
||||
}
|
||||
|
||||
const char *condstr(enum cond_state s)
|
||||
{
|
||||
static const char *strs[] = {
|
||||
[COND_OFF] = "off",
|
||||
[COND_FLUX] = "flux",
|
||||
[COND_ON] = "on",
|
||||
};
|
||||
|
||||
return strs[s];
|
||||
}
|
||||
|
||||
const char *cond_path(const char *name)
|
||||
{
|
||||
static char file[MAX_ARG_LEN];
|
||||
|
||||
snprintf(file, sizeof(file), COND_PATH "/%s", name);
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
enum cond_state cond_get_path(const char *path)
|
||||
{
|
||||
struct stat st, st_reconf;
|
||||
|
||||
if (stat(path, &st))
|
||||
return COND_OFF;
|
||||
|
||||
if (stat(COND_RECONF, &st_reconf) ||
|
||||
timespec_newer(&st.st_mtim, &st_reconf.st_mtim))
|
||||
return COND_ON;
|
||||
|
||||
return COND_FLUX;
|
||||
}
|
||||
|
||||
enum cond_state cond_get(const char *name)
|
||||
{
|
||||
return cond_get_path(cond_path(name));
|
||||
}
|
||||
|
||||
enum cond_state cond_get_agg(const char *names)
|
||||
{
|
||||
char *cond;
|
||||
enum cond_state s = COND_ON;
|
||||
static char conds[MAX_ARG_LEN];
|
||||
|
||||
if (!names)
|
||||
return COND_ON;
|
||||
|
||||
strlcpy(conds, names, sizeof(conds));
|
||||
for (cond = strtok(conds, ","); s && cond; cond = strtok(NULL, ","))
|
||||
s = min(s, cond_get(cond));
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int cond_affects(const char *name, const char *names)
|
||||
{
|
||||
char *cond;
|
||||
static char conds[MAX_ARG_LEN];
|
||||
|
||||
if (!name || !names)
|
||||
return 0;
|
||||
|
||||
strlcpy(conds, names, sizeof(conds));
|
||||
for (cond = strtok(conds, ","); cond; cond = strtok(NULL, ",")) {
|
||||
if (!strcmp(cond, name))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
#ifndef FINIT_COND_H_
|
||||
#define FINIT_COND_H_
|
||||
|
||||
#include <paths.h>
|
||||
|
||||
#define COND_PATH _PATH_VARRUN "finit/cond"
|
||||
#define COND_SVC_PATH COND_PATH "/svc/"
|
||||
#define COND_RECONF COND_PATH "/reconf"
|
||||
|
||||
typedef enum cond_state {
|
||||
COND_OFF = 0,
|
||||
COND_FLUX,
|
||||
COND_ON
|
||||
} cond_state_t;
|
||||
|
||||
const char *condstr (enum cond_state s);
|
||||
const char *cond_path (const char *name);
|
||||
enum cond_state cond_get_path(const char *path);
|
||||
enum cond_state cond_get (const char *name);
|
||||
enum cond_state cond_get_agg (const char *names);
|
||||
int cond_affects (const char *name, const char *names);
|
||||
|
||||
int cond_set_path(const char *path, enum cond_state new);
|
||||
void cond_set (const char *name);
|
||||
void cond_clear (const char *name);
|
||||
void cond_reload (void);
|
||||
void cond_reassert(const char *pat);
|
||||
|
||||
#endif /* FINIT_COND_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+527
@@ -0,0 +1,527 @@
|
||||
/* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf
|
||||
*
|
||||
* Copyright (c) 2012-2015 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 "config.h" /* Generated by configure script */
|
||||
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#include <lite/lite.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "service.h"
|
||||
#include "tty.h"
|
||||
#include "helpers.h"
|
||||
|
||||
#define MATCH_CMD(l, c, x) \
|
||||
(!strncasecmp(l, c, strlen(c)) && (x = (l) + strlen(c)))
|
||||
|
||||
static int parse_conf(char *file);
|
||||
|
||||
|
||||
void conf_parse_cmdline(void)
|
||||
{
|
||||
FILE *fp;
|
||||
char line[LINE_SIZE];
|
||||
|
||||
fp = fopen("/proc/cmdline", "r");
|
||||
if (fp) {
|
||||
fgets(line, sizeof(line), fp);
|
||||
_d("Kernel command line: %s", line);
|
||||
|
||||
if (strstr(line, "finit_debug") || strstr(line, "--debug"))
|
||||
debug = 1;
|
||||
|
||||
if (!debug && strstr(line, "quiet"))
|
||||
quiet = 1;
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Convert optional "[!123456789S]" string into a bitmask */
|
||||
int conf_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 = '0';
|
||||
|
||||
level = lvl - '0';
|
||||
if (level > 9 || level < 0)
|
||||
continue;
|
||||
|
||||
if (not)
|
||||
CLRBIT(bitmask, level);
|
||||
else
|
||||
SETBIT(bitmask, level);
|
||||
}
|
||||
|
||||
return bitmask;
|
||||
}
|
||||
|
||||
void conf_parse_cond(svc_t *svc, char *cond)
|
||||
{
|
||||
size_t i = 0;
|
||||
char *ptr;
|
||||
|
||||
if (!svc) {
|
||||
_e("Invalid service pointer");
|
||||
return;
|
||||
}
|
||||
|
||||
/* By default we assume UNIX daemons support SIGHUP */
|
||||
if (svc_is_daemon(svc))
|
||||
svc->sighup = 1;
|
||||
|
||||
if (!cond)
|
||||
return;
|
||||
|
||||
/* First character must be '!' if SIGHUP is not supported. */
|
||||
ptr = cond;
|
||||
if (ptr[i] == '!') {
|
||||
svc->sighup = 0;
|
||||
ptr++;
|
||||
}
|
||||
|
||||
while (ptr[i] != '>' && ptr[i] != 0)
|
||||
i++;
|
||||
ptr[i] = 0;
|
||||
|
||||
if (i >= sizeof(svc->cond)) {
|
||||
logit(LOG_WARNING, "Too long event list in declaration of %s: %s", svc->cmd, ptr);
|
||||
return;
|
||||
}
|
||||
|
||||
strlcpy(svc->cond, ptr, sizeof(svc->cond));
|
||||
}
|
||||
|
||||
struct rlimit_name {
|
||||
char *name;
|
||||
int val;
|
||||
};
|
||||
|
||||
static const struct rlimit_name rlimit_names[] = {
|
||||
{ "as", RLIMIT_AS },
|
||||
{ "core", RLIMIT_CORE },
|
||||
{ "cpu", RLIMIT_CPU },
|
||||
{ "data", RLIMIT_DATA },
|
||||
{ "fsize", RLIMIT_FSIZE },
|
||||
{ "locks", RLIMIT_LOCKS },
|
||||
{ "memlock", RLIMIT_MEMLOCK },
|
||||
{ "msgqueue", RLIMIT_MSGQUEUE },
|
||||
{ "nice", RLIMIT_NICE },
|
||||
{ "nofile", RLIMIT_NOFILE },
|
||||
{ "nproc", RLIMIT_NPROC },
|
||||
{ "rss", RLIMIT_RSS },
|
||||
{ "rtprio", RLIMIT_RTPRIO },
|
||||
#ifdef RLIMIT_RTTIME
|
||||
{ "rttime", RLIMIT_RTTIME },
|
||||
#endif
|
||||
{ "sigpending", RLIMIT_SIGPENDING },
|
||||
{ "stack", RLIMIT_STACK },
|
||||
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
void conf_parse_rlimit(char *line)
|
||||
{
|
||||
struct rlimit rlim;
|
||||
rlim_t new, *set;
|
||||
const struct rlimit_name *name;
|
||||
int resource = -1;
|
||||
|
||||
char *tok = strtok(line, " \t");
|
||||
|
||||
if (tok && !strcmp(tok, "soft"))
|
||||
set = &rlim.rlim_cur;
|
||||
else if (tok && !strcmp(tok, "hard"))
|
||||
set = &rlim.rlim_max;
|
||||
else
|
||||
goto error;
|
||||
|
||||
tok = strtok(NULL, " \t");
|
||||
if (!tok)
|
||||
goto error;
|
||||
|
||||
for (name = rlimit_names; name->name; name++) {
|
||||
if (!strcmp(tok, name->name))
|
||||
resource = name->val;
|
||||
}
|
||||
|
||||
if (resource < 0)
|
||||
goto fail;
|
||||
|
||||
tok = strtok(NULL, " \t");
|
||||
if (!tok)
|
||||
goto fail;
|
||||
|
||||
if (!strcmp(tok, "infinity")) {
|
||||
new = RLIM_INFINITY;
|
||||
} else {
|
||||
const char *err = NULL;
|
||||
|
||||
new = strtonum(tok, 0, (long long)2 << 31, &err);
|
||||
if (err)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (getrlimit(resource, &rlim))
|
||||
goto fail;
|
||||
|
||||
*set = new;
|
||||
if (setrlimit(resource, &rlim))
|
||||
goto fail;
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
logit(LOG_WARNING, "rlimit: Failed setting rlimit %s", name->name ? : "unknown");
|
||||
return;
|
||||
error:
|
||||
logit(LOG_WARNING, "rlimit: parse error");
|
||||
}
|
||||
|
||||
static void parse_static(char *line)
|
||||
{
|
||||
char *x;
|
||||
char cmd[CMD_SIZE];
|
||||
|
||||
/* Do this before mounting / read-write
|
||||
* XXX: Move to plugin which checks /etc/fstab instead */
|
||||
if (MATCH_CMD(line, "check ", x)) {
|
||||
char *dev = strip_line(x);
|
||||
|
||||
strcpy(cmd, "/sbin/fsck -C -a ");
|
||||
strlcat(cmd, dev, sizeof(cmd));
|
||||
run_interactive(cmd, "Checking file system %s", dev);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "user ", x)) {
|
||||
if (username) free(username);
|
||||
username = strdup(strip_line(x));
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "host ", x)) {
|
||||
if (hostname) free(hostname);
|
||||
hostname = strdup(strip_line(x));
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "module ", x)) {
|
||||
char *mod = strip_line(x);
|
||||
|
||||
strcpy(cmd, "/sbin/modprobe ");
|
||||
strlcat(cmd, mod, sizeof(cmd));
|
||||
run_interactive(cmd, "Loading kernel module %s", mod);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "mknod ", x)) {
|
||||
char *dev = strip_line(x);
|
||||
|
||||
strcpy(cmd, "/bin/mknod ");
|
||||
strlcat(cmd, dev, sizeof(cmd));
|
||||
run_interactive(cmd, "Creating device node %s", dev);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "network ", x)) {
|
||||
if (network) free(network);
|
||||
network = strdup(strip_line(x));
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "runparts ", x)) {
|
||||
if (runparts) free(runparts);
|
||||
runparts = strdup(strip_line(x));
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "include ", x)) {
|
||||
char *file = strip_line(x);
|
||||
|
||||
strlcpy(cmd, file, sizeof(cmd));
|
||||
if (!fexist(cmd)) {
|
||||
_e("Cannot find include file %s, absolute path required!", x);
|
||||
return;
|
||||
}
|
||||
|
||||
parse_conf(cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "startx ", x)) {
|
||||
service_register(SVC_TYPE_SERVICE, strip_line(x), NULL, username);
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "shutdown ", x)) {
|
||||
if (sdown) free(sdown);
|
||||
sdown = strdup(strip_line(x));
|
||||
return;
|
||||
}
|
||||
|
||||
/* The desired runlevel to start when leaving bootstrap (S).
|
||||
* Finit supports 1-9, but most systems only use 1-6, where
|
||||
* 6 is reserved for reboot */
|
||||
if (MATCH_CMD(line, "runlevel ", x)) {
|
||||
char *token = strip_line(x);
|
||||
const char *err = NULL;
|
||||
|
||||
cfglevel = strtonum(token, 1, 9, &err);
|
||||
if (err)
|
||||
cfglevel = RUNLEVEL;
|
||||
if (cfglevel < 1 || cfglevel > 9 || cfglevel == 6)
|
||||
cfglevel = 2; /* Fallback */
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: Make console & tty dynamically loadable from /etc/finit.d */
|
||||
if (MATCH_CMD(line, "console ", x)) {
|
||||
if (console) free(console);
|
||||
console = strdup(strip_line(x));
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: Make console & tty dynamically loadable from /etc/finit.d */
|
||||
if (MATCH_CMD(line, "tty ", x)) {
|
||||
tty_register(strip_line(x));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_dynamic(char *line, struct timeval *mtime)
|
||||
{
|
||||
char *x;
|
||||
|
||||
/* Skip comments, i.e. lines beginning with # */
|
||||
if (MATCH_CMD(line, "#", x))
|
||||
return;
|
||||
|
||||
/* Monitored daemon, will be respawned on exit */
|
||||
if (MATCH_CMD(line, "service ", x)) {
|
||||
service_register(SVC_TYPE_SERVICE, x, mtime, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* One-shot task, will not be respawned */
|
||||
if (MATCH_CMD(line, "task ", x)) {
|
||||
service_register(SVC_TYPE_TASK, x, mtime, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Like task but waits for completion, useful w/ [S] */
|
||||
if (MATCH_CMD(line, "run ", x)) {
|
||||
service_register(SVC_TYPE_RUN, x, mtime, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Classic inetd service */
|
||||
if (MATCH_CMD(line, "inetd ", x)) {
|
||||
#ifdef INETD_ENABLED
|
||||
service_register(SVC_TYPE_INETD, x, mtime, NULL);
|
||||
#else
|
||||
_e("Finit built with inetd support disabled, cannot register service inetd %s!", x);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (MATCH_CMD(line, "rlimit ", x)) {
|
||||
conf_parse_rlimit(x);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void tabstospaces(char *line)
|
||||
{
|
||||
if (!line)
|
||||
return;
|
||||
|
||||
for (int i = 0; line[i]; i++) {
|
||||
if (line[i] == '\t')
|
||||
line[i] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_conf_dynamic(char *file, struct timeval *mtime)
|
||||
{
|
||||
FILE *fp = fopen(file, "r");
|
||||
|
||||
if (!fp) {
|
||||
_pe("Failed opening %s", file);
|
||||
return 1;
|
||||
}
|
||||
|
||||
while (!feof(fp)) {
|
||||
char line[LINE_SIZE] = "";
|
||||
|
||||
if (!fgets(line, sizeof(line), fp))
|
||||
continue;
|
||||
|
||||
chomp(line);
|
||||
tabstospaces(line);
|
||||
_d("dyn conf: %s", line);
|
||||
|
||||
parse_dynamic(line, mtime);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_conf(char *file)
|
||||
{
|
||||
FILE *fp;
|
||||
char line[LINE_SIZE] = "";
|
||||
char *x;
|
||||
|
||||
fp = fopen(file, "r");
|
||||
if (!fp)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* If not standard finit.conf, then we want to show just the base name
|
||||
* Loading configuration ............. vs
|
||||
* Loading services configuration ....
|
||||
*/
|
||||
if (!string_match (file, FINIT_CONF)) {
|
||||
/* Remove leading path */
|
||||
x = strrchr(file, '/');
|
||||
if (!x) x = file;
|
||||
else x++;
|
||||
|
||||
/* Remove ending .conf */
|
||||
strlcpy(line, x, sizeof(line));
|
||||
x = strstr(line, ".conf");
|
||||
if (x) *x = 0;
|
||||
|
||||
/* Add empty space. */
|
||||
strcat(line, " ");
|
||||
}
|
||||
|
||||
if (!silent)
|
||||
print(0, "Loading %sconfiguration", line);
|
||||
while (!feof(fp)) {
|
||||
if (!fgets(line, sizeof(line), fp))
|
||||
continue;
|
||||
|
||||
chomp(line);
|
||||
tabstospaces(line);
|
||||
_d("conf: %s", line);
|
||||
|
||||
parse_static(line);
|
||||
parse_dynamic(line, NULL);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Reload all *.conf in /etc/finit.d/ */
|
||||
void conf_reload_dynamic(void)
|
||||
{
|
||||
int i, num;
|
||||
char *dir = rcsd;
|
||||
struct dirent **e;
|
||||
|
||||
/* Mark and sweep */
|
||||
svc_mark_dynamic();
|
||||
|
||||
num = scandir(dir, &e, NULL, alphasort);
|
||||
if (num < 0) {
|
||||
_d("No files found in %s, skipping ...", dir);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
char *name = e[i]->d_name;
|
||||
char path[CMD_SIZE];
|
||||
struct stat st;
|
||||
struct timeval mtime;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
||||
|
||||
/* Check that it's an actual file ... */
|
||||
if (stat(path, &st)) {
|
||||
_d("Cannot even read .conf file %s, skipping ...", path);
|
||||
continue;
|
||||
}
|
||||
if (S_ISEXEC(st.st_mode) || S_ISDIR(st.st_mode))
|
||||
continue;
|
||||
|
||||
/* Check that file ends with '.conf' */
|
||||
if (strcmp(&path[strlen(path) - 5], ".conf")) {
|
||||
_d("File %s is not a .conf, skipping ... ", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
TIMESPEC_TO_TIMEVAL(&mtime, &st.st_mtim);
|
||||
parse_conf_dynamic(path, &mtime);
|
||||
}
|
||||
|
||||
while (num--)
|
||||
free(e[num]);
|
||||
free(e);
|
||||
|
||||
set_hostname(&hostname);
|
||||
}
|
||||
|
||||
int conf_parse_config(void)
|
||||
{
|
||||
username = strdup(DEFUSER);
|
||||
hostname = strdup(DEFHOST);
|
||||
|
||||
return parse_conf(FINIT_CONF);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/* Parser for /etc/finit.conf and /etc/finit.d/<SVC>.conf
|
||||
*
|
||||
* Copyright (c) 2012-2015 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_CONF_H_
|
||||
#define FINIT_CONF_H_
|
||||
|
||||
#include "svc.h"
|
||||
|
||||
void conf_parse_cmdline (void);
|
||||
int conf_parse_runlevels (char *runlevels);
|
||||
void conf_parse_cond (svc_t *svc, char *cond);
|
||||
int conf_parse_config (void);
|
||||
void conf_reload_dynamic (void);
|
||||
|
||||
#endif /* FINIT_CONF_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
/* Functions for exec'ing processes
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2015 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 "config.h" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h> /* isdigit() */
|
||||
#include <dirent.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "sig.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
#define NUM_ARGS 16
|
||||
|
||||
int getty(char *tty, char *baud, char *term, char *user);
|
||||
|
||||
|
||||
/* Wait for process completion, returns status of waitpid(2) syscall */
|
||||
int complete(char *cmd, int pid)
|
||||
{
|
||||
int status = 0;
|
||||
|
||||
if (waitpid(pid, &status, 0) == -1) {
|
||||
if (errno == EINTR)
|
||||
_e("Caught unblocked signal waiting for %s, aborting", cmd);
|
||||
else if (errno == ECHILD)
|
||||
_e("Caught SIGCHLD waiting for %s, aborting", cmd);
|
||||
else
|
||||
_e("Failed starting %s, error %d: %s", cmd, errno, strerror (errno));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int run(char *cmd)
|
||||
{
|
||||
int status, result, i = 0;
|
||||
char *args[NUM_ARGS + 1], *arg, *backup;
|
||||
pid_t pid;
|
||||
|
||||
/* We must create a copy that is possible to modify. */
|
||||
backup = arg = strdup(cmd);
|
||||
if (!arg)
|
||||
return 1; /* Failed allocating a string to be modified. */
|
||||
|
||||
/* Split command line into tokens of an argv[] array. */
|
||||
args[i++] = strsep(&arg, "\t ");
|
||||
while (arg && i < NUM_ARGS) {
|
||||
/* Handle run("su -c \"dbus-daemon --system\" messagebus");
|
||||
* => "su", "-c", "\"dbus-daemon --system\"", "messagebus" */
|
||||
if (*arg == '\'' || *arg == '"') {
|
||||
char *p, delim[2] = " ";
|
||||
|
||||
delim[0] = arg[0];
|
||||
args[i++] = arg++;
|
||||
strsep(&arg, delim);
|
||||
p = arg - 1;
|
||||
*p = *delim;
|
||||
*arg++ = 0;
|
||||
} else {
|
||||
args[i++] = strsep(&arg, "\t ");
|
||||
}
|
||||
}
|
||||
args[i] = NULL;
|
||||
|
||||
if (i == NUM_ARGS && arg) {
|
||||
_e("Command too long: %s", cmd);
|
||||
free(backup);
|
||||
errno = EOVERFLOW;
|
||||
return 1;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if (0 == pid) {
|
||||
FILE *fp;
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
sig_unblock();
|
||||
setsid();
|
||||
|
||||
/* Always redirect stdio for run() */
|
||||
fp = fopen("/dev/null", "w");
|
||||
if (fp) {
|
||||
int fd = fileno(fp);
|
||||
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
}
|
||||
|
||||
sig_unblock();
|
||||
execvp(args[0], args);
|
||||
|
||||
_exit(1); /* Only if execv() fails. */
|
||||
} else if (-1 == pid) {
|
||||
_pe("%s", args[0]);
|
||||
free(backup);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = complete(args[0], pid);
|
||||
if (-1 == status) {
|
||||
free(backup);
|
||||
return 1;
|
||||
}
|
||||
|
||||
result = WEXITSTATUS(status);
|
||||
if (WIFEXITED(status)) {
|
||||
_d("Started %s and ended OK: %d", args[0], result);
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
_d("Process %s terminated by signal %d", args[0], WTERMSIG(status));
|
||||
if (!result)
|
||||
result = 1; /* Must alert callee that the command did complete successfully.
|
||||
* This is necessary since not all programs trap signals and
|
||||
* change their return code accordingly. --Jocke */
|
||||
}
|
||||
|
||||
free(backup);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int run_interactive(char *cmd, char *fmt, ...)
|
||||
{
|
||||
int status, oldout = 1, olderr = 2;
|
||||
char line[LINE_SIZE];
|
||||
va_list ap;
|
||||
FILE *fp;
|
||||
|
||||
if (!cmd) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!silent && fmt) {
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(line, sizeof(line), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
print_desc("", line);
|
||||
}
|
||||
|
||||
/* Redirect output from cmd to a tempfile */
|
||||
fp = tempfile();
|
||||
if (fp && !debug) {
|
||||
oldout = dup(STDOUT_FILENO);
|
||||
olderr = dup(STDERR_FILENO);
|
||||
dup2(fileno(fp), STDOUT_FILENO);
|
||||
dup2(fileno(fp), STDERR_FILENO);
|
||||
}
|
||||
|
||||
/* Run cmd ... */
|
||||
status = run(cmd);
|
||||
|
||||
/* Restore stderr/stdout */
|
||||
if (fp && !debug) {
|
||||
if (oldout >= 0) {
|
||||
dup2(oldout, STDOUT_FILENO);
|
||||
close(oldout);
|
||||
}
|
||||
if (olderr >= 0) {
|
||||
dup2(olderr, STDERR_FILENO);
|
||||
close(olderr);
|
||||
}
|
||||
}
|
||||
|
||||
if (!silent && fmt)
|
||||
print_result(status);
|
||||
|
||||
/* Dump any results of cmd on stderr after we've printed [ OK ] or [FAIL] */
|
||||
if (fp && !debug) {
|
||||
size_t len, written;
|
||||
|
||||
rewind(fp);
|
||||
do {
|
||||
len = fread(line, 1, sizeof(line), fp);
|
||||
written = fwrite(line, len, sizeof(char), stderr);
|
||||
} while (len > 0 && written == len);
|
||||
}
|
||||
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
pid_t run_getty(char *tty, char *speed, char *term, int console)
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
sig_unblock();
|
||||
setsid();
|
||||
|
||||
/* Set INIT_PROCESS UTMP entry */
|
||||
utmp_set_init(tty, 0);
|
||||
|
||||
if (console)
|
||||
prctl(PR_SET_NAME, "console", 0, 0, 0);
|
||||
else
|
||||
prctl(PR_SET_NAME, "finit-getty", 0, 0, 0);
|
||||
|
||||
_exit(getty(tty, speed, term, NULL));
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
|
||||
int run_parts(char *dir, char *cmd)
|
||||
{
|
||||
struct dirent **e;
|
||||
int i, num;
|
||||
|
||||
num = scandir(dir, &e, NULL, alphasort);
|
||||
if (num < 0) {
|
||||
_d("No files found in %s, skipping ...", dir);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
int j = 0;
|
||||
pid_t pid = 0;
|
||||
mode_t mode;
|
||||
char *args[NUM_ARGS];
|
||||
char *name = e[i]->d_name;
|
||||
char path[CMD_SIZE];
|
||||
|
||||
snprintf(path, sizeof(path), "%s/%s", dir, name);
|
||||
mode = fmode(path);
|
||||
if (!S_ISEXEC(mode) || S_ISDIR(mode)) {
|
||||
_d("Skipping %s ...", path);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Fill in args[], starting with full path to executable */
|
||||
args[j++] = path;
|
||||
|
||||
/* If the callee didn't supply a run_parts() argument */
|
||||
if (!cmd) {
|
||||
/* Check if S<NUM>service or K<NUM>service notation is used */
|
||||
_d("Checking if %s is a sysvinit startstop script ...", name);
|
||||
if (name[0] == 'S' && isdigit(name[1])) {
|
||||
args[j++] = "start";
|
||||
} else if (name[0] == 'K' && isdigit(name[1])) {
|
||||
args[j++] = "stop";
|
||||
}
|
||||
} else {
|
||||
args[j++] = cmd;
|
||||
}
|
||||
args[j++] = NULL;
|
||||
|
||||
pid = fork();
|
||||
if (!pid) {
|
||||
_d("Calling %s ...", path);
|
||||
sig_unblock();
|
||||
execv(path, args);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
complete(path, pid);
|
||||
}
|
||||
|
||||
while (num--)
|
||||
free(e[num]);
|
||||
free(e);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+432
@@ -0,0 +1,432 @@
|
||||
/* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2015 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 "config.h" /* Generated by configure script */
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dirent.h>
|
||||
#ifdef HAVE_FSTAB_H
|
||||
#include <fstab.h>
|
||||
#endif
|
||||
#include <mntent.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/stat.h> /* umask(), mkdir() */
|
||||
#include <sys/wait.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "private.h"
|
||||
#include "plugin.h"
|
||||
#include "service.h"
|
||||
#include "sig.h"
|
||||
#include "sm.h"
|
||||
#include "tty.h"
|
||||
#include "utmp-api.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
int debug = 0;
|
||||
int quiet = QUIET_MODE; /* Delayed disable of silent mode. */
|
||||
int silent = SILENT_MODE; /* Completely silent, including boot */
|
||||
int wdogpid = 0; /* No watchdog by default */
|
||||
int runlevel = 0; /* Bootstrap 'S' */
|
||||
int cfglevel = RUNLEVEL; /* Fallback if no configured runlevel */
|
||||
int prevlevel = -1;
|
||||
char *sdown = NULL;
|
||||
char *network = NULL;
|
||||
char *username = NULL;
|
||||
char *hostname = NULL;
|
||||
char *rcsd = FINIT_RCSD;
|
||||
char *runparts = NULL;
|
||||
char *console = NULL;
|
||||
|
||||
uev_ctx_t *ctx = NULL; /* Main loop context */
|
||||
|
||||
static int banner(void)
|
||||
{
|
||||
char buf[42] = INIT_HEADING;
|
||||
const char separator[] = "========================================================================";
|
||||
|
||||
if (silent)
|
||||
return 0;
|
||||
|
||||
fprintf(stderr, "\e[2K\e[1m%s %.*s\e[0m\n", buf, 66 - (int)strlen(buf), separator);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check all filesystems in /etc/fstab with a fs_passno > 0
|
||||
*/
|
||||
static int fsck(int pass)
|
||||
{
|
||||
int save;
|
||||
struct fstab *fs;
|
||||
|
||||
if (!setfsent()) {
|
||||
_pe("Failed opening fstab");
|
||||
return 1;
|
||||
}
|
||||
|
||||
save = debug;
|
||||
debug = 0;
|
||||
|
||||
while ((fs = getfsent())) {
|
||||
char cmd[80];
|
||||
struct stat st;
|
||||
|
||||
if (fs->fs_passno != pass)
|
||||
continue;
|
||||
|
||||
errno = 0;
|
||||
if (stat(fs->fs_spec, &st) || !S_ISBLK(st.st_mode)) {
|
||||
if (!string_match(fs->fs_spec, "UUID=") && !string_match(fs->fs_spec, "LABEL=")) {
|
||||
_d("Cannot fsck %s, not a block device: %s", fs->fs_spec, strerror(errno));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "/sbin/fsck -a %s", fs->fs_spec);
|
||||
run_interactive(cmd, "Checking filesystem %.13s", fs->fs_spec);
|
||||
}
|
||||
|
||||
debug = save;
|
||||
endfsent();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void networking(void)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
/* Run user network start script if enabled */
|
||||
if (network) {
|
||||
run_interactive(network, "Starting networking: %s", network);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Debian/Ubuntu/Busybox interfaces file */
|
||||
fp = fopen("/etc/network/interfaces", "r");
|
||||
if (fp) {
|
||||
int i = 0;
|
||||
char buf[160];
|
||||
|
||||
/* Bring up all 'auto' interfaces */
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
char cmd[80];
|
||||
char *line, *ifname = NULL;
|
||||
|
||||
chomp(buf);
|
||||
line = strip_line(buf);
|
||||
|
||||
if (!strncmp(line, "auto", 4))
|
||||
ifname = &line[5];
|
||||
if (!strncmp(line, "allow-hotplug", 13))
|
||||
ifname = &line[14];
|
||||
|
||||
if (!ifname)
|
||||
continue;
|
||||
|
||||
snprintf(cmd, 80, "/sbin/ifup %s", ifname);
|
||||
run_interactive(cmd, "Bringing up interface %s", ifname);
|
||||
i++;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
if (i)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fall back to bring up at least loopback */
|
||||
ifconfig("lo", "127.0.0.1", "255.0.0.0", 1);
|
||||
}
|
||||
|
||||
/* Requires /proc to be mounted */
|
||||
static int fismnt(char *dir)
|
||||
{
|
||||
FILE *fp;
|
||||
int found = 0;
|
||||
struct mntent *mnt;
|
||||
|
||||
fp = setmntent("/proc/mounts", "r");
|
||||
if (!fp)
|
||||
return 0; /* Dunno, maybe not */
|
||||
|
||||
while ((mnt = getmntent(fp))) {
|
||||
if (!strcmp(mnt->mnt_dir, dir)) {
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
endmntent(fp);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
/*
|
||||
* If everything goes south we can use this to give the operator an
|
||||
* emergency shell to debug the problem -- Finit should not crash!
|
||||
*/
|
||||
static void emergency_shell(void)
|
||||
{
|
||||
#ifdef EMERGENCY_SHELL
|
||||
pid_t pid;
|
||||
|
||||
pid = fork();
|
||||
if (pid) {
|
||||
waitpid(pid, NULL, 0);
|
||||
fprintf(stderr, "\n=> Embarrassingly, Finit has crashed. Check /dev/kmsg for details.\n");
|
||||
fprintf(stderr, "=> For more, remove 'quiet' and add '--debug' to the kernel command line.\n\n");
|
||||
|
||||
/*
|
||||
* Become session leader and set controlling TTY
|
||||
* to enable Ctrl-C and job control in shell.
|
||||
*/
|
||||
setsid();
|
||||
ioctl(STDIN_FILENO, TIOCSCTTY, 1);
|
||||
|
||||
execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
|
||||
}
|
||||
#endif /* EMERGENCY_SHELL */
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
char *devfsd;
|
||||
uev_ctx_t loop;
|
||||
|
||||
/*
|
||||
* finit/init/telinit client tool uses /dev/initctl pipe
|
||||
* for compatibility but initctl client tool uses socket
|
||||
*/
|
||||
if (getpid() != 1)
|
||||
return client(argc, argv);
|
||||
|
||||
/*
|
||||
* In case of emergency.
|
||||
*/
|
||||
emergency_shell();
|
||||
|
||||
/*
|
||||
* Hello world.
|
||||
*/
|
||||
banner();
|
||||
|
||||
/*
|
||||
* Initial setup of signals, ignore all until we're up.
|
||||
*/
|
||||
sig_init();
|
||||
|
||||
/*
|
||||
* Initalize event context.
|
||||
*/
|
||||
uev_init(&loop);
|
||||
ctx = &loop;
|
||||
|
||||
/*
|
||||
* Check file filesystems in /etc/fstab
|
||||
*/
|
||||
for (int pass = 1; pass < 10; pass++) {
|
||||
if (fsck(pass))
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mount base file system, kernel is assumed to run devtmpfs for /dev
|
||||
*/
|
||||
chdir("/");
|
||||
umask(0);
|
||||
mount("none", "/proc", "proc", 0, NULL);
|
||||
mount("none", "/proc/bus/usb", "usbfs", 0, NULL);
|
||||
mount("none", "/sys", "sysfs", 0, NULL);
|
||||
|
||||
/*
|
||||
* Some non-embedded systems without an initramfs may not have /dev mounted yet
|
||||
* If they do, check if system has udevadm and perform cleanup from initramfs
|
||||
*/
|
||||
if (!fismnt("/dev"))
|
||||
mount("udev", "/dev", "devtmpfs", MS_RELATIME, "size=10%,nr_inodes=61156,mode=755");
|
||||
else if (fexist("/sbin/udevadm"))
|
||||
run_interactive("/sbin/udevadm info --cleanup-db", "Cleaning up udev db");
|
||||
|
||||
/* Some systems use /dev/pts */
|
||||
makedir("/dev/pts", 0755);
|
||||
mount("devpts", "/dev/pts", "devpts", 0, "gid=5,mode=620");
|
||||
|
||||
makedir("/dev/shm", 0755);
|
||||
if (!fismnt("/dev/shm"))
|
||||
mount("shm", "/dev/shm", "tmpfs", 0, NULL);
|
||||
|
||||
/*
|
||||
* New tmpfs based /run for volatile runtime data
|
||||
* For details, see http://lwn.net/Articles/436012/
|
||||
*/
|
||||
if (fisdir("/run") && !fismnt("/run"))
|
||||
mount("tmpfs", "/run", "tmpfs", MS_NODEV, "mode=0755,size=10%");
|
||||
umask(022);
|
||||
|
||||
/*
|
||||
* Parse kernel parameters
|
||||
*/
|
||||
conf_parse_cmdline();
|
||||
|
||||
/*
|
||||
* Populate /dev and prepare for runtime events from kernel.
|
||||
*/
|
||||
if (fexist("/sbin/mdev")) {
|
||||
/* Embedded Linux systems usually have BusyBox mdev */
|
||||
if (debug)
|
||||
touch("/dev/mdev.log");
|
||||
devfsd = "/sbin/mdev -s";
|
||||
} else {
|
||||
if (fexist("/sbin/udevd"))
|
||||
devfsd = "/sbin/udevd --daemon";
|
||||
else
|
||||
devfsd = "/lib/systemd/systemd-udevd --daemon";
|
||||
}
|
||||
|
||||
run_interactive(devfsd, "Populating device tree");
|
||||
if (fexist("/sbin/udevadm")) {
|
||||
run("/sbin/udevadm trigger --action=add --type=subsystems");
|
||||
run("/sbin/udevadm trigger --action=add --type=devices");
|
||||
run("/sbin/udevadm settle --timeout=120");
|
||||
}
|
||||
|
||||
/*
|
||||
* Load plugins first, finit.conf may contain references to
|
||||
* features implemented by plugins.
|
||||
*/
|
||||
plugin_init(&loop, PLUGIN_PATH);
|
||||
|
||||
/*
|
||||
* Parse /etc/finit.conf, main configuration file
|
||||
*/
|
||||
conf_parse_config();
|
||||
|
||||
/*
|
||||
* Start built-in watchdog as soon as possible, if enabled
|
||||
*/
|
||||
watchdog(argv[0]);
|
||||
|
||||
/* Set hostname as soon as possible, for syslog et al. */
|
||||
set_hostname(&hostname);
|
||||
|
||||
/* Set default PATH, for uid 0 */
|
||||
setenv("PATH", _PATH_STDPATH, 1);
|
||||
|
||||
/*
|
||||
* Mount filesystems
|
||||
*/
|
||||
#ifdef REMOUNT_ROOTFS
|
||||
run("/bin/mount -n -o remount,rw /");
|
||||
#endif
|
||||
#ifdef SYSROOT
|
||||
mount(SYSROOT, "/", NULL, MS_MOVE, NULL);
|
||||
#endif
|
||||
|
||||
/* Debian has this little script to copy generated rules while the system was read-only */
|
||||
if (fexist("/lib/udev/udev-finish"))
|
||||
run_interactive("/lib/udev/udev-finish", "Finalizing udev");
|
||||
|
||||
_d("Root FS up, calling hooks ...");
|
||||
plugin_run_hooks(HOOK_ROOTFS_UP);
|
||||
|
||||
umask(0);
|
||||
if (run_interactive("/bin/mount -na", "Mounting filesystems"))
|
||||
plugin_run_hooks(HOOK_MOUNT_ERROR);
|
||||
|
||||
run("/sbin/swapon -ea");
|
||||
umask(0022);
|
||||
|
||||
/* Base FS up, enable standard SysV init signals */
|
||||
sig_setup(&loop);
|
||||
|
||||
_d("Base FS up, calling hooks ...");
|
||||
plugin_run_hooks(HOOK_BASEFS_UP);
|
||||
|
||||
/*
|
||||
* Initalize finit state machine and start all bootstrap tasks, no network available!
|
||||
*/
|
||||
sm_init(&sm);
|
||||
sm_step(&sm);
|
||||
|
||||
/*
|
||||
* Network stuff
|
||||
*/
|
||||
networking();
|
||||
umask(022);
|
||||
|
||||
/* Hooks that rely on loopback, or basic networking being up. */
|
||||
plugin_run_hooks(HOOK_NETWORK_UP);
|
||||
|
||||
/*
|
||||
* Start all tasks/services in the configured runlevel
|
||||
*/
|
||||
service_runlevel(cfglevel);
|
||||
|
||||
_d("Running svc up hooks ...");
|
||||
plugin_run_hooks(HOOK_SVC_UP);
|
||||
|
||||
/*
|
||||
* Run startup scripts in the runparts directory, if any.
|
||||
*/
|
||||
if (runparts && fisdir(runparts)) {
|
||||
_d("Running startup scripts in %s ...", runparts);
|
||||
run_parts(runparts, NULL);
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
/* Convenient SysV compat for when you just don't care ... */
|
||||
if (!access(FINIT_RC_LOCAL, X_OK)) {
|
||||
run_interactive(FINIT_RC_LOCAL, "Calling %s", FINIT_RC_LOCAL);
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
/* Hooks that should run at the very end */
|
||||
plugin_run_hooks(HOOK_SYSTEM_UP);
|
||||
|
||||
/* Start TTYs */
|
||||
tty_runlevel(runlevel);
|
||||
|
||||
/* Enable silent mode, if selected */
|
||||
if (quiet && !debug)
|
||||
silent = 1;
|
||||
|
||||
/* Start new initctl API responder */
|
||||
api_init(&loop);
|
||||
|
||||
/*
|
||||
* Enter main loop to monior /dev/initctl and services
|
||||
*/
|
||||
return uev_run(&loop, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/* Finit - Fast /sbin/init replacement w/ I/O, hook & service plugins
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2015 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_H_
|
||||
#define FINIT_H_
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <paths.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <lite/lite.h>
|
||||
#include <uev/uev.h>
|
||||
|
||||
#define CMD_SIZE 256
|
||||
#define LINE_SIZE 1024
|
||||
#define BUF_SIZE 4096
|
||||
|
||||
/*
|
||||
* We extend the INIT_CMD_ range for the new initctl tool, Finit only
|
||||
* support changing runlevel, reloading the configuration and setenv
|
||||
* with the old-style /dev/initctl FIFO.
|
||||
*/
|
||||
#define INIT_SOCKET _PATH_VARRUN "finit.sock"
|
||||
#define INIT_MAGIC 0x03091969
|
||||
|
||||
#define INIT_CMD_START 0
|
||||
#define INIT_CMD_RUNLVL 1
|
||||
#define INIT_CMD_POWERFAIL 2
|
||||
#define INIT_CMD_POWERFAILNOW 3
|
||||
#define INIT_CMD_POWEROK 4
|
||||
#define INIT_CMD_BSD 5
|
||||
#define INIT_CMD_SETENV 6
|
||||
#define INIT_CMD_UNSETENV 7
|
||||
|
||||
/* Finit extensions over std SysV */
|
||||
#define INIT_CMD_DEBUG 8 /* Toggle Finit debug */
|
||||
#define INIT_CMD_RELOAD 9 /* Reload *.conf in /etc/finit.d/ */
|
||||
#define INIT_CMD_START_SVC 10
|
||||
#define INIT_CMD_STOP_SVC 11
|
||||
#define INIT_CMD_RELOAD_SVC 12 /* SIGHUP service */
|
||||
#define INIT_CMD_RESTART_SVC 13 /* STOP + START service */
|
||||
#define INIT_CMD_QUERY_INETD 14
|
||||
#define INIT_CMD_EMIT 15
|
||||
#define INIT_CMD_GET_RUNLEVEL 16
|
||||
#define INIT_CMD_WDOG_HELLO 128 /* Watchdog register and hello */
|
||||
#define INIT_CMD_NACK 254
|
||||
#define INIT_CMD_ACK 255
|
||||
|
||||
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 debug;
|
||||
extern int quiet;
|
||||
extern int silent;
|
||||
extern int wdogpid;
|
||||
extern int runlevel;
|
||||
extern int cfglevel;
|
||||
extern int prevlevel;
|
||||
extern char *rcsd;
|
||||
extern char *sdown;
|
||||
extern char *network;
|
||||
extern char *hostname;
|
||||
extern char *username;
|
||||
extern char *runparts;
|
||||
extern char *console;
|
||||
extern uev_ctx_t *ctx;
|
||||
|
||||
#endif /* FINIT_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+347
@@ -0,0 +1,347 @@
|
||||
/* Initialize and serve a login terminal
|
||||
*
|
||||
* Copyright (c) 2016 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 <err.h>
|
||||
#include <errno.h>
|
||||
#include <paths.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/ttydefaults.h> /* Not included by default in musl libc */
|
||||
#include <termios.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "sig.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
#ifndef _PATH_LOGIN
|
||||
#define _PATH_LOGIN "/bin/login"
|
||||
#endif
|
||||
|
||||
#define print(s) (void)write(STDOUT_FILENO, s, strlen(s))
|
||||
|
||||
|
||||
/*
|
||||
* Read one character from stdin.
|
||||
*/
|
||||
static int readch(char *tty)
|
||||
{
|
||||
int st;
|
||||
char ch1;
|
||||
|
||||
st = read(STDIN_FILENO, &ch1, 1);
|
||||
if (st == 0) {
|
||||
print("\n");
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
if (st < 0)
|
||||
errx(1, "getty: %s: read error", tty);
|
||||
|
||||
return ch1 & 0xFF;
|
||||
}
|
||||
|
||||
static void stty(int fd, speed_t speed)
|
||||
{
|
||||
struct termios term;
|
||||
|
||||
tcdrain(fd);
|
||||
if (tcgetattr(fd, &term))
|
||||
return;
|
||||
|
||||
cfsetispeed(&term, speed);
|
||||
cfsetospeed(&term, speed);
|
||||
tcsetattr(fd, TCSAFLUSH, &term);
|
||||
tcflush(fd, TCIOFLUSH);
|
||||
|
||||
/* Disable modem specific flags */
|
||||
term.c_cflag &= ~(0|CSTOPB|PARENB|PARODD);
|
||||
term.c_cflag &= ~CRTSCTS;
|
||||
term.c_cflag |= CLOCAL;
|
||||
|
||||
/* Timeouts, minimum chars and default flags */
|
||||
term.c_cc[VTIME] = 0;
|
||||
term.c_cc[VMIN] = 1;
|
||||
term.c_iflag = ICRNL|IXON|IXOFF;
|
||||
term.c_oflag = OPOST|ONLCR;
|
||||
term.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
|
||||
term.c_lflag |= ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOKE;
|
||||
|
||||
/* Reset special characters to defaults */
|
||||
term.c_cc[VINTR] = CTRL('C');
|
||||
term.c_cc[VQUIT] = CTRL('\\');
|
||||
term.c_cc[VEOF] = CTRL('D');
|
||||
term.c_cc[VEOL] = '\n';
|
||||
term.c_cc[VKILL] = CTRL('U');
|
||||
term.c_cc[VERASE] = CERASE;
|
||||
tcsetattr(fd, TCSANOW, &term);
|
||||
|
||||
/* Clear screen in silent mode */
|
||||
if (silent)
|
||||
print("\033[r\033[H\033[J");
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse and display a line from /etc/issue
|
||||
*/
|
||||
static void do_parse(char *line, struct utsname *uts, char *tty)
|
||||
{
|
||||
char *s, *s0;
|
||||
|
||||
s0 = line;
|
||||
for (s = line; *s != 0; s++) {
|
||||
if (*s == '\\') {
|
||||
(void)write(1, s0, s - s0);
|
||||
s0 = s + 2;
|
||||
switch (*++s) {
|
||||
case 'l':
|
||||
print(tty);
|
||||
break;
|
||||
case 'm':
|
||||
print(uts->machine);
|
||||
break;
|
||||
case 'n':
|
||||
print(uts->nodename);
|
||||
break;
|
||||
#ifdef _GNU_SOURCE
|
||||
case 'o':
|
||||
print(uts->domainname);
|
||||
break;
|
||||
#endif
|
||||
case 'r':
|
||||
print(uts->release);
|
||||
break;
|
||||
case 's':
|
||||
print(uts->sysname);
|
||||
break;
|
||||
case 'v':
|
||||
print(uts->version);
|
||||
break;
|
||||
case 0:
|
||||
goto leave;
|
||||
default:
|
||||
s0 = s - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
leave:
|
||||
(void)write(1, s0, s - s0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse and display /etc/issue
|
||||
*/
|
||||
static void do_issue(char *tty)
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[BUFSIZ] = "Welcome to \\s \\v \\n \\l\n\n";
|
||||
struct utsname uts;
|
||||
|
||||
/*
|
||||
* Get data about this machine.
|
||||
*/
|
||||
uname(&uts);
|
||||
|
||||
print("\n");
|
||||
fp = fopen("/etc/issue", "r");
|
||||
if (fp) {
|
||||
while (fgets(buf, sizeof(buf), fp))
|
||||
do_parse(buf, &uts, tty);
|
||||
|
||||
fclose(fp);
|
||||
} else {
|
||||
do_parse(buf, &uts, tty);
|
||||
}
|
||||
|
||||
do_parse("\\n login: ", &uts, tty);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the process of a GETTY.
|
||||
*/
|
||||
static void do_getty(char *tty, char *name, size_t len)
|
||||
{
|
||||
int ch;
|
||||
char *np;
|
||||
|
||||
/*
|
||||
* Clean up tty name.
|
||||
*/
|
||||
if (!strncmp(tty, _PATH_DEV, strlen(_PATH_DEV)))
|
||||
tty += 5;
|
||||
|
||||
/*
|
||||
* Display prompt.
|
||||
*/
|
||||
ch = ' ';
|
||||
*name = '\0';
|
||||
while (ch != '\n') {
|
||||
do_issue(tty);
|
||||
|
||||
np = name;
|
||||
while ((ch = readch(tty)) != '\n') {
|
||||
if (np < name + len)
|
||||
*np++ = ch;
|
||||
}
|
||||
|
||||
*np = '\0';
|
||||
if (*name == '\0')
|
||||
ch = ' '; /* blank line typed! */
|
||||
}
|
||||
|
||||
name[len - 1] = 0;
|
||||
}
|
||||
|
||||
/* Execute the login(1) command with the current
|
||||
* username as its argument. It will reply to the
|
||||
* calling user by typing "Password: "...
|
||||
*/
|
||||
static int do_login(char *name)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
execl(_PATH_LOGIN, _PATH_LOGIN, name, NULL);
|
||||
|
||||
/*
|
||||
* Failed to exec login, should be impossible. Try a starting a
|
||||
* fallback shell instead.
|
||||
*
|
||||
* Note: Add /etc/securetty handling.
|
||||
*/
|
||||
warnx("Failed exec %s, attempting fallback to %s ...", _PATH_LOGIN, _PATH_BSHELL);
|
||||
if (fstat(0, &st) == 0 && S_ISCHR(st.st_mode))
|
||||
execl(_PATH_BSHELL, _PATH_BSHELL, NULL);
|
||||
|
||||
return 1; /* We shouldn't get here ... */
|
||||
}
|
||||
|
||||
static speed_t do_parse_speed(char *baud)
|
||||
{
|
||||
char *ptr;
|
||||
size_t i;
|
||||
unsigned long val;
|
||||
struct { unsigned long val; speed_t speed; } v2s[] = {
|
||||
{ 0, B0 },
|
||||
{ 50, B50 },
|
||||
{ 75, B75 },
|
||||
{ 110, B110 },
|
||||
{ 134, B134 },
|
||||
{ 150, B150 },
|
||||
{ 200, B200 },
|
||||
{ 300, B300 },
|
||||
{ 600, B600 },
|
||||
{ 1200, B1200 },
|
||||
{ 1800, B1800 },
|
||||
{ 2400, B2400 },
|
||||
{ 4800, B4800 },
|
||||
{ 9600, B9600 },
|
||||
{ 19200, B19200 },
|
||||
{ 38400, B38400 },
|
||||
{ 57600, B57600 },
|
||||
{ 115200, B115200 },
|
||||
{ 230400, B230400 },
|
||||
{ 460800, B460800 },
|
||||
{ 500000, B500000 },
|
||||
{ 576000, B576000 },
|
||||
{ 921600, B921600 },
|
||||
{ 1000000, B1000000 },
|
||||
{ 1152000, B1152000 },
|
||||
{ 1500000, B1500000 },
|
||||
{ 2000000, B2000000 },
|
||||
{ 2500000, B2500000 },
|
||||
{ 3000000, B3000000 },
|
||||
{ 3500000, B3500000 },
|
||||
{ 4000000, B4000000 },
|
||||
};
|
||||
|
||||
errno = 0;
|
||||
val = strtoul(baud, &ptr, 10);
|
||||
if (errno || ptr == baud)
|
||||
return B0;
|
||||
|
||||
for (i = 0; i < sizeof(v2s) / sizeof(v2s[0]); i++) {
|
||||
if (v2s[i].val == val)
|
||||
return v2s[i].speed;
|
||||
}
|
||||
|
||||
return B0;
|
||||
}
|
||||
|
||||
int getty(char *tty, char *baud, char *UNUSED(term), char *user)
|
||||
{
|
||||
int fd;
|
||||
char name[30];
|
||||
speed_t speed = B38400;
|
||||
struct sigaction sa;
|
||||
|
||||
if (baud) {
|
||||
speed = do_parse_speed(baud);
|
||||
if (speed == B0) {
|
||||
logit(LOG_CRIT, "TTY %s: Invalid speed %s", tty, baud);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Detach from initial controlling TTY */
|
||||
vhangup();
|
||||
|
||||
fd = open(tty, O_RDWR);
|
||||
if (fd < 0)
|
||||
err(1, "Failed opening %s", tty);
|
||||
|
||||
dup2(fd, STDIN_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
|
||||
if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0)
|
||||
warn("Failed TIOCSCTTY");
|
||||
|
||||
/*
|
||||
* Don't let QUIT dump core.
|
||||
*/
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sa.sa_handler = _exit;
|
||||
sigaction(SIGQUIT, &sa, NULL);
|
||||
|
||||
stty(fd, speed);
|
||||
close(fd);
|
||||
utmp_set_login(tty, NULL);
|
||||
|
||||
if (!user)
|
||||
do_getty(tty, name, sizeof(name));
|
||||
else
|
||||
strlcpy(name, user, sizeof(name));
|
||||
|
||||
return do_login(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+326
@@ -0,0 +1,326 @@
|
||||
/* Misc. utility functions for finit and its plugins
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2015 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 <ctype.h> /* isblank() */
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#include <limits.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <lite/lite.h>
|
||||
#include <lite/conio.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "private.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
int _slup = 0; /* INTERNAL: Is syslog up yet? */
|
||||
|
||||
char *strip_line(char *line)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
/* Trim leading whitespace */
|
||||
while (*line && isblank(*line))
|
||||
line++;
|
||||
|
||||
/* Strip any comment at end of line */
|
||||
ptr = line;
|
||||
while (*ptr && *ptr != '#')
|
||||
ptr++;
|
||||
*ptr = 0;
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
void runlevel_set(int pre, int now)
|
||||
{
|
||||
utmp_set_runlevel(pre, now);
|
||||
}
|
||||
|
||||
/**
|
||||
* runlevel_string - Convert a bit encoded runlevel to .conf syntax
|
||||
* @levels: Bit encoded runlevels
|
||||
*
|
||||
* Returns:
|
||||
* Pointer to string on the form [2345]
|
||||
*/
|
||||
char *runlevel_string(int levels)
|
||||
{
|
||||
int i, pos = 1;
|
||||
static char lvl[20];
|
||||
|
||||
memset(lvl, 0, sizeof(lvl));
|
||||
lvl[0] = '[';
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (ISSET(levels, i)) {
|
||||
if (runlevel == i)
|
||||
pos = strlcat(lvl, "\e[1m", sizeof(lvl));
|
||||
|
||||
if (i == 0)
|
||||
lvl[pos++] = 'S';
|
||||
else
|
||||
lvl[pos++] = '0' + i;
|
||||
|
||||
if (runlevel == i)
|
||||
pos = strlcat(lvl, "\e[0m", sizeof(lvl));
|
||||
}
|
||||
}
|
||||
|
||||
lvl[pos++] = ']';
|
||||
lvl[pos] = 0;
|
||||
|
||||
return lvl;
|
||||
}
|
||||
|
||||
static int print_timestamp(void)
|
||||
{
|
||||
#if defined(CONFIG_PRINTK_TIME)
|
||||
FILE * uptimefile;
|
||||
float num1, num2;
|
||||
char uptime_str[30];
|
||||
|
||||
if((uptimefile = fopen("/proc/uptime", "r")) == NULL)
|
||||
return 1;
|
||||
|
||||
fgets(uptime_str, 20, uptimefile);
|
||||
fclose(uptimefile);
|
||||
|
||||
sscanf(uptime_str, "%f %f", &num1, &num2);
|
||||
sprintf(uptime_str, "[ %.6f ]", num1);
|
||||
|
||||
write(STDERR_FILENO, uptime_str, strlen(uptime_str));
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
void print(int action, const char *fmt, ...)
|
||||
{
|
||||
char buf[80];
|
||||
size_t len;
|
||||
va_list ap;
|
||||
const char success[] = " \e[1m[ OK ]\e[0m\n";
|
||||
const char failure[] = " \e[7m[FAIL]\e[0m\n";
|
||||
const char warning[] = " \e[7m[WARN]\e[0m\n";
|
||||
const char pending[] = " \e[1m[ \\/ ]\e[0m\n";
|
||||
const char dots[] = " .....................................................................";
|
||||
|
||||
if (silent)
|
||||
return;
|
||||
|
||||
if (fmt) {
|
||||
va_start(ap, fmt);
|
||||
len = vsnprintf(buf, sizeof(buf), fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
delline();
|
||||
print_timestamp();
|
||||
|
||||
write(STDERR_FILENO, "\r", 1);
|
||||
write(STDERR_FILENO, buf, len);
|
||||
write(STDERR_FILENO, dots, 60 - len); /* pad with dots. */
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case -1:
|
||||
break;
|
||||
|
||||
case 0:
|
||||
write(STDERR_FILENO, success, sizeof(success));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
write(STDERR_FILENO, failure, sizeof(failure));
|
||||
break;
|
||||
|
||||
case 2:
|
||||
write(STDERR_FILENO, warning, sizeof(warning));
|
||||
break;
|
||||
|
||||
default:
|
||||
write(STDERR_FILENO, pending, sizeof(pending));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void print_desc(char *action, char *desc)
|
||||
{
|
||||
print(-1, "%s%s", action ?: "", desc ?: "");
|
||||
}
|
||||
|
||||
int print_result(int fail)
|
||||
{
|
||||
print(!!fail, NULL);
|
||||
return fail;
|
||||
}
|
||||
|
||||
/*
|
||||
* Log to /dev/kmsg until syslogd has started, then openlog()
|
||||
* and continue logging as a regular daemon.
|
||||
*/
|
||||
void logit(int prio, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (!_slup && !fexist("/dev/log")) {
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/dev/kmsg", "w");
|
||||
if (fp) {
|
||||
if (debug)
|
||||
prio = LOG_ERR;
|
||||
|
||||
fprintf(fp, "<%d>finit[1]:", LOG_DAEMON | prio);
|
||||
vfprintf(fp, fmt, ap);
|
||||
fclose(fp);
|
||||
} else {
|
||||
fprintf(stderr, "Failed opening /dev/kmsg for appending ...\n");
|
||||
if (debug || prio <= LOG_ERR)
|
||||
vfprintf(stderr, fmt, ap);
|
||||
}
|
||||
va_end(ap);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_slup) {
|
||||
openlog("finit", LOG_PID, LOG_DAEMON);
|
||||
_slup = 1;
|
||||
}
|
||||
|
||||
vsyslog(prio, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
int getuser(char *username, char **home)
|
||||
{
|
||||
#ifdef ENABLE_STATIC
|
||||
*home = "/"; /* XXX: Fixme */
|
||||
return fgetint("/etc/passwd", "x:\n", username);
|
||||
#else
|
||||
struct passwd *usr;
|
||||
|
||||
if (!username || (usr = getpwnam(username)) == NULL)
|
||||
return -1;
|
||||
|
||||
*home = usr->pw_dir;
|
||||
return usr->pw_uid;
|
||||
#endif
|
||||
}
|
||||
|
||||
int getgroup(char *group)
|
||||
{
|
||||
#ifdef ENABLE_STATIC
|
||||
return fgetint("/etc/group", "x:\n", group);
|
||||
#else
|
||||
struct group *grp;
|
||||
|
||||
if ((grp = getgrnam(group)) == NULL)
|
||||
return -1;
|
||||
|
||||
return grp->gr_gid;
|
||||
#endif
|
||||
}
|
||||
|
||||
void set_hostname(char **hostname)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
fp = fopen("/etc/hostname", "r");
|
||||
if (fp) {
|
||||
struct stat st;
|
||||
|
||||
if (fstat(fileno(fp), &st)) {
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
*hostname = realloc(*hostname, st.st_size);
|
||||
if (!*hostname) {
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
|
||||
fgets(*hostname, st.st_size, fp);
|
||||
chomp(*hostname);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (*hostname)
|
||||
sethostname(*hostname, strlen(*hostname));
|
||||
}
|
||||
|
||||
#ifndef HAVE_GETFSENT
|
||||
static lfile_t *fstab = NULL;
|
||||
|
||||
int setfsent(void)
|
||||
{
|
||||
if (fstab)
|
||||
lfclose(fstab);
|
||||
|
||||
fstab = lfopen("/etc/fstab", " \t\n");
|
||||
if (!fstab)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct fstab *getfsent(void)
|
||||
{
|
||||
static struct fstab fs;
|
||||
|
||||
fs.fs_spec = lftok(fstab);
|
||||
if (fs.fs_spec == NULL)
|
||||
return NULL;
|
||||
|
||||
fs.fs_file = lftok(fstab);
|
||||
fs.fs_vfstype = lftok(fstab);
|
||||
fs.fs_mntops = lftok(fstab);
|
||||
fs.fs_type = "rw";
|
||||
fs.fs_freq = atoi(lftok(fstab) ?: "0");
|
||||
fs.fs_passno = atoi(lftok(fstab) ?: "0");
|
||||
|
||||
return &fs;
|
||||
}
|
||||
|
||||
void endfsent(void)
|
||||
{
|
||||
if (fstab)
|
||||
lfclose(fstab);
|
||||
|
||||
fstab = NULL;
|
||||
}
|
||||
#endif /* HAVE_GETFSENT */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,99 @@
|
||||
/* Misc. utility functions and C-library extensions for finit and its plugins
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2015 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_HELPERS_H_
|
||||
#define FINIT_HELPERS_H_
|
||||
|
||||
#include <err.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h> /* strerror() */
|
||||
#include <syslog.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/*
|
||||
* Error and debug messages
|
||||
*
|
||||
* Use of logit() is preferred, but these are guaranteed to display on
|
||||
* /dev/console as well. Useful for critical errors and early debug.
|
||||
*/
|
||||
#define _l(prio, fmt, args...) do { if (_slup) warnx(fmt, ##args); logit(prio, fmt "\n", ##args); } while (0)
|
||||
#define _m(prio, fmt, args...) do { _l(prio, "%s() - " fmt, __func__, ##args); } while (0)
|
||||
#define _d(fmt, args...) do { if (debug) { _m(LOG_DEBUG, fmt, ##args); } } while (0)
|
||||
#define _w(fmt, args...) do { _m(LOG_WARNING, fmt, ##args); } while (0)
|
||||
#define _e(fmt, args...) do { _m(LOG_ERR, fmt, ##args); } while (0)
|
||||
#define _pe(fmt, args...) do { _m(LOG_ERR, fmt ". Error %d: %s", ##args, errno, strerror(errno)); } while (0)
|
||||
|
||||
extern int _slup; /* INTERNAL: Is syslog up yet? */
|
||||
|
||||
char *strip_line (char *line);
|
||||
|
||||
void runlevel_set (int pre, int now);
|
||||
char *runlevel_string (int levels);
|
||||
|
||||
int pid_alive (pid_t pid);
|
||||
char *pid_get_name (pid_t pid, char *name, size_t len);
|
||||
|
||||
void procname_set (char *name, char *args[]);
|
||||
|
||||
void print (int action, const char *fmt, ...);
|
||||
void print_desc (char *action, char *desc);
|
||||
int print_result (int fail);
|
||||
|
||||
void logit (int prio, const char *fmt, ...);
|
||||
|
||||
int start_process (char *cmd, char *args[], int console);
|
||||
int getuser (char *username, char **home);
|
||||
int getgroup (char *group);
|
||||
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 *tty, char *speed, char *term, int console);
|
||||
int run_parts (char *dir, char *cmd);
|
||||
|
||||
#ifndef HAVE_GETFSENT
|
||||
struct fstab {
|
||||
char *fs_spec; /* block device name */
|
||||
char *fs_file; /* mount point */
|
||||
char *fs_vfstype; /* file-system type */
|
||||
char *fs_mntops; /* mount options */
|
||||
const char *fs_type; /* rw/rq/ro/sw/xx option */
|
||||
int fs_freq; /* dump frequency, in days */
|
||||
int fs_passno; /* pass number on parallel dump */
|
||||
};
|
||||
|
||||
int setfsent(void);
|
||||
void endfsent(void);
|
||||
struct fstab *getfsent(void);
|
||||
#endif /* HAVE_GETFSENT */
|
||||
|
||||
#endif /* FINIT_HELPERS_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+735
@@ -0,0 +1,735 @@
|
||||
/* Classic inetd services launcher for Finit
|
||||
*
|
||||
* Copyright (c) 2015 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 <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <uev/uev.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "inetd.h"
|
||||
#include "helpers.h"
|
||||
#include "private.h"
|
||||
#include "service.h"
|
||||
|
||||
#define ENABLE_SOCKOPT(sd, level, opt) \
|
||||
do { \
|
||||
int val = 1; \
|
||||
if (setsockopt(sd, level, opt, &val, sizeof(val)) < 0) \
|
||||
logit(LOG_CRIT, "Failed enabling %s on %s service", \
|
||||
#opt, inetd->name); \
|
||||
} while (0);
|
||||
|
||||
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
|
||||
static int inetd_dgram_peek(int sd, char *ifname)
|
||||
{
|
||||
char cmbuf[0x100];
|
||||
struct msghdr msgh;
|
||||
struct cmsghdr *cmsg;
|
||||
|
||||
memset(&msgh, 0, sizeof(msgh));
|
||||
msgh.msg_control = cmbuf;
|
||||
msgh.msg_controllen = sizeof(cmbuf);
|
||||
|
||||
if (recvmsg(sd, &msgh, MSG_PEEK) < 0)
|
||||
return -1;
|
||||
|
||||
for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg; cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
|
||||
struct in_pktinfo *ipi = (struct in_pktinfo *)CMSG_DATA(cmsg);
|
||||
|
||||
if (cmsg->cmsg_level != SOL_IP || cmsg->cmsg_type != IP_PKTINFO)
|
||||
continue;
|
||||
|
||||
if_indextoname(ipi->ipi_ifindex, ifname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
|
||||
static int inetd_stream_peek(int sd, char *ifname)
|
||||
{
|
||||
struct ifaddrs *ifaddr, *ifa;
|
||||
struct sockaddr_in sin;
|
||||
socklen_t len = sizeof(sin);
|
||||
|
||||
if (-1 == getsockname(sd, (struct sockaddr *)&sin, &len))
|
||||
return -1;
|
||||
|
||||
if (-1 == getifaddrs(&ifaddr))
|
||||
return -1;
|
||||
|
||||
for (ifa = ifaddr; ifa; ifa = ifa->ifa_next) {
|
||||
size_t len = sizeof(struct in_addr);
|
||||
struct sockaddr_in *iin;
|
||||
|
||||
if (!ifa->ifa_addr)
|
||||
continue;
|
||||
|
||||
if (ifa->ifa_addr->sa_family != AF_INET)
|
||||
continue;
|
||||
|
||||
iin = (struct sockaddr_in *)ifa->ifa_addr;
|
||||
if (!memcmp(&sin.sin_addr, &iin->sin_addr, len)) {
|
||||
strncpy(ifname, ifa->ifa_name, IF_NAMESIZE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(ifaddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_stdin(svc_t *svc)
|
||||
{
|
||||
int stdin = svc->inetd.watcher.fd;
|
||||
char ifname[IF_NAMESIZE] = "UNKNOWN";
|
||||
|
||||
if (svc->inetd.type == SOCK_STREAM) {
|
||||
/* Open new client socket from server socket */
|
||||
stdin = accept(stdin, NULL, NULL);
|
||||
if (stdin < 0) {
|
||||
logit(LOG_CRIT, "Failed accepting inetd service %d/tcp", svc->inetd.port);
|
||||
return -1;
|
||||
}
|
||||
|
||||
_d("New client socket %d accepted for inetd service %d/tcp", stdin, svc->inetd.port);
|
||||
|
||||
inetd_stream_peek(stdin, ifname);
|
||||
} else { /* SOCK_DGRAM */
|
||||
inetd_dgram_peek(stdin, ifname);
|
||||
}
|
||||
|
||||
if (!inetd_is_allowed(&svc->inetd, ifname)) {
|
||||
logit(LOG_INFO, "Service %s on %s:%d is not allowed", svc->inetd.name, ifname, svc->inetd.port);
|
||||
if (svc->inetd.type == SOCK_STREAM)
|
||||
close(stdin);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return stdin;
|
||||
}
|
||||
|
||||
/* Socket callback, looks up correct svc and starts it as an inetd service */
|
||||
static void socket_cb(uev_t *UNUSED(w), void *arg, int UNUSED(events))
|
||||
{
|
||||
svc_t *svc = (svc_t *)arg, *task;
|
||||
int stdin;
|
||||
|
||||
_d("Got event on %s socket ...", svc->cmd);
|
||||
stdin = get_stdin(svc);
|
||||
if (stdin < 0) {
|
||||
logit(LOG_CRIT, "%s: Unable to accept incoming connection", svc->cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure to disable O_NONBLOCK on the descriptor before
|
||||
* passing it to the inetd service, that's what is expected.
|
||||
*/
|
||||
if (fcntl(stdin, F_SETFL, fcntl(stdin, F_GETFL, 0) & ~O_NONBLOCK) < 0) {
|
||||
logit(LOG_CRIT, "Failed disabling non-blocking on %s socket", svc->cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
task = svc_new(svc->cmd, svc->inetd.next_id++, SVC_TYPE_INETD_CONN);
|
||||
if (!task) {
|
||||
logit(LOG_CRIT, "%s: Unable to allocate service for inetd client",
|
||||
svc->cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!svc->inetd.forking) {
|
||||
svc_busy(svc);
|
||||
service_step(svc);
|
||||
}
|
||||
|
||||
/* Copy inherited attributes from inetd service's svc */
|
||||
task->runlevels = svc->runlevels;
|
||||
|
||||
/* Only copy the most relevant parts of inetd, in particular we
|
||||
* must *not* copy the watcher data to the clone! */
|
||||
task->inetd.svc = svc;
|
||||
task->inetd.cmd = svc->inetd.cmd;
|
||||
task->inetd.type = svc->inetd.type;
|
||||
|
||||
memcpy(task->cond, svc->cond, sizeof(task->cond));
|
||||
memcpy(task->username, svc->username, sizeof(task->username));
|
||||
memcpy(task->group, svc->group, sizeof(task->group));
|
||||
memcpy(task->args, svc->args, sizeof(task->args));
|
||||
snprintf(task->desc, sizeof(task->desc), "%s connection", svc->desc);
|
||||
|
||||
task->stdin_fd = stdin;
|
||||
service_step(task);
|
||||
}
|
||||
|
||||
/*
|
||||
* Refuse service if the request specifies a reply port corresponding to any internal service.
|
||||
* This is done as a defense against looping attacks; the remote IP address is logged.
|
||||
* http://www.freebsd.org/cgi/man.cgi?inetd(8)
|
||||
* https://svnweb.freebsd.org/base/head/usr.sbin/inetd/inetd.c?revision=298909&view=markup#l2094
|
||||
*/
|
||||
int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name)
|
||||
{
|
||||
svc_t *svc;
|
||||
char pname[NI_MAXHOST];
|
||||
|
||||
for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) {
|
||||
inetd_t *i = &svc->inetd;
|
||||
|
||||
if (!i->builtin || i->type != SOCK_DGRAM)
|
||||
continue;
|
||||
|
||||
if (((const struct sockaddr_in *)sa)->sin_port == i->port) {
|
||||
getnameinfo(sa, len, pname, sizeof(pname), NULL, 0, NI_NUMERICHOST);
|
||||
logit(LOG_WARNING, "%s/%s:%s/%s loop request REFUSED from %s", i->name, "UDP", name, "UDP", pname);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Launch Inet socket for service.
|
||||
* TODO: Add filtering ALLOW/DENY per interface.
|
||||
*/
|
||||
static int spawn_socket(inetd_t *inetd)
|
||||
{
|
||||
int sd;
|
||||
socklen_t len = sizeof(struct sockaddr);
|
||||
struct sockaddr_in s;
|
||||
|
||||
if (!inetd->type) {
|
||||
logit(LOG_CRIT, "Invalid inetd service %s, skipping ...", inetd->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
_d("Spawning server socket for inetd %s, type %s ...", inetd->name, inetd->type == SOCK_STREAM ? "stream" : "dgram");
|
||||
sd = socket(AF_INET, inetd->type | SOCK_NONBLOCK | SOCK_CLOEXEC, inetd->proto);
|
||||
if (-1 == sd) {
|
||||
logit(LOG_CRIT, "Failed opening inetd socket type %d proto %d", inetd->type, inetd->proto);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ENABLE_SOCKOPT(sd, SOL_SOCKET, SO_REUSEADDR);
|
||||
#ifdef SO_REUSEPORT
|
||||
ENABLE_SOCKOPT(sd, SOL_SOCKET, SO_REUSEPORT);
|
||||
#endif
|
||||
|
||||
memset(&s, 0, sizeof(s));
|
||||
s.sin_family = AF_INET;
|
||||
s.sin_addr.s_addr = INADDR_ANY;
|
||||
s.sin_port = htons(inetd->port);
|
||||
if (bind(sd, (struct sockaddr *)&s, len) < 0) {
|
||||
logit(LOG_CRIT, "Failed binding to port %d, maybe another %s server is already running?",
|
||||
inetd->port, inetd->name);
|
||||
close(sd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
if (inetd->port) {
|
||||
if (inetd->type == SOCK_STREAM) {
|
||||
if (-1 == listen(sd, 10)) {
|
||||
logit(LOG_CRIT, "Failed listening to inetd service %s", inetd->name);
|
||||
close(sd);
|
||||
return -errno;
|
||||
}
|
||||
} else { /* SOCK_DGRAM */
|
||||
/* Set extra sockopt to get ifindex from inbound packets */
|
||||
ENABLE_SOCKOPT(sd, SOL_IP, IP_PKTINFO);
|
||||
}
|
||||
}
|
||||
|
||||
if (uev_io_init(ctx, &inetd->watcher, socket_cb, inetd->svc, sd, UEV_READ)) {
|
||||
logit(LOG_CRIT, "Failed setting up inetd watcher for %s", inetd->name);
|
||||
close(sd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inetd_start(inetd_t *inetd)
|
||||
{
|
||||
int sd;
|
||||
char buf[BUFSIZ];
|
||||
ssize_t len;
|
||||
|
||||
sd = inetd->watcher.fd;
|
||||
if (sd == -1)
|
||||
return spawn_socket(inetd);
|
||||
|
||||
/* Read anything lingering, or clean up socket after failure */
|
||||
len = recv(sd, buf, sizeof(buf), MSG_DONTWAIT);
|
||||
if (len > 0)
|
||||
_d("Read %zd lingering bytes from socket before (re)starting %s ...", len, inetd->svc->cmd);
|
||||
|
||||
/* Restore O_NONBLOCK for socket */
|
||||
if (fcntl(sd, F_SETFL, fcntl(sd, F_GETFL, 0) | O_NONBLOCK)) {
|
||||
logit(LOG_CRIT, "Cannot safely (re)start %s inetd service", inetd->svc->cmd);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
_d("Re-starting %s socket watcher ...", inetd->svc->cmd);
|
||||
uev_io_start(&inetd->watcher);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void inetd_stop(inetd_t *inetd)
|
||||
{
|
||||
if (inetd->watcher.fd != -1) {
|
||||
_d("Stopping %s socket watcher ...", inetd->svc->cmd);
|
||||
uev_io_stop(&inetd->watcher);
|
||||
|
||||
/* For dgram inetd services we block the parent SVC
|
||||
* and halt the watcher, so don't close the socket! */
|
||||
if (!svc_is_busy(inetd->svc)) {
|
||||
_d("Shutting down inet socket %d ...", inetd->watcher.fd);
|
||||
if (inetd->type == SOCK_STREAM)
|
||||
shutdown(inetd->watcher.fd, SHUT_RDWR);
|
||||
close(inetd->watcher.fd);
|
||||
inetd->watcher.fd = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static struct servent *fallback_service(char *service, char *proto)
|
||||
{
|
||||
int service_num;
|
||||
static struct servent lfs;
|
||||
|
||||
service_num = fgetint("/etc/services", " \n\t", service);
|
||||
if (service_num > 0) {
|
||||
lfs.s_name = service;
|
||||
lfs.s_port = htons(service_num);
|
||||
lfs.s_proto = NULL;
|
||||
if (!strcmp("tcp", proto) || !strcmp("udp", proto))
|
||||
lfs.s_proto = proto;
|
||||
|
||||
return &lfs;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct servent *getent_service(char *service, char *proto)
|
||||
{
|
||||
struct servent *ent;
|
||||
|
||||
#ifdef ENABLE_STATIC
|
||||
ent = fallback_service(service, proto);
|
||||
#else
|
||||
ent = getservbyname(service, proto);
|
||||
if (!ent)
|
||||
ent = fallback_service(service, proto);
|
||||
#endif
|
||||
return ent;
|
||||
}
|
||||
|
||||
static struct protoent *fallback_proto(char *proto)
|
||||
{
|
||||
int proto_num;
|
||||
static struct protoent lfp;
|
||||
|
||||
proto_num = fgetint("/etc/protocols", " \n\t", proto);
|
||||
if (proto_num > 0) {
|
||||
lfp.p_name = proto;
|
||||
lfp.p_proto = proto_num;
|
||||
|
||||
return &lfp;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct protoent *getent_proto(char *proto)
|
||||
{
|
||||
struct protoent *ent;
|
||||
|
||||
#ifdef ENABLE_STATIC
|
||||
ent = fallback_proto(proto);
|
||||
#else
|
||||
ent = getprotobyname(proto);
|
||||
if (!ent)
|
||||
ent = fallback_proto(proto);
|
||||
#endif
|
||||
return ent;
|
||||
}
|
||||
|
||||
static int getent(char *service, char *proto, struct servent **sv, struct protoent **pv)
|
||||
{
|
||||
if (!fexist("/etc/services") || !fexist("/etc/protocols")) {
|
||||
_w("Cannot register inetd %s/%s, system missing /etc/services or /etc/protocols", service, proto);
|
||||
return errno = ECANCELED;
|
||||
}
|
||||
|
||||
*sv = getent_service(service, proto);
|
||||
if (!*sv) {
|
||||
const char *errstr;
|
||||
static struct servent s;
|
||||
|
||||
s.s_name = service;
|
||||
s.s_port = strtonum(service, 1, UINT16_MAX, &errstr);
|
||||
s.s_proto = NULL;
|
||||
if (!strcmp("tcp", proto) || !strcmp("udp", proto))
|
||||
s.s_proto = proto;
|
||||
|
||||
if (errstr || !s.s_proto) {
|
||||
_e("Invalid/unknown inetd service, cannot create custom entry");
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
_d("Creating cutom inetd service %s/%s", service, proto);
|
||||
s.s_port = htons(s.s_port);
|
||||
*sv = &s;
|
||||
}
|
||||
|
||||
if (pv && (*sv)->s_proto) {
|
||||
*pv = getent_proto((*sv)->s_proto);
|
||||
if (!*pv) {
|
||||
_e("Cannot find proto %s, skipping ...", (*sv)->s_proto);
|
||||
return errno = EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find exact match.
|
||||
*/
|
||||
static inetd_filter_t *find_filter(inetd_t *inetd, char *ifname)
|
||||
{
|
||||
inetd_filter_t *filter;
|
||||
|
||||
if (!ifname)
|
||||
ifname = "*";
|
||||
|
||||
TAILQ_FOREACH(filter, &inetd->filters, link) {
|
||||
_d("Checking filters for %s: '%s' vs '%s' (exact match) ...",
|
||||
inetd->name, filter->ifname, ifname);
|
||||
|
||||
if (!strcmp(filter->ifname, ifname))
|
||||
return filter;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* First try exact match, then fall back to any match.
|
||||
*/
|
||||
inetd_filter_t *inetd_filter_match(inetd_t *inetd, char *ifname)
|
||||
{
|
||||
inetd_filter_t *filter = find_filter(inetd, ifname);
|
||||
|
||||
if (filter)
|
||||
return filter;
|
||||
|
||||
TAILQ_FOREACH(filter, &inetd->filters, link) {
|
||||
_d("Checking filters for %s: '%s' vs '%s' (any match) ...",
|
||||
inetd->name, filter->ifname, ifname);
|
||||
|
||||
if (!strcmp(filter->ifname, "*"))
|
||||
return filter;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int inetd_flush(inetd_t *inetd)
|
||||
{
|
||||
inetd_filter_t *filter, *next;
|
||||
|
||||
TAILQ_FOREACH_SAFE(filter, &inetd->filters, link, next) {
|
||||
TAILQ_REMOVE(&inetd->filters, filter, link);
|
||||
free(filter);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Poor man's tcpwrappers filtering */
|
||||
int inetd_allow(inetd_t *inetd, char *ifname)
|
||||
{
|
||||
inetd_filter_t *filter;
|
||||
|
||||
if (!inetd)
|
||||
return errno = EINVAL;
|
||||
|
||||
if (!ifname)
|
||||
ifname = "*";
|
||||
|
||||
filter = inetd_filter_match(inetd, ifname);
|
||||
if (filter) {
|
||||
_d("Filter %s for inetd %s already exists, skipping ...", ifname, inetd->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_d("Allow iface %s for service %s (port %d)", ifname, inetd->name, inetd->port);
|
||||
filter = calloc(1, sizeof(*filter));
|
||||
if (!filter) {
|
||||
_e("Out of memory, cannot add filter to service %s", inetd->name);
|
||||
return errno = ENOMEM;
|
||||
}
|
||||
|
||||
filter->deny = 0;
|
||||
strlcpy(filter->ifname, ifname, sizeof(filter->ifname));
|
||||
TAILQ_INSERT_TAIL(&inetd->filters, filter, link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inetd_deny(inetd_t *inetd, char *ifname)
|
||||
{
|
||||
inetd_filter_t *filter;
|
||||
|
||||
if (!inetd)
|
||||
return errno = EINVAL;
|
||||
|
||||
if (!ifname)
|
||||
ifname = "*";
|
||||
|
||||
filter = find_filter(inetd, ifname);
|
||||
if (filter) {
|
||||
_d("%s filter %s for inetd %s already exists, cannot set deny filter for same, skipping ...",
|
||||
filter->deny ? "Deny" : "Allow", ifname, inetd->name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
_d("Deny iface %s for service %s (port %d)", ifname, inetd->name, inetd->port);
|
||||
filter = calloc(1, sizeof(*filter));
|
||||
if (!filter) {
|
||||
_e("Out of memory, cannot add filter to service %s", inetd->name);
|
||||
return errno = ENOMEM;
|
||||
}
|
||||
|
||||
filter->deny = 1;
|
||||
strlcpy(filter->ifname, ifname, sizeof(filter->ifname));
|
||||
TAILQ_INSERT_TAIL(&inetd->filters, filter, link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inetd_is_allowed(inetd_t *inetd, char *ifname)
|
||||
{
|
||||
inetd_filter_t *filter;
|
||||
|
||||
if (!inetd) {
|
||||
errno = EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
filter = inetd_filter_match(inetd, ifname);
|
||||
if (filter) {
|
||||
_d("Found matching filter for %s, deny: %d ... ", inetd->name, filter->deny);
|
||||
return !filter->deny;
|
||||
}
|
||||
|
||||
_d("No matching filter for %s ... ", inetd->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inetd_match(inetd_t *inetd, char *service, char *proto)
|
||||
{
|
||||
struct servent *sv = NULL;
|
||||
struct protoent *pv = NULL;
|
||||
|
||||
if (!inetd || !service || !proto)
|
||||
return errno = EINVAL;
|
||||
|
||||
if (strncmp(inetd->name, service, sizeof(inetd->name)))
|
||||
return 0;
|
||||
|
||||
if (getent(service, proto, &sv, &pv))
|
||||
return 0;
|
||||
|
||||
if (inetd->proto == ntohs(pv->p_proto) &&
|
||||
inetd->port == ntohs(sv->s_port))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compose presentable string of inetd filters: !eth0,eth1,eth2,!eth3 */
|
||||
int inetd_filter_str(inetd_t *inetd, char *str, size_t len)
|
||||
{
|
||||
int prev = 0;
|
||||
char buf[42];
|
||||
inetd_filter_t *filter;
|
||||
|
||||
if (!inetd || !str || len <= 0) {
|
||||
_e("Dafuq?");
|
||||
return 1;
|
||||
}
|
||||
|
||||
snprintf(str, len, "%s allow %s ", inetd->name,
|
||||
inetd->type == SOCK_DGRAM ? "UDP" : "TCP");
|
||||
TAILQ_FOREACH(filter, &inetd->filters, link) {
|
||||
char ifname[IFNAMSIZ];
|
||||
|
||||
if (filter->deny)
|
||||
continue;
|
||||
|
||||
if (!strlen(filter->ifname))
|
||||
snprintf(ifname, sizeof(ifname), "*");
|
||||
else
|
||||
strlcpy(ifname, filter->ifname, sizeof(ifname));
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s%s:%d", prev ? "," : "",
|
||||
ifname, inetd->port);
|
||||
prev = 1;
|
||||
strlcat(str, buf, len);
|
||||
}
|
||||
|
||||
prev = 0;
|
||||
TAILQ_FOREACH(filter, &inetd->filters, link) {
|
||||
char ifname[IFNAMSIZ];
|
||||
|
||||
if (!filter->deny)
|
||||
continue;
|
||||
if (!prev) {
|
||||
snprintf(buf, sizeof(buf), " deny ");
|
||||
strlcat(str, buf, len);
|
||||
}
|
||||
|
||||
if (!strlen(filter->ifname))
|
||||
snprintf(ifname, sizeof(ifname), "*");
|
||||
else
|
||||
strlcpy(ifname, filter->ifname, sizeof(ifname));
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s%s", prev ? "," : "", ifname);
|
||||
prev = 1;
|
||||
strlcat(str, buf, len);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
svc_t *inetd_find_svc(char *path, char *service, char *proto)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_inetd_iterator(1); svc; svc = svc_inetd_iterator(0)) {
|
||||
if (strncmp(path, svc->cmd, strlen(svc->cmd)))
|
||||
continue;
|
||||
|
||||
if (inetd_match(&svc->inetd, service, proto)) {
|
||||
_d("Found a matching inetd svc for %s %s %s", path, service, proto);
|
||||
return svc;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called to add a new, unique, inetd service. When an
|
||||
* ifname is given as argument this means *only* run service on this
|
||||
* interface. If a similar service runs on another port, this function
|
||||
* must add this ifname as "deny" to that other service.
|
||||
*
|
||||
* Example:
|
||||
* inetd ssh@eth0:222/tcp nowait [2345] /usr/sbin/sshd -i
|
||||
* inetd ssh/tcp nowait [2345] /usr/sbin/sshd -i
|
||||
*
|
||||
* In this example eth0:222 is very specific, so when ssh/tcp (default)
|
||||
* is added we must find the previous 'ssh' service and add its eth0 as
|
||||
* deny, so we don't accept port 22 session on eth0.
|
||||
*
|
||||
* In the reverse case, where the default (ssh/tcp) entry is listed
|
||||
* before the specific (eth0:222), the new inetd service must find the
|
||||
* (any!) previous rule and add its ifname to their deny list.
|
||||
*
|
||||
* If equivalent service exists already service_register() will instead call
|
||||
* inetd_allow().
|
||||
*/
|
||||
int inetd_new(inetd_t *inetd, char *name, char *service, char *proto, int forking, svc_t *svc)
|
||||
{
|
||||
int result;
|
||||
struct servent *sv = NULL;
|
||||
struct protoent *pv = NULL;
|
||||
|
||||
if (!inetd || !service || !proto)
|
||||
return errno = EINVAL;
|
||||
|
||||
result = getent(service, proto, &sv, &pv);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
/* Setup defaults */
|
||||
inetd->std = 1;
|
||||
inetd->port = ntohs(sv->s_port);
|
||||
inetd->proto = pv->p_proto;
|
||||
inetd->forking = !!forking;
|
||||
inetd->next_id = 2;
|
||||
if (!name)
|
||||
name = service;
|
||||
strlcpy(inetd->name, name, sizeof(inetd->name));
|
||||
TAILQ_INIT(&inetd->filters);
|
||||
|
||||
/* Naïve mapping tcp->stream, udp->dgram, other->dgram */
|
||||
if (!strcasecmp(sv->s_proto, "tcp"))
|
||||
inetd->type = SOCK_STREAM;
|
||||
else
|
||||
inetd->type = SOCK_DGRAM;
|
||||
|
||||
if (inetd->type == SOCK_DGRAM && inetd->forking) {
|
||||
logit(LOG_WARNING, "%s: 'nowait' is not applicable on UDP services, ignoring", svc->cmd);
|
||||
inetd->forking = 0;
|
||||
}
|
||||
|
||||
/* Reset descriptor, used internally */
|
||||
inetd->watcher.fd = -1;
|
||||
|
||||
/* Setup socket callback argument */
|
||||
inetd->svc = svc;
|
||||
|
||||
_d("New service %s (default port %d proto %s:%d)", name, inetd->port, sv->s_proto, pv->p_proto);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int inetd_del(inetd_t *inetd)
|
||||
{
|
||||
svc_unblock(inetd->svc);
|
||||
inetd_stop(inetd);
|
||||
|
||||
return inetd_flush(inetd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
/* Classic inetd services launcher for Finit
|
||||
*
|
||||
* Copyright (c) 2015 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_INETD_H_
|
||||
#define FINIT_INETD_H_
|
||||
|
||||
#include <netdb.h>
|
||||
#include <net/if.h>
|
||||
#include <uev/uev.h>
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
|
||||
typedef struct svc svc_t;
|
||||
|
||||
typedef struct inetd_filter {
|
||||
TAILQ_ENTRY(inetd_filter) link;
|
||||
int deny; /* 0:allow, 1:deny */
|
||||
char ifname[IFNAMSIZ]; /* E.g., eth0 */
|
||||
} inetd_filter_t;
|
||||
|
||||
typedef struct {
|
||||
uev_t watcher;
|
||||
svc_t *svc; /* svc_t pointer for the socket callback */
|
||||
|
||||
int type; /* Socket type: SOCK_STREAM/SOCK_DGRAM */
|
||||
int std; /* Standard proto/port from /etc/services */
|
||||
int proto;
|
||||
int port;
|
||||
int forking;
|
||||
int builtin; /* Set by built-in inetd services only */
|
||||
int next_id; /* Next child job's id */
|
||||
char name[10];
|
||||
int (*cmd)(int type); /* internal inetd service, like 'time' */
|
||||
|
||||
TAILQ_HEAD(, inetd_filter) filters;
|
||||
} inetd_t;
|
||||
|
||||
int inetd_check_loop(struct sockaddr *sa, socklen_t len, char *name);
|
||||
|
||||
int inetd_start (inetd_t *inetd);
|
||||
void inetd_stop (inetd_t *inetd);
|
||||
|
||||
int inetd_new (inetd_t *inetd, char *name, char *service, char *proto, int forking, svc_t *svc);
|
||||
int inetd_del (inetd_t *inetd);
|
||||
|
||||
svc_t *inetd_find_svc (char *path, char *service, char *proto);
|
||||
|
||||
int inetd_match (inetd_t *inetd, char *service, char *proto);
|
||||
int inetd_filter_str(inetd_t *inetd, char *str, size_t len);
|
||||
|
||||
int inetd_flush (inetd_t *inetd);
|
||||
int inetd_allow (inetd_t *inetd, char *ifname);
|
||||
int inetd_deny (inetd_t *inetd, char *ifname);
|
||||
int inetd_is_allowed(inetd_t *inetd, char *ifname);
|
||||
|
||||
#endif /* FINIT_INETD_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+582
@@ -0,0 +1,582 @@
|
||||
/* New client tool, complements old /dev/initctl API and telinit tool
|
||||
*
|
||||
* Copyright (c) 2015-2016 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 <err.h>
|
||||
#include <ctype.h>
|
||||
#include <getopt.h>
|
||||
#include <glob.h>
|
||||
#include <paths.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "helpers.h"
|
||||
#include "service.h"
|
||||
#include "util.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
#define _PATH_COND _PATH_VARRUN "finit/cond/"
|
||||
|
||||
typedef struct {
|
||||
char *cmd;
|
||||
int (*cb)(char *arg);
|
||||
} command_t;
|
||||
|
||||
int debug = 0;
|
||||
int verbose = 0;
|
||||
int silent = 1; /* For helpers.c */
|
||||
int runlevel = 0;
|
||||
|
||||
static int do_send(struct init_request *rq, ssize_t len)
|
||||
{
|
||||
int sd, result = 255;
|
||||
struct sockaddr_un sun = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
|
||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (-1 == sd)
|
||||
return -1;
|
||||
|
||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
|
||||
goto error;
|
||||
|
||||
if (write(sd, rq, len) != len)
|
||||
goto error;
|
||||
|
||||
if (read(sd, rq, len) != len)
|
||||
goto error;
|
||||
|
||||
result = 0;
|
||||
goto exit;
|
||||
error:
|
||||
perror("Failed communicating with finit");
|
||||
exit:
|
||||
close(sd);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int runlevel_get(void)
|
||||
{
|
||||
int sd, result = 255;
|
||||
struct init_request rq;
|
||||
struct sockaddr_un sun = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
|
||||
memset(&rq, 0, sizeof(rq));
|
||||
rq.cmd = INIT_CMD_GET_RUNLEVEL;
|
||||
rq.magic = INIT_MAGIC;
|
||||
|
||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (-1 == sd)
|
||||
return -1;
|
||||
|
||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
|
||||
goto error;
|
||||
|
||||
if (write(sd, &rq, sizeof(rq)) != sizeof(rq))
|
||||
goto error;
|
||||
|
||||
if (read(sd, &rq, sizeof(rq)) != sizeof(rq))
|
||||
goto error;
|
||||
|
||||
result = rq.runlevel;
|
||||
goto exit;
|
||||
error:
|
||||
perror("Failed communicating with finit");
|
||||
result = -1;
|
||||
exit:
|
||||
close(sd);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int toggle_debug(char *UNUSED(arg))
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_DEBUG,
|
||||
};
|
||||
|
||||
return do_send(&rq, sizeof(rq));
|
||||
}
|
||||
|
||||
static int do_log(char *svc)
|
||||
{
|
||||
char cmd[128];
|
||||
char *logfile = "/var/log/messages";
|
||||
|
||||
if (!svc || !svc[0])
|
||||
svc = "finit";
|
||||
|
||||
if (!fexist(logfile))
|
||||
logfile = "/var/log/syslog";
|
||||
|
||||
snprintf(cmd, sizeof(cmd), "cat %s | grep %s | tail -10", logfile, svc);
|
||||
|
||||
return system(cmd);
|
||||
}
|
||||
|
||||
static int do_runlevel(char *arg)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_RUNLVL,
|
||||
.runlevel = (int)arg[0],
|
||||
};
|
||||
|
||||
/* Not compatible with the SysV runlevel(8) command, it prints
|
||||
* "PREVLEVEL RUNLEVEL", we just print the current runlevel. */
|
||||
if (!rq.runlevel) {
|
||||
printf("%d\n", runlevel_get());
|
||||
return 0;
|
||||
}
|
||||
|
||||
return do_send(&rq, sizeof(rq));
|
||||
}
|
||||
|
||||
static int do_svc(int cmd, char *arg)
|
||||
{
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = cmd,
|
||||
};
|
||||
|
||||
strlcpy(rq.data, arg, sizeof(rq.data));
|
||||
|
||||
return do_send(&rq, sizeof(rq));
|
||||
}
|
||||
|
||||
static int do_emit (char *arg) { return do_svc(INIT_CMD_EMIT, arg); }
|
||||
static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD, arg); }
|
||||
|
||||
static int missing(char *job, int id)
|
||||
{
|
||||
char idstr[10] = "";
|
||||
|
||||
if (!job)
|
||||
job = "";
|
||||
if (id > 1)
|
||||
snprintf(idstr, sizeof(idstr), ":%d", id);
|
||||
|
||||
if (verbose)
|
||||
fprintf(stderr, "No such service %s%s\n", job, idstr);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is a wrapper for do_svc() that adds a simple sanity check of
|
||||
* the service(s) provided as argument. If a service does not exist
|
||||
* we make sure to return an error code.
|
||||
*/
|
||||
static int do_startstop(int cmd, char *arg)
|
||||
{
|
||||
int ret;
|
||||
char *svcs;
|
||||
|
||||
if (!arg) {
|
||||
fprintf(stderr, "Usage: initctl %s <JOB|NAME>[:ID]",
|
||||
cmd == INIT_CMD_START_SVC ? "start" :
|
||||
(cmd == INIT_CMD_STOP_SVC ? "stop" : "restart"));
|
||||
return 1;
|
||||
}
|
||||
|
||||
svcs = strdup(arg);
|
||||
if (!svcs) {
|
||||
perror("Failed validating service(s) name");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Include \0 in len, needed by parser */
|
||||
ret = svc_parse_jobstr(svcs, strlen(svcs) + 1, NULL, missing);
|
||||
free(svcs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return do_svc(cmd, arg);
|
||||
}
|
||||
|
||||
static int do_start (char *arg) { return do_startstop(INIT_CMD_START_SVC, arg); }
|
||||
static int do_stop (char *arg) { return do_startstop(INIT_CMD_STOP_SVC, arg); }
|
||||
static int do_restart(char *arg) { return do_startstop(INIT_CMD_RESTART_SVC, arg); }
|
||||
|
||||
static void show_cond_one(const char *_conds)
|
||||
{
|
||||
static char conds[MAX_ARG_LEN];
|
||||
char *cond;
|
||||
|
||||
strlcpy(conds, _conds, sizeof(conds));
|
||||
|
||||
putchar('<');
|
||||
|
||||
for (cond = strtok(conds, ","); cond; cond = strtok(NULL, ",")) {
|
||||
if (cond != conds)
|
||||
putchar(',');
|
||||
|
||||
switch (cond_get(cond)) {
|
||||
case COND_ON:
|
||||
printf("+%s", cond);
|
||||
break;
|
||||
|
||||
case COND_FLUX:
|
||||
printf("\e[1m~%s\e[0m", cond);
|
||||
break;
|
||||
|
||||
case COND_OFF:
|
||||
printf("\e[1m-%s\e[0m", cond);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
putchar('>');
|
||||
}
|
||||
|
||||
static int do_cond_magic(char op, char *cond)
|
||||
{
|
||||
cond[0] = op;
|
||||
return do_emit(cond);
|
||||
}
|
||||
|
||||
static int do_cond_set (char *cond) { return do_cond_magic('+', cond); }
|
||||
static int do_cond_clear(char *cond) { return do_cond_magic('-', cond); }
|
||||
|
||||
static int do_cond_show(char *UNUSED(arg))
|
||||
{
|
||||
svc_t *svc;
|
||||
enum cond_state cond;
|
||||
|
||||
if (verbose) {
|
||||
glob_t gl;
|
||||
|
||||
printf("Asserted conditions (taken from %s)\n", _PATH_COND);
|
||||
printf("===============================================================================\n");
|
||||
|
||||
if (!glob(_PATH_COND "*/*/*", 0, NULL, &gl)) {
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < gl.gl_pathc; i++) {
|
||||
char *cond, *name = gl.gl_pathv[i];
|
||||
struct stat st;
|
||||
|
||||
if (stat(name, &st) || S_ISDIR(st.st_mode))
|
||||
continue;
|
||||
|
||||
cond = name + strlen(_PATH_COND);
|
||||
printf("\t%s\n", cond);
|
||||
}
|
||||
globfree(&gl);
|
||||
}
|
||||
puts("");
|
||||
}
|
||||
|
||||
printf("PID Service Status Condition (+ on, ~ flux, - off)\n");
|
||||
printf("===============================================================================\n");
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (!svc->cond[0])
|
||||
continue;
|
||||
|
||||
cond = cond_get_agg(svc->cond);
|
||||
|
||||
printf("%-6d %-20.20s ", svc->pid, svc->cmd);
|
||||
|
||||
if (cond == COND_ON)
|
||||
printf("%-6.6s ", condstr(cond));
|
||||
else
|
||||
printf("\e[1m%-6.6s\e[0m ", condstr(cond));
|
||||
|
||||
show_cond_one(svc->cond);
|
||||
puts("");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_cond(char *cmd)
|
||||
{
|
||||
int c;
|
||||
char *arg;
|
||||
command_t command[] = {
|
||||
{ "set", do_cond_set },
|
||||
{ "clear", do_cond_clear },
|
||||
{ "show", do_cond_show },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
arg = strpbrk(cmd, " ");
|
||||
if (arg) {
|
||||
*arg = 0;
|
||||
|
||||
for (c = 0; command[c].cmd; c++) {
|
||||
if (!strcmp(command[c].cmd, cmd))
|
||||
return command[c].cb(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return do_cond_show(NULL);
|
||||
}
|
||||
|
||||
static int do_signal(int signo, const char *msg)
|
||||
{
|
||||
if (kill(1, signo))
|
||||
err(1, "Failed signalling init to %s", msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_halt (char *UNUSED(arg)) { return do_signal(SIGUSR1, "halt"); }
|
||||
static int do_poweroff(char *UNUSED(arg)) { return do_signal(SIGUSR2, "power off"); }
|
||||
static int do_reboot (char *UNUSED(arg)) { return do_signal(SIGTERM, "reboot"); }
|
||||
|
||||
static int do_utmp(char *UNUSED(cmd))
|
||||
{
|
||||
return utmp_show(_PATH_WTMP) ||
|
||||
utmp_show(_PATH_UTMP);
|
||||
}
|
||||
|
||||
static int show_version(char *UNUSED(arg))
|
||||
{
|
||||
puts("v" PACKAGE_VERSION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* In verbose mode we skip the header and each service description.
|
||||
* This in favor of having all info on one line so a machine can more
|
||||
* easily parse it. */
|
||||
static int show_status(char *arg)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
/* Fetch UTMP runlevel, needed for svc_status() call below */
|
||||
runlevel = runlevel_get();
|
||||
|
||||
if (arg && arg[0]) {
|
||||
int id = 1;
|
||||
char *ptr, *next;
|
||||
|
||||
ptr = strchr(arg, ':');
|
||||
if (ptr) {
|
||||
*ptr++ = 0;
|
||||
next = strchr(ptr, ' ');
|
||||
if (next)
|
||||
*next = 0;
|
||||
id = atonum(ptr);
|
||||
}
|
||||
|
||||
if (isdigit(arg[0]))
|
||||
svc = svc_find_by_jobid(atonum(arg), id);
|
||||
else
|
||||
svc = svc_find_by_nameid(arg, id);
|
||||
|
||||
if (!svc)
|
||||
return 1;
|
||||
|
||||
printf("%s\n", svc_status(svc));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!verbose) {
|
||||
printf("# Status PID Runlevels Service Description\n");
|
||||
printf("===============================================================================\n");
|
||||
}
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
char jobid[10], args[512] = "", *lvls;
|
||||
|
||||
if (svc_is_unique(svc))
|
||||
snprintf(jobid, sizeof(jobid), "%d", svc->job);
|
||||
else
|
||||
snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id);
|
||||
|
||||
printf("%-5s %7s ", jobid, svc_status(svc));
|
||||
if (svc_is_inetd(svc))
|
||||
printf("inetd ");
|
||||
else
|
||||
printf("%-6d ", svc->pid);
|
||||
|
||||
lvls = runlevel_string(svc->runlevels);
|
||||
if (strchr(lvls, '\e'))
|
||||
printf("%-18.18s ", lvls);
|
||||
else
|
||||
printf("%-10.10s ", lvls);
|
||||
|
||||
if (!verbose) {
|
||||
char *name = strrchr(svc->cmd, '/');
|
||||
|
||||
if (!name)
|
||||
name = svc->cmd;
|
||||
else
|
||||
name++;
|
||||
|
||||
printf("%-20.20s %-21.21s\n", name, svc->desc);
|
||||
continue;
|
||||
}
|
||||
|
||||
#ifdef INETD_ENABLED
|
||||
if (svc_is_inetd(svc)) {
|
||||
char *info;
|
||||
struct init_request rq = {
|
||||
.magic = INIT_MAGIC,
|
||||
.cmd = INIT_CMD_QUERY_INETD,
|
||||
};
|
||||
|
||||
snprintf(rq.data, sizeof(rq.data), "%s", jobid);
|
||||
if (do_send(&rq, sizeof(rq))) {
|
||||
snprintf(args, sizeof(args), "Unknown inetd");
|
||||
info = args;
|
||||
} else {
|
||||
info = rq.data;
|
||||
if (strcmp("internal", svc->cmd)) {
|
||||
char *ptr = strchr(info, ' ');
|
||||
if (ptr)
|
||||
info = ptr + 1;
|
||||
}
|
||||
}
|
||||
|
||||
printf("%s %s\n", svc->cmd, info);
|
||||
}
|
||||
else
|
||||
#endif /* INETD_ENABLED */
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < MAX_NUM_SVC_ARGS; i++) {
|
||||
strlcat(args, svc->args[i], sizeof(args));
|
||||
strlcat(args, " ", sizeof(args));
|
||||
}
|
||||
|
||||
printf("%s %s\n", svc->cmd, args);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int usage(int rc)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [OPTIONS] <COMMAND>\n\n"
|
||||
"Options:\n"
|
||||
" -d, --debug Debug initctl (client)\n"
|
||||
" -v, --verbose Verbose output\n"
|
||||
" -h, --help This help text\n\n"
|
||||
"Commands:\n"
|
||||
" debug Toggle Finit (daemon) debug\n"
|
||||
" help This help text\n"
|
||||
" log [NAME] Show ten last Finit, or NAME, messages from syslog\n"
|
||||
" reload Reload *.conf in /etc/finit.d and activate changes\n"
|
||||
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
|
||||
" status | show Show status of services\n"
|
||||
" cond set <COND> Set (assert) condition => +COND\n"
|
||||
" cond clear <COND> Clear (deassert) condition => -COND\n"
|
||||
" cond show Show condition status\n"
|
||||
" start <JOB|NAME>[:ID] Start service by job# or name, with optional ID\n"
|
||||
" stop <JOB|NAME>[:ID] Stop/Pause a running service by job# or name\n"
|
||||
" restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name\n"
|
||||
" reboot Reboot system\n"
|
||||
" halt Halt system\n"
|
||||
" poweroff Halt and power off system\n"
|
||||
" utmp show Raw dump of UTMP/WTMP db\n"
|
||||
" version Show Finit version\n\n", prognm);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int c;
|
||||
command_t command[] = {
|
||||
{ "debug", toggle_debug },
|
||||
{ "log", do_log },
|
||||
{ "reload", do_reload },
|
||||
{ "runlevel", do_runlevel },
|
||||
{ "status", show_status },
|
||||
{ "show", show_status }, /* Convenience alias */
|
||||
{ "cond", do_cond },
|
||||
{ "start", do_start },
|
||||
{ "stop", do_stop },
|
||||
{ "restart", do_restart },
|
||||
{ "reboot", do_reboot },
|
||||
{ "halt", do_halt },
|
||||
{ "poweroff", do_poweroff },
|
||||
{ "utmp", do_utmp },
|
||||
{ "version", show_version },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
struct option long_options[] = {
|
||||
{"help", 0, NULL, 'h'},
|
||||
{"debug", 0, NULL, 'd'},
|
||||
{"verbose", 0, NULL, 'v'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
progname(argv[0]);
|
||||
while ((c = getopt_long(argc, argv, "dh?v", long_options, NULL)) != EOF) {
|
||||
switch(c) {
|
||||
case 'h':
|
||||
case '?':
|
||||
return usage(0);
|
||||
|
||||
case 'd':
|
||||
debug = 1;
|
||||
break;
|
||||
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
silent = !verbose;
|
||||
|
||||
if (optind < argc) {
|
||||
char *cmd = argv[optind++];
|
||||
char arg[120] = "";
|
||||
|
||||
while (optind < argc) {
|
||||
strlcat(arg, argv[optind++], sizeof(arg));
|
||||
strlcat(arg, " ", sizeof(arg));
|
||||
}
|
||||
|
||||
for (c = 0; command[c].cmd; c++) {
|
||||
if (!strcmp(command[c].cmd, cmd))
|
||||
return command[c].cb(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return usage(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/* Handle MD arrays
|
||||
*
|
||||
* Copyright (c) 2016 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 <glob.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
|
||||
static glob_t *get_arrays(void)
|
||||
{
|
||||
static glob_t arrays;
|
||||
|
||||
glob("/sys/block/md*", 0, NULL, &arrays);
|
||||
if (arrays.gl_pathc > 0)
|
||||
return &arrays;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If system has an MD raid, we must tell it to stop before continuing
|
||||
* with the shutdown. Some controller cards, in particular the Intel(R)
|
||||
* Matrix Storage Manager, must be properly notified.
|
||||
*/
|
||||
void mdadm_wait(void)
|
||||
{
|
||||
size_t i;
|
||||
glob_t *gl;
|
||||
char cmd[160];
|
||||
|
||||
/* Setup kernel specific settings, e.g. allow broadcast ping, etc. */
|
||||
gl = get_arrays();
|
||||
if (gl) {
|
||||
for (i = 0; i < gl->gl_pathc; i++) {
|
||||
char *array;
|
||||
|
||||
array = basename(gl->gl_pathv[i]);
|
||||
snprintf(cmd, sizeof(cmd), "mdadm --wait-clean /dev/%s >/dev/null", array);
|
||||
run_interactive(cmd, "Marking MD array %s as clean", array);
|
||||
}
|
||||
globfree(gl);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/* Mount and unmount helpers
|
||||
*
|
||||
* Copyright (c) 2016 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 <string.h>
|
||||
#include <mntent.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
/*
|
||||
* SysV init on Debian/Ubuntu skips these protected mount points
|
||||
*
|
||||
* It also skips anything below /proc, /sys and /run for good meausre so
|
||||
* we do the same here. What we want is a list of non-protected tmpfs
|
||||
* and regular filesystems that can be safely unmounted before we do
|
||||
* swapoff, and remount / as read-only, respectively.
|
||||
*/
|
||||
static int is_protected(char *dir)
|
||||
{
|
||||
size_t i;
|
||||
char *protected[] = {
|
||||
"/sys", "/proc",
|
||||
"/.dev", "/dev", "/dev/pts", "/dev/shm", "dev/.static/dev", "/dev/vcs",
|
||||
"/run", "/var/run",
|
||||
"/", NULL
|
||||
};
|
||||
char *any[] = {
|
||||
"/proc/", "/sys/", "/run/", NULL
|
||||
};
|
||||
|
||||
for (i = 0; protected[i]; i++) {
|
||||
if (!strcmp(dir, protected[i]))
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; any[i]; i++) {
|
||||
if (!strncmp(dir, any[i], strlen(any[i])))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct mntent *iterator(char *fstab)
|
||||
{
|
||||
static FILE *fp = NULL;
|
||||
static struct mntent *mnt;
|
||||
|
||||
if (!fp && fstab) {
|
||||
fp = setmntent(fstab, "r");
|
||||
if (!fp)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while ((mnt = getmntent(fp))) {
|
||||
if (is_protected(mnt->mnt_dir))
|
||||
continue;
|
||||
|
||||
return mnt;
|
||||
}
|
||||
|
||||
endmntent(fp);
|
||||
fp = NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void unmount_tmpfs(void)
|
||||
{
|
||||
struct mntent *mnt;
|
||||
|
||||
while ((mnt = iterator("/proc/mounts"))) {
|
||||
if (!strcmp("tmpfs", mnt->mnt_fsname))
|
||||
umount(mnt->mnt_dir);
|
||||
}
|
||||
}
|
||||
|
||||
void unmount_regular(void)
|
||||
{
|
||||
struct mntent *mnt;
|
||||
|
||||
while ((mnt = iterator("/proc/mounts"))) {
|
||||
umount(mnt->mnt_dir);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,98 @@
|
||||
/* Misc. utility functions and C-library extensions for finit and its plugins
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2015 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
|
||||
/**
|
||||
* pid_alive - Check if a given process ID is running
|
||||
* @pid: Process ID to check for.
|
||||
*
|
||||
* Returns:
|
||||
* %TRUE(1) if pid is alive (/proc/pid exists), otherwise %FALSE(0)
|
||||
*/
|
||||
int pid_alive(pid_t pid)
|
||||
{
|
||||
char name[24]; /* Enough for max pid_t */
|
||||
|
||||
snprintf(name, sizeof(name), "/proc/%d", pid);
|
||||
|
||||
return fexist(name);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* pid_get_name - Find name of a process
|
||||
* @pid: PID of process to find.
|
||||
* @name: Pointer to buffer where to return process name, may be %NULL
|
||||
* @len: Length in bytes of @name buffer.
|
||||
*
|
||||
* If @name is %NULL, or @len is zero the function will return
|
||||
* a static string. This may be useful to one-off calls.
|
||||
*
|
||||
* Returns:
|
||||
* %NULL on error, otherwise a va
|
||||
*/
|
||||
char *pid_get_name(pid_t pid, char *name, size_t len)
|
||||
{
|
||||
int ret = 1;
|
||||
FILE *fp;
|
||||
char *pname, path[32];
|
||||
static char line[64];
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/%d/status", pid);
|
||||
fp = fopen(path, "r");
|
||||
if (fp) {
|
||||
if (fgets(line, sizeof (line), fp)) {
|
||||
pname = line + 6; /* Skip first part of line --> "Name:\t" */
|
||||
chomp(pname);
|
||||
if (name)
|
||||
strlcpy(name, pname, len);
|
||||
ret = 0; /* Found it! */
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return NULL;
|
||||
|
||||
if (name)
|
||||
return name;
|
||||
|
||||
return pname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+376
@@ -0,0 +1,376 @@
|
||||
/* Plugin based services architecture for finit
|
||||
*
|
||||
* Copyright (c) 2012 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 <dlfcn.h> /* dlopen() et al */
|
||||
#include <dirent.h> /* readdir() et al */
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <lite/lite.h>
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
|
||||
#include "config.h"
|
||||
#include "finit.h"
|
||||
#include "private.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
|
||||
#define is_io_plugin(p) ((p)->io.cb && (p)->io.fd > 0)
|
||||
#define SEARCH_PLUGIN(str) \
|
||||
PLUGIN_ITERATOR(p, tmp) { \
|
||||
if (!strcmp(p->name, str)) \
|
||||
return p; \
|
||||
}
|
||||
|
||||
static char *plugpath = NULL; /* Set by first load. */
|
||||
static TAILQ_HEAD(plugin_head, plugin) plugins = TAILQ_HEAD_INITIALIZER(plugins);
|
||||
|
||||
#ifndef ENABLE_STATIC
|
||||
static void check_plugin_depends(plugin_t *plugin);
|
||||
#endif
|
||||
|
||||
|
||||
static char *trim_ext(char *name)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
if (name) {
|
||||
ptr = strstr(name, ".so");
|
||||
if (!ptr)
|
||||
ptr = strstr(name, ".c");
|
||||
if (ptr)
|
||||
*ptr = 0;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
int plugin_register(plugin_t *plugin)
|
||||
{
|
||||
if (!plugin) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Setup default name if none is provided */
|
||||
if (!plugin->name) {
|
||||
#ifndef ENABLE_STATIC
|
||||
Dl_info info;
|
||||
|
||||
if (dladdr(plugin, &info) && info.dli_fname)
|
||||
plugin->name = basename(info.dli_fname);
|
||||
#endif
|
||||
if (!plugin->name)
|
||||
plugin->name = "unknown";
|
||||
}
|
||||
plugin->name = trim_ext(strdup(plugin->name));
|
||||
|
||||
/* Already registered? */
|
||||
if (plugin_find(plugin->name)) {
|
||||
_d("... %s already loaded", plugin->name);
|
||||
free(plugin->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef ENABLE_STATIC
|
||||
/* Resolve plugin dependencies */
|
||||
check_plugin_depends(plugin);
|
||||
#endif
|
||||
|
||||
TAILQ_INSERT_TAIL(&plugins, plugin, link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Not called, at the moment plugins cannot be unloaded. */
|
||||
int plugin_unregister(plugin_t *plugin)
|
||||
{
|
||||
#ifndef ENABLE_STATIC
|
||||
TAILQ_REMOVE(&plugins, plugin, link);
|
||||
|
||||
_d("%s exiting ...", plugin->name);
|
||||
free(plugin->name);
|
||||
#else
|
||||
_d("Finit built statically, cannot unload %s ...", plugin->name);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* plugin_find - Find a plugin by name
|
||||
* @name: With or without path, or .so extension
|
||||
*
|
||||
* This function uses an opporunistic search for a suitable plugin and
|
||||
* returns the first match. Albeit with at least some measure of
|
||||
* heuristics.
|
||||
*
|
||||
* First it checks for an exact match. If no match is found and @name
|
||||
* starts with a slash the search ends. Otherwise a new search with the
|
||||
* plugin path prepended to @name is made. Also, if @name does not end
|
||||
* with .so it too is added to @name before searching.
|
||||
*
|
||||
* Returns:
|
||||
* On success the pointer to the matching &plugin_t is returned,
|
||||
* otherwise %NULL is returned.
|
||||
*/
|
||||
plugin_t *plugin_find(char *name)
|
||||
{
|
||||
plugin_t *p, *tmp;
|
||||
|
||||
if (!name) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SEARCH_PLUGIN(name);
|
||||
|
||||
if (plugpath && name[0] != '/') {
|
||||
int noext;
|
||||
char path[CMD_SIZE];
|
||||
|
||||
noext = strcmp(name + strlen(name) - 3, ".so");
|
||||
snprintf(path, sizeof(path), "%s%s%s%s", plugpath,
|
||||
fisslashdir(plugpath) ? "" : "/",
|
||||
name, noext ? ".so" : "");
|
||||
|
||||
SEARCH_PLUGIN(path);
|
||||
}
|
||||
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Private daemon API *******************************************************/
|
||||
|
||||
/* Some hooks are called with a fixed argument, like HOOK_SVC_LOST */
|
||||
void plugin_run_hook(hook_point_t no, void *arg)
|
||||
{
|
||||
plugin_t *p, *tmp;
|
||||
|
||||
PLUGIN_ITERATOR(p, tmp) {
|
||||
if (p->hook[no].cb) {
|
||||
_d("Calling %s hook n:o %d (arg: %p) ...", basename(p->name), no, arg);
|
||||
p->hook[no].cb(arg ? arg : p->hook[no].arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Regular hooks are called with the registered plugin's argument */
|
||||
void plugin_run_hooks(hook_point_t no)
|
||||
{
|
||||
plugin_run_hook(no, NULL);
|
||||
}
|
||||
|
||||
/* Generic libev I/O callback, looks up correct plugin and calls its callback */
|
||||
static void generic_io_cb(uev_t *w, void *arg, int events)
|
||||
{
|
||||
plugin_t *p = (plugin_t *)arg;
|
||||
|
||||
if (is_io_plugin(p) && p->io.fd == w->fd) {
|
||||
/* Stop watcher, callback may close descriptor on us ... */
|
||||
uev_io_stop(w);
|
||||
|
||||
_d("Calling I/O %s from runloop...", basename(p->name));
|
||||
p->io.cb(p->io.arg, w->fd, events);
|
||||
|
||||
/* Update fd, may be changed by plugin callback, e.g., if FIFO */
|
||||
uev_io_set(w, p->io.fd, p->io.flags);
|
||||
}
|
||||
}
|
||||
|
||||
int plugin_io_init(plugin_t *p)
|
||||
{
|
||||
if (!is_io_plugin(p))
|
||||
return 0;
|
||||
|
||||
_d("Initializing plugin %s for I/O", basename(p->name));
|
||||
if (uev_io_init(ctx, &p->watcher, generic_io_cb, p, p->io.fd, p->io.flags)) {
|
||||
_e("Failed setting up I/O plugin %s", basename(p->name));
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Setup any I/O callbacks for plugins that use them */
|
||||
static int init_plugins(uev_ctx_t *UNUSED(ctx))
|
||||
{
|
||||
int fail = 0;
|
||||
plugin_t *p, *tmp;
|
||||
|
||||
PLUGIN_ITERATOR(p, tmp) {
|
||||
if (plugin_io_init(p))
|
||||
fail++;
|
||||
}
|
||||
|
||||
return fail;
|
||||
}
|
||||
|
||||
#ifndef ENABLE_STATIC
|
||||
/**
|
||||
* load_one - Load one plugin
|
||||
* @path: Path to finit plugins, usually %PLUGIN_PATH
|
||||
* @name: Name of plugin, optionally ending in ".so"
|
||||
*
|
||||
* Loads a plugin from @path/@name[.so]. Note, if ".so" is missing from
|
||||
* the plugin @name it is added before attempting to load.
|
||||
*
|
||||
* It is up to the plugin itself ot register itself as a "ctor" with the
|
||||
* %PLUGIN_INIT macro so that plugin_register() is called automatically.
|
||||
*
|
||||
* Returns:
|
||||
* POSIX OK(0) on success, non-zero otherwise.
|
||||
*/
|
||||
static int load_one(char *path, char *name)
|
||||
{
|
||||
int noext;
|
||||
char sofile[CMD_SIZE];
|
||||
void *handle;
|
||||
plugin_t *plugin;
|
||||
|
||||
if (!path || !fisdir(path) || !name) {
|
||||
errno = EINVAL;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Compose full path, with optional .so extension, to plugin */
|
||||
noext = strcmp(name + strlen(name) - 3, ".so");
|
||||
snprintf(sofile, sizeof(sofile), "%s/%s%s", path, name, noext ? ".so" : "");
|
||||
|
||||
_d("Loading plugin %s ...", basename(sofile));
|
||||
handle = dlopen(sofile, RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!handle) {
|
||||
_e("Failed loading plugin %s: %s", sofile, dlerror());
|
||||
return 1;
|
||||
}
|
||||
|
||||
plugin = TAILQ_LAST(&plugins, plugin_head);
|
||||
if (!plugin) {
|
||||
_e("Plugin %s failed to register, unloading from memory", sofile);
|
||||
dlclose(handle);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Remember handle from dlopen() for plugin_unregister() */
|
||||
plugin->handle = handle;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* check_plugin_depends - Check and load any plugins this one depends on.
|
||||
* @plugin: Plugin with possible depends to check
|
||||
*
|
||||
* Very simple dependency resolver, should actually load the plugin of
|
||||
* the correct .name, but currently loads a matching filename.
|
||||
*
|
||||
* Works, but only for now. A better way might be to have a try_load()
|
||||
* that actually loads all plugins and checks their &plugin_t for the
|
||||
* correct .name.
|
||||
*/
|
||||
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]);
|
||||
if (plugin_find(plugin->depends[i])) {
|
||||
// _d("OK plugin %s was already loaded.", plugin->depends[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
load_one(plugpath, plugin->depends[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int load_plugins(char *path)
|
||||
{
|
||||
int fail = 0;
|
||||
DIR *dp;
|
||||
struct dirent *entry;
|
||||
|
||||
if (!silent)
|
||||
print_desc("Loading plugins", NULL);
|
||||
|
||||
dp = opendir(path);
|
||||
if (!dp) {
|
||||
_e("Failed, cannot open plugin directory %s: %s", path, strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
plugpath = path;
|
||||
|
||||
while ((entry = readdir(dp))) {
|
||||
if (entry->d_name[0] == '.')
|
||||
continue; /* Skip . and .. directories */
|
||||
|
||||
if (load_one(path, entry->d_name))
|
||||
fail++;
|
||||
}
|
||||
|
||||
closedir(dp);
|
||||
|
||||
return fail;
|
||||
}
|
||||
#else
|
||||
static int load_plugins(char *UNUSED(path))
|
||||
{
|
||||
if (!silent)
|
||||
print_desc("Initializing plugins", NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* ENABLE_STATIC */
|
||||
|
||||
int plugin_init(uev_ctx_t *ctx, char *path)
|
||||
{
|
||||
int fail = 1;
|
||||
|
||||
if (!load_plugins(path))
|
||||
fail = init_plugins(ctx);
|
||||
|
||||
if (!silent)
|
||||
print_result(fail);
|
||||
|
||||
return fail;
|
||||
}
|
||||
|
||||
void plugin_exit(void)
|
||||
{
|
||||
#ifndef ENABLE_STATIC
|
||||
plugin_t *p, *tmp;
|
||||
|
||||
PLUGIN_ITERATOR(p, tmp) {
|
||||
if (dlclose(p->handle))
|
||||
_e("Failed: unloading plugin %s: %s", p->name, dlerror());
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
/* Plugin based services architecture for finit
|
||||
*
|
||||
* Copyright (c) 2012 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_PLUGIN_H_
|
||||
#define FINIT_PLUGIN_H_
|
||||
|
||||
#include <uev/uev.h>
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
|
||||
#include "svc.h"
|
||||
|
||||
#ifndef PLUGIN_PATH /* Fallback for --enable-static builds */
|
||||
#define PLUGIN_PATH "/dev/null"
|
||||
#endif
|
||||
|
||||
#define PLUGIN_DEP_MAX 10
|
||||
#define PLUGIN_IO_READ UEV_READ
|
||||
#define PLUGIN_IO_WRITE UEV_WRITE
|
||||
|
||||
#define PLUGIN_INIT(x) static void __attribute__ ((constructor)) x(void)
|
||||
#define PLUGIN_EXIT(x) static void __attribute__ ((destructor)) x(void)
|
||||
|
||||
#define PLUGIN_ITERATOR(x, tmp) TAILQ_FOREACH_SAFE(x, &plugins, link, tmp)
|
||||
|
||||
/*
|
||||
* Predefined hook points for easier plugin debugging
|
||||
*/
|
||||
typedef enum {
|
||||
/* Bootstrap hooks */
|
||||
HOOK_ROOTFS_UP = 0,
|
||||
HOOK_MOUNT_ERROR,
|
||||
HOOK_BASEFS_UP,
|
||||
HOOK_NETWORK_UP,
|
||||
HOOK_SVC_UP,
|
||||
HOOK_SYSTEM_UP,
|
||||
|
||||
/* Runtime hooks */
|
||||
HOOK_SVC_RECONF,
|
||||
HOOK_SVC_LOST,
|
||||
HOOK_SVC_START,
|
||||
HOOK_RUNLEVEL_CHANGE,
|
||||
|
||||
/* Shutdown hooks */
|
||||
HOOK_SHUTDOWN,
|
||||
HOOK_MAX_NUM
|
||||
} hook_point_t;
|
||||
|
||||
/**
|
||||
* plugin_t - Finit &plugin_t object
|
||||
* @link: BSD sys/queue.h linked list node
|
||||
* @name: Plugin name, or identifier to match against a &svc_t object
|
||||
* @svc: Service callback for a loaded &svc_t object
|
||||
* @hook: Hook callback definitions
|
||||
* @io: I/O hook callback
|
||||
*
|
||||
* To setup an &svc_t object callback for a service monitor the @name
|
||||
* must match the @svc_t @cmd exactly for them to "pair".
|
||||
*
|
||||
* A &plugin_t object may contain a service callback, several hooks
|
||||
* and/or an I/O callback as well. This way all critical extensions
|
||||
* to finit can fit in one single plugin, if needed.
|
||||
*
|
||||
* The "dynamic events" discussed in the svc callback is for external
|
||||
* service plugins to implement. However, it can be anything that
|
||||
* can cause a service to need to SIGHUP at runtime. E.g., acquiring
|
||||
* a DHCP lease, or an interface going UP/DOWN. The event itself can
|
||||
* be passed as an integer @event and any optional argument @event_arg
|
||||
*
|
||||
* It is up to the external service plugin to track these events and
|
||||
* relay them to each @dynamic service plugins' callback. I.e., to
|
||||
* all those with the dynamic flag set.
|
||||
*/
|
||||
typedef struct plugin {
|
||||
/* BSD sys/queue.h linked list node. */
|
||||
TAILQ_ENTRY(plugin) link;
|
||||
|
||||
/* Handle to be sent to dlclose() when plugins is unloaded. */
|
||||
void *handle;
|
||||
|
||||
/* Event loop handler, used internally by Finit */
|
||||
uev_t watcher;
|
||||
|
||||
/* Plugin name, defaults to basename of plugin path if unset.
|
||||
* NOTE: Must match cmd for services or inetd plugins! */
|
||||
char *name;
|
||||
|
||||
/* List of hook callbacks. */
|
||||
struct {
|
||||
void *arg; /* Optional argument to callback func. */
|
||||
void (*cb)(void *arg);
|
||||
} hook[HOOK_MAX_NUM];
|
||||
|
||||
/* I/O Plugin */
|
||||
struct {
|
||||
int fd, flags; /* 1:READ, 2:WRITE */
|
||||
void *arg;
|
||||
void (*cb)(void *arg, int fd, int events);
|
||||
} io;
|
||||
|
||||
/* Inetd Plugin, stdio used as client socket.
|
||||
* @type argument will be either SOCK_DGRAM or SOCK_STREAM */
|
||||
struct {
|
||||
int (*cmd)(int type);
|
||||
} inetd;
|
||||
|
||||
char *depends[PLUGIN_DEP_MAX]; /* List of other .name's this depends on. */
|
||||
} plugin_t;
|
||||
|
||||
/* Public plugin API */
|
||||
int plugin_io_init (plugin_t *plugin);
|
||||
|
||||
int plugin_register (plugin_t *plugin);
|
||||
int plugin_unregister (plugin_t *plugin);
|
||||
|
||||
/* Helper API */
|
||||
plugin_t *plugin_find (char *name);
|
||||
|
||||
#endif /* FINIT_PLUGIN_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,56 @@
|
||||
/* Private header file for main finit daemon, not for plugins
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-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_PRIVATE_H_
|
||||
#define FINIT_PRIVATE_H_
|
||||
|
||||
#include "svc.h"
|
||||
#include "plugin.h"
|
||||
|
||||
/*
|
||||
* Main loop context
|
||||
*/
|
||||
uev_ctx_t *ctx;
|
||||
|
||||
int api_init (uev_ctx_t *ctx);
|
||||
int api_exit (void);
|
||||
|
||||
int client (int argc, char *argv[]);
|
||||
|
||||
void service_monitor (pid_t lost);
|
||||
|
||||
void plugin_run_hook (hook_point_t no, void *arg);
|
||||
void plugin_run_hooks (hook_point_t no);
|
||||
|
||||
int plugin_init (uev_ctx_t *ctx, char *path);
|
||||
void plugin_exit (void);
|
||||
|
||||
#endif /* FINIT_PRIVATE_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
/* reboot, halt, poweroff, shutdown, suspend for finit
|
||||
*
|
||||
* Copyright (c) 2009-2016 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 <err.h>
|
||||
#include <getopt.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/reboot.h>
|
||||
#include "util.h"
|
||||
|
||||
#ifndef RB_SW_SUSPEND /* Since Linux 2.5.18, but not in GLIBC until 2.19 */
|
||||
#define RB_SW_SUSPEND 0xd000fce2
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
CMD_UNKNOWN = -1,
|
||||
CMD_REBOOT = 0,
|
||||
CMD_HALT,
|
||||
CMD_POWEROFF,
|
||||
CMD_SUSPEND,
|
||||
} cmd_t;
|
||||
|
||||
static cmd_t cmd = CMD_UNKNOWN;
|
||||
static char *msg = NULL;
|
||||
|
||||
static void transform(char *nm)
|
||||
{
|
||||
if (!nm)
|
||||
nm = prognm;
|
||||
|
||||
if (cmd == CMD_UNKNOWN) {
|
||||
if (!strcmp(nm, "halt") || !strcmp(nm, "shutdown"))
|
||||
cmd = CMD_HALT;
|
||||
else if (!strcmp(nm, "poweroff"))
|
||||
cmd = CMD_POWEROFF;
|
||||
else if (!strcmp(nm, "suspend"))
|
||||
cmd = CMD_SUSPEND;
|
||||
else
|
||||
cmd = CMD_REBOOT;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case CMD_REBOOT:
|
||||
msg = "reboot";
|
||||
break;
|
||||
|
||||
case CMD_HALT:
|
||||
msg = "halt";
|
||||
break;
|
||||
|
||||
case CMD_POWEROFF:
|
||||
msg = "power-off";
|
||||
break;
|
||||
|
||||
case CMD_SUSPEND:
|
||||
msg = "suspend";
|
||||
break;
|
||||
|
||||
case CMD_UNKNOWN:
|
||||
cmd = CMD_REBOOT;
|
||||
msg = "reboot";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void sig(int signo)
|
||||
{
|
||||
(void)signo; /* NOP */
|
||||
}
|
||||
|
||||
static int usage(int rc)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [OPTIONS]\n\n"
|
||||
"Options:\n"
|
||||
" -h, --help This help text\n"
|
||||
" -f, --force Force unsafe %s now, do not contact the init system.\n"
|
||||
" --halt Halt system, regardless of how the command is called.\n"
|
||||
" -p, --poweroff Power-off system, regardless of how the command is called.\n"
|
||||
" --reboot Reboot system, regardless of how the command is called.\n"
|
||||
"\n", prognm, msg);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int c, force = 0;
|
||||
struct option long_options[] = {
|
||||
{"help", 0, NULL, 'h'},
|
||||
{"force", 0, NULL, 'f'},
|
||||
{"halt", 0, NULL, 'H'},
|
||||
{"poweroff", 0, NULL, 'p'},
|
||||
{"reboot", 0, NULL, 'r'},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
/* Initial command taken from program name */
|
||||
transform(progname(argv[0]));
|
||||
|
||||
while ((c = getopt_long(argc, argv, "h?fHpr", long_options, NULL)) != EOF) {
|
||||
switch(c) {
|
||||
case 'h':
|
||||
case '?':
|
||||
return usage(0);
|
||||
|
||||
case 'f':
|
||||
force = 1;
|
||||
break;
|
||||
|
||||
case 'H':
|
||||
cmd = CMD_HALT;
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
cmd = CMD_POWEROFF;
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
cmd = CMD_REBOOT;
|
||||
break;
|
||||
|
||||
default:
|
||||
return usage(1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for any overrides */
|
||||
transform(NULL);
|
||||
|
||||
/* Catch and ignore SIGTERM on halt/poweroff */
|
||||
signal(SIGTERM, sig);
|
||||
|
||||
if (force) {
|
||||
switch (cmd) {
|
||||
case CMD_REBOOT:
|
||||
c = reboot(RB_AUTOBOOT);
|
||||
break;
|
||||
|
||||
case CMD_HALT:
|
||||
c = reboot(RB_HALT_SYSTEM);
|
||||
break;
|
||||
|
||||
case CMD_POWEROFF:
|
||||
c = reboot(RB_POWER_OFF);
|
||||
break;
|
||||
|
||||
case CMD_SUSPEND:
|
||||
c = reboot(RB_SW_SUSPEND);
|
||||
break;
|
||||
|
||||
case CMD_UNKNOWN:
|
||||
errx(1, "Invalid command");
|
||||
break;
|
||||
}
|
||||
|
||||
if (c)
|
||||
warn("Failed forced %s", msg);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case CMD_REBOOT:
|
||||
c = kill(1, SIGTERM);
|
||||
break;
|
||||
|
||||
case CMD_HALT:
|
||||
c = kill(1, SIGUSR1);
|
||||
break;
|
||||
|
||||
case CMD_POWEROFF:
|
||||
c = kill(1, SIGUSR2);
|
||||
break;
|
||||
|
||||
case CMD_SUSPEND:
|
||||
/*
|
||||
* Only initctl supports suspend, we avoid adding
|
||||
* another signal to finit for compat reasons.
|
||||
*/
|
||||
|
||||
case CMD_UNKNOWN:
|
||||
errx(1, "Invalid command");
|
||||
break;
|
||||
}
|
||||
|
||||
if (c)
|
||||
err(1, "Failed signalling init to %s", msg);
|
||||
do_sleep(2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+1007
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,47 @@
|
||||
/* Finit service monitor, task starter and generic API for managing svc_t
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2015 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_SERVICE_H_
|
||||
#define FINIT_SERVICE_H_
|
||||
|
||||
#include "svc.h"
|
||||
|
||||
void service_runlevel (int newlevel);
|
||||
int service_register (int type, char *line, struct timeval *mtime, char *username);
|
||||
void service_unregister (svc_t *svc);
|
||||
int service_enabled (svc_t *svc);
|
||||
|
||||
void service_reload_dynamic (void);
|
||||
|
||||
void service_step (svc_t *svc);
|
||||
void service_step_all (int types);
|
||||
|
||||
#endif /* FINIT_SERVICE_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,404 @@
|
||||
/* Signal management - Be conservative with what finit responds to!
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2014 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.
|
||||
*
|
||||
* Finit is often used on embedded and small Linux systems with BusyBox.
|
||||
* For compatibility with its existing toolset (halt, poweroff, reboot),
|
||||
* the following signals have been adopted:
|
||||
*
|
||||
* SIGHUP
|
||||
* Same effect as init/telinit q, reloads *.conf in /etc/finit.d/
|
||||
*
|
||||
* SIGUSR1
|
||||
* Calls shutdown hooks, including HOOK_SHUTDOWN, stops all running
|
||||
* processes, and unmounts all file systems. Then tells kernel to
|
||||
* halt. Most people these days want SIGUSR2 though.
|
||||
*
|
||||
* (SysV init and systemd use this to re-open their FIFO/D-Bus.)
|
||||
*
|
||||
* SIGUSR2
|
||||
* Like SIGUSR1, but tell kernel to power-off the system, if ACPI
|
||||
* or similar exists to actually do this. If the kernel fails
|
||||
* power-off, Finit falls back to halt.
|
||||
*
|
||||
* (SysV init N/A, systemd dumps its internal state to log.)
|
||||
*
|
||||
* SIGTERM
|
||||
* Like SIGUSR1, but tell kernel to reboot the system when done.
|
||||
*
|
||||
* (SysV init N/A, systemd rexecutes itself.)
|
||||
*
|
||||
* SIGINT
|
||||
* Sent from kernel when the CTRL-ALT-DEL key combo is pressed.
|
||||
* SysV init and systemd default to reboot with `shutdown -r`.
|
||||
* Finit currently forwards this to SIGTERM.
|
||||
*
|
||||
* SIGWINCH
|
||||
* Sent from kernel when the KeyboardSignal key is pressed. Some
|
||||
* users bind keys, see loadkeys(1), to create consoles on demand.
|
||||
* Finit currently ignores this signal.
|
||||
*
|
||||
* SIGPWR
|
||||
* Sent from a power daemon, like powstatd(8), on changes to the
|
||||
* UPS status. Traditionally SysV init read /etc/powerstatus and
|
||||
* acted on "OK", "FAIL", or "LOW" and then removed the file.
|
||||
* Finit currently forwards this to SIGUSR2.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <string.h> /* strerror() */
|
||||
#include <sys/reboot.h>
|
||||
#include <sys/wait.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "conf.h"
|
||||
#include "config.h"
|
||||
#include "helpers.h"
|
||||
#include "plugin.h"
|
||||
#include "private.h"
|
||||
#include "sig.h"
|
||||
#include "service.h"
|
||||
#include "util.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
/*
|
||||
* Old-style SysV shutdown sends a setenv cmd INIT_HALT with "=HALT",
|
||||
* "=POWERDOWN", or "" to cancel shutdown, before requesting change to
|
||||
* runlevel 6 over the /dev/initctl FIFO. See plugins/initctl.c
|
||||
*/
|
||||
shutop_t halt = SHUT_DEFAULT;
|
||||
|
||||
static int stopped = 0;
|
||||
static uev_t sigterm_watcher, sigusr1_watcher, sigusr2_watcher;
|
||||
static uev_t sighup_watcher, sigint_watcher, sigpwr_watcher;
|
||||
static uev_t sigchld_watcher;
|
||||
static uev_t sigstop_watcher, sigtstp_watcher, sigcont_watcher;
|
||||
|
||||
void mdadm_wait(void);
|
||||
void unmount_tmpfs(void);
|
||||
void unmount_regular(void);
|
||||
|
||||
/*
|
||||
* Kernel threads have no cmdline so fgets() returns NULL for them. We
|
||||
* also skip "special" processes, e.g. mdadm/mdmon or watchdogd that must
|
||||
* not be stopped here, for various reasons.
|
||||
*
|
||||
* https://www.freedesktop.org/wiki/Software/systemd/RootStorageDaemons/
|
||||
*/
|
||||
void do_kill(int signo)
|
||||
{
|
||||
DIR *dirp;
|
||||
|
||||
dirp = opendir("/proc");
|
||||
if (dirp) {
|
||||
struct dirent *d;
|
||||
|
||||
while ((d = readdir(dirp))) {
|
||||
int pid;
|
||||
FILE *fp;
|
||||
char file[80] = "";
|
||||
|
||||
if (d->d_type != DT_DIR)
|
||||
continue;
|
||||
|
||||
pid = atoi(d->d_name);
|
||||
if (!pid)
|
||||
continue;
|
||||
|
||||
snprintf(file, sizeof(file), "/proc/%s/cmdline", d->d_name);
|
||||
fp = fopen(file, "r");
|
||||
if (!fp)
|
||||
continue;
|
||||
|
||||
if (fgets(file, sizeof(file), fp)) {
|
||||
if (file[0] != '@')
|
||||
kill(pid, signo);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
closedir(dirp);
|
||||
}
|
||||
}
|
||||
|
||||
void do_shutdown(shutop_t op)
|
||||
{
|
||||
touch(SYNC_SHUTDOWN);
|
||||
|
||||
if (sdown)
|
||||
run_interactive(sdown, "Calling shutdown hook: %s", sdown);
|
||||
|
||||
/* Update UTMP db */
|
||||
utmp_set_halt();
|
||||
|
||||
/*
|
||||
* Tell all remaining non-monitored processes to exit, give them
|
||||
* some time to exit gracefully, 2 sec is customary.
|
||||
*/
|
||||
do_kill(SIGTERM);
|
||||
do_sleep(2);
|
||||
do_kill(SIGKILL);
|
||||
|
||||
/* Exit plugins and API gracefully */
|
||||
plugin_exit();
|
||||
api_exit();
|
||||
|
||||
/* Reap 'em */
|
||||
while (waitpid(-1, NULL, WNOHANG) > 0)
|
||||
;
|
||||
|
||||
/* Close all local non-console descriptors */
|
||||
for (int fd = 3; fd < 128; fd++)
|
||||
close(fd);
|
||||
|
||||
if (vfork()) {
|
||||
/*
|
||||
* Put PID 1 aside and let child perform reboot/halt
|
||||
* kernel may exit child and we don't want to exit PID 1
|
||||
* ... causing "aiii killing init" during reboot ...
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
/* Unmount any tmpfs before unmounting swap ... */
|
||||
unmount_tmpfs();
|
||||
run("/sbin/swapoff -e -a");
|
||||
|
||||
/* ... unmount remaining regular file systems. */
|
||||
unmount_regular();
|
||||
|
||||
/* We sit on / so we must remount it ro, try all the things! */
|
||||
sync();
|
||||
run("/bin/mount -n -o remount,ro -t dummytype dummydev /");
|
||||
run("/bin/mount -n -o remount,ro dummydev /");
|
||||
run("/bin/mount -n -o remount,ro /");
|
||||
|
||||
/* Call mdadm to mark any RAID array(s) as clean before halting. */
|
||||
mdadm_wait();
|
||||
|
||||
/* Reboot via watchdog or kernel, or shutdown? */
|
||||
if (op == SHUT_REBOOT) {
|
||||
if (wdogpid) {
|
||||
int timeout = 10;
|
||||
|
||||
/* Wait here until the WDT reboots, or timeout with fallback */
|
||||
print(kill(wdogpid, SIGPWR) == 1, "Pending watchdog reboot");
|
||||
while (timeout--)
|
||||
do_sleep(1);
|
||||
}
|
||||
|
||||
_d("Rebooting ...");
|
||||
reboot(RB_AUTOBOOT);
|
||||
} else if (op == SHUT_OFF) {
|
||||
_d("Powering down ...");
|
||||
reboot(RB_POWER_OFF);
|
||||
}
|
||||
|
||||
/* Also fallback if any of the other two fails */
|
||||
_d("Halting ...");
|
||||
reboot(RB_HALT_SYSTEM);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reload .conf files in /etc/finit.d/
|
||||
*/
|
||||
static void sighup_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
_d("...");
|
||||
/* INIT_CMD_RELOAD: 'init q', 'initctl reload', and SIGHUP */
|
||||
service_reload_dynamic();
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGINT: Should generate <sys/key/ctrlaltdel> condition, for now reboot
|
||||
*/
|
||||
static void sigint_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
_d("...");
|
||||
halt = SHUT_REBOOT;
|
||||
service_runlevel(6);
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGUSR1: BusyBox style halt
|
||||
*/
|
||||
static void sigusr1_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
_d("...");
|
||||
halt = SHUT_HALT;
|
||||
service_runlevel(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGUSR2: BusyBox style poweroff
|
||||
*/
|
||||
static void sigusr2_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
_d("...");
|
||||
halt = SHUT_OFF;
|
||||
service_runlevel(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGTERM: BusyBox style reboot
|
||||
*/
|
||||
static void sigterm_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
_d("...");
|
||||
halt = SHUT_REBOOT;
|
||||
service_runlevel(6);
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGCHLD: one of our children has died
|
||||
*/
|
||||
static void sigchld_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
pid_t pid;
|
||||
|
||||
/* Reap all the children! */
|
||||
do {
|
||||
pid = waitpid(-1, NULL, WNOHANG);
|
||||
if (pid > 0) {
|
||||
_d("Collected child %d", pid);
|
||||
service_monitor(pid);
|
||||
}
|
||||
} while (pid > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGSTOP/SIGTSTP: Paused by user or netflash
|
||||
*/
|
||||
static void sigstop_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
touch(SYNC_STOPPED);
|
||||
stopped++;
|
||||
}
|
||||
|
||||
/*
|
||||
* SIGCONT: Restart service monitor
|
||||
*/
|
||||
static void sigcont_cb(uev_t *UNUSED(w), void *UNUSED(arg), int UNUSED(events))
|
||||
{
|
||||
stopped = 0;
|
||||
erase(SYNC_STOPPED);
|
||||
}
|
||||
|
||||
/*
|
||||
* Is SIGSTOP asserted?
|
||||
*/
|
||||
int sig_stopped(void)
|
||||
{
|
||||
return stopped;
|
||||
}
|
||||
|
||||
/*
|
||||
* Inital signal setup - ignore everything but SIGCHLD until we're capable of responding
|
||||
*/
|
||||
static void chld_handler(int UNUSED(sig), siginfo_t *UNUSED(info), void *UNUSED(ctx))
|
||||
{
|
||||
/* NOP */
|
||||
}
|
||||
|
||||
void sig_init(void)
|
||||
{
|
||||
int i;
|
||||
struct sigaction sa;
|
||||
|
||||
for (i = 1; i < NSIG; i++)
|
||||
IGNSIG(sa, i, SA_RESTART);
|
||||
|
||||
SETSIG(sa, SIGCHLD, chld_handler, SA_RESTART);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unblock all signals blocked by finit when starting children
|
||||
*/
|
||||
void sig_unblock(void)
|
||||
{
|
||||
int i;
|
||||
sigset_t nmask;
|
||||
struct sigaction sa;
|
||||
|
||||
sigemptyset(&nmask);
|
||||
sigaddset(&nmask, SIGHUP);
|
||||
sigaddset(&nmask, SIGCHLD);
|
||||
sigaddset(&nmask, SIGINT);
|
||||
sigaddset(&nmask, SIGPWR);
|
||||
sigaddset(&nmask, SIGSTOP);
|
||||
sigaddset(&nmask, SIGTSTP);
|
||||
sigaddset(&nmask, SIGCONT);
|
||||
sigaddset(&nmask, SIGTERM);
|
||||
sigaddset(&nmask, SIGUSR1);
|
||||
sigaddset(&nmask, SIGUSR2);
|
||||
sigprocmask(SIG_UNBLOCK, &nmask, NULL);
|
||||
|
||||
/* Reset signal handlers that were set by the parent process */
|
||||
for (i = 1; i < NSIG; i++)
|
||||
DFLSIG(sa, i, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup limited set of SysV compatible signals to respond to
|
||||
*/
|
||||
void sig_setup(uev_ctx_t *ctx)
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
_d("Setup signals");
|
||||
|
||||
/* Cleanup any stale finit control files */
|
||||
erase(SYNC_SHUTDOWN);
|
||||
erase(SYNC_STOPPED);
|
||||
|
||||
/* Standard SysV init calls ctrl-alt-delete handler */
|
||||
uev_signal_init(ctx, &sigint_watcher, sigint_cb, NULL, SIGINT);
|
||||
uev_signal_init(ctx, &sigpwr_watcher, sigint_cb, NULL, SIGPWR);
|
||||
|
||||
/* BusyBox init style signals for halt, power-off and reboot. */
|
||||
uev_signal_init(ctx, &sigusr1_watcher, sigusr1_cb, NULL, SIGUSR1);
|
||||
uev_signal_init(ctx, &sigusr2_watcher, sigusr2_cb, NULL, SIGUSR2);
|
||||
uev_signal_init(ctx, &sigterm_watcher, sigterm_cb, NULL, SIGTERM);
|
||||
|
||||
/* Some C APIs may need SIGALRM for implementing timers. */
|
||||
IGNSIG(sa, SIGALRM, 0);
|
||||
|
||||
/* /etc/inittab not supported yet, instead /etc/finit.d/ is scanned for *.conf */
|
||||
uev_signal_init(ctx, &sighup_watcher, sighup_cb, NULL, SIGHUP);
|
||||
|
||||
/* After initial bootstrap of Finit we call the service monitor to reap children */
|
||||
uev_signal_init(ctx, &sigchld_watcher, sigchld_cb, NULL, SIGCHLD);
|
||||
|
||||
/* Stopping init is a bit tricky. */
|
||||
uev_signal_init(ctx, &sigstop_watcher, sigstop_cb, NULL, SIGSTOP);
|
||||
uev_signal_init(ctx, &sigtstp_watcher, sigstop_cb, NULL, SIGTSTP);
|
||||
uev_signal_init(ctx, &sigcont_watcher, sigcont_cb, NULL, SIGCONT);
|
||||
|
||||
setsid();
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,84 @@
|
||||
/* Signal management - Be conservative with what finit responds to!
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2014 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_SIG_H_
|
||||
#define FINIT_SIG_H_
|
||||
|
||||
#include <signal.h>
|
||||
#include <uev/uev.h>
|
||||
|
||||
#define SYNC_SHUTDOWN "/var/lock/finit.shutdown"
|
||||
#define SYNC_STOPPED "/var/lock/finit.stopped"
|
||||
|
||||
#define SETSIG(sa, sig, fun, flags) \
|
||||
do { \
|
||||
sa.sa_sigaction = fun; \
|
||||
sa.sa_flags = SA_SIGINFO | flags; \
|
||||
sigemptyset(&sa.sa_mask); \
|
||||
sigaction(sig, &sa, NULL); \
|
||||
} while (0)
|
||||
|
||||
#define IGNSIG(sa, sig, flags) \
|
||||
do { \
|
||||
sa.sa_handler = SIG_IGN; \
|
||||
sa.sa_flags = flags; \
|
||||
sigemptyset(&sa.sa_mask); \
|
||||
sigaction(sig, &sa, NULL); \
|
||||
} while (0)
|
||||
|
||||
#define DFLSIG(sa, sig, flags) \
|
||||
do { \
|
||||
sa.sa_handler = SIG_DFL; \
|
||||
sa.sa_flags = flags; \
|
||||
sigemptyset(&sa.sa_mask); \
|
||||
sigaction(sig, &sa, NULL); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* For old-style /dev/initctl shutdown we default to halt
|
||||
*/
|
||||
#define SHUT_DEFAULT SHUT_HALT
|
||||
|
||||
typedef enum {
|
||||
SHUT_OFF,
|
||||
SHUT_HALT,
|
||||
SHUT_REBOOT
|
||||
} shutop_t;
|
||||
|
||||
extern shutop_t halt;
|
||||
|
||||
void do_shutdown (shutop_t op);
|
||||
int sig_stopped (void);
|
||||
void sig_init (void);
|
||||
void sig_unblock (void);
|
||||
void sig_setup (uev_ctx_t *ctx);
|
||||
|
||||
#endif /* FINIT_SIG_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,229 @@
|
||||
/* Finit state machine
|
||||
*
|
||||
* Copyright (c) 2016 Jonas Johansson <jonas.johansson@westermo.se>
|
||||
*
|
||||
* 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 "config.h" /* Generated by configure script */
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "cond.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "private.h"
|
||||
#include "service.h"
|
||||
#include "sig.h"
|
||||
#include "tty.h"
|
||||
#include "sm.h"
|
||||
|
||||
sm_t sm;
|
||||
|
||||
void sm_init(sm_t *sm)
|
||||
{
|
||||
sm->state = SM_BOOTSTRAP_STATE;
|
||||
sm->newlevel = -1;
|
||||
sm->reload = 0;
|
||||
sm->in_teardown = 0;
|
||||
}
|
||||
|
||||
static char *sm_status(sm_t *sm)
|
||||
{
|
||||
switch (sm->state) {
|
||||
case SM_BOOTSTRAP_STATE:
|
||||
return "bootstrap";
|
||||
|
||||
case SM_RUNNING_STATE:
|
||||
return "running";
|
||||
|
||||
case SM_RUNLEVEL_CHANGE_STATE:
|
||||
return "runlevel/change";
|
||||
|
||||
case SM_RUNLEVEL_WAIT_STATE:
|
||||
return "runlevel/wait";
|
||||
|
||||
case SM_RELOAD_CHANGE_STATE:
|
||||
return "reload/change";
|
||||
|
||||
case SM_RELOAD_WAIT_STATE:
|
||||
return "reload/wait";
|
||||
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
void sm_set_runlevel(sm_t *sm, int newlevel)
|
||||
{
|
||||
sm->newlevel = newlevel;
|
||||
}
|
||||
|
||||
void sm_set_reload(sm_t *sm)
|
||||
{
|
||||
sm->reload = 1;
|
||||
}
|
||||
|
||||
int sm_is_in_teardown(sm_t *sm)
|
||||
{
|
||||
return sm->in_teardown;
|
||||
}
|
||||
|
||||
void sm_step(sm_t *sm)
|
||||
{
|
||||
sm_state_t old_state;
|
||||
|
||||
restart:
|
||||
old_state = sm->state;
|
||||
|
||||
_d("state: %s", sm_status(sm));
|
||||
|
||||
switch (sm->state) {
|
||||
case SM_BOOTSTRAP_STATE:
|
||||
_d("Bootstrapping all services in runlevel S from %s", FINIT_CONF);
|
||||
service_step_all(SVC_TYPE_RUN | SVC_TYPE_TASK | SVC_TYPE_SERVICE);
|
||||
sm->state = SM_RUNNING_STATE;
|
||||
break;
|
||||
|
||||
case SM_RUNNING_STATE:
|
||||
/* runlevel changed? */
|
||||
if (sm->newlevel >= 0 && sm->newlevel <= 9) {
|
||||
if (runlevel == sm->newlevel) {
|
||||
sm->newlevel = -1;
|
||||
break;
|
||||
}
|
||||
sm->state = SM_RUNLEVEL_CHANGE_STATE;
|
||||
break;
|
||||
}
|
||||
/* reload ? */
|
||||
if (sm->reload) {
|
||||
sm->reload = 0;
|
||||
sm->state = SM_RELOAD_CHANGE_STATE;
|
||||
}
|
||||
break;
|
||||
|
||||
case SM_RUNLEVEL_CHANGE_STATE:
|
||||
prevlevel = runlevel;
|
||||
runlevel = sm->newlevel;
|
||||
sm->newlevel = -1;
|
||||
|
||||
_d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel);
|
||||
runlevel_set(prevlevel, runlevel);
|
||||
|
||||
/* Disable login in single-user mode as well as shutdown/reboot */
|
||||
if (runlevel == 1 || runlevel == 0 || runlevel == 6)
|
||||
touch("/etc/nologin");
|
||||
else
|
||||
erase("/etc/nologin");
|
||||
|
||||
/* Make sure to (re)load all *.conf in /etc/finit.d/ */
|
||||
conf_reload_dynamic();
|
||||
|
||||
_d("Stopping services services not allowed in new runlevel ...");
|
||||
sm->in_teardown = 1;
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
sm->state = SM_RUNLEVEL_WAIT_STATE;
|
||||
break;
|
||||
|
||||
case SM_RUNLEVEL_WAIT_STATE:
|
||||
/* Need to wait for any services to stop? If so, exit early
|
||||
* and perform second stage from service_monitor later. */
|
||||
if (!svc_stop_completed())
|
||||
break;
|
||||
|
||||
/* Prev runlevel services stopped, call hooks before starting new runlevel ... */
|
||||
_d("All services have been stoppped, calling runlevel change hooks ...");
|
||||
plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */
|
||||
|
||||
_d("Starting services services new to this runlevel ...");
|
||||
sm->in_teardown = 0;
|
||||
service_step_all(SVC_TYPE_ANY);
|
||||
|
||||
/* Cleanup stale services */
|
||||
svc_clean_dynamic(service_unregister);
|
||||
|
||||
/*
|
||||
* "I've seen things you people wouldn't believe. Attack ships on fire off
|
||||
* the shoulder of Orion. I watched C-beams glitter in the dark near the
|
||||
* Tannhäuser Gate. All those .. moments .. will be lost in time, like
|
||||
* tears ... in ... rain."
|
||||
*/
|
||||
if (runlevel == 0 || runlevel == 6) {
|
||||
do_shutdown(halt);
|
||||
sm->state = SM_RUNNING_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* No TTYs run at bootstrap, they have a delayed start. */
|
||||
if (prevlevel > 0)
|
||||
tty_runlevel(runlevel);
|
||||
|
||||
sm->state = SM_RUNNING_STATE;
|
||||
break;
|
||||
|
||||
case SM_RELOAD_CHANGE_STATE:
|
||||
/* First reload all *.conf in /etc/finit.d/ */
|
||||
conf_reload_dynamic();
|
||||
|
||||
/* Then, mark all affected service conditions as in-flux and
|
||||
* let all affected services move to WAITING/HALTED */
|
||||
_d("Stopping services services not allowed after reconf ...");
|
||||
sm->in_teardown = 1;
|
||||
cond_reload();
|
||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
|
||||
|
||||
sm->state = SM_RELOAD_WAIT_STATE;
|
||||
break;
|
||||
|
||||
case SM_RELOAD_WAIT_STATE:
|
||||
/* Need to wait for any services to stop? If so, exit early
|
||||
* and perform second stage from service_monitor later. */
|
||||
if (!svc_stop_completed())
|
||||
break;
|
||||
|
||||
sm->in_teardown = 0;
|
||||
/* Cleanup stale services */
|
||||
svc_clean_dynamic(service_unregister);
|
||||
|
||||
_d("Starting services after reconf ...");
|
||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
|
||||
|
||||
_d("Calling reconf hooks ...");
|
||||
plugin_run_hooks(HOOK_SVC_RECONF);
|
||||
|
||||
service_step_all(SVC_TYPE_SERVICE | SVC_TYPE_INETD);
|
||||
_d("Reconfiguration done");
|
||||
|
||||
sm->state = SM_RUNNING_STATE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sm->state != old_state) {
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,58 @@
|
||||
/* Finit state machine
|
||||
*
|
||||
* Copyright (c) 2016 Jonas Johansson <jonas.johansson@westermo.se>
|
||||
*
|
||||
* 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_SM_H_
|
||||
#define FINIT_SM_H_
|
||||
|
||||
typedef enum {
|
||||
SM_BOOTSTRAP_STATE = 0, /* Init state, bootstrap services */
|
||||
SM_RUNNING_STATE, /* Normal state, services running */
|
||||
SM_RUNLEVEL_CHANGE_STATE, /* A runlevel change has occured */
|
||||
SM_RUNLEVEL_WAIT_STATE, /* Waiting for all stopped runlevel processes to be halted */
|
||||
SM_RELOAD_CHANGE_STATE, /* A reload event has occured */
|
||||
SM_RELOAD_WAIT_STATE, /* Waiting for all stopped reload processes to be halted */
|
||||
} sm_state_t;
|
||||
|
||||
typedef struct sm {
|
||||
sm_state_t state; /* Running, Changed, Waiting, ... */
|
||||
int newlevel; /* Set on runlevel change to new runlevel, -1 if not change */
|
||||
int reload; /* Set on reload event, else 0 */
|
||||
int in_teardown; /* Set when waiting for all processes to be halted */
|
||||
} sm_t;
|
||||
|
||||
extern sm_t sm;
|
||||
|
||||
void sm_init(sm_t *sm);
|
||||
void sm_step(sm_t *sm);
|
||||
void sm_set_runlevel(sm_t *sm, int newlevel);
|
||||
void sm_set_reload(sm_t *sm);
|
||||
int sm_is_in_teardown(sm_t *sm);
|
||||
|
||||
#endif /* FINIT_SM_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,646 @@
|
||||
/* Low-level service primitives and generic API for managing svc_t structures
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2015 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 <ctype.h> /* isdigit() */
|
||||
#include <stdlib.h>
|
||||
#include <strings.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "svc.h"
|
||||
#include "helpers.h"
|
||||
#include "util.h"
|
||||
|
||||
/* Each svc_t needs a unique job# */
|
||||
static int jobcounter = 1;
|
||||
|
||||
static svc_t *__connect_shm(void)
|
||||
{
|
||||
svc_t *list = finit_svc_connect();
|
||||
|
||||
if (!list) {
|
||||
/* This should never happen, but if it does we're probably
|
||||
* knee-deep in more serious problems already... */
|
||||
_e("Failed connecting to shared memory, error %d: %s", errno, strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_new - Create a new service
|
||||
* @cmd: External program to call, or 'internal' for internal inetd services
|
||||
* @id: Instance id
|
||||
* @type: Service type, one of service, task, run or inetd
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to a new &svc_t object, or %NULL if out of empty slots.
|
||||
*/
|
||||
svc_t *svc_new(char *cmd, int id, int type)
|
||||
{
|
||||
int i, job = -1;
|
||||
svc_t *svc, *list = __connect_shm();
|
||||
|
||||
/* Find first job n:o if registering multiple instances */
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (!strcmp(svc->cmd, cmd)) {
|
||||
job = svc->job;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (job == -1)
|
||||
job = jobcounter++;
|
||||
|
||||
for (i = 0; i < MAX_NUM_SVC; i++) {
|
||||
svc_t *svc = &list[i];
|
||||
|
||||
if (svc->type == SVC_TYPE_FREE) {
|
||||
char *desc;
|
||||
|
||||
memset(svc, 0, sizeof(*svc));
|
||||
svc->type = type;
|
||||
svc->job = job;
|
||||
svc->id = id;
|
||||
strlcpy(svc->cmd, cmd, sizeof(svc->cmd));
|
||||
|
||||
/* Default description, if missing */
|
||||
desc = rindex(cmd, '/');
|
||||
if (desc)
|
||||
desc++;
|
||||
else
|
||||
desc = cmd;
|
||||
strlcpy(svc->desc, desc, sizeof(svc->desc));
|
||||
|
||||
return svc;
|
||||
}
|
||||
}
|
||||
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_del - Delete a service object
|
||||
* @svc: Pointer to an &svc_t object
|
||||
*
|
||||
* Returns:
|
||||
* Always succeeds, returning POSIX OK(0).
|
||||
*/
|
||||
int svc_del(svc_t *svc)
|
||||
{
|
||||
svc->type = SVC_TYPE_FREE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_iterator - Naive iterator over all registered services.
|
||||
* @first: Get first &svc_t object, or next until end.
|
||||
*
|
||||
* Returns:
|
||||
* The first &svc_t when @first is set, otherwise the next &svc_t until
|
||||
* the end when %NULL is returned.
|
||||
*/
|
||||
svc_t *svc_iterator(int first)
|
||||
{
|
||||
static int i = 0;
|
||||
svc_t *list = __connect_shm();
|
||||
|
||||
if (first)
|
||||
i = 0;
|
||||
|
||||
while (i < MAX_NUM_SVC) {
|
||||
svc_t *svc = &list[i++];
|
||||
|
||||
if (svc->type != SVC_TYPE_FREE)
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_inetd_iterator - Naive iterator over all registered inetd services.
|
||||
* @first: Get first &svc_t object, or next until end.
|
||||
*
|
||||
* Returns:
|
||||
* The first inetd &svc_t when @first is set, otherwise the next
|
||||
* inetd &svc_t until the end when %NULL is returned.
|
||||
*/
|
||||
svc_t *svc_inetd_iterator(int first)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
|
||||
if (svc_is_inetd(svc))
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_dynamic_iterator - Naive iterator over all registered dynamic services.
|
||||
* @first: Get first &svc_t object, or next until end.
|
||||
*
|
||||
* Returns:
|
||||
* The first dynamically loaded &svc_t when @first is set, otherwise the
|
||||
* next dynamically loaded &svc_t until the end when %NULL is returned.
|
||||
*/
|
||||
svc_t *svc_dynamic_iterator(int first)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
|
||||
if (svc->mtime.tv_sec)
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_named_iterator - Iterates over all instances of a service.
|
||||
* @first: Get first &svc_t object, or next until end.
|
||||
* @cmd: Service name to look for.
|
||||
*
|
||||
* Returns:
|
||||
* The first matching &svc_t when @first is set, otherwise the next
|
||||
* &svc_t instance with the same @cmd until the end when %NULL is
|
||||
* returned.
|
||||
*/
|
||||
svc_t *svc_named_iterator(int first, char *cmd)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
|
||||
char *name = basename(svc->cmd);
|
||||
|
||||
if (!strncmp(name, cmd, strlen(name)))
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_job_iterator - Iterates over all instances of a service.
|
||||
* @first: Get first &svc_t object, or next until end.
|
||||
* @job: Job to look for.
|
||||
*
|
||||
* Returns:
|
||||
* The first matching &svc_t when @first is set, otherwise the next
|
||||
* &svc_t instance with the same @job until the end when %NULL is
|
||||
* returned.
|
||||
*/
|
||||
svc_t *svc_job_iterator(int first, int job)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(first); svc; svc = svc_iterator(0)) {
|
||||
if (svc->job == job)
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_foreach - Run a callback for each registered service
|
||||
* @cb: Callback to run for each service
|
||||
*/
|
||||
void svc_foreach(void (*cb)(svc_t *))
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
if (!cb)
|
||||
return;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0))
|
||||
cb(svc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_foreach_dynamic - Run a callback for each registered dynamic service
|
||||
* @cb: Callback to run for each dynamic service
|
||||
*/
|
||||
void svc_foreach_dynamic(void (*cb)(svc_t *))
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
if (!cb)
|
||||
return;
|
||||
|
||||
for (svc = svc_dynamic_iterator(1); svc; svc = svc_dynamic_iterator(0))
|
||||
cb(svc);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_foreach_type - Run a callback for each matching type
|
||||
* @types: Mask of service types
|
||||
* @cb: Callback to run for each matching type
|
||||
*/
|
||||
void svc_foreach_type(int types, void (*cb)(svc_t *))
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
if (!cb)
|
||||
return;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (!(svc->type & types))
|
||||
continue;
|
||||
|
||||
cb(svc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* svc_stop_completed - Have all stopped services been collected?
|
||||
*
|
||||
* Returns:
|
||||
* 1, if all stopped services have been collected. 0 otherwise.
|
||||
*/
|
||||
int svc_stop_completed(void)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0))
|
||||
if (svc->state == SVC_STOPPING_STATE)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_find - Find a service object by its full path name
|
||||
* @cmd: Full path name, e.g., /sbin/syslogd
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to an &svc_t object, or %NULL if not found.
|
||||
*/
|
||||
svc_t *svc_find(char *cmd, int id)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (svc->id == id && !strncmp(svc->cmd, cmd, strlen(svc->cmd))) {
|
||||
_d("Found a matching svc for %s", cmd);
|
||||
return svc;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_find_by_pid - Find an service object by its PID
|
||||
* @pid: Process ID to match
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to an &svc_t object, or %NULL if not found.
|
||||
*/
|
||||
svc_t *svc_find_by_pid(pid_t pid)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (svc->pid == pid)
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_find_by_jobid - Find an service object by its JOB:ID
|
||||
* @job: Job n:o
|
||||
* @id: Instance id
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to an &svc_t object, or %NULL if not found.
|
||||
*/
|
||||
svc_t *svc_find_by_jobid(int job, int id)
|
||||
{
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (svc->job == job && svc->id == id)
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_find_by_nameid - Find an service object by its basename:ID
|
||||
* @name: Process name to match
|
||||
* @id: Instance id
|
||||
*
|
||||
* Returns:
|
||||
* A pointer to an &svc_t object, or %NULL if not found.
|
||||
*/
|
||||
svc_t *svc_find_by_nameid(char *name, int id)
|
||||
{
|
||||
char *ptr;
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
ptr = strrchr(svc->cmd, '/');
|
||||
if (ptr)
|
||||
ptr++;
|
||||
else
|
||||
ptr = svc->cmd;
|
||||
|
||||
if (svc->id == id && !strcmp(name, ptr))
|
||||
return svc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_mark_dynamic - Mark dynamically loaded services for deletion.
|
||||
*
|
||||
* This function traverses the list of known services, marking all that
|
||||
* have been loaded from /etc/finit.d/ for deletion. If a .conf file
|
||||
* has been removed svc_cleanup_dynamic() will stop and delete the
|
||||
* service.
|
||||
*
|
||||
* This function is called from sighup_cb().
|
||||
*/
|
||||
void svc_mark_dynamic(void)
|
||||
{
|
||||
svc_t *svc = svc_dynamic_iterator(1);
|
||||
|
||||
while (svc) {
|
||||
*((int *)&svc->dirty) = -1;
|
||||
svc = svc_dynamic_iterator(0);
|
||||
}
|
||||
}
|
||||
|
||||
void svc_mark_dirty(svc_t *svc)
|
||||
{
|
||||
*((int *)&svc->dirty) = 1;
|
||||
}
|
||||
|
||||
void svc_mark_clean(svc_t *svc)
|
||||
{
|
||||
*((int *)&svc->dirty) = 0;
|
||||
}
|
||||
|
||||
void svc_check_dirty(svc_t *svc, struct timeval *mtime)
|
||||
{
|
||||
if (mtime && timercmp(&svc->mtime, mtime, !=))
|
||||
svc_mark_dirty(svc);
|
||||
else
|
||||
svc_mark_clean(svc);
|
||||
|
||||
svc->mtime.tv_sec = mtime ? mtime->tv_sec : 0;
|
||||
svc->mtime.tv_usec = mtime ? mtime->tv_usec : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_clean_dynamic - Stop and cleanup stale services removed from /etc/finit.d
|
||||
* @cb: Callback to run for each stale service
|
||||
*
|
||||
* This function traverses the list of known services to stop and clean
|
||||
* up all non-existing services previously marked by svc_mark_dynamic().
|
||||
*/
|
||||
void svc_clean_dynamic(void (*cb)(svc_t *))
|
||||
{
|
||||
svc_t *svc = svc_dynamic_iterator(1);
|
||||
|
||||
while (svc) {
|
||||
if (svc->dirty == -1 && cb)
|
||||
cb(svc);
|
||||
|
||||
svc = svc_dynamic_iterator(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* svc_clean_bootstrap - Remove bootstrap-only services after boot
|
||||
* @svc: Pointer to &svc_t object
|
||||
*
|
||||
* Returns:
|
||||
* %TRUE(1) if object was bootstrap-only, otherwise %FALSE(0)
|
||||
*/
|
||||
int svc_clean_bootstrap(svc_t *svc)
|
||||
{
|
||||
if (!ISOTHER(svc->runlevels, 0)) {
|
||||
svc->pid = 0;
|
||||
svc_del(svc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *svc_status(svc_t *svc)
|
||||
{
|
||||
switch (svc->state) {
|
||||
case SVC_HALTED_STATE:
|
||||
switch (svc->block) {
|
||||
case SVC_BLOCK_NONE:
|
||||
return "halted";
|
||||
|
||||
case SVC_BLOCK_MISSING:
|
||||
return "missing";
|
||||
|
||||
case SVC_BLOCK_CRASHING:
|
||||
return "crashed";
|
||||
|
||||
case SVC_BLOCK_USER:
|
||||
return "blocked";
|
||||
|
||||
case SVC_BLOCK_BUSY:
|
||||
return "busy";
|
||||
|
||||
case SVC_BLOCK_RESTARTING:
|
||||
return "restart";
|
||||
}
|
||||
|
||||
case SVC_DONE_STATE:
|
||||
return "done";
|
||||
|
||||
case SVC_STOPPING_STATE:
|
||||
switch (svc->type) {
|
||||
case SVC_TYPE_INETD_CONN:
|
||||
case SVC_TYPE_RUN:
|
||||
case SVC_TYPE_TASK:
|
||||
return "active";
|
||||
|
||||
default:
|
||||
return "stopping";
|
||||
}
|
||||
|
||||
case SVC_WAITING_STATE:
|
||||
return "waiting";
|
||||
|
||||
case SVC_READY_STATE:
|
||||
return "ready";
|
||||
|
||||
case SVC_RUNNING_STATE:
|
||||
return "running";
|
||||
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
const char *svc_dirtystr(svc_t *svc)
|
||||
{
|
||||
if (svc_is_removed(svc))
|
||||
return "removed";
|
||||
if (svc_is_updated(svc))
|
||||
return "updated";
|
||||
if (svc_is_changed(svc))
|
||||
return "UNKNOWN";
|
||||
|
||||
return "clean";
|
||||
}
|
||||
|
||||
/* Same base service, return unique ID */
|
||||
int svc_next_id(char *cmd)
|
||||
{
|
||||
int id = 0;
|
||||
svc_t *svc;
|
||||
|
||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||
if (!strcmp(svc->cmd, cmd) && id < svc->id)
|
||||
id = svc->id;
|
||||
}
|
||||
|
||||
return id + 1;
|
||||
}
|
||||
|
||||
int svc_is_unique(svc_t *svc)
|
||||
{
|
||||
svc_t *list = __connect_shm();
|
||||
int i, unique = 1;
|
||||
|
||||
for (i = 0; i < MAX_NUM_SVC; i++) {
|
||||
svc_t *s = &list[i];
|
||||
|
||||
if (svc->type == SVC_TYPE_FREE)
|
||||
continue;
|
||||
|
||||
if (s == svc)
|
||||
continue;
|
||||
|
||||
if (s->job == svc->job) {
|
||||
unique = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return unique;
|
||||
}
|
||||
|
||||
/*
|
||||
* Used by api.c (to start/stop/restart) and initctl.c (for input validation)
|
||||
*/
|
||||
int svc_parse_jobstr(char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, int))
|
||||
{
|
||||
int result = 0;
|
||||
char *input, *token, *pos;
|
||||
|
||||
input = sanitize(str, len);
|
||||
if (!input)
|
||||
return -1;
|
||||
|
||||
token = strtok_r(input, " ", &pos);
|
||||
while (token) {
|
||||
int id = 1;
|
||||
svc_t *svc;
|
||||
char *ptr = strchr(token, ':');
|
||||
|
||||
if (isdigit(token[0])) {
|
||||
int job = atonum(token);
|
||||
|
||||
if (!ptr) {
|
||||
svc = svc_job_iterator(1, job);
|
||||
if (!svc && not_found)
|
||||
result += not_found(NULL, job);
|
||||
|
||||
while (svc) {
|
||||
if (found)
|
||||
result += found(svc);
|
||||
svc = svc_job_iterator(0, job);
|
||||
}
|
||||
} else {
|
||||
*ptr++ = 0;
|
||||
id = atonum(ptr);
|
||||
job = atonum(token);
|
||||
|
||||
svc = svc_find_by_jobid(job, id);
|
||||
if (!svc && not_found)
|
||||
result += not_found(token, id);
|
||||
else if (found)
|
||||
result += found(svc);
|
||||
}
|
||||
} else {
|
||||
if (!ptr) {
|
||||
svc = svc_named_iterator(1, token);
|
||||
if (!svc && not_found)
|
||||
result += not_found(token, id);
|
||||
|
||||
while (svc) {
|
||||
if (found)
|
||||
result += found(svc);
|
||||
svc = svc_named_iterator(0, token);
|
||||
}
|
||||
} else {
|
||||
*ptr++ = 0;
|
||||
id = atonum(ptr);
|
||||
|
||||
svc = svc_find_by_nameid(token, id);
|
||||
if (!svc && not_found)
|
||||
result += not_found(token, id);
|
||||
else if (found)
|
||||
result += found(svc);
|
||||
}
|
||||
}
|
||||
|
||||
token = strtok_r(NULL, " ", &pos);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,208 @@
|
||||
/* Low-level service primitives and generic API for managing svc_t structures
|
||||
*
|
||||
* Copyright (c) 2008-2010 Claudio Matsuoka <cmatsuoka@gmail.com>
|
||||
* Copyright (c) 2008-2015 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_SVC_H_
|
||||
#define FINIT_SVC_H_
|
||||
|
||||
#include <sys/ipc.h> /* IPC_CREAT */
|
||||
#include <sys/shm.h> /* shmat() */
|
||||
#include <sys/types.h> /* pid_t */
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "inetd.h"
|
||||
#include "helpers.h"
|
||||
|
||||
typedef int svc_cmd_t;
|
||||
|
||||
typedef enum {
|
||||
SVC_TYPE_FREE = 0, /* Free to allocate */
|
||||
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_INETD = 8, /* Classic inetd service */
|
||||
SVC_TYPE_INETD_CONN = 16, /* Single inetd connection */
|
||||
} svc_type_t;
|
||||
|
||||
#define SVC_TYPE_ANY (-1)
|
||||
|
||||
typedef enum {
|
||||
SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */
|
||||
SVC_DONE_STATE, /* Task/Run job has been run */
|
||||
SVC_STOPPING_STATE, /* Waiting to collect the child process */
|
||||
SVC_WAITING_STATE, /* Condition is in flux, process SIGSTOPed */
|
||||
SVC_READY_STATE, /* Enabled but condition not satisfied */
|
||||
SVC_RUNNING_STATE, /* Process running */
|
||||
} svc_state_t;
|
||||
|
||||
typedef enum {
|
||||
SVC_BLOCK_NONE = 0,
|
||||
SVC_BLOCK_MISSING,
|
||||
SVC_BLOCK_CRASHING,
|
||||
SVC_BLOCK_USER,
|
||||
SVC_BLOCK_BUSY,
|
||||
SVC_BLOCK_RESTARTING,
|
||||
} svc_block_t;
|
||||
|
||||
#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */
|
||||
#define MAX_ARG_LEN 64
|
||||
#define MAX_STR_LEN 64
|
||||
#define MAX_USER_LEN 16
|
||||
#define MAX_NUM_FDS 64 /* Max number of I/O plugins */
|
||||
#define MAX_NUM_SVC 64 /* Enough? */
|
||||
#define MAX_NUM_SVC_ARGS 32
|
||||
|
||||
/*
|
||||
* Default enable for all services, can be stopped by means
|
||||
* of issuing an initctl call. E.g.
|
||||
*
|
||||
* initctl <stop|start|restart> service
|
||||
*/
|
||||
typedef struct svc {
|
||||
/* Instance specifics */
|
||||
int job, id; /* JOB:ID */
|
||||
|
||||
/* Service details */
|
||||
pid_t pid;
|
||||
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
|
||||
svc_type_t type;
|
||||
struct timeval mtime; /* Modification time for .conf from /etc/finit.d/ */
|
||||
const int dirty; /* Set if old mtime != new mtime => reloaded,
|
||||
* or -1 when marked for removal */
|
||||
int starting; /* ... waiting for pidfile to be re-asserted */
|
||||
int runlevels;
|
||||
int sighup; /* This service supports SIGHUP :) */
|
||||
svc_block_t block; /* Reason that this service is currently blocked */
|
||||
char cond[MAX_ARG_LEN];
|
||||
|
||||
/* Incremented for each restart by service monitor. */
|
||||
const unsigned int restart_counter;
|
||||
|
||||
/* For inetd services */
|
||||
inetd_t inetd;
|
||||
int stdin_fd;
|
||||
|
||||
/* Set for services we need to redirect stdout/stderr to syslog */
|
||||
int log;
|
||||
|
||||
/* Identity */
|
||||
char username[MAX_USER_LEN];
|
||||
char group[MAX_USER_LEN];
|
||||
|
||||
/* Command, arguments and service description */
|
||||
char cmd[MAX_ARG_LEN];
|
||||
char args[MAX_NUM_SVC_ARGS][MAX_ARG_LEN];
|
||||
char desc[MAX_STR_LEN];
|
||||
|
||||
/*
|
||||
* Used to forcefully kill services that won't shutdown on
|
||||
* termination and to delay restarts of crashing services.
|
||||
*/
|
||||
uev_t timer;
|
||||
void (*timer_cb)(struct svc *svc);
|
||||
} svc_t;
|
||||
|
||||
typedef struct svc_map svc_map_t;
|
||||
|
||||
/* Put services array in shm */
|
||||
static inline svc_t *finit_svc_connect(void)
|
||||
{
|
||||
static void *ptr = (void *)-1;
|
||||
|
||||
if ((void *)-1 == ptr) {
|
||||
ptr = shmat(shmget(FINIT_SHM_ID, sizeof(svc_t) * MAX_NUM_SVC,
|
||||
0600 | IPC_CREAT), NULL, 0);
|
||||
if ((void *)-1 == ptr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (svc_t *)ptr;
|
||||
}
|
||||
|
||||
svc_t *svc_new (char *cmd, int id, int type);
|
||||
int svc_del (svc_t *svc);
|
||||
|
||||
svc_t *svc_find (char *cmd, int id);
|
||||
svc_t *svc_find_by_pid (pid_t pid);
|
||||
svc_t *svc_find_by_jobid (int job, int id);
|
||||
svc_t *svc_find_by_nameid (char *name, int id);
|
||||
|
||||
svc_t *svc_iterator (int first);
|
||||
svc_t *svc_inetd_iterator (int first);
|
||||
svc_t *svc_dynamic_iterator (int first);
|
||||
svc_t *svc_named_iterator (int first, char *cmd);
|
||||
svc_t *svc_job_iterator (int first, int job);
|
||||
|
||||
void svc_foreach (void (*cb)(svc_t *));
|
||||
void svc_foreach_dynamic (void (*cb)(svc_t *));
|
||||
void svc_foreach_type (int types, void (*cb)(svc_t *));
|
||||
|
||||
int svc_stop_completed (void);
|
||||
|
||||
void svc_mark_dynamic (void);
|
||||
void svc_check_dirty (svc_t *svc, struct timeval *mtime);
|
||||
void svc_mark_dirty (svc_t *svc);
|
||||
void svc_mark_clean (svc_t *svc);
|
||||
void svc_clean_dynamic (void (*cb)(svc_t *));
|
||||
int svc_clean_bootstrap (svc_t *svc);
|
||||
|
||||
char *svc_status (svc_t *svc);
|
||||
const char *svc_dirtystr (svc_t *svc);
|
||||
int svc_next_id (char *cmd);
|
||||
int svc_is_unique (svc_t *svc);
|
||||
|
||||
int svc_parse_jobstr (char *str, size_t len, int (*found)(svc_t *), int (not_found)(char *, int));
|
||||
|
||||
static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && ISSET(svc->runlevels, runlevel); }
|
||||
static inline int svc_has_sighup (svc_t *svc) { return svc && 0 != svc->sighup; }
|
||||
|
||||
static inline void svc_starting (svc_t *svc) { svc->starting = 1; }
|
||||
static inline void svc_started (svc_t *svc) { svc->starting = 0; }
|
||||
static inline int svc_is_starting (svc_t *svc) { return 0 != svc->starting; }
|
||||
|
||||
static inline int svc_is_dynamic (svc_t *svc) { return svc && 0 != svc->mtime.tv_sec; }
|
||||
static inline int svc_is_removed (svc_t *svc) { return svc && -1 == svc->dirty; }
|
||||
static inline int svc_is_changed (svc_t *svc) { return svc && 0 != svc->dirty; }
|
||||
static inline int svc_is_updated (svc_t *svc) { return svc && 1 == svc->dirty; }
|
||||
|
||||
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
|
||||
static inline int svc_is_inetd_conn(svc_t *svc) { return svc && SVC_TYPE_INETD_CONN == svc->type; }
|
||||
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == svc->type; }
|
||||
|
||||
static inline int svc_is_blocked (svc_t *svc) { return svc->block != SVC_BLOCK_NONE; }
|
||||
static inline int svc_is_busy (svc_t *svc) { return svc->block == SVC_BLOCK_BUSY; }
|
||||
static inline void svc_unblock (svc_t *svc) { svc->block = SVC_BLOCK_NONE; }
|
||||
static inline void svc_block (svc_t *svc) { svc->block = SVC_BLOCK_USER; }
|
||||
static inline void svc_busy (svc_t *svc) { svc->block = SVC_BLOCK_BUSY; }
|
||||
static inline void svc_missing (svc_t *svc) { svc->block = SVC_BLOCK_MISSING; }
|
||||
static inline void svc_restarting (svc_t *svc) { svc->block = SVC_BLOCK_RESTARTING; }
|
||||
static inline void svc_crashing (svc_t *svc) { svc->block = SVC_BLOCK_CRASHING; }
|
||||
|
||||
#endif /* FINIT_SVC_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,335 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#include <signal.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
#include "config.h" /* Generated by configure script */
|
||||
#include "finit.h"
|
||||
#include "conf.h"
|
||||
#include "helpers.h"
|
||||
#include "tty.h"
|
||||
#include "util.h"
|
||||
#include "utmp-api.h"
|
||||
|
||||
#ifdef FALLBACK_SHELL
|
||||
static pid_t fallback = 0;
|
||||
#endif
|
||||
static LIST_HEAD(, tty_node) tty_list = LIST_HEAD_INITIALIZER();
|
||||
|
||||
|
||||
/* tty [!1-9,S] <DEV> [BAUD[,BAUD,...]] [TERM] */
|
||||
int tty_register(char *line)
|
||||
{
|
||||
tty_node_t *entry;
|
||||
int insert = 0;
|
||||
char *cmd, *dev, *baud = NULL;
|
||||
char *runlevels = NULL, *term = NULL;
|
||||
|
||||
if (!line) {
|
||||
_e("Invalid input argument");
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
cmd = strtok(line, " ");
|
||||
if (!cmd) {
|
||||
incomplete:
|
||||
_e("Incomplete tty, cannot register");
|
||||
return errno = EINVAL;
|
||||
}
|
||||
|
||||
if (cmd[0] == '[') { /* [runlevels] */
|
||||
runlevels = &cmd[0];
|
||||
dev = strtok(NULL, " ");
|
||||
if (!dev)
|
||||
goto incomplete;
|
||||
} else {
|
||||
dev = cmd;
|
||||
}
|
||||
|
||||
cmd = strtok(NULL, " ");
|
||||
if (cmd) {
|
||||
baud = strdup(cmd);
|
||||
term = strtok(NULL, " ");
|
||||
}
|
||||
|
||||
entry = tty_find(dev);
|
||||
if (!entry) {
|
||||
insert = 1;
|
||||
entry = calloc(1, sizeof(*entry));
|
||||
if (!entry) {
|
||||
if (baud)
|
||||
free(baud);
|
||||
|
||||
return errno = ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
entry->data.name = strdup(dev);
|
||||
entry->data.baud = baud;
|
||||
entry->data.term = term ? strdup(term) : NULL;
|
||||
entry->data.runlevels = conf_parse_runlevels(runlevels);
|
||||
_d("Registering tty %s at %s baud with term=%s on runlevels %s", dev, baud ?: "NULL", term ?: "N/A", runlevels);
|
||||
|
||||
if (insert)
|
||||
LIST_INSERT_HEAD(&tty_list, entry, link);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
tty_node_t *tty_find(char *dev)
|
||||
{
|
||||
tty_node_t *entry;
|
||||
|
||||
LIST_FOREACH(entry, &tty_list, link) {
|
||||
if (!strcmp(dev, entry->data.name))
|
||||
return entry;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t tty_num(void)
|
||||
{
|
||||
size_t num = 0;
|
||||
tty_node_t *entry;
|
||||
|
||||
LIST_FOREACH(entry, &tty_list, link)
|
||||
num++;
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
size_t tty_num_active(void)
|
||||
{
|
||||
size_t num = 0;
|
||||
tty_node_t *entry;
|
||||
|
||||
LIST_FOREACH(entry, &tty_list, link) {
|
||||
if (entry->data.pid)
|
||||
num++;
|
||||
}
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static char *canonicalize(char *tty)
|
||||
{
|
||||
struct stat st;
|
||||
static char path[80];
|
||||
|
||||
if (!tty)
|
||||
return NULL;
|
||||
|
||||
strlcpy(path, tty, sizeof(path));
|
||||
if (stat(path, &st)) {
|
||||
snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty);
|
||||
if (stat(path, &st))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!S_ISCHR(st.st_mode))
|
||||
return NULL;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
int tty_check(char *dev)
|
||||
{
|
||||
int fd;
|
||||
struct termios c;
|
||||
|
||||
if (access(dev, F_OK))
|
||||
return 1;
|
||||
|
||||
fd = open(dev, O_RDONLY);
|
||||
if (-1 == fd)
|
||||
return 1;
|
||||
|
||||
/* XXX: Add check for errno == EIO? */
|
||||
if (tcgetattr(fd, &c)) {
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void tty_start(finit_tty_t *fitty)
|
||||
{
|
||||
int is_console = 0;
|
||||
char *tty;
|
||||
|
||||
if (fitty->pid) {
|
||||
_d("%s: TTY already active", fitty->name);
|
||||
return;
|
||||
}
|
||||
|
||||
tty = canonicalize(fitty->name);
|
||||
if (!tty) {
|
||||
_d("%s: Cannot find TTY device: %s", fitty->name, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (console && !strcmp(tty, console))
|
||||
is_console = 1;
|
||||
|
||||
if (tty_check(tty)) {
|
||||
_d("%s: Not a valid TTY: %s", tty, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
fitty->pid = run_getty(tty, fitty->baud, fitty->term, is_console);
|
||||
}
|
||||
|
||||
void tty_stop(finit_tty_t *tty)
|
||||
{
|
||||
if (!tty->pid)
|
||||
return;
|
||||
|
||||
_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;
|
||||
}
|
||||
|
||||
int tty_enabled(finit_tty_t *tty, int runlevel)
|
||||
{
|
||||
if (!tty)
|
||||
return 0;
|
||||
|
||||
if (!ISSET(tty->runlevels, runlevel))
|
||||
return 0;
|
||||
if (fexist(tty->name))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fallback shell if no TTYs are active
|
||||
*/
|
||||
int tty_fallback(pid_t lost)
|
||||
{
|
||||
#ifdef FALLBACK_SHELL
|
||||
if (lost == 1) {
|
||||
if (fallback) {
|
||||
_d("Stopping fallback shell");
|
||||
kill(fallback, SIGKILL);
|
||||
fallback = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fallback != lost || tty_num_active()) {
|
||||
_d("Not starting fallback (%d) shell, lost pid %d", fallback, lost);
|
||||
return 0;
|
||||
}
|
||||
|
||||
_d("Starting fallback shell");
|
||||
fallback = fork();
|
||||
if (fallback)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Become session leader and set controlling TTY
|
||||
* to enable Ctrl-C and job control in shell.
|
||||
*/
|
||||
setsid();
|
||||
ioctl(STDIN_FILENO, TIOCSCTTY, 1);
|
||||
|
||||
_exit(execl(_PATH_BSHELL, _PATH_BSHELL, NULL));
|
||||
#else
|
||||
(void)lost;
|
||||
#endif /* FALLBACK_SHELL */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* TTY monitor, called by service_monitor()
|
||||
*/
|
||||
int tty_respawn(pid_t pid)
|
||||
{
|
||||
tty_node_t *entry = tty_find_by_pid(pid);
|
||||
|
||||
if (!entry)
|
||||
return tty_fallback(pid);
|
||||
|
||||
/* Set DEAD_PROCESS UTMP entry */
|
||||
utmp_set_dead(pid);
|
||||
|
||||
/* Clear PID to be able to respawn it. */
|
||||
entry->data.pid = 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 fallback shell if enabled && no TTYs */
|
||||
tty_fallback(tty_num_active() > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,67 @@
|
||||
/* 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 <limits.h>
|
||||
#include <lite/queue.h> /* BSD sys/queue.h API */
|
||||
|
||||
#define EVENT_SIZE ((sizeof(struct inotify_event) + NAME_MAX + 1))
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
char *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);
|
||||
size_t tty_num (void);
|
||||
size_t tty_num_active (void);
|
||||
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:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/* Misc. shared utility functions for initctl, reboot and finit
|
||||
*
|
||||
* Copyright (c) 2016 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 <ctype.h> /* isprint() */
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
char *prognm = NULL;
|
||||
|
||||
char *progname(char *arg0)
|
||||
{
|
||||
prognm = strrchr(arg0, '/');
|
||||
if (prognm)
|
||||
prognm++;
|
||||
else
|
||||
prognm = arg0;
|
||||
|
||||
return prognm;
|
||||
}
|
||||
|
||||
void do_sleep(unsigned int sec)
|
||||
{
|
||||
while ((sec = sleep(sec)))
|
||||
;
|
||||
}
|
||||
|
||||
/* Allowed characters in job/id/name */
|
||||
static int isallowed(int ch)
|
||||
{
|
||||
return isprint(ch);
|
||||
}
|
||||
|
||||
/* Sanitize user input, make sure to NUL terminate. */
|
||||
char *sanitize(char *arg, size_t len)
|
||||
{
|
||||
size_t i = 0;
|
||||
|
||||
while (isallowed(arg[i]) && i < len)
|
||||
i++;
|
||||
|
||||
if (i + 1 < len) {
|
||||
arg[i + 1] = 0;
|
||||
return arg;
|
||||
}
|
||||
|
||||
if (i > 1 && arg[i] == 0)
|
||||
return arg;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/* Misc. shared utility functions for initctl, reboot and finit
|
||||
*
|
||||
* Copyright (c) 2016 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_UTIL_H_
|
||||
#define FINIT_UTIL_H_
|
||||
|
||||
extern char *prognm;
|
||||
|
||||
char *progname(char *arg0);
|
||||
void do_sleep(unsigned int sec);
|
||||
char *sanitize(char *arg, size_t len);
|
||||
|
||||
#endif /* FINIT_UTIL_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
/* UTMP API
|
||||
*
|
||||
* Copyright (c) 2016 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 <stdio.h>
|
||||
#include <time.h>
|
||||
#include <utmp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <lite/lite.h>
|
||||
|
||||
|
||||
static void utmp_strncpy(char *dst, const char *src, size_t dlen)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < dlen && src[i]; i++)
|
||||
dst[i] = src[i];
|
||||
|
||||
if (i < dlen)
|
||||
dst[i] = 0;
|
||||
}
|
||||
|
||||
int utmp_set(int type, int pid, char *line, char *id, char *user)
|
||||
{
|
||||
int result = 0;
|
||||
struct utmp ut;
|
||||
struct utsname uts;
|
||||
|
||||
switch (type) {
|
||||
case RUN_LVL:
|
||||
case BOOT_TIME:
|
||||
line = "~";
|
||||
id = "~~";
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
memset(&ut, 0, sizeof(ut));
|
||||
ut.ut_type = type;
|
||||
ut.ut_pid = pid;
|
||||
if (user)
|
||||
utmp_strncpy(ut.ut_user, user, sizeof(ut.ut_user));
|
||||
if (line)
|
||||
utmp_strncpy(ut.ut_line, line, sizeof(ut.ut_line));
|
||||
if (id)
|
||||
utmp_strncpy(ut.ut_id, id, sizeof(ut.ut_id));
|
||||
if (!uname(&uts))
|
||||
utmp_strncpy(ut.ut_host, uts.release, sizeof(ut.ut_host));
|
||||
ut.ut_tv.tv_sec = time(NULL);
|
||||
|
||||
if (type != DEAD_PROCESS) {
|
||||
setutent();
|
||||
result += pututline(&ut) ? 0 : 1;
|
||||
endutent();
|
||||
}
|
||||
updwtmp(_PATH_WTMP, &ut);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int utmp_set_boot(void)
|
||||
{
|
||||
return utmp_set(BOOT_TIME, 0, NULL, NULL, "reboot");
|
||||
}
|
||||
|
||||
int utmp_set_halt(void)
|
||||
{
|
||||
return utmp_set(RUN_LVL, 0, NULL, NULL, "shutdown");
|
||||
}
|
||||
|
||||
static int set_getty(int type, char *tty, char *id, char *user)
|
||||
{
|
||||
char *line;
|
||||
|
||||
if (!tty)
|
||||
line = "";
|
||||
else if (!strncmp(tty, "/dev/", 5))
|
||||
line = tty + 5;
|
||||
else
|
||||
line = tty;
|
||||
|
||||
if (!id) {
|
||||
id = line;
|
||||
if (!strncmp(id, "pts/", 4))
|
||||
id += 4;
|
||||
}
|
||||
|
||||
return utmp_set(type, getpid(), line, id, user);
|
||||
}
|
||||
|
||||
int utmp_set_init(char *tty, char *id)
|
||||
{
|
||||
return set_getty(INIT_PROCESS, tty, id, NULL);
|
||||
}
|
||||
|
||||
int utmp_set_login(char *tty, char *id)
|
||||
{
|
||||
return set_getty(LOGIN_PROCESS, tty, id, "LOGIN");
|
||||
}
|
||||
|
||||
int utmp_set_dead(int pid)
|
||||
{
|
||||
return utmp_set(DEAD_PROCESS, pid, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
static int encode(int lvl)
|
||||
{
|
||||
if (!lvl) return 0;
|
||||
return lvl + '0';
|
||||
}
|
||||
|
||||
int utmp_set_runlevel(int pre, int now)
|
||||
{
|
||||
return utmp_set(RUN_LVL, (encode(pre) << 8) | (encode(now) & 0xFF), NULL, NULL, "runlevel");
|
||||
}
|
||||
|
||||
int utmp_show(char *file)
|
||||
{
|
||||
time_t sec;
|
||||
struct utmp *ut;
|
||||
struct tm *sectm;
|
||||
|
||||
int pid;
|
||||
char id[sizeof(ut->ut_id) + 1], user[sizeof(ut->ut_user) + 1], when[80];
|
||||
|
||||
printf("%s =============================================================================================\n", file);
|
||||
utmpname(file);
|
||||
|
||||
setutent();
|
||||
while ((ut = getutent())) {
|
||||
char addr[64];
|
||||
|
||||
memset(id, 0, sizeof(id));
|
||||
strlcpy(id, ut->ut_id, sizeof(id));
|
||||
|
||||
memset(user, 0, sizeof(user));
|
||||
strlcpy(user, ut->ut_user, sizeof(user));
|
||||
|
||||
sec = ut->ut_tv.tv_sec;
|
||||
sectm = localtime(&sec);
|
||||
strftime(when, sizeof(when), "%F %T", sectm);
|
||||
|
||||
pid = ut->ut_pid;
|
||||
if (ut->ut_addr_v6[1] || ut->ut_addr_v6[2] || ut->ut_addr_v6[3])
|
||||
inet_ntop(AF_INET6, ut->ut_addr_v6, addr, sizeof(addr));
|
||||
else
|
||||
inet_ntop(AF_INET, &ut->ut_addr, addr, sizeof(addr));
|
||||
|
||||
printf("[%d] [%05d] [%-4.4s] [%-8.8s] [%-12.12s] [%-20.20s] [%-15.15s] [%-19.19s]\n",
|
||||
ut->ut_type, pid, id, user, ut->ut_line, ut->ut_host, addr, when);
|
||||
}
|
||||
endutent();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,43 @@
|
||||
/* UTMP API
|
||||
*
|
||||
* Copyright (c) 2016 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_UTMP_API_H_
|
||||
#define FINIT_UTMP_API_H_
|
||||
|
||||
int utmp_set (int type, int pid, char *line, char *id, char *user);
|
||||
int utmp_set_boot (void);
|
||||
int utmp_set_halt (void);
|
||||
int utmp_set_init (char *tty, char *id);
|
||||
int utmp_set_login (char *tty, char *id);
|
||||
int utmp_set_dead (int pid);
|
||||
int utmp_set_runlevel(int pre, int now);
|
||||
int utmp_show (char *file);
|
||||
|
||||
#endif /* FINIT_UTMP_API_H_ */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/* Built-in watchdog daemon
|
||||
*
|
||||
* Copyright (c) 2016 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 <paths.h>
|
||||
#include <signal.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/watchdog.h>
|
||||
|
||||
#include "finit.h"
|
||||
#include "helpers.h"
|
||||
#include "util.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
int running = 1;
|
||||
int handover = 0;
|
||||
|
||||
static int notify(void)
|
||||
{
|
||||
int sd, result = 255;
|
||||
ssize_t len;
|
||||
struct sockaddr_un sun = {
|
||||
.sun_family = AF_UNIX,
|
||||
.sun_path = INIT_SOCKET,
|
||||
};
|
||||
struct init_request rq;
|
||||
|
||||
sd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (-1 == sd)
|
||||
return -1;
|
||||
|
||||
if (connect(sd, (struct sockaddr*)&sun, sizeof(sun)) == -1)
|
||||
goto exit;
|
||||
|
||||
rq.cmd = INIT_CMD_WDOG_HELLO;
|
||||
rq.magic = INIT_MAGIC;
|
||||
rq.runlevel = getpid();
|
||||
len = sizeof(rq);
|
||||
if (write(sd, &rq, len) != len)
|
||||
goto exit;
|
||||
|
||||
if (read(sd, &rq, len) != len)
|
||||
goto exit;
|
||||
|
||||
result = 0;
|
||||
exit:
|
||||
close(sd);
|
||||
return result;
|
||||
}
|
||||
|
||||
static void sighandler(int signo)
|
||||
{
|
||||
if (signo == SIGTERM)
|
||||
handover = 1;
|
||||
running = 0;
|
||||
}
|
||||
|
||||
static int init(char *progname, char *devnode, int timeout)
|
||||
{
|
||||
int fd;
|
||||
|
||||
sprintf(progname, "@finit-watchdog");
|
||||
signal(SIGTERM, sighandler);
|
||||
signal(SIGPWR, sighandler);
|
||||
|
||||
fd = open(devnode, O_WRONLY);
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int loop(int fd)
|
||||
{
|
||||
int done = 0;
|
||||
|
||||
while (running) {
|
||||
do_sleep(1);
|
||||
ioctl(fd, WDIOC_KEEPALIVE, &done);
|
||||
|
||||
if (!done && !notify())
|
||||
done = 1;
|
||||
}
|
||||
|
||||
if (handover)
|
||||
return !write(fd, "V", 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void watchdog(char *progname)
|
||||
{
|
||||
if (fork() == 0) {
|
||||
int fd, ret;
|
||||
|
||||
fd = init(progname, WDT_DEVNODE, WDT_TIMEOUT);
|
||||
if (fd == -1) {
|
||||
_pe("Failed connecting to watchdog %s", WDT_DEVNODE);
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
ret = loop(fd);
|
||||
close(fd);
|
||||
|
||||
_exit(ret);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,40 @@
|
||||
/* Built-in watchdog daemon
|
||||
*
|
||||
* Copyright (c) 2016 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 "config.h" /* Generated by configure script */
|
||||
|
||||
#define WDT_DEVNODE "/dev/watchdog"
|
||||
#define WDT_TIMEOUT 3
|
||||
|
||||
#ifdef BUILTIN_WATCHDOG
|
||||
void watchdog(char *progname);
|
||||
#else
|
||||
#define watchdog(progname)
|
||||
#endif /* BUILTIN_WATCHDOG */
|
||||
|
||||
/**
|
||||
* Local Variables:
|
||||
* indent-tabs-mode: t
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
||||
Reference in new issue
Block a user