//watchdog.c: /* * Copyright (C) Philipp 'ph3-der-loewe' Schafft - 2012-2013 * * This file is part of libroar a part of RoarAudio, * a cross-platform sound system for both, home and professional use. * See README for details. * * This file is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 * as published by the Free Software Foundation. * * libroar 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 General Public License * along with this software; see the file COPYING. If not, write to * the Free Software Foundation, 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * * NOTE for everyone want's to change something and send patches: * read README and HACKING! There a addition information on * the license of this document you need to read before you send * any patches. * * NOTE for uses of non-GPL (LGPL,...) software using libesd, libartsc * or libpulse*: * The libs libroaresd, libroararts and libroarpulse link this lib * and are therefore GPL. Because of this it may be illigal to use * them with any software that uses libesd, libartsc or libpulse*. */ #include "libroar.h" #define _CONF_DEFAULT ROAR_WATCHDOG_CONF_STOPPABLE|ROAR_WATCHDOG_CONF_CLOCK_INTERNAL; static int _config = _CONF_DEFAULT; static int _triggered = 0; static int _running = 0; static int_least32_t _timeout = 10000; // 10 sec. static int (*_callback)(enum roar_watchdog_event event) = NULL; static pid_t _starter_pid = 0; #ifdef ROAR_HAVE_ALARM static void _on_sig_alarm (int signum) { (void)signum; signal(SIGALRM, _on_sig_alarm); alarm(_timeout/1000); roar_watchdog_tick(); } #endif static int __event_handler(enum roar_watchdog_event event) { int ret; if ( event == ROAR_WATCHDOG_DOUBLETIMEOUT ) { ROAR_ERR("Watchdog event: DoubleTimeOut. Bad. Terminating program."); // panic... roar_panic(ROAR_FATAL_ERROR_WATCHDOG, NULL); } else if ( event == ROAR_WATCHDOG_TIMEOUT ) { ROAR_ERR("Watchdog event: TimeOut. Cleaning Up."); } if ( _callback != NULL ) { if ( (_config & ROAR_WATCHDOG_CONF_EVENTS_ALSO_MINOR) || event == ROAR_WATCHDOG_TIMEOUT || event == ROAR_WATCHDOG_START || event == ROAR_WATCHDOG_STOP ) { ret = _callback(event); if ( ret == -1 && event == ROAR_WATCHDOG_TIMEOUT ) __event_handler(ROAR_WATCHDOG_DOUBLETIMEOUT); } } if ( event == ROAR_WATCHDOG_TIMEOUT ) { ROAR_ERR("Watchdog event: TimeOut. Terminating program."); roar_panic(ROAR_FATAL_ERROR_WATCHDOG, NULL); } return 0; } // start the watchdog. // timeout is in ms. int roar_watchdog_start(int config, int_least32_t timeout, int (*callback)(enum roar_watchdog_event event)) { int ret = -1; if ( config == ROAR_WATCHDOG_CONF_DEFAULTS ) { config = _CONF_DEFAULT; } else if ( config == ROAR_WATCHDOG_CONF_RESTART ) { config = _config; if ( timeout == -1 ) timeout = _timeout; if ( callback == NULL ) callback = _callback; } if ( timeout < 1 ) { roar_err_set(ROAR_ERROR_CAUSALITY); return -1; } if ( _running ) { if ( _config & ROAR_WATCHDOG_CONF_STOPPABLE ) { if ( roar_watchdog_stop() == -1 ) return -1; } else { roar_err_set(ROAR_ERROR_BUSY); return -1; } } if ( !(config & ROAR_WATCHDOG_CONF_CLOCK_EXTERNAL) ) { #ifdef ROAR_HAVE_ALARM if ( config & ROAR_WATCHDOG_CONF_CLOCK_ROUND_UP ) { if ( timeout % 1000 ) timeout += 1000; } timeout /= 1000; if ( timeout == 0 ) timeout = 1; timeout *= 1000; #else roar_err_set(ROAR_ERROR_NOSYS); return -1; #endif } _config = config; _timeout = timeout; _callback = callback; if ( config & ROAR_WATCHDOG_CONF_CLOCK_EXTERNAL ) { ret = __event_handler(ROAR_WATCHDOG_START); #ifdef ROAR_HAVE_ALARM } else { signal(SIGALRM, _on_sig_alarm); alarm(_timeout/1000); ret = 0; #endif } if ( ret == -1 ) return -1; _running = 1; _starter_pid = getpid(); return 0; } // Get timeout for watchdog. // This may be diffrent from the requested time. int_least32_t roar_watchdog_gettime(void) { if ( !_running ) { roar_err_set(ROAR_ERROR_NOENT); return -1; } return _timeout; } // stop it, if stopping is enabled. int roar_watchdog_stop(void) { int ret; if ( !_running ) return 0; // test if we are allowed to stop the watchdog. // In addition always allow to stop it in childs. if ( !(_config & ROAR_WATCHDOG_CONF_STOPPABLE) && _starter_pid == getpid() ) { ROAR_WARN("roar_watchdog_stop(void): Software tries to stop watchdog but is not allowed to."); roar_err_set(ROAR_ERROR_PERM); return -1; } #ifdef ROAR_HAVE_ALARM if ( !(_config & ROAR_WATCHDOG_CONF_CLOCK_EXTERNAL) ) { alarm(0); _running = 0; } #endif ret = __event_handler(ROAR_WATCHDOG_STOP); if ( ret != -1 ) _running = 0; return ret; } // Trigger the watchdog to show that we are still alive. int roar_watchdog_trigger(void) { if ( !_running ) return 0; _triggered = 1; return __event_handler(ROAR_WATCHDOG_TRIGGER); } // Trigger the watchdog clock. This is only used if configured to run with external clock source. int roar_watchdog_tick(void) { int ret = 0; ROAR_DBG("roar_watchdog_tick(void) = ?"); if ( !_running ) return 0; if ( _triggered == 0 ) { _triggered = -1; ret = __event_handler(ROAR_WATCHDOG_TIMEOUT); } else if ( _triggered == -1 ) { ret = __event_handler(ROAR_WATCHDOG_DOUBLETIMEOUT); } else { _triggered = 0; } return ret; } //ll