mirror of
https://github.com/troglobit/finit.git
synced 2026-10-02 05:52:48 +07:00
This commit adds support for event based services to Finit. Along with
the previously added support for multiple instances of the same process,
the feature scope for Finit v2.0 has been reached!
As of now a service can be declared in /etc/finit.conf or /etc/finit.d/
like this:
service :1 [2345] <!IFUP:eth0,GW> /sbin/dropbear -R -F -p 22 -- SSH daemon
Here the first instance `:1` of dropbear is declared to run in runlevels
2-5, but only if eth0 `IFUP:eth0` is up and a gateway `GW` is set. When
the configuration changes, a new gateway is set, or somehow a new `IFUP`
event for eth0 is received, then dropbear is not SIGHUP'ed, but instead
stop-started `<!>`. The latter trick applies to all services, even
those that do not define any events.
Currently inetd services are not supported for event based starting, but
it could easily be added. Likely scenario is to deny incoming requests
on, e.g., eth0 if there is no gateway.
Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
236 lines
5.0 KiB
C
236 lines
5.0 KiB
C
/* 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 validate_events(char *events)
|
|
{
|
|
return !events || !events[0];
|
|
}
|
|
|
|
int event_service_cond(char *events)
|
|
{
|
|
int cond = 1;
|
|
char *msg;
|
|
char temp[MAX_ARG_LEN];
|
|
|
|
/* No required events, condition satisfied */
|
|
if (validate_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 || validate_events(svc->events))
|
|
continue;
|
|
|
|
if (change == 1) {
|
|
if (!service_enabled(svc, 1, NULL))
|
|
continue;
|
|
|
|
_d("Got event %s, (re)starting %s ...", 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Local Variables:
|
|
* version-control: t
|
|
* indent-tabs-mode: t
|
|
* c-file-style: "linux"
|
|
* End:
|
|
*/
|