/* $NetBSD: schedule.c,v 1.4 2006/09/09 16:22:10 manu Exp $ */
/* $KAME: schedule.c,v 1.19 2001/11/05 10:53:19 sakane Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the project nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "config.h"
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include "misc.h"
#include "plog.h"
#include "schedule.h"
#include "var.h"
#include "gcmalloc.h"
#define FIXY2038PROBLEM
#ifndef TAILQ_FOREACH
#define TAILQ_FOREACH(elm, head, field) \
for (elm = TAILQ_FIRST(head); elm; elm = TAILQ_NEXT(elm, field))
#endif
static struct timeval timeout;
#ifdef FIXY2038PROBLEM
#define Y2038TIME_T 0x7fffffff
static time_t launched; /* time when the program launched. */
static time_t deltaY2038;
#endif
static TAILQ_HEAD(_schedtree, sched) sctree;
static void sched_add __P((struct sched *));
static time_t current_time __P((void));
/*
* schedule handler
* OUT:
* time to block until next event.
* if no entry, NULL returned.
*/
struct timeval *
schedular()
{
time_t now, delta;
struct sched *p, *next = NULL;
now = current_time();
for (p = TAILQ_FIRST(&sctree); p; p = next) {
/* if the entry has been daed, remove it */
if (p->dead)
goto next_schedule;
/* if the time hasn't come, proceed to the next entry */
if (now < p->xtime) {
next = TAILQ_NEXT(p, chain);
continue;
}
/* mark it with dead. and call the function. */
p->dead = 1;
if (p->func != NULL)
(p->func)(p->param);
next_schedule:
next = TAILQ_NEXT(p, chain);
TAILQ_REMOVE(&sctree, p, chain);
racoon_free(p);
}
p = TAILQ_FIRST(&sctree);
if (p == NULL)
return NULL;
now = current_time();
delta = p->xtime - now;
timeout.tv_sec = delta < 0 ? 0 : delta;
timeout.tv_usec = 0;
return &timeout;
}
/*
* add new schedule to schedule table.
*/
struct sched *
sched_new(tick, func, param)
time_t tick;
void (*func) __P((void *));
void *param;
{
static long id = 1;
struct sched *new;
new = (struct sched *)racoon_malloc(sizeof(*new));
if (new == NULL)
return NULL;
memset(new, 0, sizeof(*new));
new->func = func;
new->param = param;
new->id = id++;
time(&new->created);
new->tick = tick;
new->xtime = current_time() + tick;
new->dead = 0;
/* add to schedule table */
sched_add(new);
return(new);
}
/* add new schedule to schedule table */
static void
sched_add(sc)
struct sched *sc;
{
struct sched *p;
TAILQ_FOREACH(p, &sctree, chain) {
if (sc->xtime < p->xtime) {
TAILQ_INSERT_BEFORE(p, sc, chain);
return;
}
}
if (p == NULL)
TAILQ_INSERT_TAIL(&sctree, sc, chain);
return;
}
/* get current time.
* if defined FIXY2038PROBLEM, base time is the time when called sched_init().
* Otherwise, conform to time(3).
*/
static time_t
current_time()
{
time_t n;
#ifdef FIXY2038PROBLEM
time_t t;
time(&n);
t = n - launched;
if (t < 0)
t += deltaY2038;
return t;
#else
return time(&n);
#endif
}
void
sched_kill(sc)
struct sched *sc;
{
sc->dead = 1;
return;
}
/* XXX this function is probably unnecessary. */
void
sched_scrub_param(param)
void *param;
{
struct sched *sc;
TAILQ_FOREACH(sc, &sctree, chain) {
if (sc->param == param) {
if (!sc->dead) {
plog(LLV_DEBUG, LOCATION, NULL,
"an undead schedule has been deleted.\n");
}
sched_kill(sc);
}
}
}
/*
* for debug
*/
int
sched_dump(buf, len)
caddr_t *buf;
int *len;
{
caddr_t new;
struct sched *p;
struct scheddump *dst;
int cnt = 0;
/* initialize */
*len = 0;
*buf = NULL;
TAILQ_FOREACH(p, &sctree, chain)
cnt++;
/* no entry */
if (cnt == 0)
return -1;
*len = cnt * sizeof(*dst);
new = racoon_malloc(*len);
if (new == NULL)
return -1;
dst = (struct scheddump *)new;
p = TAILQ_FIRST(&sctree);
while (p) {
dst->xtime = p->xtime;
dst->id = p->id;
dst->created = p->created;
dst->tick = p->tick;
p = TAILQ_NEXT(p, chain);
if (p == NULL)
break;
dst++;
}
*buf = new;
return 0;
}
/* initialize schedule table */
void
sched_init()
{
#ifdef FIXY2038PROBLEM
time(&launched);
deltaY2038 = Y2038TIME_T - launched;
#endif
TAILQ_INIT(&sctree);
return;
}
#ifdef STEST
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <err.h>
void
test(tick)
int *tick;
{
printf("execute %d\n", *tick);
racoon_free(tick);
}
void
getstdin()
{
int *tick;
char buf[16];
read(0, buf, sizeof(buf));
if (buf[0] == 'd') {
struct scheddump *scbuf, *p;
int len;
sched_dump((caddr_t *)&scbuf, &len);
if (scbuf == NULL)
return;
for (p = scbuf; len; p++) {
printf("xtime=%ld\n", p->xtime);
len -= sizeof(*p);
}
racoon_free(scbuf);
return;
}
tick = (int *)racoon_malloc(sizeof(*tick));
*tick = atoi(buf);
printf("new queue tick = %d\n", *tick);
sched_new(*tick, test, tick);
}
int
main()
{
static fd_set mask0;
int nfds = 0;
fd_set rfds;
struct timeval *timeout;
int error;
FD_ZERO(&mask0);
FD_SET(0, &mask0);
nfds = 1;
/* initialize */
sched_init();
while (1) {
rfds = mask0;
timeout = schedular();
error = select(nfds, &rfds, (fd_set *)0, (fd_set *)0, timeout);
if (error < 0) {
switch (errno) {
case EINTR: continue;
default:
err(1, "select");
}
/*NOTREACHED*/
}
if (FD_ISSET(0, &rfds))
getstdin();
}
}
#endif