alix-usv/daemon/usvstate.c

214 lines
5.3 KiB
C

/***************************************************************************
* 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 <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#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;
if (status != NULL)
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));
if (status != NULL) {
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);
}
}
old_state = state;
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),
(short)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;
}