mirror of
https://github.com/troglobit/finit.git
synced 2026-11-09 16:21:24 +00:00
Merge pull request #19 from westermo/wmo/2.4
Service state machine and refactored event/condition handling Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
commit
901bdc68ba
24 files changed
+1297
-919
No files matched your search
@@ -1,3 +1,4 @@
|
|||||||
|
*~
|
||||||
*.o
|
*.o
|
||||||
*.d
|
*.d
|
||||||
*.so
|
*.so
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ ARCHTOOL = `which git-archive-all`
|
|||||||
ARCHIVE = $(PKG).tar
|
ARCHIVE = $(PKG).tar
|
||||||
ARCHIVEZ = ../$(ARCHIVE).xz
|
ARCHIVEZ = ../$(ARCHIVE).xz
|
||||||
EXEC = finit initctl reboot
|
EXEC = finit initctl reboot
|
||||||
HEADERS = finit.h plugin.h svc.h inetd.h helpers.h queue.h
|
HEADERS = finit.h plugin.h svc.h inetd.h helpers.h queue.h cond.h
|
||||||
DISTFILES = LICENSE README ChangeLog finit.conf services
|
DISTFILES = LICENSE README ChangeLog finit.conf services
|
||||||
OBJS = finit.o api.o client.o conf.o exec.o helpers.o pid.o sig.o \
|
OBJS = finit.o api.o client.o conf.o exec.o helpers.o pid.o sig.o \
|
||||||
svc.o service.o plugin.o tty.o inetd.o event.o
|
svc.o service.o plugin.o tty.o inetd.o cond.o cond-w.o
|
||||||
DEPLIBS =
|
DEPLIBS =
|
||||||
TOPDIR = $(shell pwd)
|
TOPDIR = $(shell pwd)
|
||||||
-include config.mk
|
-include config.mk
|
||||||
@@ -70,7 +70,7 @@ config.h: configure
|
|||||||
|
|
||||||
finit: $(OBJS) $(DEPLIBS)
|
finit: $(OBJS) $(DEPLIBS)
|
||||||
|
|
||||||
initctl: initctl.o svc.o helpers.o $(DEPLIBS)
|
initctl: initctl.o svc.o helpers.o cond.o $(DEPLIBS)
|
||||||
|
|
||||||
reboot: reboot.o $(DEPLIBS)
|
reboot: reboot.o $(DEPLIBS)
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
|
#include "cond.h"
|
||||||
#include "conf.h"
|
#include "conf.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "plugin.h"
|
#include "plugin.h"
|
||||||
@@ -46,7 +47,7 @@ uev_t api_watcher;
|
|||||||
/* Allowed characters in job/id/name */
|
/* Allowed characters in job/id/name */
|
||||||
static int isallowed(int ch)
|
static int isallowed(int ch)
|
||||||
{
|
{
|
||||||
return isalnum(ch) || isspace(ch) || ch == ':';
|
return isprint(ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sanitize user input, make sure to NUL terminate. */
|
/* Sanitize user input, make sure to NUL terminate. */
|
||||||
@@ -113,14 +114,29 @@ static int call(int (*action)(svc_t *), char *buf, size_t len)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int service_pause(svc_t *svc)
|
static int service_block(svc_t *svc)
|
||||||
{
|
{
|
||||||
return service_stop(svc, SVC_PAUSED_STATE);
|
svc->block = SVC_BLOCK_USER;
|
||||||
|
service_step(svc);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_start (char *buf, size_t len) { return call(service_start, buf, len); }
|
static int service_unblock(svc_t *svc)
|
||||||
static int do_pause (char *buf, size_t len) { return call(service_pause, buf, len); }
|
{
|
||||||
static int do_reload (char *buf, size_t len) { return call(service_reload, buf, len); }
|
svc->block = SVC_BLOCK_NONE;
|
||||||
|
service_step(svc);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int service_restart(svc_t *svc)
|
||||||
|
{
|
||||||
|
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); }
|
static int do_restart(char *buf, size_t len) { return call(service_restart, buf, len); }
|
||||||
|
|
||||||
#ifndef INETD_DISABLED
|
#ifndef INETD_DISABLED
|
||||||
@@ -155,9 +171,7 @@ typedef struct {
|
|||||||
} ev_t;
|
} ev_t;
|
||||||
|
|
||||||
ev_t ev_list[] = {
|
ev_t ev_list[] = {
|
||||||
{ "RELOAD", conf_reload_dynamic },
|
{ "RELOAD", service_reload_dynamic },
|
||||||
{ "STOP", service_stop_dynamic },
|
|
||||||
{ "START", service_start_dynamic },
|
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -175,9 +189,13 @@ static int do_handle_event(char *event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* XXX: iterate over all services' events before failing. */
|
if (event[0] == '-')
|
||||||
|
cond_clear(&event[1]);
|
||||||
return -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)
|
static int do_handle_emit(char *buf, size_t len)
|
||||||
@@ -284,10 +302,6 @@ static void cb(uev_t *w, void *UNUSED(arg), int UNUSED(events))
|
|||||||
result = do_pause(rq.data, sizeof(rq.data));
|
result = do_pause(rq.data, sizeof(rq.data));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case INIT_CMD_RELOAD_SVC:
|
|
||||||
result = do_reload(rq.data, sizeof(rq.data));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case INIT_CMD_RESTART_SVC:
|
case INIT_CMD_RESTART_SVC:
|
||||||
result = do_restart(rq.data, sizeof(rq.data));
|
result = do_restart(rq.data, sizeof(rq.data));
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
# Top directory for building complete system, fall back to this directory
|
# Top directory for building complete system, fall back to this directory
|
||||||
ROOTDIR ?= $(TOPDIR)
|
ROOTDIR ?= $(TOPDIR)
|
||||||
|
|
||||||
|
# Strip binaries unless user explicitly disables it
|
||||||
|
STRIPARGS ?= -s --strip-program=$(CROSS)strip -m 0755
|
||||||
|
|
||||||
# Some junk files we always want to be removed when doing a make clean.
|
# Some junk files we always want to be removed when doing a make clean.
|
||||||
JUNK = *~ *.bak *.map .*.d *.d DEADJOE semantic.cache *.gdb *.elf core core.*
|
JUNK = *~ *.bak *.map .*.d *.d DEADJOE semantic.cache *.gdb *.elf core core.*
|
||||||
MAKE := @$(MAKE)
|
MAKE := @$(MAKE)
|
||||||
MAKEFLAGS = --no-print-directory --silent
|
MAKEFLAGS = --no-print-directory --silent
|
||||||
CHECK := cppcheck $(CPPFLAGS) --quiet --enable=all
|
CHECK := cppcheck $(CPPFLAGS) --quiet --enable=all
|
||||||
INSTALL := install --backup=off
|
INSTALL := install --backup=off
|
||||||
STRIPINST := $(INSTALL) -s --strip-program=$(CROSS)strip -m 0755
|
STRIPINST := $(INSTALL) $(STRIPARGS)
|
||||||
ARFLAGS := crus
|
ARFLAGS := crus
|
||||||
|
|
||||||
export libdir plugindir incdir ROOTDIR CPPFLAGS LDFLAGS LDLIBS STATIC
|
export libdir plugindir incdir ROOTDIR CPPFLAGS LDFLAGS LDLIBS STATIC
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
#include <libgen.h>
|
||||||
|
#include <libite/lite.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <utime.h>
|
||||||
|
|
||||||
|
#include "finit.h"
|
||||||
|
#include "cond.h"
|
||||||
|
#include "service.h"
|
||||||
|
|
||||||
|
int cond_set_path(const char *path, enum cond_state new)
|
||||||
|
{
|
||||||
|
static char dir[MAX_ARG_LEN];
|
||||||
|
|
||||||
|
enum cond_state old;
|
||||||
|
|
||||||
|
old = cond_get_path(path);
|
||||||
|
|
||||||
|
switch (new) {
|
||||||
|
case COND_ON:
|
||||||
|
strlcpy(dir, path, sizeof(dir));
|
||||||
|
makepath(dirname(dir));
|
||||||
|
touch(path);
|
||||||
|
utime(path, NULL);
|
||||||
|
break;
|
||||||
|
case COND_OFF:
|
||||||
|
unlink(path);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
_e("Invalid condition state");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new != old;
|
||||||
|
}
|
||||||
|
|
||||||
|
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->type != SVC_TYPE_SERVICE ||
|
||||||
|
!svc->cond[0] ||
|
||||||
|
(name && !cond_affects(name, svc->cond))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_d("%s: match <%s> %s", name, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* version-control: t
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
/* Event aggregator, also serves as event cache, remembering GW and IFUP states
|
||||||
|
*
|
||||||
|
* 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 <libite/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)
|
||||||
|
{
|
||||||
|
static char conds[MAX_ARG_LEN];
|
||||||
|
|
||||||
|
enum cond_state s = COND_ON;
|
||||||
|
char *cond;
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
static char conds[MAX_ARG_LEN];
|
||||||
|
|
||||||
|
char *cond;
|
||||||
|
|
||||||
|
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:
|
||||||
|
* version-control: t
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -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);
|
||||||
|
|
||||||
|
#endif /* FINIT_COND_H_ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* version-control: t
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -28,6 +28,7 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
|
#include "cond.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
#include "tty.h"
|
#include "tty.h"
|
||||||
#include "libite/lite.h"
|
#include "libite/lite.h"
|
||||||
@@ -113,7 +114,7 @@ int conf_parse_runlevels(char *runlevels)
|
|||||||
return bitmask;
|
return bitmask;
|
||||||
}
|
}
|
||||||
|
|
||||||
void conf_parse_events(svc_t *svc, char *events)
|
void conf_parse_cond(svc_t *svc, char *cond)
|
||||||
{
|
{
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
char *ptr;
|
char *ptr;
|
||||||
@@ -126,11 +127,11 @@ void conf_parse_events(svc_t *svc, char *events)
|
|||||||
/* By default we assume UNIX daemons support SIGHUP */
|
/* By default we assume UNIX daemons support SIGHUP */
|
||||||
svc->sighup = 1;
|
svc->sighup = 1;
|
||||||
|
|
||||||
if (!events)
|
if (!cond)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* First character must be '!' if SIGHUP is not supported. */
|
/* First character must be '!' if SIGHUP is not supported. */
|
||||||
ptr = events;
|
ptr = cond;
|
||||||
if (ptr[i] == '!') {
|
if (ptr[i] == '!') {
|
||||||
svc->sighup = 0;
|
svc->sighup = 0;
|
||||||
ptr++;
|
ptr++;
|
||||||
@@ -140,13 +141,12 @@ void conf_parse_events(svc_t *svc, char *events)
|
|||||||
i++;
|
i++;
|
||||||
ptr[i] = 0;
|
ptr[i] = 0;
|
||||||
|
|
||||||
if (i >= sizeof(svc->events)) {
|
if (i >= sizeof(svc->cond)) {
|
||||||
FLOG_WARN("Too long event list in declaration of %s: %s", svc->cmd, ptr);
|
FLOG_WARN("Too long event list in declaration of %s: %s", svc->cmd, ptr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
svc->state = SVC_CONDHALT_STATE;
|
strlcpy(svc->cond, ptr, sizeof(svc->cond));
|
||||||
strlcpy(svc->events, ptr, sizeof(svc->events));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_static(char *line)
|
static void parse_static(char *line)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
void conf_parse_cmdline (void);
|
void conf_parse_cmdline (void);
|
||||||
int conf_parse_runlevels (char *runlevels);
|
int conf_parse_runlevels (char *runlevels);
|
||||||
void conf_parse_events (svc_t *svc, char *events);
|
void conf_parse_cond (svc_t *svc, char *cond);
|
||||||
int conf_parse_config (void);
|
int conf_parse_config (void);
|
||||||
void conf_reload_dynamic (void);
|
void conf_reload_dynamic (void);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
Finit Conditions
|
||||||
|
================
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
Table of Contents
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
* [Introduction](#introduction)
|
||||||
|
* [Triggering](#triggering)
|
||||||
|
* [Built-in Conditions](#built-in--conditions)
|
||||||
|
* [Debugging](#debugging)
|
||||||
|
* [Internals](#internals)
|
||||||
|
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
|
||||||
|
In addition to runlevels, services can declare user-defined conditions
|
||||||
|
as dependencies. Conditions are specified within angle brackets (<>) in
|
||||||
|
the service stanza. Multiple conditions may be specified separated by
|
||||||
|
comma. Conditions are AND'ed during evaluation, i.e. all conditions
|
||||||
|
must be satisfied in order for a service to run.
|
||||||
|
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```shell
|
||||||
|
service [2345] <svc/sbin/setupd,svc/sbin/zebra> /sbin/netd -- Network monitor
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example the Network monitor daemon `netd` is not started until
|
||||||
|
both the `svc/sbin/setupd` *and* `svc/sbin/zebra` conditions are
|
||||||
|
satisfied. An `svc` condition is satisfied by the corresponding
|
||||||
|
service's pidfile being created.
|
||||||
|
|
||||||
|
|
||||||
|
Triggering
|
||||||
|
----------
|
||||||
|
|
||||||
|
Conditions are triggered by using the `emit` command of the `initctl`
|
||||||
|
control tool.
|
||||||
|
|
||||||
|
* `initctl emit +your/cond/here`
|
||||||
|
|
||||||
|
To set a condition
|
||||||
|
|
||||||
|
* `initctl emit -your/cond/here`
|
||||||
|
|
||||||
|
To clear a condition
|
||||||
|
|
||||||
|
Conditions retain their current state until the next reconfiguration or
|
||||||
|
runlevel change. At that point all set conditions transition into the
|
||||||
|
`flux` state, meaning the condition's state is unknown. (See
|
||||||
|
[Internals](#internals) for the rationale behind this.) Thus, after a
|
||||||
|
reconfiguration it is up to the "owner" of the condition to convey the
|
||||||
|
new (or possibly unchanged) state of it.
|
||||||
|
|
||||||
|
|
||||||
|
Built-in Conditions
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Finit is distributed with the `pidfile`-plugin. If enabled, it will
|
||||||
|
watch `/var/run/` for pidfiles created by services that it controls
|
||||||
|
and set a corresponding condition in the `svc/` namespace.
|
||||||
|
|
||||||
|
For example, if Finit starts the `/sbin/netd` daemon and it creates the
|
||||||
|
file `/var/run/netd.pid`, the condition `svc/sbin/netd` is satisfied.
|
||||||
|
If the file is removed, the condition is cleared.
|
||||||
|
|
||||||
|
|
||||||
|
Debugging
|
||||||
|
---------
|
||||||
|
|
||||||
|
If a service is not being started as it should, the problem might be
|
||||||
|
that one of its conditions are not in the expected state. Use the
|
||||||
|
command `initctl status` to inspect service status. Services in the
|
||||||
|
`ready` state are pending a condition.
|
||||||
|
|
||||||
|
In that situation, running `initctl cond show` reveals which of the
|
||||||
|
conditions that are not satisfied. Listed as `off` below.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
|
||||||
|
```shell
|
||||||
|
~ # initctl cond show
|
||||||
|
PID Service Status Condition (+ on, ~ flux, - off)
|
||||||
|
===============================================================================
|
||||||
|
1419 /sbin/netd on <+svc/sbin/setupd,+svc/sbin/zebra>
|
||||||
|
0 /sbin/udhcpc off <-net/vlan1/exist>
|
||||||
|
```
|
||||||
|
|
||||||
|
Here we can see that `netd` is allowed to run since both its conditions
|
||||||
|
are in the `on` state, as indicated by the `+`-prefix. `udhcpc` however
|
||||||
|
is not allowed to run since `net/vlan1/exist` condition is not satsifed.
|
||||||
|
As indicated by the `-`-prefix.
|
||||||
|
|
||||||
|
|
||||||
|
Internals
|
||||||
|
---------
|
||||||
|
|
||||||
|
A condition is always in one of three states:
|
||||||
|
|
||||||
|
* `on`: The condition is asserted.
|
||||||
|
* `off`: The condition is deasserted.
|
||||||
|
* `flux`: The conditions state is unknown.
|
||||||
|
|
||||||
|
All conditions that have not explicitly been set are interpreted as
|
||||||
|
being in the `off` state.
|
||||||
|
|
||||||
|
When a reconfiguration is requested, Finit transitions all conditions to
|
||||||
|
the `flux` state. As a result, services that depend on a condition are
|
||||||
|
sent `SIGSTOP`. Once the new state of the condition is asserted, the
|
||||||
|
service receives `SIGCONT`. If the condition is no longer satisfied the
|
||||||
|
service will then be stopped, otherwise no further action is taken.
|
||||||
|
|
||||||
|
This STOP/CONT handling minimizes the number of unnecessary service
|
||||||
|
restarts that would otherwise occur because a depending service was sent
|
||||||
|
`SIGHUP` for example.
|
||||||
|
|
||||||
|
Therefore, any plugin that supplies Finit with conditions must ensure
|
||||||
|
that their state is updated after each reconfiguration. This can be
|
||||||
|
done by binding to the `HOOK_SVC_RECONF` hook. For an example of how
|
||||||
|
to do this, see `plugins/pidfile.c`.
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# Services
|
||||||
|
|
||||||
|
## State Machine
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
A service is bound to a state machine that is in one of six (6)
|
||||||
|
states. For `run`s and `task`s there is an additional end state called
|
||||||
|
`DONE`, not shown in the diagram for brevity. All services start in
|
||||||
|
the `HALTED` state.
|
||||||
|
|
||||||
|
The current state depends on the two following conditions:
|
||||||
|
|
||||||
|
* `E`: Service enabled. In order for `E` to be satisfied, the service
|
||||||
|
must be allowed to run in the current runlevel and must not be
|
||||||
|
blocked. A service may be blocked for several reasons:
|
||||||
|
|
||||||
|
- The user has manually stopped the service using `initctl stop
|
||||||
|
JOB`.
|
||||||
|
|
||||||
|
- The binary repeatedly dies after being started. I.e. keeps
|
||||||
|
crashing.
|
||||||
|
|
||||||
|
- The binary is missing in the filesystem.
|
||||||
|
|
||||||
|
* `C`: Service conditions are satisfied.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
@@ -1,241 +0,0 @@
|
|||||||
/* Event aggregator, also serves as event cache, remembering GW and IFUP states
|
|
||||||
*
|
|
||||||
* 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 "config.h" /* Generated by configure script */
|
|
||||||
#include "libite/lite.h"
|
|
||||||
|
|
||||||
#include "finit.h"
|
|
||||||
#include "service.h"
|
|
||||||
|
|
||||||
struct ifc {
|
|
||||||
LIST_ENTRY(ifc) link;
|
|
||||||
|
|
||||||
int updown;
|
|
||||||
char ifname[IFNAMSIZ];
|
|
||||||
};
|
|
||||||
|
|
||||||
static int gwup = 0;
|
|
||||||
static LIST_HEAD(, ifc) ifcache = LIST_HEAD_INITIALIZER();
|
|
||||||
|
|
||||||
|
|
||||||
static int cache_gw(char *updown)
|
|
||||||
{
|
|
||||||
int oldgw = gwup;
|
|
||||||
|
|
||||||
if (!strncasecmp(updown, "UP", 2))
|
|
||||||
gwup = 1;
|
|
||||||
else
|
|
||||||
gwup = 0;
|
|
||||||
|
|
||||||
return (oldgw == gwup) ? 0 : gwup == 0 ? -1 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct ifc *find_if(char *ifname)
|
|
||||||
{
|
|
||||||
struct ifc *entry;
|
|
||||||
|
|
||||||
LIST_FOREACH(entry, &ifcache, link) {
|
|
||||||
if (!strncmp(entry->ifname, ifname, sizeof(entry->ifname)))
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int alloc_if(char *ifname, int updown)
|
|
||||||
{
|
|
||||||
struct ifc *entry = calloc(1, sizeof(struct ifc));
|
|
||||||
|
|
||||||
if (!entry) {
|
|
||||||
_pe("Failed recording interface event");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
_d("Creating new node for %s", ifname);
|
|
||||||
entry->updown = updown;
|
|
||||||
strlcpy(entry->ifname, ifname, sizeof(entry->ifname));
|
|
||||||
LIST_INSERT_HEAD(&ifcache, entry, link);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int free_if(char *ifname)
|
|
||||||
{
|
|
||||||
struct ifc *entry = find_if(ifname);
|
|
||||||
|
|
||||||
if (!entry)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
LIST_REMOVE(entry, link);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cache_if(char *ifname, int updown)
|
|
||||||
{
|
|
||||||
struct ifc *entry = find_if(ifname);
|
|
||||||
|
|
||||||
if (entry) {
|
|
||||||
int old = entry->updown;
|
|
||||||
|
|
||||||
entry->updown = updown;
|
|
||||||
return old == updown ? 0 : updown ? 1 : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return alloc_if(ifname, updown);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* System events like GW/IF are cached, this function caters to that
|
|
||||||
* Returns: 0 if no change, -1 on condition low, +1 on condition high
|
|
||||||
*/
|
|
||||||
static int event_cache(char *msg)
|
|
||||||
{
|
|
||||||
if (!strncmp(msg, "GW:", 3))
|
|
||||||
return cache_gw(&msg[3]);
|
|
||||||
|
|
||||||
if (!strncmp(msg, "IFUP:", 5))
|
|
||||||
return cache_if(&msg[5], 1);
|
|
||||||
|
|
||||||
if (!strncmp(msg, "IFDN:", 5))
|
|
||||||
return cache_if(&msg[5], 0);
|
|
||||||
|
|
||||||
if (!strncmp(msg, "IFDEL:", 6))
|
|
||||||
return free_if(&msg[6]);
|
|
||||||
|
|
||||||
return 0; /* No chnage, unknown event. */
|
|
||||||
}
|
|
||||||
|
|
||||||
int event_cache_gw(void)
|
|
||||||
{
|
|
||||||
return gwup;
|
|
||||||
}
|
|
||||||
|
|
||||||
int event_cache_if(char *ifname)
|
|
||||||
{
|
|
||||||
struct ifc *entry = find_if(ifname);
|
|
||||||
|
|
||||||
if (!entry)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
return entry->updown;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int has_events(char *events)
|
|
||||||
{
|
|
||||||
return events && events[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
static int matches_event(char *events, char *event)
|
|
||||||
{
|
|
||||||
return strcasestr(events, event) ? 1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int event_service_cond(char *events)
|
|
||||||
{
|
|
||||||
int cond = 1;
|
|
||||||
char *msg;
|
|
||||||
char temp[MAX_ARG_LEN];
|
|
||||||
|
|
||||||
/* No required events, condition satisfied */
|
|
||||||
if (!has_events(events))
|
|
||||||
return cond;
|
|
||||||
|
|
||||||
strlcpy(temp, events, sizeof(temp));
|
|
||||||
msg = strtok(temp, ",");
|
|
||||||
while (msg) {
|
|
||||||
_d("Looking for %s (in %s), cond %d", msg, events, cond);
|
|
||||||
|
|
||||||
if (!strncmp(msg, "GW", 2))
|
|
||||||
cond &= event_cache_gw();
|
|
||||||
else if (!strncmp(msg, "IFUP:", 5))
|
|
||||||
cond &= event_cache_if(&msg[5]);
|
|
||||||
else if (!strncmp(msg, "IFDN:", 5))
|
|
||||||
cond &= event_cache_if(&msg[5]);
|
|
||||||
else
|
|
||||||
_e("Unknown event %s, discarding.", msg);
|
|
||||||
|
|
||||||
_d("=> cond %d", cond);
|
|
||||||
msg = strtok(NULL, ",");
|
|
||||||
}
|
|
||||||
|
|
||||||
return cond;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Dispatch an event
|
|
||||||
*
|
|
||||||
* Called by initctl (via api.c) or plugins/netlink.so
|
|
||||||
* May also cache for future reference.
|
|
||||||
*/
|
|
||||||
void event_dispatch(char *msg)
|
|
||||||
{
|
|
||||||
int change;
|
|
||||||
svc_t *svc;
|
|
||||||
|
|
||||||
if (!msg) {
|
|
||||||
_e("Invalid message received.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_d("%s", msg);
|
|
||||||
change = event_cache(msg);
|
|
||||||
if (!change) {
|
|
||||||
_d("Nothing to do");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Iterate over svc_t and call service_restart() for event matches */
|
|
||||||
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
|
||||||
if (svc->type != SVC_TYPE_SERVICE ||
|
|
||||||
!service_enabled(svc, 1, NULL) ||
|
|
||||||
!has_events(svc->events) ||
|
|
||||||
!matches_event(svc->events, msg)) {
|
|
||||||
_d("No match for %s in service %s", msg, svc->cmd);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (change == 1) {
|
|
||||||
_d("%s matches <%s> %s (re)starting ...", msg, svc->events, svc->cmd);
|
|
||||||
if (!svc->pid) {
|
|
||||||
service_start(svc);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (svc->sighup)
|
|
||||||
service_reload(svc);
|
|
||||||
else
|
|
||||||
service_restart(svc);
|
|
||||||
} else { /* change == -1 */
|
|
||||||
if (svc->pid && !event_service_cond(svc->events))
|
|
||||||
service_stop(svc, SVC_CONDHALT_STATE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Local Variables:
|
|
||||||
* version-control: t
|
|
||||||
* indent-tabs-mode: t
|
|
||||||
* c-file-style: "linux"
|
|
||||||
* End:
|
|
||||||
*/
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
/* Event aggregator, also serves as event cache, remembering GW and IFUP states
|
|
||||||
*
|
|
||||||
* 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_EVENT_H_
|
|
||||||
#define FINIT_EVENT_H_
|
|
||||||
|
|
||||||
int event_cache_gw (void);
|
|
||||||
int event_cache_if (char *ifname);
|
|
||||||
int event_service_cond (char *events);
|
|
||||||
void event_dispatch (char *msg);
|
|
||||||
|
|
||||||
#endif /* FINIT_EVENT_H_ */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Local Variables:
|
|
||||||
* version-control: t
|
|
||||||
* indent-tabs-mode: t
|
|
||||||
* c-file-style: "linux"
|
|
||||||
* End:
|
|
||||||
*/
|
|
||||||
@@ -43,75 +43,8 @@
|
|||||||
#opt, inetd->name); \
|
#opt, inetd->name); \
|
||||||
} while (0);
|
} while (0);
|
||||||
|
|
||||||
/* Socket callback, looks up correct svc and starts it as an inetd service */
|
|
||||||
static void socket_cb(uev_t *w, void *arg, int UNUSED(events))
|
|
||||||
{
|
|
||||||
svc_t *svc = (svc_t *)arg;
|
|
||||||
|
|
||||||
if (SVC_START != service_enabled(svc, -1, NULL))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!svc->inetd.forking)
|
|
||||||
uev_io_stop(w);
|
|
||||||
|
|
||||||
service_start(svc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Launch Inet socket for service.
|
|
||||||
* TODO: Add filtering ALLOW/DENY per interface.
|
|
||||||
*/
|
|
||||||
static void spawn_socket(inetd_t *inetd)
|
|
||||||
{
|
|
||||||
int sd;
|
|
||||||
socklen_t len = sizeof(struct sockaddr);
|
|
||||||
struct sockaddr_in s;
|
|
||||||
|
|
||||||
if (!inetd->type) {
|
|
||||||
FLOG_ERROR("Skipping invalid inetd service %s", inetd->name);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_d("Spawning server socket for inetd %s ...", inetd->name);
|
|
||||||
sd = socket(AF_INET, inetd->type | SOCK_NONBLOCK | SOCK_CLOEXEC, inetd->proto);
|
|
||||||
if (-1 == sd) {
|
|
||||||
FLOG_PERROR("Failed opening inetd socket type %d proto %d", inetd->type, inetd->proto);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
FLOG_PERROR("Failed binding to port %d, maybe another %s server is already running",
|
|
||||||
inetd->port, inetd->name);
|
|
||||||
close(sd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inetd->port) {
|
|
||||||
if (inetd->type == SOCK_STREAM) {
|
|
||||||
if (-1 == listen(sd, 20)) {
|
|
||||||
FLOG_PERROR("Failed listening to inetd service %s", inetd->name);
|
|
||||||
close(sd);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else { /* SOCK_DGRAM */
|
|
||||||
/* Set extra sockopt to get ifindex from inbound packets */
|
|
||||||
ENABLE_SOCKOPT(sd, SOL_IP, IP_PKTINFO);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uev_io_init(ctx, &inetd->watcher, socket_cb, inetd->arg, sd, UEV_READ);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
|
/* Peek into SOCK_DGRAM socket to figure out where an inbound packet comes from. */
|
||||||
int inetd_dgram_peek(int sd, char *ifname)
|
static int inetd_dgram_peek(int sd, char *ifname)
|
||||||
{
|
{
|
||||||
char cmbuf[0x100];
|
char cmbuf[0x100];
|
||||||
struct msghdr msgh;
|
struct msghdr msgh;
|
||||||
@@ -139,7 +72,7 @@ int inetd_dgram_peek(int sd, char *ifname)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
|
/* Peek into SOCK_STREAM on accepted client socket to figure out inbound interface */
|
||||||
int inetd_stream_peek(int sd, char *ifname)
|
static int inetd_stream_peek(int sd, char *ifname)
|
||||||
{
|
{
|
||||||
struct ifaddrs *ifaddr, *ifa;
|
struct ifaddrs *ifaddr, *ifa;
|
||||||
struct sockaddr_in sin;
|
struct sockaddr_in sin;
|
||||||
@@ -173,46 +106,146 @@ int inetd_stream_peek(int sd, char *ifname)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Inetd monitor, called by service_monitor() */
|
static int get_stdin(svc_t *svc)
|
||||||
int inetd_respawn(pid_t pid)
|
|
||||||
{
|
{
|
||||||
svc_t *svc = svc_find_by_pid(pid);
|
char ifname[IF_NAMESIZE] = "UNKNOWN";
|
||||||
|
int stdin = svc->inetd.watcher.fd;
|
||||||
|
|
||||||
if (svc_is_inetd(svc)) {
|
if (svc->inetd.type == SOCK_STREAM) {
|
||||||
inetd_t *inetd = &svc->inetd;
|
/* Open new client socket from server socket */
|
||||||
|
stdin = accept(stdin, NULL, NULL);
|
||||||
|
if (stdin < 0) {
|
||||||
|
FLOG_PERROR("Failed accepting inetd service %d/tcp", svc->inetd.port);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
svc->pid = 0;
|
_d("New client socket %d accepted for inetd service %d/tcp", stdin, svc->inetd.port);
|
||||||
|
|
||||||
if (svc_in_runlevel(svc, runlevel) && !inetd->forking)
|
inetd_stream_peek(stdin, ifname);
|
||||||
uev_io_set(&inetd->watcher, inetd->watcher.fd, UEV_READ);
|
} else { /* SOCK_DGRAM */
|
||||||
|
inetd_dgram_peek(stdin, ifname);
|
||||||
return 1; /* It was us! */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0; /* Not an inetd service */
|
if (!inetd_is_allowed(&svc->inetd, ifname)) {
|
||||||
|
FLOG_INFO("Service %s on port %d not allowed from interface %s.",
|
||||||
|
svc->inetd.name, svc->inetd.port, ifname);
|
||||||
|
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 *w, void *arg, int UNUSED(events))
|
||||||
|
{
|
||||||
|
svc_t *svc = (svc_t *)arg, *task;
|
||||||
|
int stdin;
|
||||||
|
|
||||||
void inetd_start(inetd_t *inetd)
|
stdin = get_stdin(svc);
|
||||||
|
if (stdin < 0) {
|
||||||
|
FLOG_ERROR("%s: Unable to accept incoming connection",
|
||||||
|
svc->cmd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
task = svc_new(svc->cmd, svc->inetd.next_id++, SVC_TYPE_INETD_CONN);
|
||||||
|
if (!task) {
|
||||||
|
FLOG_ERROR("%s: Unable to allocate service for inetd client",
|
||||||
|
svc->cmd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy inherited attributes from inetd */
|
||||||
|
task->runlevels = svc->runlevels;
|
||||||
|
task->inetd = svc->inetd;
|
||||||
|
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 = stdin;
|
||||||
|
service_step(task);
|
||||||
|
|
||||||
|
if (!svc->inetd.forking) {
|
||||||
|
svc->block = SVC_BLOCK_INETD_BUSY;
|
||||||
|
service_step(svc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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) {
|
||||||
|
FLOG_ERROR("Skipping invalid inetd service %s", inetd->name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
_d("Spawning server socket for inetd %s ...", inetd->name);
|
||||||
|
sd = socket(AF_INET, inetd->type | SOCK_NONBLOCK | SOCK_CLOEXEC, inetd->proto);
|
||||||
|
if (-1 == sd) {
|
||||||
|
FLOG_PERROR("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) {
|
||||||
|
FLOG_PERROR("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, 20)) {
|
||||||
|
FLOG_PERROR("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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uev_io_init(ctx, &inetd->watcher, socket_cb, inetd->svc, sd, UEV_READ);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int inetd_start(inetd_t *inetd)
|
||||||
{
|
{
|
||||||
if (inetd->watcher.fd == -1)
|
if (inetd->watcher.fd == -1)
|
||||||
spawn_socket(inetd);
|
return spawn_socket(inetd);
|
||||||
|
|
||||||
|
return -EEXIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
void inetd_stop(inetd_t *inetd)
|
void inetd_stop(inetd_t *inetd)
|
||||||
{
|
{
|
||||||
svc_t *svc = (svc_t *)inetd->arg;
|
|
||||||
|
|
||||||
if (inetd->watcher.fd != -1) {
|
if (inetd->watcher.fd != -1) {
|
||||||
uev_io_stop(&inetd->watcher);
|
uev_io_stop(&inetd->watcher);
|
||||||
shutdown(inetd->watcher.fd, SHUT_RDWR);
|
shutdown(inetd->watcher.fd, SHUT_RDWR);
|
||||||
close(inetd->watcher.fd);
|
close(inetd->watcher.fd);
|
||||||
inetd->watcher.fd = -1;
|
inetd->watcher.fd = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Stop any running service, not allowed anymore. */
|
|
||||||
if (svc->pid)
|
|
||||||
service_stop(svc, SVC_WAITING_STATE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int getent(char *service, char *proto, struct servent **sv, struct protoent **pv)
|
static int getent(char *service, char *proto, struct servent **sv, struct protoent **pv)
|
||||||
@@ -497,7 +530,7 @@ int inetd_filter_str(inetd_t *inetd, char *str, size_t len)
|
|||||||
* If equivalent service exists already service_register() will instead call
|
* If equivalent service exists already service_register() will instead call
|
||||||
* inetd_allow().
|
* inetd_allow().
|
||||||
*/
|
*/
|
||||||
int inetd_new(inetd_t *inetd, char *name, char *service, char *proto, int forking, void *arg)
|
int inetd_new(inetd_t *inetd, char *name, char *service, char *proto, int forking, svc_t *svc)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
struct servent *sv = NULL;
|
struct servent *sv = NULL;
|
||||||
@@ -515,6 +548,7 @@ int inetd_new(inetd_t *inetd, char *name, char *service, char *proto, int forkin
|
|||||||
inetd->port = ntohs(sv->s_port);
|
inetd->port = ntohs(sv->s_port);
|
||||||
inetd->proto = pv->p_proto;
|
inetd->proto = pv->p_proto;
|
||||||
inetd->forking = !!forking;
|
inetd->forking = !!forking;
|
||||||
|
inetd->next_id = 2;
|
||||||
if (!name)
|
if (!name)
|
||||||
name = service;
|
name = service;
|
||||||
strlcpy(inetd->name, name, sizeof(inetd->name));
|
strlcpy(inetd->name, name, sizeof(inetd->name));
|
||||||
@@ -526,11 +560,18 @@ int inetd_new(inetd_t *inetd, char *name, char *service, char *proto, int forkin
|
|||||||
else
|
else
|
||||||
inetd->type = SOCK_DGRAM;
|
inetd->type = SOCK_DGRAM;
|
||||||
|
|
||||||
|
if (inetd->type == SOCK_DGRAM && inetd->forking) {
|
||||||
|
FLOG_WARN("%s: 'nowait' is not applicable on UDP services, ignoring",
|
||||||
|
svc->cmd);
|
||||||
|
|
||||||
|
inetd->forking = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Reset descriptor, used internally */
|
/* Reset descriptor, used internally */
|
||||||
inetd->watcher.fd = -1;
|
inetd->watcher.fd = -1;
|
||||||
|
|
||||||
/* Setup socket callback argument */
|
/* Setup socket callback argument */
|
||||||
inetd->arg = arg;
|
inetd->svc = svc;
|
||||||
|
|
||||||
_d("New service %s (default port %d proto %s:%d)", name, inetd->port, sv->s_proto, pv->p_proto);
|
_d("New service %s (default port %d proto %s:%d)", name, inetd->port, sv->s_proto, pv->p_proto);
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include "queue.h"
|
#include "queue.h"
|
||||||
#include "libuev/uev.h"
|
#include "libuev/uev.h"
|
||||||
|
|
||||||
|
typedef struct svc svc_t;
|
||||||
|
|
||||||
typedef struct inetd_filter {
|
typedef struct inetd_filter {
|
||||||
TAILQ_ENTRY(inetd_filter) link;
|
TAILQ_ENTRY(inetd_filter) link;
|
||||||
@@ -39,28 +40,25 @@ typedef struct inetd_filter {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uev_t watcher;
|
uev_t watcher;
|
||||||
void *arg; /* svc_t pointer for the socket callback */
|
svc_t *svc; /* svc_t pointer for the socket callback */
|
||||||
|
|
||||||
int type; /* Socket type: SOCK_STREAM/SOCK_DGRAM */
|
int type; /* Socket type: SOCK_STREAM/SOCK_DGRAM */
|
||||||
int std; /* Standard proto/port from /etc/services */
|
int std; /* Standard proto/port from /etc/services */
|
||||||
int proto;
|
int proto;
|
||||||
int port;
|
int port;
|
||||||
int forking;
|
int forking;
|
||||||
|
int next_id; /* Next child job's id */
|
||||||
char name[10];
|
char name[10];
|
||||||
int (*cmd)(int type); /* internal inetd service, like 'time' */
|
int (*cmd)(int type); /* internal inetd service, like 'time' */
|
||||||
|
|
||||||
TAILQ_HEAD(, inetd_filter) filters;
|
TAILQ_HEAD(, inetd_filter) filters;
|
||||||
} inetd_t;
|
} inetd_t;
|
||||||
|
|
||||||
int inetd_dgram_peek (int sd, char *ifname);
|
int inetd_start (inetd_t *inetd);
|
||||||
int inetd_stream_peek (int sd, char *ifname);
|
|
||||||
|
|
||||||
void inetd_start (inetd_t *inetd);
|
|
||||||
void inetd_stop (inetd_t *inetd);
|
void inetd_stop (inetd_t *inetd);
|
||||||
|
|
||||||
int inetd_respawn (pid_t pid);
|
int inetd_new (inetd_t *inetd, char *name, char *service, char *proto,
|
||||||
|
int forking, svc_t *svc);
|
||||||
int inetd_new (inetd_t *inetd, char *name, char *service, char *proto, int forking, void *arg);
|
|
||||||
int inetd_del (inetd_t *inetd);
|
int inetd_del (inetd_t *inetd);
|
||||||
|
|
||||||
int inetd_match (inetd_t *inetd, char *service, char *proto);
|
int inetd_match (inetd_t *inetd, char *service, char *proto);
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
|
|
||||||
#include "finit.h"
|
#include "finit.h"
|
||||||
|
#include "cond.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "service.h"
|
#include "service.h"
|
||||||
|
|
||||||
@@ -107,26 +108,78 @@ static int do_svc(int cmd, char *arg)
|
|||||||
.cmd = cmd,
|
.cmd = cmd,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!arg || !arg[0]) {
|
|
||||||
if (cmd == INIT_CMD_RELOAD_SVC) {
|
|
||||||
rq.cmd = INIT_CMD_RELOAD;
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
strlcpy(rq.data, arg, sizeof(rq.data));
|
strlcpy(rq.data, arg, sizeof(rq.data));
|
||||||
|
|
||||||
exit:
|
|
||||||
return do_send(&rq, sizeof(rq));
|
return do_send(&rq, sizeof(rq));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_emit (char *arg) { return do_svc(INIT_CMD_EMIT, arg); }
|
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 do_start (char *arg) { return do_svc(INIT_CMD_START_SVC, arg); }
|
static int do_start (char *arg) { return do_svc(INIT_CMD_START_SVC, arg); }
|
||||||
static int do_stop (char *arg) { return do_svc(INIT_CMD_STOP_SVC, arg); }
|
static int do_stop (char *arg) { return do_svc(INIT_CMD_STOP_SVC, arg); }
|
||||||
static int do_reload (char *arg) { return do_svc(INIT_CMD_RELOAD_SVC, arg); }
|
|
||||||
static int do_restart(char *arg) { return do_svc(INIT_CMD_RESTART_SVC, arg); }
|
static int do_restart(char *arg) { return do_svc(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 void show_cond(void)
|
||||||
|
{
|
||||||
|
enum cond_state cond;
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
|
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);
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int do_cond(char *arg)
|
||||||
|
{
|
||||||
|
show_cond();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int show_version(char *UNUSED(arg))
|
static int show_version(char *UNUSED(arg))
|
||||||
{
|
{
|
||||||
puts("v" VERSION);
|
puts("v" VERSION);
|
||||||
@@ -181,7 +234,12 @@ static int show_status(char *arg)
|
|||||||
else
|
else
|
||||||
snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id);
|
snprintf(jobid, sizeof(jobid), "%d:%d", svc->job, svc->id);
|
||||||
|
|
||||||
printf("%-5s %7s %-6d ", jobid, svc_status(svc), svc->pid);
|
printf("%-5s %7s ", jobid, svc_status(svc));
|
||||||
|
if (svc_is_inetd(svc))
|
||||||
|
printf("inetd ");
|
||||||
|
else
|
||||||
|
printf("%-6d ", svc->pid);
|
||||||
|
|
||||||
lvls = runlevel_string(svc->runlevels);
|
lvls = runlevel_string(svc->runlevels);
|
||||||
if (strchr(lvls, '\e'))
|
if (strchr(lvls, '\e'))
|
||||||
printf("%-18.18s ", lvls);
|
printf("%-18.18s ", lvls);
|
||||||
@@ -250,12 +308,10 @@ static int usage(int rc)
|
|||||||
" reload Reload *.conf in /etc/finit.d/ and activate changes\n"
|
" reload Reload *.conf in /etc/finit.d/ and activate changes\n"
|
||||||
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
|
" runlevel [0-9] Show or set runlevel: 0 halt, 6 reboot\n"
|
||||||
" status | show Show status of services\n"
|
" status | show Show status of services\n"
|
||||||
|
" cond show Show condition status\n"
|
||||||
" start <JOB|NAME>[:ID] Start service by job# or name, with optional ID\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"
|
" 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"
|
" restart <JOB|NAME>[:ID] Restart (stop/start) service by job# or name\n"
|
||||||
" reload <JOB|NAME>[:ID] Reload (SIGHUP) service by job# or name\n"
|
|
||||||
" status <JOB|NAME>[:ID] Show status of a service by job# or name\n"
|
|
||||||
" show <JOB|NAME>[:ID] Alias to 'status <JOB|NAME>[:ID]'\n"
|
|
||||||
" version Show Finit version\n\n", __progname);
|
" version Show Finit version\n\n", __progname);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
@@ -271,6 +327,7 @@ int main(int argc, char *argv[])
|
|||||||
{ "runlevel", do_runlevel },
|
{ "runlevel", do_runlevel },
|
||||||
{ "status", show_status },
|
{ "status", show_status },
|
||||||
{ "show", show_status }, /* Convenience alias */
|
{ "show", show_status }, /* Convenience alias */
|
||||||
|
{ "cond", do_cond },
|
||||||
{ "start", do_start },
|
{ "start", do_start },
|
||||||
{ "stop", do_stop },
|
{ "stop", do_stop },
|
||||||
{ "restart", do_restart },
|
{ "restart", do_restart },
|
||||||
|
|||||||
+1
-1
@@ -8,7 +8,7 @@ LDFLAGS += -shared
|
|||||||
ifneq ($(STATIC), 1)
|
ifneq ($(STATIC), 1)
|
||||||
PLUGINS ?= initctl.so alsa-utils.so bootmisc.so dbus.so hwclock.so \
|
PLUGINS ?= initctl.so alsa-utils.so bootmisc.so dbus.so hwclock.so \
|
||||||
resolvconf.so urandom.so x11-common.so tty.so time.so \
|
resolvconf.so urandom.so x11-common.so tty.so time.so \
|
||||||
netlink.so
|
netlink.so pidfile.so
|
||||||
DEPS := $(PLUGINS:.so=.d)
|
DEPS := $(PLUGINS:.so=.d)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|||||||
+18
-15
@@ -16,6 +16,7 @@
|
|||||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <net/if.h> /* IFNAMSIZ */
|
#include <net/if.h> /* IFNAMSIZ */
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
@@ -25,7 +26,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "../finit.h"
|
#include "../finit.h"
|
||||||
#include "../event.h"
|
#include "../cond.h"
|
||||||
#include "../helpers.h"
|
#include "../helpers.h"
|
||||||
#include "../plugin.h"
|
#include "../plugin.h"
|
||||||
|
|
||||||
@@ -86,13 +87,10 @@ static void nl_route(struct nlmsghdr *nlmsg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ((!dst && !mask) && (gw || idx)) {
|
if ((!dst && !mask) && (gw || idx)) {
|
||||||
char msg[MAX_ARG_LEN];
|
|
||||||
|
|
||||||
if (nlmsg->nlmsg_type == RTM_DELROUTE)
|
if (nlmsg->nlmsg_type == RTM_DELROUTE)
|
||||||
snprintf(msg, sizeof(msg), "GW:DN");
|
cond_clear("net/gw");
|
||||||
else
|
else
|
||||||
snprintf(msg, sizeof(msg), "GW:UP");
|
cond_set("net/gw");
|
||||||
event_dispatch(msg);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,19 +121,24 @@ static void nl_link(struct nlmsghdr *nlmsg)
|
|||||||
* New interface has appearad, or interface flags has changed.
|
* New interface has appearad, or interface flags has changed.
|
||||||
* Check ifi_flags here to see if the interface is UP/DOWN
|
* Check ifi_flags here to see if the interface is UP/DOWN
|
||||||
*/
|
*/
|
||||||
if (i->ifi_change & IFF_UP)
|
if (i->ifi_change & IFF_UP) {
|
||||||
snprintf(msg, sizeof(msg), "IF%s:%s",
|
snprintf(msg, sizeof(msg), "net/%s/up", ifname);
|
||||||
(i->ifi_flags & IFF_UP) ? "UP" : "DN",
|
|
||||||
ifname);
|
if (i->ifi_flags & IFF_UP)
|
||||||
else
|
cond_set(msg);
|
||||||
snprintf(msg, sizeof(msg), "IFADD:%s", ifname);
|
else
|
||||||
event_dispatch(msg);
|
cond_clear(msg);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
|
||||||
|
cond_set(msg);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RTM_DELLINK:
|
case RTM_DELLINK:
|
||||||
/* NOTE: Interface has dissapeared, not link down ... */
|
/* NOTE: Interface has dissapeared, not link down ... */
|
||||||
snprintf(msg, sizeof(msg), "IFDEL:%s", ifname);
|
snprintf(msg, sizeof(msg), "net/%s/exist", ifname);
|
||||||
event_dispatch(msg);
|
cond_clear(msg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RTM_NEWADDR:
|
case RTM_NEWADDR:
|
||||||
|
|||||||
@@ -0,0 +1,121 @@
|
|||||||
|
#include <limits.h>
|
||||||
|
#include <paths.h>
|
||||||
|
|
||||||
|
#include <sys/inotify.h>
|
||||||
|
|
||||||
|
#include "../finit.h"
|
||||||
|
#include "../cond.h"
|
||||||
|
#include "../helpers.h"
|
||||||
|
#include "../plugin.h"
|
||||||
|
|
||||||
|
struct context {
|
||||||
|
int fd;
|
||||||
|
int wd;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void pidfile_callback(void *UNUSED(arg), int fd, int UNUSED(events))
|
||||||
|
{
|
||||||
|
static char ev_buf[8 *(sizeof(struct inotify_event) + NAME_MAX + 1)];
|
||||||
|
static char cond[MAX_ARG_LEN];
|
||||||
|
|
||||||
|
struct inotify_event *ev;
|
||||||
|
ssize_t sz, len;
|
||||||
|
char *basename;
|
||||||
|
svc_t *svc;
|
||||||
|
|
||||||
|
sz = read(fd, ev_buf, sizeof(ev_buf));
|
||||||
|
if (sz <= 0) {
|
||||||
|
_pe("invalid inotify event\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ev = (void *)ev_buf; sz > sizeof(*ev);
|
||||||
|
len = sizeof(*ev) + ev->len, ev = (void *)ev + len, sz -= len) {
|
||||||
|
/* ev = (void *)(ev + 1) + ev->len, sz -= sizeof(*ev) + ev->len) { */
|
||||||
|
if (!ev->mask || !strstr(ev->name, ".pid"))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
basename = strtok(ev->name, ".");
|
||||||
|
svc = svc_find_by_nameid(basename, 1);
|
||||||
|
if (!svc)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* TODO FIXME XXX WKZ check that pid is controlled by finit */
|
||||||
|
|
||||||
|
_d("%s: match %s", basename, svc->cmd);
|
||||||
|
snprintf(cond, sizeof(cond), "svc%s", svc->cmd);
|
||||||
|
if (ev->mask & (IN_CREATE | IN_ATTRIB))
|
||||||
|
cond_set(cond);
|
||||||
|
else if (ev->mask & IN_DELETE)
|
||||||
|
cond_clear(cond);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pidfile_reconf(void *_null)
|
||||||
|
{
|
||||||
|
static char name[MAX_ARG_LEN];
|
||||||
|
|
||||||
|
svc_t *svc;
|
||||||
|
(void)(_null);
|
||||||
|
|
||||||
|
for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) {
|
||||||
|
if (svc->state == SVC_RUNNING_STATE && !svc_is_changed(svc)) {
|
||||||
|
snprintf(name, MAX_ARG_LEN, "svc%s", svc->cmd);
|
||||||
|
cond_set_path(cond_path(name), COND_ON);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pidfile_init (void *arg)
|
||||||
|
{
|
||||||
|
struct context *ctx = arg;
|
||||||
|
|
||||||
|
ctx->wd = inotify_add_watch(ctx->fd, _PATH_VARRUN,
|
||||||
|
IN_CREATE | IN_ATTRIB | IN_DELETE);
|
||||||
|
if (ctx->wd < 0) {
|
||||||
|
_pe("inotify_add_watch()");
|
||||||
|
close(ctx->fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_d("pidfile monitor active");
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct context pidfile_ctx;
|
||||||
|
|
||||||
|
static plugin_t plugin = {
|
||||||
|
.hook[HOOK_BASEFS_UP] = { .arg = &pidfile_ctx, .cb = pidfile_init },
|
||||||
|
.hook[HOOK_SVC_RECONF] = { .cb = pidfile_reconf },
|
||||||
|
.io = {
|
||||||
|
.cb = pidfile_callback,
|
||||||
|
.flags = PLUGIN_IO_READ,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
PLUGIN_INIT(plugin_init)
|
||||||
|
{
|
||||||
|
pidfile_ctx.fd = inotify_init();
|
||||||
|
if (pidfile_ctx.fd < 0) {
|
||||||
|
_pe("inotify_init()");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
plugin.io.fd = pidfile_ctx.fd;
|
||||||
|
plugin_register(&plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
PLUGIN_EXIT(plugin_exit)
|
||||||
|
{
|
||||||
|
inotify_rm_watch(pidfile_ctx.fd, pidfile_ctx.wd);
|
||||||
|
close(pidfile_ctx.fd);
|
||||||
|
|
||||||
|
plugin_unregister(&plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Local Variables:
|
||||||
|
* version-control: t
|
||||||
|
* indent-tabs-mode: t
|
||||||
|
* c-file-style: "linux"
|
||||||
|
* End:
|
||||||
|
*/
|
||||||
@@ -30,16 +30,13 @@
|
|||||||
void service_runlevel (int newlevel);
|
void service_runlevel (int newlevel);
|
||||||
int service_register (int type, char *line, time_t mtime, char *username);
|
int service_register (int type, char *line, time_t mtime, char *username);
|
||||||
void service_unregister (svc_t *svc);
|
void service_unregister (svc_t *svc);
|
||||||
svc_cmd_t service_enabled (svc_t *svc, int event, void *arg);
|
int service_enabled (svc_t *svc);
|
||||||
|
|
||||||
int service_start (svc_t *svc);
|
|
||||||
int service_stop (svc_t *svc, int state);
|
|
||||||
void service_start_dynamic (void);
|
|
||||||
void service_stop_dynamic (void);
|
|
||||||
int service_restart (svc_t *svc);
|
|
||||||
int service_reload (svc_t *svc);
|
|
||||||
void service_reload_dynamic (void);
|
void service_reload_dynamic (void);
|
||||||
|
|
||||||
|
void service_step(svc_t *svc);
|
||||||
|
void service_step_all(int types);
|
||||||
|
|
||||||
#endif /* FINIT_SERVICE_H_ */
|
#endif /* FINIT_SERVICE_H_ */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -352,17 +352,27 @@ void svc_mark_dynamic(void)
|
|||||||
svc_t *svc = svc_dynamic_iterator(1);
|
svc_t *svc = svc_dynamic_iterator(1);
|
||||||
|
|
||||||
while (svc) {
|
while (svc) {
|
||||||
svc->dirty = -1;
|
*((int *)&svc->dirty) = -1;
|
||||||
svc = svc_dynamic_iterator(0);
|
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, time_t mtime)
|
void svc_check_dirty(svc_t *svc, time_t mtime)
|
||||||
{
|
{
|
||||||
if (svc->mtime != mtime)
|
if (svc->mtime != mtime)
|
||||||
svc->dirty = 1;
|
svc_mark_dirty(svc);
|
||||||
else
|
else
|
||||||
svc->dirty = 0;
|
svc_mark_clean(svc);
|
||||||
svc->mtime = mtime;
|
svc->mtime = mtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,7 +391,6 @@ void svc_clean_dynamic(void (*cb)(svc_t *))
|
|||||||
if (svc->dirty == -1 && cb)
|
if (svc->dirty == -1 && cb)
|
||||||
cb(svc);
|
cb(svc);
|
||||||
|
|
||||||
svc->dirty = 0;
|
|
||||||
svc = svc_dynamic_iterator(0);
|
svc = svc_dynamic_iterator(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -406,36 +415,53 @@ int svc_clean_bootstrap(svc_t *svc)
|
|||||||
|
|
||||||
char *svc_status(svc_t *svc)
|
char *svc_status(svc_t *svc)
|
||||||
{
|
{
|
||||||
if (!svc_in_runlevel(svc, runlevel))
|
|
||||||
return "halted";
|
|
||||||
|
|
||||||
switch (svc->state) {
|
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 "crashing";
|
||||||
|
case SVC_BLOCK_USER:
|
||||||
|
return "blocked";
|
||||||
|
case SVC_BLOCK_INETD_BUSY:
|
||||||
|
return "busy";
|
||||||
|
}
|
||||||
|
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:
|
case SVC_WAITING_STATE:
|
||||||
return "waiting";
|
return "waiting";
|
||||||
|
case SVC_READY_STATE:
|
||||||
case SVC_PAUSED_STATE:
|
return "ready";
|
||||||
return "stopped";
|
|
||||||
|
|
||||||
case SVC_CONDHALT_STATE:
|
|
||||||
return "nocond";
|
|
||||||
|
|
||||||
case SVC_RESTART_STATE:
|
|
||||||
return "restart";
|
|
||||||
|
|
||||||
case SVC_RELOAD_STATE:
|
|
||||||
return "reload";
|
|
||||||
|
|
||||||
case SVC_RUNNING_STATE:
|
case SVC_RUNNING_STATE:
|
||||||
if (svc->pid)
|
return "running";
|
||||||
return "running";
|
|
||||||
/* Fall through */
|
|
||||||
|
|
||||||
case SVC_HALTED_STATE:
|
|
||||||
default:
|
default:
|
||||||
break;
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return "halted";
|
const char *svc_dirtystr(svc_t *svc)
|
||||||
|
{
|
||||||
|
if (svc_is_removed(svc))
|
||||||
|
return "removed";
|
||||||
|
else if (svc_is_updated(svc))
|
||||||
|
return "updated";
|
||||||
|
else if (svc_is_changed(svc))
|
||||||
|
return "UNKNOWN";
|
||||||
|
else
|
||||||
|
return "clean";
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Same base service, return unique ID */
|
/* Same base service, return unique ID */
|
||||||
|
|||||||
@@ -39,23 +39,33 @@ typedef enum {
|
|||||||
} svc_cmd_t;
|
} svc_cmd_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SVC_TYPE_FREE = 0, /* Free to allocate */
|
SVC_TYPE_FREE = 0, /* Free to allocate */
|
||||||
SVC_TYPE_SERVICE, /* Monitored, will be respawned */
|
SVC_TYPE_SERVICE = 1, /* Monitored, will be respawned */
|
||||||
SVC_TYPE_TASK, /* One-shot, runs in parallell */
|
SVC_TYPE_TASK = 2, /* One-shot, runs in parallell */
|
||||||
SVC_TYPE_RUN, /* Like task, but wait for completion */
|
SVC_TYPE_RUN = 4, /* Like task, but wait for completion */
|
||||||
SVC_TYPE_INETD /* Classic inetd service */
|
SVC_TYPE_INETD = 8, /* Classic inetd service */
|
||||||
|
SVC_TYPE_INETD_CONN = 16, /* Single inetd connection */
|
||||||
} svc_type_t;
|
} svc_type_t;
|
||||||
|
|
||||||
|
#define SVC_TYPE_ANY (-1)
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */
|
SVC_HALTED_STATE = 0, /* Not allowed in runlevel, or not enabled. */
|
||||||
SVC_WAITING_STATE, /* Waiting for connection (inetd service) */
|
SVC_DONE_STATE, /* Task/Run job has been run */
|
||||||
SVC_PAUSED_STATE, /* Stopped/Paused by user started on reload */
|
SVC_STOPPING_STATE, /* Waiting to collect the child process */
|
||||||
SVC_CONDHALT_STATE, /* Not allowed to run atm. event/state lost */
|
SVC_WAITING_STATE, /* Condition is in flux, process SIGSTOPed */
|
||||||
SVC_RESTART_STATE, /* Restarting service waiting to be stopped */
|
SVC_READY_STATE, /* Enabled but condition not satisfied */
|
||||||
SVC_RELOAD_STATE, /* Reloading services, after .conf changed */
|
SVC_RUNNING_STATE, /* Process running */
|
||||||
SVC_RUNNING_STATE, /* Currently running service, see svc->pid */
|
|
||||||
} svc_state_t;
|
} svc_state_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SVC_BLOCK_NONE = 0,
|
||||||
|
SVC_BLOCK_MISSING,
|
||||||
|
SVC_BLOCK_CRASHING,
|
||||||
|
SVC_BLOCK_USER,
|
||||||
|
SVC_BLOCK_INETD_BUSY,
|
||||||
|
} svc_block_t;
|
||||||
|
|
||||||
#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */
|
#define FINIT_SHM_ID 0x494E4954 /* "INIT", see ascii(7) */
|
||||||
#define MAX_ARG_LEN 64
|
#define MAX_ARG_LEN 64
|
||||||
#define MAX_STR_LEN 64
|
#define MAX_STR_LEN 64
|
||||||
@@ -73,20 +83,22 @@ typedef struct svc {
|
|||||||
|
|
||||||
/* Service details */
|
/* Service details */
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
|
const svc_state_t state; /* Paused, Reloading, Restart, Running, ... */
|
||||||
svc_type_t type;
|
svc_type_t type;
|
||||||
time_t mtime; /* Modification time for .conf from /etc/finit.d/ */
|
time_t mtime; /* Modification time for .conf from /etc/finit.d/ */
|
||||||
int dirty; /* Set if old mtime != new mtime => reloaded,
|
const int dirty; /* Set if old mtime != new mtime => reloaded,
|
||||||
* or -1 when marked for removal */
|
* or -1 when marked for removal */
|
||||||
int runlevels;
|
int runlevels;
|
||||||
int sighup; /* This service supports SIGHUP :) */
|
int sighup; /* This service supports SIGHUP :) */
|
||||||
char events[MAX_ARG_LEN];
|
svc_block_t block; /* Reason that this service is currently blocked */
|
||||||
|
char cond[MAX_ARG_LEN];
|
||||||
|
|
||||||
/* Incremented for each restart by service monitor. */
|
/* Incremented for each restart by service monitor. */
|
||||||
unsigned int restart_counter;
|
const unsigned int restart_counter;
|
||||||
|
|
||||||
/* For inetd services */
|
/* For inetd services */
|
||||||
inetd_t inetd;
|
inetd_t inetd;
|
||||||
|
int stdin;
|
||||||
|
|
||||||
/* Identity */
|
/* Identity */
|
||||||
char username[MAX_USER_LEN];
|
char username[MAX_USER_LEN];
|
||||||
@@ -144,6 +156,8 @@ void svc_foreach_dynamic (void (*cb)(svc_t *));
|
|||||||
|
|
||||||
void svc_mark_dynamic (void);
|
void svc_mark_dynamic (void);
|
||||||
void svc_check_dirty (svc_t *svc, time_t mtime);
|
void svc_check_dirty (svc_t *svc, time_t mtime);
|
||||||
|
void svc_mark_dirty (svc_t *svc);
|
||||||
|
void svc_mark_clean (svc_t *svc);
|
||||||
void svc_clean_dynamic (void (*cb)(svc_t *));
|
void svc_clean_dynamic (void (*cb)(svc_t *));
|
||||||
int svc_clean_bootstrap (svc_t *svc);
|
int svc_clean_bootstrap (svc_t *svc);
|
||||||
|
|
||||||
@@ -158,9 +172,11 @@ static inline int svc_is_dynamic(svc_t *svc) { return svc && 0 != svc->mtime; }
|
|||||||
static inline int svc_is_removed(svc_t *svc) { return svc && -1 == svc->dirty; }
|
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_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_updated(svc_t *svc) { return svc && 1 == svc->dirty; }
|
||||||
|
const char *svc_dirtystr (svc_t *svc);
|
||||||
|
|
||||||
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
|
static inline int svc_is_inetd (svc_t *svc) { return svc && SVC_TYPE_INETD == svc->type; }
|
||||||
static inline int svc_is_daemon (svc_t *svc) { return svc && SVC_TYPE_SERVICE == 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; }
|
||||||
|
|
||||||
#endif /* FINIT_SVC_H_ */
|
#endif /* FINIT_SVC_H_ */
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user