/* Systemd service wd notification for OC-2G BTS management daemon */ /* Copyright (C) 2015 by Yves Godin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include "misc/oc2gbts_mgr.h" #include "misc/oc2gbts_swd.h" #include /* Needed for service watchdog notification */ #include /* This is the period used to verify if all events have been registered to be allowed to notify the systemd service watchdog */ #define SWD_PERIOD 30 static void swd_start(struct oc2gbts_mgr_instance *mgr); static void swd_process(struct oc2gbts_mgr_instance *mgr); static void swd_close(struct oc2gbts_mgr_instance *mgr); static void swd_state_reset(struct oc2gbts_mgr_instance *mgr, int reason); static int swd_run(struct oc2gbts_mgr_instance *mgr, int from_loop); static void swd_loop_run(void *_data); enum swd_state { SWD_INITIAL, SWD_IN_PROGRESS, }; enum swd_result { SWD_FAIL_START, SWD_FAIL_NOTIFY, SWD_SUCCESS, }; static void swd_start(struct oc2gbts_mgr_instance *mgr) { swd_process(mgr); } static void swd_process(struct oc2gbts_mgr_instance *mgr) { int rc = 0, notify = 0; /* Did we get all needed conditions ? */ if (mgr->swd.swd_eventmasks == mgr->swd.swd_events) { /* Ping systemd service wd if enabled */ rc = sd_notify(0, "WATCHDOG=1"); LOGP(DSWD, LOGL_INFO, "Watchdog notification attempt\n"); notify = 1; } else { LOGP(DSWD, LOGL_INFO, "Missing watchdog events: e:0x%016llx,m:0x%016llx\n",mgr->swd.swd_events,mgr->swd.swd_eventmasks); } if (rc < 0) { LOGP(DSWD, LOGL_ERROR, "Failed to notify system service watchdog: %d\n", rc); swd_state_reset(mgr, SWD_FAIL_NOTIFY); return; } else { /* Did we notified the watchdog? */ if (notify) { mgr->swd.swd_events = 0; /* Makes sure we really cleared it in case any event was notified at this same moment (it would be lost) */ if (mgr->swd.swd_events != 0) mgr->swd.swd_events = 0; } } swd_state_reset(mgr, SWD_SUCCESS); return; } static void swd_close(struct oc2gbts_mgr_instance *mgr) { } static void swd_state_reset(struct oc2gbts_mgr_instance *mgr, int outcome) { if (mgr->swd.swd_from_loop) { mgr->swd.swd_timeout.data = mgr; mgr->swd.swd_timeout.cb = swd_loop_run; osmo_timer_schedule(&mgr->swd.swd_timeout, SWD_PERIOD, 0); } mgr->swd.state = SWD_INITIAL; swd_close(mgr); } static int swd_run(struct oc2gbts_mgr_instance *mgr, int from_loop) { if (mgr->swd.state != SWD_INITIAL) { LOGP(DSWD, LOGL_ERROR, "Swd is already in progress.\n"); return -1; } mgr->swd.swd_from_loop = from_loop; /* From now on everything will be handled from the failure */ mgr->swd.state = SWD_IN_PROGRESS; swd_start(mgr); return 0; } static void swd_loop_run(void *_data) { int rc; struct oc2gbts_mgr_instance *mgr = _data; LOGP(DSWD, LOGL_INFO, "Going to check for watchdog notification.\n"); rc = swd_run(mgr, 1); if (rc != 0) { swd_state_reset(mgr, SWD_FAIL_START); } } /* 'swd_num_events' configures the number of events to be monitored before notifying the systemd service watchdog. It must be in the range of [1,64]. Events are notified through the function 'oc2gbts_swd_event' */ int oc2gbts_swd_init(struct oc2gbts_mgr_instance *mgr, int swd_num_events) { /* Checks for a valid number of events to validate */ if (swd_num_events < 1 || swd_num_events > 64) return(-1); mgr->swd.state = SWD_INITIAL; mgr->swd.swd_timeout.data = mgr; mgr->swd.swd_timeout.cb = swd_loop_run; osmo_timer_schedule(&mgr->swd.swd_timeout, 0, 0); if (swd_num_events == 64){ mgr->swd.swd_eventmasks = 0xffffffffffffffffULL; } else { mgr->swd.swd_eventmasks = ((1ULL << swd_num_events) - 1); } mgr->swd.swd_events = 0; mgr->swd.num_events = swd_num_events; return 0; } /* Notifies that the specified event 'swd_event' happened correctly; the value must be in the range of [0,'swd_num_events'[ (see oc2gbts_swd_init). For example, if 'swd_num_events' was 64, 'swd_event' events are numbered 0 to 63. WARNING: if this function can be used from multiple threads at the same time, it must be protected with a kind of mutex to avoid loosing event notification. */ int oc2gbts_swd_event(struct oc2gbts_mgr_instance *mgr, enum mgr_swd_events swd_event) { /* Checks for a valid specified event (smaller than max possible) */ if ((int)(swd_event) < 0 || (int)(swd_event) >= mgr->swd.num_events) return(-1); mgr->swd.swd_events = mgr->swd.swd_events | ((unsigned long long int)(1) << (int)(swd_event)); /* !!! Uncomment following line to debug events notification */ LOGP(DSWD, LOGL_DEBUG,"Swd event notified: %d\n", (int)(swd_event)); return 0; }