/* Event aggregator, also serves as event cache, remembering GW and IFUP states * * Copyright (c) 2015 Joachim Nilsson * * 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: */