/*****************************************************************************
 *
 * $Id$
 * Purpose ...............: Keep track of server status 
 *
 *****************************************************************************
 * Copyright (C) 1997-2005
 *   
 * Michiel Broek		FIDO:		2:280/2802
 * Beekmansbos 10
 * 1971 BV IJmuiden
 * the Netherlands
 *
 * This file is part of MBSE BBS.
 *
 * This BBS 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; either version 2, or (at your option) any
 * later version.
 *
 * MB BBS 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 MB BBS; see the file COPYING.  If not, write to the Free
 * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *****************************************************************************/

#include "../config.h"
#include "../lib/mbselib.h"
#include "taskstat.h"
#include "callstat.h"
#include "outstat.h"
#include "taskibc.h"
#include "taskutil.h"



/*
 *  Semafores
 */
int			s_scanout  = FALSE;
int			s_mailout  = FALSE;
int			s_mailin   = FALSE;
int			s_reqindex = FALSE;
int			s_index    = FALSE;
int			s_msglink  = FALSE;
int			s_newnews  = FALSE;
int			s_bbsopen  = FALSE;
int			s_do_inet  = FALSE;
int			tosswait   = TOSSWAIT_TIME;
extern int		UPSalarm;
extern int		UPSdown;
extern int		ptimer;
extern int		rescan;


extern struct taskrec	TCFG;
extern int		internet;
extern int		ZMH;


typedef struct {
	int		tot_clt;	/* Total client connects 	*/
	int		peak_clt;	/* Peak simultaneous tot_cltes	*/
	int		s_error;	/* Syntax errors from clients	*/
	int		c_error;	/* Comms errors from clients	*/
} cl_stat;


typedef struct {
	int		start;		/* Start date/time		*/
	int		laststart;	/* Last start date/time		*/
	int		daily;		/* Last daily update		*/
	int		startups;	/* Total starts			*/
	int		clients;	/* Connected clients		*/
	cl_stat		total;		/* Total statistics		*/
	cl_stat		today;		/* Todays statistics		*/
	unsigned	open	: 1;	/* Is BBS open			*/
	unsigned int	sequence;	/* Sequencer counter		*/
} status_r;


static char	stat_fn[PATH_MAX];	/* Statusfile name		*/
static status_r	status;			/* Status data			*/
extern double	Load;			/* System Load			*/
extern int	Processing;		/* Is system running		*/

extern srv_list         *servers;           /* Connected servers        */
extern usr_list         *users;             /* Connected users          */
extern chn_list         *channels;          /* Connected channels       */


/************************************************************************
 *
 *  Initialize the statusfile, create it if necesary.
 */
void status_init()
{
    size_t  cnt;
    int	    stat_fd;

    snprintf(stat_fn, PATH_MAX, "%s/var/status.mbsed", getenv("MBSE_ROOT"));

    /*
     * First check if this is the very first time we start the show.
     * If so, we generate an empty status file with only the start
     * date in it.
     */
    stat_fd = open(stat_fn, O_RDWR);
    if (stat_fd == -1) {
	memset((char *)&status, 0, sizeof(status_r));
	status.start = (int)time(NULL);
	status.daily = (int)time(NULL);
	status.sequence = (unsigned int)time(NULL);
	stat_fd = open(stat_fn, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); 
	cnt = write(stat_fd, &status, sizeof(status_r));
	Syslog('+', "New statusfile created");
	lseek(stat_fd, 0, SEEK_SET);
    }
	
    cnt = read(stat_fd, &status, sizeof(status_r));
    if (cnt != sizeof(status_r)) {
	printf("Error reading status file\n");
	exit(MBERR_INIT_ERROR);
    }
    status.startups++;
    status.laststart = (int)time(NULL);
    status.clients = 1;		/* We are a client ourself */
    s_bbsopen = status.open;
    lseek(stat_fd, 0, SEEK_SET);
    cnt = write(stat_fd, &status, sizeof(status_r));
    if (cnt != sizeof(status_r)) {
	Syslog('?', "$Error rewrite status file\n");
	exit(MBERR_INIT_ERROR);
    }
    close(stat_fd);
}



/*
 *  Writeback the updated status record.
 */
void status_write(void);
void status_write(void)
{
    int	 	d, stat_fd, yday;
    struct tm	ttm;
    time_t	temp;

#if defined(__OpenBSD)
    temp = time(NULL);
    localtime_r(&temp, &ttm);
    yday = ttm.tm_yday;
    temp = (time_t)status.daily;
    localtime_r(&temp, &ttm);
#else
    temp = time(NULL);
    ttm = *localtime(&temp);
    yday = ttm.tm_yday;
    temp = (time_t)status.daily;
    ttm = *localtime(&temp);
#endif

    /*
     * If we passed to the next day, zero the today counters 
     */
    if (yday != ttm.tm_yday) {
	Syslog('+', "Last days statistics:");
	Syslog('+', "Total clients : %lu", status.today.tot_clt);
	Syslog('+', "Peak clients  : %lu", status.today.peak_clt);
	Syslog('+', "Syntax errors : %lu", status.today.s_error);
	Syslog('+', "Comms errors  : %lu", status.today.c_error);

	memset((char *)&status.today, 0, sizeof(cl_stat));
	status.daily = time(NULL);
	Syslog('+', "Zeroed todays status counters");
    }

    if ((stat_fd = open(stat_fn, O_RDWR)) == -1) {
	Syslog('?', "$Error open statusfile %s", stat_fn);
	return;
    }

    if ((d = lseek(stat_fd, 0, SEEK_SET)) != 0) {
	Syslog('?', "$Error seeking in statusfile");
	return;
    }

    d = write(stat_fd, &status, sizeof(status_r));
    if (d != sizeof(status_r))
	Syslog('?', "$Error writing statusfile, only %d bytes", d);

    /*
     * CLose the statusfile
     */
    if (close(stat_fd) != 0)
	Syslog('?', "$Error closing statusfile");
}



/*************************************************************************
 *
 *  Various actions on the statusfile.
 */


/*
 * Check for Zone Mail Hour, return TRUE if it is.
 */
int get_zmh()
{
    struct tm	l_date; 
    char	sstime[6];
    time_t	Now;

    Now = time(NULL);
#if defined(__OpenBSD__)
    gmtime_r(&Now, &l_date);
#else
    l_date = *gmtime(&Now);
#endif
    snprintf(sstime, 6, "%02d:%02d", l_date.tm_hour, l_date.tm_min);

    if ((strncmp(sstime, TCFG.zmh_start, 5) >= 0) && (strncmp(sstime, TCFG.zmh_end, 5) < 0)) {
	if (!ZMH) {
	    CreateSema((char *)"zmh");
	    Syslog('!', "Start of Zone Mail Hour");
	    ZMH = TRUE;
	}
    } else {
	if (ZMH) {
	    RemoveSema((char *)"zmh");
	    Syslog('!', "End of Zone Mail Hour");
	    ZMH = FALSE;
	}
    }
    return ZMH;
}



void stat_inc_clients()
{
    status.clients++;
    status.total.tot_clt++;	
    status.today.tot_clt++;
    if (status.clients >= status.total.peak_clt)
	status.total.peak_clt = status.clients;
    if (status.clients >= status.today.peak_clt)
	status.today.peak_clt = status.clients;	

    status_write();
}



void stat_dec_clients()
{
    status.clients--; 
    status_write();
}



void stat_set_open(int op)
{
    if (op) {
	if (!s_bbsopen) {
	    Syslog('!', "The bbs is open");
	    sem_set((char *)"scanout", TRUE);
	}
    } else {
	if (s_bbsopen) {
	    Syslog('!', "The bbs is closed");
	}
    }
    s_bbsopen = status.open = op;
    status_write();
}



void stat_inc_serr()
{
    status.total.s_error++;
    status.today.s_error++;
    status_write();
}



void stat_inc_cerr()
{
    status.total.c_error++;
    status.today.c_error++;
    status_write();
}



char *stat_status()
{
    static char buf[160];
    int		srvcnt = 0, chncnt = 0, usrcnt = 0;
    srv_list	*tmps;
    chn_list	*tmpc;
    usr_list	*tmpu;

    buf[0] = '\0';

    for (tmps = servers; tmps; tmps = tmps->next)
	srvcnt++;
    for (tmpc = channels; tmpc; tmpc = tmpc->next)
	chncnt++;
    for (tmpu = users; tmpu; tmpu = tmpu->next)
	usrcnt++;
    snprintf(buf, 160, "100:23,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%2.2f,%u,%d,%d,%d;",
	status.start, status.laststart, status.daily,
	status.startups, status.clients, 
	status.total.tot_clt, status.total.peak_clt,
	status.total.s_error, status.total.c_error,
	status.today.tot_clt, status.today.peak_clt,
	status.today.s_error, status.today.c_error,
	status.open, get_zmh(), internet, s_do_inet, Processing, Load, status.sequence,
	srvcnt, chncnt, usrcnt);
    return buf;
}



/*
 * Return open status:
 * 0 = open.
 * 1 = closed.
 * 2 = Zone Mail Hour.
 */
int stat_bbs_stat()
{
    if (!status.open) 
	return 1;
    if (get_zmh())
	return 2;
    return 0;
}



/*
 * Get next sequence number
 */
char *getseq(void)
{
    static char	buf[80];

    buf[0] = '\0';
    status.sequence++;
    status_write();
    snprintf(buf, 80, "100:1,%u;", status.sequence);
    return buf;
}



unsigned int gettoken(void)
{
    status.sequence++;
    status_write();
    return status.sequence;
}



int sem_set(char *sem, int value)
{
    if (!strcmp(sem, "scanout")) {
	s_scanout = value;
	if (value)
	    rescan = TRUE;
    } else if (!strcmp(sem, "mailout")) {
	s_mailout = value;
    } else if (!strcmp(sem, "mailin")) {
	s_mailin = value;
	if (value)
	    tosswait = TOSSWAIT_TIME;
    } else if (!strcmp(sem, "mbindex")) {
	s_index = value;
    } else if (!strcmp(sem, "newnews")) {
	s_newnews = value;
    } else if (!strcmp(sem, "msglink")) {
	s_msglink = value;
    } else if (!strcmp(sem, "reqindex")) {
	s_reqindex = value;
    } else if (!strcmp(sem, "do_inet")) {
	s_do_inet = value;
    } else {
	return FALSE;
    }
    ptimer = PAUSETIME;
    return TRUE;
}



char *sem_status(char *data)
{
    char	*cnt, *sem;
    static char	buf[40];
    int		value;

    buf[0] = '\0';
    snprintf(buf, 40, "200:1,16;");
    cnt = strtok(data, ",");
    sem = strtok(NULL, ";");

    if (!strcmp(sem, "scanout")) {
	value = s_scanout;
    } else if (!strcmp(sem, "mailout")) {
        value = s_mailout;
    } else if (!strcmp(sem, "mailin")) {
        value = s_mailin;
    } else if (!strcmp(sem, "mbindex")) {
        value = s_index;
    } else if (!strcmp(sem, "newnews")) {
        value = s_newnews;
    } else if (!strcmp(sem, "msglink")) {
        value = s_msglink;
    } else if (!strcmp(sem, "reqindex")) {
        value = s_reqindex;
    } else if (!strcmp(sem, "upsalarm")) {
	value = UPSalarm;
    } else if (!strcmp(sem, "upsdown")) {
	value = UPSdown;
    } else if (!strcmp(sem, "do_inet")) {
	value = s_do_inet;
    } else {
	Syslog('s', "sem_status(%s) buf=%s", sem, buf);
	return buf;
    }

    snprintf(buf, 40, "100:1,%s;", value ? "1":"0");
    return buf;
}



char *sem_create(char *data)
{
    static char buf[40];
    char    	*cnt, *sem;

    cnt = strtok(data, ",");
    sem = strtok(NULL, ";");
    buf[0] = '\0';
    snprintf(buf, 40, "200:1,16;");

    if (sem_set(sem, TRUE))
	snprintf(buf, 40, "100:0;");

    return buf;
}



char *sem_remove(char *data)
{
    static char buf[40];
    char    	*cnt, *sem;

    cnt = strtok(data, ",");
    sem = strtok(NULL, ";");
    buf[0] = '\0';
    snprintf(buf, 40, "200:1,16;");

    if (sem_set(sem, FALSE))
	snprintf(buf, 40, "100:0;");

    return buf;
}