tinymush-3.1p1/game/backups/
tinymush-3.1p1/game/bin/
tinymush-3.1p1/game/data/
tinymush-3.1p1/game/modules/
tinymush-3.1p1/game/modules/old/
tinymush-3.1p1/src/modules/comsys/
tinymush-3.1p1/src/modules/hello/
tinymush-3.1p1/src/modules/mail/
tinymush-3.1p1/src/tools/
/* timer.c - Subroutines for (system-) timed events */
/* $Id: timer.c,v 1.22 2002/07/30 00:39:54 lwl Exp $ */

#include "copyright.h"
#include "autoconf.h"
#include "config.h"

#include "alloc.h"	/* required by mudconf */
#include "flags.h"	/* required by mudconf */
#include "htab.h"	/* required by mudconf */
#include "mudconf.h"	/* required by code */

#include "db.h"		/* required by externs */
#include "externs.h"	/* required by interface */
#include "interface.h"	/* required by code */

#include "match.h"	/* required by code */
#include "command.h"	/* required by code */
#include "powers.h"	/* required by code */
#include "bitstring.h"	/* required by code */

extern void NDECL(pool_reset);
extern unsigned int FDECL(alarm, (unsigned int seconds));
extern void NDECL(pcache_trim);

/* ---------------------------------------------------------------------------
 * Cron-related things. This implementation is somewhat derivative of
 * Paul Vixie's cron implementation. See bitstring.h for the copyright
 * and other details.
 */

#define FIRST_MINUTE    0
#define LAST_MINUTE     59
#define MINUTE_COUNT    (LAST_MINUTE - FIRST_MINUTE + 1)

#define FIRST_HOUR      0
#define LAST_HOUR       23
#define HOUR_COUNT      (LAST_HOUR - FIRST_HOUR + 1)

#define FIRST_DOM       1
#define LAST_DOM        31
#define DOM_COUNT       (LAST_DOM - FIRST_DOM + 1)

#define FIRST_MONTH     1
#define LAST_MONTH      12
#define MONTH_COUNT     (LAST_MONTH - FIRST_MONTH + 1)

/* Note on DOW: 0 and 7 are both Sunday, for compatibility reasons. */
#define FIRST_DOW       0
#define LAST_DOW        7
#define DOW_COUNT       (LAST_DOW - FIRST_DOW + 1)

#define DOM_STAR        0x01
#define DOW_STAR        0x02

typedef struct cron_entry CRONTAB;
struct cron_entry {
    dbref obj;
    int atr;
    char *cronstr;
    bitstr_t bit_decl(minute, MINUTE_COUNT);
    bitstr_t bit_decl(hour, HOUR_COUNT);
    bitstr_t bit_decl(dom, DOM_COUNT);
    bitstr_t bit_decl(month, MONTH_COUNT);
    bitstr_t bit_decl(dow, DOW_COUNT);
    int flags;
    CRONTAB *next;
};

CRONTAB *cron_head = NULL;

#define set_cronbits(b,l,h,n) \
        if (((n) >= (l)) && ((n) <= (h))) bit_set((b), (n) - (l));

static void NDECL(check_cron)
{
    struct tm *ltime;
    int minute, hour, dom, month, dow;
    CRONTAB *crp;
    char *cmd;
    dbref aowner;
    int aflags, alen;

    /* Convert our time to a zero basis, so the elements can be used
     * as indices.
     */

    ltime = localtime(&mudstate.events_counter);
    minute = ltime->tm_min - FIRST_MINUTE;
    hour = ltime->tm_hour - FIRST_HOUR;
    dom = ltime->tm_mday - FIRST_DOM;
    month = ltime->tm_mon + 1 - FIRST_MONTH; /* must convert 0-11 to 1-12 */
    dow = ltime->tm_wday - FIRST_DOW;

    /* Do it if the minute, hour, and month match, plus a day selection
     * matches. We handle stars and the day-of-month vs. day-of-week
     * exactly like Unix (Vixie) cron does.
     */

    for (crp = cron_head; crp != NULL; crp = crp->next) {
	if (bit_test(crp->minute, minute) &&
	    bit_test(crp->hour, hour) &&
	    bit_test(crp->month, month) &&
	    (((crp->flags & DOM_STAR) || (crp->flags & DOW_STAR)) ?
	     (bit_test(crp->dow, dow) && bit_test(crp->dom, dom)) :
	     (bit_test(crp->dow, dow) || bit_test(crp->dom, dom)))) {
	    cmd = atr_pget(crp->obj, crp->atr, &aowner, &aflags, &alen);
	    if (*cmd && Good_obj(crp->obj)) {
		wait_que(crp->obj, crp->obj, 0, NOTHING, 0, cmd,
			 (char **) NULL, 0, NULL);
	    }
	    free_lbuf(cmd);
	}
    }
}

static char *parse_cronlist(player, bits, low, high, bufp)
    dbref player;
    bitstr_t *bits;
    int low, high;
    char *bufp;
{
    int i, n_begin, n_end, step_size;

    bit_nclear(bits, 0, (high - low + 1)); /* Default is all off */

    if (!bufp || !*bufp)
	return NULL;
    if (!*bufp)
	return NULL;

    /* We assume we're at the beginning of what we needed to parse.
     * All leading whitespace-skipping should have been taken care of
     * either before this function was called, or at the end of this
     * function.
     */

    while (*bufp && !isspace(*bufp)) {
	if (*bufp == '*') {
	    n_begin = low;
	    n_end = high;
	    bufp++;
	} else if (isdigit(*bufp)) {
	    n_begin = atoi(bufp); /* atoi() ignores trailing non-digits */
	    while (*bufp && isdigit(*bufp))
		bufp++;
	    if (*bufp != '-') {
		/* We have a single number, not a range. */
		n_end = n_begin;
	    } else {
		/* Eat the dash, get the range. */
		bufp++;
		n_end = atoi(bufp);
		while (*bufp && isdigit(*bufp))
		    bufp++;
	    }
	} else {
	    notify(player, tprintf("Cron parse error at: %s", bufp));
	    break;
	}

	/* Check for step size. */

	if (*bufp == '/') {
	    bufp++;		/* eat the slash */
	    step_size = atoi(bufp);
	    while (*bufp && isdigit(*bufp))
		bufp++;
	} else {
	    step_size = 1;
	}

	/* Go set it. */

	for (i = n_begin; i <= n_end; i += step_size)
	    set_cronbits(bits, low, high, i);

	/* We've made it through one pass. If the next character isn't
	 * a comma, we break out of this loop.
	 */
	if (*bufp == ',')
	    bufp++;
	else
	    break;
    }

    /* Skip over trailing garbage. */

    while (*bufp && !isspace(*bufp))
	bufp++;

    /* Initially, bufp pointed to the beginning of what we parsed. We have
     * to return it so we know where to start the next bit of parsing.
     * Skip spaces as well.
     */

    while (isspace(*bufp))
	bufp++;

    return bufp;
}

int call_cron(player, thing, attrib, timestr)
    dbref player, thing;
    int attrib;
    char *timestr;
{
    int errcode;
    CRONTAB *crp;
    char *bufp;

    /* Don't allow duplicate entries. */

    for (crp = cron_head; crp != NULL; crp = crp->next) {
	if ((crp->obj == thing) && (crp->atr == attrib) &&
	    !strcmp(crp->cronstr, timestr))
	    return -1;
    }

    crp = (CRONTAB *) XMALLOC(sizeof(CRONTAB), "cron_entry");
    crp->obj = thing;
    crp->atr = attrib;
    crp->flags = 0;
    crp->cronstr = XSTRDUP(timestr, "cron_entry.time");

    /* The time string is: <min> <hour> <day of month> <month> <day of week>
     * Legal values also include asterisks, and <x>-<y> (for a range).
     * We do NOT support step size.
     */

    errcode = 0;
    bufp = timestr;
    while (isspace(*bufp))
	bufp++;
    bufp = parse_cronlist(player, crp->minute,
			  FIRST_MINUTE, LAST_MINUTE, bufp);
    if (!bufp || !*bufp) {
	errcode = 1;
    } else {
	bufp = parse_cronlist(player, crp->hour, FIRST_HOUR, LAST_HOUR, bufp);
	if (!bufp || !*bufp)
	    errcode = 1;
    }
    if (!errcode) {
	if (*bufp == '*')
	    crp->flags |= DOM_STAR;
	bufp = parse_cronlist(player, crp->dom, FIRST_DOM, LAST_DOM, bufp);
	if (!bufp || !*bufp)
	    errcode = 1;
    }
    if (!errcode) {
	bufp = parse_cronlist(player, crp->month,
			      FIRST_MONTH, LAST_MONTH, bufp);
	if (!bufp || !*bufp)
	    errcode = 1;
    }
    if (!errcode) {
	if (*bufp == '*')
	    crp->flags |= DOW_STAR;
	bufp = parse_cronlist(player, crp->dow, FIRST_DOW, LAST_DOW, bufp);
    }

    /* Sundays can be either 0 or 7. */

    if (bit_test(crp->dow, 0))
	bit_set(crp->dow, 7);
    if (bit_test(crp->dow, 7))
	bit_set(crp->dow, 0);

    if (errcode) {
	XFREE(crp->cronstr, "cron_entry.time");
	XFREE(crp, "cron_entry");
	return 0;
    }

    /* Relink the list, now that we know we have something good. */

    crp->next = cron_head;
    cron_head = crp;

    return 1;
}

void do_cron(player, cause, key, objstr, timestr)
    dbref player, cause;
    int key;
    char *objstr, *timestr;
{
    dbref thing;
    int attrib, retcode;

    if (!timestr || !*timestr) {
	notify(player, "No times given.");
	return;
    }

    if (!parse_attrib(player, objstr, &thing, &attrib, 0) ||
	(attrib == NOTHING) || !Good_obj(thing)) {
	notify(player, "No match.");
	return;
    }

    if (!Controls(player, thing)) {
	notify(player, NOPERM_MESSAGE);
	return;
    }

    retcode = call_cron(player, thing, attrib, timestr);
    if (retcode == 0)
	notify(player, "Syntax errors. No cron entry made.");
    else if (retcode == -1)
	notify(player, "That cron entry already exists.");
    else
	notify(player, "Cron entry added.");
}    

int cron_clr(thing, attr)
    dbref thing;
    int attr;
{
    CRONTAB *crp, *next, *prev;
    int count;

    count = 0;
    for (crp = cron_head, prev = NULL; crp != NULL; ) {
	if ((crp->obj == thing) &&
	    ((attr == NOTHING) || (crp->atr == attr))) {
	    count++;
	    next = crp->next;
	    XFREE(crp->cronstr, "cron_entry.time");
	    XFREE(crp, "cron_entry");
	    if (prev)
		prev->next = next;
	    else
		cron_head = next;
	    crp = next;
	} else {
	    prev = crp;
	    crp = crp->next;
	}
    }
    return count;
}

void do_crondel(player, cause, key, objstr)
    dbref player, cause;
    int key;
    char *objstr;
{
    dbref thing;
    int attr, count;

    if (!objstr || !*objstr) {
	notify(player, "No match.");
	return;
    }

    attr = NOTHING;
    if (!parse_attrib(player, objstr, &thing, &attr, 0) ||
	(attr == NOTHING)) {
	if ((*objstr != '#') || 
	    ((thing = parse_dbref(objstr + 1)) == NOTHING)) {
	    notify(player, "No match.");
	}
    }

    if (!Controls(player, thing)) {
	notify(player, NOPERM_MESSAGE);
	return;
    }

    count = cron_clr(thing, attr);
    notify(player, tprintf("Removed %d cron entries.", count));
}

void do_crontab(player, cause, key, objstr)
    dbref player, cause;
    int key;
    char *objstr;
{
    dbref thing;
    int count;
    CRONTAB *crp;
    char *bufp;
    ATTR *ap;

    if (objstr && *objstr) {
	thing = match_thing(player, objstr);
	if (!Good_obj(thing))
	    return;
	if (!Controls(player, thing)) {
	    notify(player, NOPERM_MESSAGE);
	    return;
	}
    } else {
	thing = NOTHING;
    }

    /* List it if it's the thing we want, or, if we didn't specify a
     * thing, list things belonging to us (or everything, if we're
     * normally entitled to see the entire queue).
     */

    count = 0;
    for (crp = cron_head; crp != NULL; crp = crp->next) {
	if (((thing == NOTHING) &&
	     ((Owner(crp->obj) == player) || See_Queue(player))) ||
	    (crp->obj == thing)) {
	    count++;
	    bufp = unparse_object(player, crp->obj, 0);
	    ap = atr_num(crp->atr);
	    if (!ap) {
		notify(player,
		       tprintf("%s has a cron entry that contains bad attribute number %d.",
			       bufp, crp->atr));
	    } else {
		notify(player, tprintf("%s/%s: %s", bufp, ap->name,
				       crp->cronstr));
	    }
	    free_lbuf(bufp);
	}
    }

    notify(player, tprintf("Matched %d cron %s.",
			   count, (count == 1) ? "entry" : "entries"));
}

/* ---------------------------------------------------------------------------
 * General timer things.
 */

void NDECL(init_timer)
{
	mudstate.now = time(NULL);
	mudstate.dump_counter = ((mudconf.dump_offset == 0) ?
		mudconf.dump_interval : mudconf.dump_offset) + mudstate.now;
	mudstate.check_counter = ((mudconf.check_offset == 0) ?
	      mudconf.check_interval : mudconf.check_offset) + mudstate.now;
	mudstate.idle_counter = mudconf.idle_interval + mudstate.now;
	mudstate.mstats_counter = 15 + mudstate.now;

	/* The events counter is the next time divisible by sixty (i.e.,
	 * that puts us at the beginning of a minute).
	 */

	mudstate.events_counter = mudstate.now + (60 - (mudstate.now % 60));

	alarm(1);
}

void NDECL(dispatch)
{
	char *cmdsave;

	cmdsave = mudstate.debug_cmd;
	mudstate.debug_cmd = (char *)"< dispatch >";

	/* This routine can be used to poll from interface.c */

	if (!mudstate.alarm_triggered)
		return;
	mudstate.alarm_triggered = 0;
	mudstate.now = time(NULL);

	do_second();
	
	/* Module API hook */
	
	CALL_ALL_MODULES(do_second, ());

	/* Free list reconstruction */

	if ((mudconf.control_flags & CF_DBCHECK) &&
	    (mudstate.check_counter <= mudstate.now)) {
		mudstate.check_counter = mudconf.check_interval + mudstate.now;
		mudstate.debug_cmd = (char *)"< dbck >";
		do_dbck(NOTHING, NOTHING, 0);
		SYNC;
		pcache_trim();
		pool_reset();
	}
	
	/* Database dump routines */

	if ((mudconf.control_flags & CF_CHECKPOINT) &&
	    (mudstate.dump_counter <= mudstate.now)) {
		mudstate.dump_counter = mudconf.dump_interval + mudstate.now;
		mudstate.debug_cmd = (char *)"< dump >";
		fork_and_dump(0);
	}
	
	/* Idle user check */

	if ((mudconf.control_flags & CF_IDLECHECK) &&
	    (mudstate.idle_counter <= mudstate.now)) {
		mudstate.idle_counter = mudconf.idle_interval + mudstate.now;
		mudstate.debug_cmd = (char *)"< idlecheck >";
		check_idle();

	}
	
	/* Check for execution of attribute events */

	if (mudconf.control_flags & CF_EVENTCHECK) {
	    if (mudstate.now >= mudstate.events_counter) {
		mudstate.debug_cmd = (char *) "< croncheck >";
		check_cron();
		mudstate.events_counter += 60;
	    }
	}

#if defined(HAVE_GETRUSAGE) && defined(STRUCT_RUSAGE_COMPLETE)

	/* Memory use stats */

	if (mudstate.mstats_counter <= mudstate.now) {

		int curr;

		mudstate.mstats_counter = 15 + mudstate.now;
		curr = mudstate.mstat_curr;
		if (mudstate.now > mudstate.mstat_secs[curr]) {

			struct rusage usage;

			curr = 1 - curr;
			getrusage(RUSAGE_SELF, &usage);
			mudstate.mstat_ixrss[curr] = usage.ru_ixrss;
			mudstate.mstat_idrss[curr] = usage.ru_idrss;
			mudstate.mstat_isrss[curr] = usage.ru_isrss;
			mudstate.mstat_secs[curr] = mudstate.now;
			mudstate.mstat_curr = curr;
		}
	}
#endif

	/* reset alarm */

	alarm(1);
	mudstate.debug_cmd = cmdsave;
}

/*
 * ---------------------------------------------------------------------------
 * * do_timewarp: Adjust various internal timers.
 */

void do_timewarp(player, cause, key, arg)
dbref player, cause;
int key;
char *arg;
{
	int secs;

	secs = atoi(arg);

	if ((key == 0) || (key & TWARP_QUEUE))	/*
						 * Sem/Wait queues 
						 */
		do_queue(player, cause, QUEUE_WARP, arg);
	if (key & TWARP_DUMP)
		mudstate.dump_counter -= secs;
	if (key & TWARP_CLEAN)
		mudstate.check_counter -= secs;
	if (key & TWARP_IDLE)
		mudstate.idle_counter -= secs;
	if (key & TWARP_EVENTS)
		mudstate.events_counter -= secs;
}