From d8a80d24b9a3a958808733b90fc4c6ab786bda34 Mon Sep 17 00:00:00 2001 From: Olaf Rempel Date: Sun, 3 May 2009 16:16:56 +0200 Subject: [PATCH] refactoring, restart with SIGHUP --- daemon/Makefile | 3 +- daemon/alix-usvd.c | 369 ++++----------------------------------------- daemon/signals.c | 149 ++++++++++++++++++ daemon/signals.h | 12 ++ daemon/usvdevice.c | 211 ++++++++++++++++++++++++++ daemon/usvdevice.h | 15 ++ daemon/usvstate.c | 215 ++++++++++++++++++++++++++ daemon/usvstate.h | 7 + 8 files changed, 639 insertions(+), 342 deletions(-) create mode 100644 daemon/signals.c create mode 100644 daemon/signals.h create mode 100644 daemon/usvdevice.c create mode 100644 daemon/usvdevice.h create mode 100644 daemon/usvstate.c create mode 100644 daemon/usvstate.h diff --git a/daemon/Makefile b/daemon/Makefile index a280870..acb2973 100644 --- a/daemon/Makefile +++ b/daemon/Makefile @@ -6,7 +6,8 @@ CFLAGS = -Wall -O2 -MD -MP -MF $(*F).d # ------ SRC1 := alix-usv.c logging.c unixsocket.c -SRC2 := alix-usvd.c configfile.c event.c logging.c pidfile.c unixsocket.c +SRC2 := alix-usvd.c configfile.c event.c logging.c pidfile.c signals.c unixsocket.c +SRC2 += usvdevice.c usvstate.c all: $(TARGET1) $(TARGET2) diff --git a/daemon/alix-usvd.c b/daemon/alix-usvd.c index e2282a3..b8668a6 100644 --- a/daemon/alix-usvd.c +++ b/daemon/alix-usvd.c @@ -20,61 +20,21 @@ #include #include #include -#include #include -#include -#include -#include -#include -#include - -#include "i2c-dev.h" +#include #include "configfile.h" #include "event.h" #include "logging.h" #include "pidfile.h" -#include "unixsocket.h" - -/* eeprom parameters / thresholds & defaults */ -#include "../eeprom.h" +#include "signals.h" +#include "usvdevice.h" +#include "usvstate.h" #define DEFAULT_CONFIG "alix-usvd.conf" #define DEFAULT_LOGFILE "alix-usvd.log" #define DEFAULT_PIDFILE "alix-usvd.pid" -#define DEFAULT_SOCKET "alix-usvd.sock" -#define SENDMAIL "/usr/sbin/sendmail -t" - -enum { - REG_STATUS = 0x00, - - REG_CURRENT = 0x10, - REG_UBAT = 0x11, - REG_UIN = 0x12, - - REG_UIN_LOSS = 0x20, - REG_UIN_RESTORE = 0x21, - REG_UBAT_FULL = 0x22, - REG_UBAT_LOW = 0x23, - REG_UBAT_CRIT = 0x24, - REG_IBAT_FULL = 0x25, - REG_CRC = 0x26, -}; - -enum { - STATE_IDLE = 0x01, - STATE_TEST = 0x02, - STATE_CHARGE = 0x04, - STATE_DISCHARGE = 0x08, - STATE_POWEROFF = 0x10, -}; - -enum { - I2CDEV_ERR = -1, - I2CDEV_UNKNOWN = 0, - I2CDEV_OK = 1, -}; static struct option opts[] = { {"nofork", 0, 0, 'd'}, @@ -83,288 +43,6 @@ static struct option opts[] = { {0, 0, 0, 0} }; -static int i2cdev_open(const char *i2c_device, int i2c_address) -{ - int fd = open(i2c_device, O_RDWR); - if (fd < 0) - return -1; - - unsigned long funcs; - if (ioctl(fd, I2C_FUNCS, &funcs)) { - close(fd); - return -1; - } - - if ((funcs & I2C_FUNC_SMBUS_WORD_DATA) != I2C_FUNC_SMBUS_WORD_DATA) { - close(fd); - return -1; - } - - if (ioctl(fd, I2C_SLAVE, i2c_address) < 0) { - close(fd); - return -1; - } - - return fd; -} - -static int alix_open(void) -{ - static const char *i2c_device; - static int i2c_address; - static int i2c_status = I2CDEV_UNKNOWN; - - if (i2c_device == NULL) { - i2c_device = config_get_string("global", "i2c-device", "/dev/i2c-0"); - i2c_address = config_get_int("global", "i2c-address" , 16); - } - - int dev = i2cdev_open(i2c_device, i2c_address); - if (dev < 0) { - if (i2c_status != I2CDEV_ERR) - log_print(LOG_WARN, "failed to open %s:0x%02x", i2c_device, i2c_address); - - i2c_status = I2CDEV_ERR; - return -1; - } - - if (i2c_status != I2CDEV_OK) - log_print(LOG_INFO, "successfully opened %s:0x%02x", i2c_device, i2c_address); - - i2c_status = I2CDEV_OK; - return dev; -} - -static int check_thresholds(void) -{ - int dev = alix_open(); - if (dev < 0) - return -1; - - struct ee_param defaults = DEFAULT_PARAMETERS; - - struct ee_param config; - config.uin_loss = config_get_int("thresholds", "uin_loss" , defaults.uin_loss); - config.uin_restore = config_get_int("thresholds", "uin_restore" , defaults.uin_restore); - config.ubat_full = config_get_int("thresholds", "ubat_full" , defaults.ubat_full); - config.ubat_low = config_get_int("thresholds", "ubat_low" , defaults.ubat_low); - config.ubat_critical = config_get_int("thresholds", "ubat_critical" , defaults.ubat_critical); - config.ibat_full = config_get_int("thresholds", "ibat_full" , defaults.ibat_full); - - struct ee_param device; - device.uin_loss = i2c_smbus_read_word_data(dev, REG_UIN_LOSS); - device.uin_restore = i2c_smbus_read_word_data(dev, REG_UIN_RESTORE); - device.ubat_full = i2c_smbus_read_word_data(dev, REG_UBAT_FULL); - device.ubat_low = i2c_smbus_read_word_data(dev, REG_UBAT_LOW); - device.ubat_critical = i2c_smbus_read_word_data(dev, REG_UBAT_CRIT); - device.ibat_full = i2c_smbus_read_word_data(dev, REG_IBAT_FULL); - device.crc16 = 0x0000; - - int changed = 0; - - if (device.uin_loss != config.uin_loss) { - log_print(LOG_INFO, "update UIN_LOSS: %d => %d", device.uin_loss, config.uin_loss); - i2c_smbus_write_word_data(dev, REG_UIN_LOSS, config.uin_loss); - changed = 1; - } - - if (device.uin_restore != config.uin_restore) { - log_print(LOG_INFO, "update UIN_RESTORE: %d => %d", device.uin_restore, config.uin_restore); - i2c_smbus_write_word_data(dev, REG_UIN_RESTORE, config.uin_restore); - changed = 1; - } - - if (device.ubat_full != config.ubat_full) { - log_print(LOG_INFO, "update UBAT_FULL: %d => %d", device.ubat_full, config.ubat_full); - i2c_smbus_write_word_data(dev, REG_UBAT_FULL, config.ubat_full); - changed = 1; - } - - if (device.ubat_low != config.ubat_low) { - log_print(LOG_INFO, "update UBAT_LOW: %d => %d", device.ubat_low, config.ubat_low); - i2c_smbus_write_word_data(dev, REG_UBAT_LOW, config.ubat_low); - changed = 1; - } - - if (device.ubat_critical != config.ubat_critical) { - log_print(LOG_INFO, "update UBAT_CRIT: %d => %d", device.ubat_critical, config.ubat_critical); - i2c_smbus_write_word_data(dev, REG_UBAT_CRIT, config.ubat_critical); - changed = 1; - } - - if (device.ibat_full != config.ibat_full) { - log_print(LOG_INFO, "update IBAT_FULL: %d => %d", device.ibat_full, config.ibat_full); - i2c_smbus_write_word_data(dev, REG_IBAT_FULL, config.ibat_full); - changed = 1; - } - - if (changed == 1) - i2c_smbus_write_word_data(dev, REG_CRC, 0x0000); - - close(dev); - return changed; -} - -static char * state2str(int state) -{ - switch (state) { - case STATE_IDLE: - return "IDLE"; - - case STATE_TEST: - return "TEST"; - - case STATE_CHARGE: - return "CHARGE"; - - case STATE_DISCHARGE: - return "DISCHARGE"; - - case STATE_POWEROFF: - return "POWEROFF"; - - default: - return "UNKNOWN"; - } -} - -static int str2state(char *buf) -{ - int state = -1; - if (strncasecmp(buf, "IDLE", 4) == 0) - state = STATE_IDLE; - - else if (strncasecmp(buf, "TEST", 4) == 0) - state = STATE_TEST; - - else if (strncasecmp(buf, "CHARGE", 6) == 0) - state = STATE_CHARGE; - - else if (strncasecmp(buf, "DISCHARGE", 9) == 0) - state = STATE_DISCHARGE; - - else if (strncasecmp(buf, "POWEROFF", 8) == 0) - state = STATE_POWEROFF; - - return state; -} - -static int alix_state = -1; - -static void alix_state_change(int old_state, int new_state) -{ - static const char *mail_from; - static const char *mail_to; - static int mail_data; - - if (mail_data == 0) { - mail_from = config_get_string("alerts", "mail-from", NULL); - mail_to = config_get_string("alerts", "mail-to", NULL); - mail_data = 1; - } - - log_print(LOG_INFO, "usv state changed: %s => %s", state2str(old_state), state2str(new_state)); - - if (mail_to == NULL) - return; - - FILE *mail = popen(SENDMAIL, "w"); - if (mail == NULL) - return; - - fprintf(mail, "From: %s\n", mail_from); - fprintf(mail, "To: %s\n", mail_to); - fprintf(mail, "Subject: alix-usvd state change: %s => %s\n\n", - state2str(old_state), state2str(new_state)); - - fprintf(mail, "Faithfully yours, etc.\n"); - - fclose(mail); - return; -} - -static int unix_read_cb(int fd, void *privdata) -{ - char buf[64]; - int len = read(fd, buf, sizeof(buf)); - if (len < 0) - return -1; - - int dev = alix_open(); - if (dev < 0) - return 0; - - int new_state = str2state(buf); - if (new_state != -1) { - if (i2c_smbus_write_word_data(dev, REG_STATUS, new_state) == 0) { - alix_state_change(alix_state, new_state); - alix_state = new_state; - } - - int len = snprintf(buf, sizeof(buf), "%s", state2str(alix_state)); - write(fd, buf, len); - - } else if (strncasecmp(buf, "status", 6) == 0) { - new_state = i2c_smbus_read_word_data(dev, REG_STATUS); - int adc_ibat = i2c_smbus_read_word_data(dev, REG_CURRENT); - int adc_ubat = i2c_smbus_read_word_data(dev, REG_UBAT); - int adc_uin = i2c_smbus_read_word_data(dev, REG_UIN); - - if (new_state < 0 || adc_ibat < 0 || adc_ubat < 0 || adc_uin < 0) { - new_state = -1; - adc_ibat = 0; - adc_ubat = 0; - adc_uin = 0; - - } else if (new_state != alix_state) { - alix_state_change(alix_state, new_state); - alix_state = new_state; - } - - int len = snprintf(buf, sizeof(buf), "%s:%d:%d:%d", - state2str(new_state), - (int16_t)adc_ibat, adc_ubat, adc_uin); - write(fd, buf, len); - } - - close(dev); - close(fd); - return -1; -} - -static int unix_accept_cb(int fd, void *privdata) -{ - int con = accept(fd, NULL, NULL); - if (con < 0) { - log_print(LOG_ERROR, "unix_accept_cb: accept()"); - return 0; - } - - event_add_readfd(NULL, con, unix_read_cb, NULL); - return 0; -} - -static int status_interval(void *privdata) -{ - int dev = alix_open(); - if (dev < 0) - return 0; - - int new_state = i2c_smbus_read_word_data(dev, REG_STATUS); - if (new_state == -1) { - log_print(LOG_WARN, "failed to get status from i2c-device"); - return 0; - } - - if (new_state != alix_state) { - alix_state_change(alix_state, new_state); - alix_state = new_state; - } - - close(dev); - return 0; -} - int main(int argc, char *argv[]) { char *config = DEFAULT_CONFIG; @@ -405,7 +83,7 @@ int main(int argc, char *argv[]) if (config_parse(config) < 0) exit(1); - if (debug == 0) { + if (!debug) { /* check pidfile */ const char *pidfile = config_get_string("global", "pidfile", DEFAULT_PIDFILE); if (pidfile_check(pidfile, 1) != 0) { @@ -430,24 +108,33 @@ int main(int argc, char *argv[]) log_print(LOG_INFO, "alix-usvd started (pid: %d)", getpid()); - const char *socket_path = config_get_string("global", "socket", DEFAULT_SOCKET); - int sockfd = unix_listen(socket_path); - if (sockfd < 0) - exit(1); + signal_init(); + signal_set_callback(SIGHUP, event_loop_break); - event_add_readfd(NULL, sockfd, unix_accept_cb, NULL); + while (1) { + if (usvdev_init() < 0) + break; - int interval = config_get_int("global", "check-interval", 60); - struct timeval tv = { .tv_sec = interval, .tv_usec = 0 }; - event_add_timeout(&tv, status_interval, NULL); + if (usvstate_init() < 0) + break; - int tmp = check_thresholds(); - if (tmp < 0) - log_print(LOG_WARN, "unable to check thresholds"); + event_loop(); - else if (tmp == 1) - log_print(LOG_INFO, "updated thresholds"); + usvstate_close(); + + log_close(); + + config_free(); + + if (config_parse(config) < 0) + break; + + const char *logfile = config_get_string("global", "logfile", DEFAULT_LOGFILE); + if (!debug && log_init(logfile) < 0) + break; + + log_print(LOG_INFO, "alix-usvd restarted (pid: %d)", getpid()); + } - event_loop(); return 0; } diff --git a/daemon/signals.c b/daemon/signals.c new file mode 100644 index 0000000..f640a3b --- /dev/null +++ b/daemon/signals.c @@ -0,0 +1,149 @@ +/*************************************************************************** + * Copyright (C) 05/2009 by Olaf Rempel * + * razzor@kopf-tisch.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; version 2 of the License * + * * + * 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 General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include +#include + +#include +#include + +#include "event.h" +#include "list.h" +#include "logging.h" +#include "signals.h" + +struct signal_entry { + struct list_head list; + int signum; + int deleted; + + void (*callback)(void); +}; + +static LIST_HEAD(callback_list); +static int sig_pipe[2]; + +static void sig_handler(int signal) +{ + unsigned char signum = (signal & 0xFF); + write(sig_pipe[1], &signum, 1); +} + +int signal_remove_callback(int signum, int type) +{ + /* mark all old handler for deletion */ + struct signal_entry *search; + list_for_each_entry(search, &callback_list, list) { + if (search->signum == signum) + search->deleted = 1; + } + + struct sigaction sig_action; + + switch (type) { + case SIG_IGNORE: + sig_action.sa_handler = SIG_IGN; + break; + + default: + case SIG_DEFAULT: + sig_action.sa_handler = SIG_DFL; + break; + } + + if (sigaction(signum, &sig_action, NULL) < 0) { + log_print(LOG_WARN, "signal_remove_callback(): sigaction()"); + return -1; + } + + /* trigger remove */ + sig_handler(0); + return 0; +} + +int signal_set_callback(int signum, void (*callback)(void)) +{ + struct signal_entry *entry = malloc(sizeof(struct signal_entry)); + if (entry == NULL) { + log_print(LOG_WARN, "signal_add_callback(): out of memory"); + return -1; + } + + entry->signum = signum; + entry->deleted = 0; + entry->callback = callback; + list_add_tail(&entry->list, &callback_list); + + struct sigaction sig_action = { + .sa_handler = sig_handler, + }; + + if (sigaction(signum, &sig_action, NULL) < 0) { + log_print(LOG_WARN, "signal_add_callback(): sigaction()"); + list_del(&entry->list); + free(entry); + return -1; + } + + return 0; +} + +static int sig_event(int fd, void *privdata) +{ + unsigned char signum; + int len = read(fd, &signum, 1); + if (len <= 0) { + log_print(LOG_WARN, "sig_event(): read()"); + return -1; + } + + struct signal_entry *search, *tmp; + list_for_each_entry_safe(search, tmp, &callback_list, list) { + if (search->deleted) { + list_del(&search->list); + free(search); + continue; + } + + if (search->signum == signum) + search->callback(); + } + + return 0; +} + +int signal_init(void) +{ + if (pipe(sig_pipe) < 0) { + log_print(LOG_ERROR, "signal_init(): pipe()"); + return -1; + } + + if (fcntl(sig_pipe[0], F_SETFD, FD_CLOEXEC) < 0) { + log_print(LOG_WARN, "signal_init(): fcntl(FD_CLOEXEC)"); + return -1; + } + + if (fcntl(sig_pipe[1], F_SETFD, FD_CLOEXEC) < 0) { + log_print(LOG_WARN, "signal_init(): fcntl(FD_CLOEXEC)"); + return -1; + } + + event_add_readfd(NULL, sig_pipe[0], sig_event, NULL); + return 0; +} diff --git a/daemon/signals.h b/daemon/signals.h new file mode 100644 index 0000000..f2c8a06 --- /dev/null +++ b/daemon/signals.h @@ -0,0 +1,12 @@ +#ifndef _SIGNALS_H +#define _SIGNALS_H + +#define SIG_DEFAULT 0x00 +#define SIG_IGNORE 0x01 + +int signal_remove_callback(int signum, int type); +int signal_set_callback(int signum, void (*callback)(void)); + +int signal_init(void); + +#endif // _SIGNALS_H diff --git a/daemon/usvdevice.c b/daemon/usvdevice.c new file mode 100644 index 0000000..1683227 --- /dev/null +++ b/daemon/usvdevice.c @@ -0,0 +1,211 @@ +/*************************************************************************** + * Copyright (C) 05/2009 by Olaf Rempel * + * razzor@kopf-tisch.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; version 2 of the License * + * * + * 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 General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include +#include + +#include +#include +#include +#include + +#include "i2c-dev.h" + +#include "configfile.h" +#include "logging.h" +#include "usvdevice.h" + +/* eeprom parameters / thresholds & defaults */ +#include "../eeprom.h" + +enum { + REG_STATUS = 0x00, + + REG_CURRENT = 0x10, + REG_UBAT = 0x11, + REG_UIN = 0x12, + + REG_UIN_LOSS = 0x20, + REG_UIN_RESTORE = 0x21, + REG_UBAT_FULL = 0x22, + REG_UBAT_LOW = 0x23, + REG_UBAT_CRIT = 0x24, + REG_IBAT_FULL = 0x25, + REG_CRC = 0x26, +}; + +static const char *i2c_device; +static int i2c_address; + +static int _usvdev_open(void) +{ + int fd = open(i2c_device, O_RDWR); + if (fd < 0) + return -1; + + unsigned long funcs; + if (ioctl(fd, I2C_FUNCS, &funcs)) { + close(fd); + return -1; + } + + if ((funcs & I2C_FUNC_SMBUS_WORD_DATA) != I2C_FUNC_SMBUS_WORD_DATA) { + close(fd); + return -1; + } + + if (ioctl(fd, I2C_SLAVE, i2c_address) < 0) { + close(fd); + return -1; + } + + return fd; +} + +static int usvdev_open(void) +{ + static int failcnt = 0; + + int dev = _usvdev_open(); + if (dev < 0) { + if (failcnt++ == 0) + log_print(LOG_WARN, "failed to open %s:0x%02x", i2c_device, i2c_address); + + return -1; + } + + if (failcnt != 0) { + log_print(LOG_WARN, "opened %s:0x%02x after %d failed attempts", + i2c_device, i2c_address, failcnt); + + failcnt = 0; + } + return dev; +} + +int usvdev_getstatus(struct usvdev_status *status) +{ + int dev = usvdev_open(); + if (dev < 0) + return -1; + + status->state = i2c_smbus_read_word_data(dev, REG_STATUS); + status->ibat = i2c_smbus_read_word_data(dev, REG_CURRENT); + status->ubat = i2c_smbus_read_word_data(dev, REG_UBAT); + status->uin = i2c_smbus_read_word_data(dev, REG_UIN); + + close(dev); + + if (status->state < 0 || status->ibat < 0 || status->ubat < 0 || status->uin < 0) { + status->state = -1; + status->ibat = 0; + status->ubat = 0; + status->uin = 0; + return -1; + } + return 0; +} + +int usvdev_setstate(int state) +{ + int dev = usvdev_open(); + if (dev < 0) + return -1; + + int retval = i2c_smbus_write_word_data(dev, REG_STATUS, state); + + close(dev); + return retval; +} + +int usvdev_init(void) +{ + i2c_device = config_get_string("global", "i2c-device", "/dev/i2c-0"); + i2c_address = config_get_int("global", "i2c-address" , 16); + + int dev = usvdev_open(); + if (dev < 0) { + log_print(LOG_WARN, "could not check thresholds"); + return 0; + } + + struct ee_param defaults = DEFAULT_PARAMETERS; + + struct ee_param config; + config.uin_loss = config_get_int("thresholds", "uin_loss" , defaults.uin_loss); + config.uin_restore = config_get_int("thresholds", "uin_restore" , defaults.uin_restore); + config.ubat_full = config_get_int("thresholds", "ubat_full" , defaults.ubat_full); + config.ubat_low = config_get_int("thresholds", "ubat_low" , defaults.ubat_low); + config.ubat_critical = config_get_int("thresholds", "ubat_critical" , defaults.ubat_critical); + config.ibat_full = config_get_int("thresholds", "ibat_full" , defaults.ibat_full); + + struct ee_param device; + device.uin_loss = i2c_smbus_read_word_data(dev, REG_UIN_LOSS); + device.uin_restore = i2c_smbus_read_word_data(dev, REG_UIN_RESTORE); + device.ubat_full = i2c_smbus_read_word_data(dev, REG_UBAT_FULL); + device.ubat_low = i2c_smbus_read_word_data(dev, REG_UBAT_LOW); + device.ubat_critical = i2c_smbus_read_word_data(dev, REG_UBAT_CRIT); + device.ibat_full = i2c_smbus_read_word_data(dev, REG_IBAT_FULL); + device.crc16 = 0x0000; + + int changed = 0; + + if (device.uin_loss != config.uin_loss) { + log_print(LOG_INFO, "update UIN_LOSS: %d => %d", device.uin_loss, config.uin_loss); + i2c_smbus_write_word_data(dev, REG_UIN_LOSS, config.uin_loss); + changed = 1; + } + + if (device.uin_restore != config.uin_restore) { + log_print(LOG_INFO, "update UIN_RESTORE: %d => %d", device.uin_restore, config.uin_restore); + i2c_smbus_write_word_data(dev, REG_UIN_RESTORE, config.uin_restore); + changed = 1; + } + + if (device.ubat_full != config.ubat_full) { + log_print(LOG_INFO, "update UBAT_FULL: %d => %d", device.ubat_full, config.ubat_full); + i2c_smbus_write_word_data(dev, REG_UBAT_FULL, config.ubat_full); + changed = 1; + } + + if (device.ubat_low != config.ubat_low) { + log_print(LOG_INFO, "update UBAT_LOW: %d => %d", device.ubat_low, config.ubat_low); + i2c_smbus_write_word_data(dev, REG_UBAT_LOW, config.ubat_low); + changed = 1; + } + + if (device.ubat_critical != config.ubat_critical) { + log_print(LOG_INFO, "update UBAT_CRIT: %d => %d", device.ubat_critical, config.ubat_critical); + i2c_smbus_write_word_data(dev, REG_UBAT_CRIT, config.ubat_critical); + changed = 1; + } + + if (device.ibat_full != config.ibat_full) { + log_print(LOG_INFO, "update IBAT_FULL: %d => %d", device.ibat_full, config.ibat_full); + i2c_smbus_write_word_data(dev, REG_IBAT_FULL, config.ibat_full); + changed = 1; + } + + if (changed == 1) { + i2c_smbus_write_word_data(dev, REG_CRC, 0x0000); + log_print(LOG_INFO, "committing threshold changes"); + } + + close(dev); + return 0; +} diff --git a/daemon/usvdevice.h b/daemon/usvdevice.h new file mode 100644 index 0000000..ecb4928 --- /dev/null +++ b/daemon/usvdevice.h @@ -0,0 +1,15 @@ +#ifndef _USVDEVICE_H_ +#define _USVDEVICE_H_ + +struct usvdev_status { + int state; + int ibat; + int ubat; + int uin; +}; + +int usvdev_getstatus(struct usvdev_status *status); +int usvdev_setstate(int state); +int usvdev_init(void); + +#endif // _USVDEVICE_H_ diff --git a/daemon/usvstate.c b/daemon/usvstate.c new file mode 100644 index 0000000..afe9df2 --- /dev/null +++ b/daemon/usvstate.c @@ -0,0 +1,215 @@ +/*************************************************************************** + * Copyright (C) 05/2009 by Olaf Rempel * + * razzor@kopf-tisch.de * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; version 2 of the License * + * * + * 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 General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include +#include +#include + +#include + +#include "configfile.h" +#include "event.h" +#include "logging.h" +#include "unixsocket.h" +#include "usvdevice.h" + +#define DEFAULT_SOCKET "alix-usvd.sock" +#define SENDMAIL "/usr/sbin/sendmail -t" + +enum { + STATE_IDLE = 0x01, + STATE_TEST = 0x02, + STATE_CHARGE = 0x04, + STATE_DISCHARGE = 0x08, + STATE_POWEROFF = 0x10, +}; + +static struct event_fd *listen_event; +static struct event_timeout *timeout_event; + +static const char *mail_from; +static const char *mail_to; + +static char * state2str(int state) +{ + switch (state) { + case STATE_IDLE: + return "IDLE"; + + case STATE_TEST: + return "TEST"; + + case STATE_CHARGE: + return "CHARGE"; + + case STATE_DISCHARGE: + return "DISCHARGE"; + + case STATE_POWEROFF: + return "POWEROFF"; + + default: + return "UNKNOWN"; + } +} + +static int str2state(char *buf) +{ + int state = -1; + if (strncasecmp(buf, "IDLE", 4) == 0) + state = STATE_IDLE; + + else if (strncasecmp(buf, "TEST", 4) == 0) + state = STATE_TEST; + + else if (strncasecmp(buf, "CHARGE", 6) == 0) + state = STATE_CHARGE; + + else if (strncasecmp(buf, "DISCHARGE", 9) == 0) + state = STATE_DISCHARGE; + + else if (strncasecmp(buf, "POWEROFF", 8) == 0) + state = STATE_POWEROFF; + + return state; +} + +static int usvstate_update(int state, struct usvdev_status *status) +{ + static int old_state = -1; + + if (status == NULL) { + if (state != old_state) + log_print(LOG_INFO, "usv state forced to: %s", state2str(state)); + + old_state = state; + return state; + } + + state = status->state; + if (state == old_state) + return state; + + log_print(LOG_INFO, "usv state changed: %s => %s", state2str(old_state), state2str(state)); + + if (mail_to != NULL) { + FILE *mail = popen(SENDMAIL, "w"); + if (mail == NULL) { + fprintf(mail, "From: %s\n", mail_from); + fprintf(mail, "To: %s\n", mail_to); + fprintf(mail, "Subject: alix-usvd state change: %s => %s\n\n", + state2str(old_state), state2str(state)); + + fprintf(mail, "Current USV status:\n"); + fprintf(mail, "Ibat: %1.3lf mA\nUbat: %2.3lf V\nUin : %2.3lf V\n\n", + status->ibat / 1000.0, status->ubat / 1000.0, status->uin / 1000.0); + + fprintf(mail, "Faithfully yours, etc.\n"); + fclose(mail); + } + } + return state; +} + +static int unix_read_cb(int fd, void *privdata) +{ + char buf[64]; + int len = read(fd, buf, sizeof(buf)); + if (len < 0) + return -1; + + int new_state = str2state(buf); + if (new_state != -1) { + usvdev_setstate(new_state); + new_state = usvstate_update(new_state, NULL); + + int len = snprintf(buf, sizeof(buf), "%s", state2str(new_state)); + write(fd, buf, len); + + } else if (strncasecmp(buf, "status", 6) == 0) { + + struct usvdev_status status; + if (usvdev_getstatus(&status) == 0) + usvstate_update(-1, &status); + + int len = snprintf(buf, sizeof(buf), "%s:%d:%d:%d", + state2str(status.state), + (signed)status.ibat, status.ubat, status.uin); + write(fd, buf, len); + } + + close(fd); + return -1; +} + +static int status_interval(void *privdata) +{ + struct usvdev_status status; + if (usvdev_getstatus(&status) < 0) + return 0; + + usvstate_update(-1, &status); + return 0; +} + +static int unix_accept_cb(int fd, void *privdata) +{ + int con = accept(fd, NULL, NULL); + if (con < 0) { + log_print(LOG_ERROR, "unix_accept_cb: accept()"); + return 0; + } + + event_add_readfd(NULL, con, unix_read_cb, NULL); + return 0; +} + +int usvstate_init(void) +{ + const char *socket_path = config_get_string("global", "socket", DEFAULT_SOCKET); + int sockfd = unix_listen(socket_path); + if (sockfd < 0) + return -1; + + listen_event = event_add_readfd(NULL, sockfd, unix_accept_cb, NULL); + if (listen_event == NULL) { + close(sockfd); + return -1; + } + + int interval = config_get_int("global", "check-interval", 60); + struct timeval tv = { .tv_sec = interval, .tv_usec = 0 }; + + timeout_event = event_add_timeout(&tv, status_interval, NULL); + + mail_from = config_get_string("alerts", "mail-from", NULL); + mail_to = config_get_string("alerts", "mail-to", NULL); + + usvstate_update(-1, NULL); + return 0; +} + +int usvstate_close(void) +{ + event_remove_timeout(timeout_event); + + event_remove_fd(listen_event); + close(event_get_fd(listen_event)); + + return 0; +} diff --git a/daemon/usvstate.h b/daemon/usvstate.h new file mode 100644 index 0000000..7f5e346 --- /dev/null +++ b/daemon/usvstate.h @@ -0,0 +1,7 @@ +#ifndef _USVSTATE_H_ +#define _USVSTATE_H_ + +int usvstate_init(void); +int usvstate_close(void); + +#endif // _USVSTATE_H_