static char rcsid[] = "@(#)$Id: schedule.c,v 1.19 2006/04/09 07:37:06 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.19 $   $State: Exp $
 *
 *  Author: Kari Hurtta <hurtta+elm@posti.FMI.FI> (was hurtta+elm@ozone.FMI.FI)
 *****************************************************************************/

#include "headers.h"

DEBUG_VAR(Debug,__FILE__,"net");

#include <errno.h>
#ifndef ANSI_C
extern int errno;
#endif

VOLATILE int wait_can_signal = 0;

int no_action_routine(fd,data)
     int fd; 
     void * data;
{
    return 0;
}

static int ANY_ACTION P_((int fd, void * data));
static int ANY_ACTION(fd,data)
     int fd; 
     void * data;
{
    return 0;
}

enum schedule_return no_schedule_routine(fd,data)
     int fd; 
     void * data;
{
    return schedule_done;
}


static struct actions {
    int    fd; 
    int    timeout_sec;
    action_routine * read_act; 
    action_routine * write_act; 		       
    action_routine * timeout_act; 		       
    time_t next_timeout;
    void  * data;
    int    busy;                     /* Prevent nested calls --
				      * when read_act(), write_act() or
				      * timeout_act() calls wait_for_timeout()
				      * or wait_for_action() directly or
				      * indirectly (for example with lib_error)
				      */

    schedule_routine * schedule_act;
    void             * schedule_data;

}         * actions       = NULL;
static int  actions_count = 0; 

static void remove_action P_((int ptr));
static void remove_action(ptr)
     int ptr;
{
    int i;

    DPRINT(Debug,4,(&Debug,
		    "** Removing action %d  (fd=%d)\n", ptr,
		    actions[ptr].fd)); 
    for (i = ptr+1; i < actions_count; i++) {
	actions[i-1] = actions[i];
    }
    actions_count--;
}

void change_action (fd,timeout_sec,read_act,write_act,timeout_act,data)
     int fd; 
     int timeout_sec;
     action_routine * read_act; 
     action_routine * write_act; 	
     action_routine * timeout_act;
     void *data;
{
    int ptr;

    for (ptr = 0; ptr < actions_count; ptr++)
	if (actions[ptr].fd == fd)
	    break;

    if (ptr == actions_count) {	

	if (no_action_routine == read_act &&
	    no_action_routine == write_act &&
	    no_action_routine == timeout_act) {
	    DPRINT(Debug,9,(&Debug,
			    "change_action: action for (fd=%d) already cleared\n", 
			    fd)); 
	    return;
	}

	actions = safe_realloc(actions, (++actions_count) * 
			       sizeof (struct actions));	
	actions[ptr].timeout_sec  = 0;
	actions[ptr].next_timeout = 0;
	actions[ptr].busy         = 0;

	actions[ptr].schedule_act  = no_schedule_routine;
	actions[ptr].schedule_data = NULL;

	DPRINT(Debug,4,(&Debug,
			"** Adding action %d  (fd=%d)\n", ptr,fd)); 
    }

    if (actions[ptr].timeout_sec != timeout_sec) {
	actions[ptr].next_timeout = time(NULL) + timeout_sec;
    }

    actions[ptr].fd          = fd;
    actions[ptr].timeout_sec = timeout_sec;
    actions[ptr].read_act    = read_act;
    actions[ptr].write_act   = write_act;
    actions[ptr].timeout_act = timeout_act;
    actions[ptr].data        = data;
    
    if (no_action_routine == read_act &&
	no_action_routine == write_act &&
	no_action_routine == timeout_act &&
	actions[ptr].schedule_act == no_schedule_routine) {

	if (actions[ptr].busy) {
	    DPRINT(Debug,4,(&Debug,
			    "** Action %d busy (fd=%d): remove skipped\n", 
			    ptr, actions[ptr].fd)); 
	} else
	    remove_action(ptr);
    }
}

void set_schedule_action (fd,routine,data)
     int fd;
     schedule_routine *routine;
     void * data;
{
    int ptr;

    for (ptr = 0; ptr < actions_count; ptr++)
	if (actions[ptr].fd == fd)
	    break;

    if (ptr == actions_count) {	

	if (no_schedule_routine == routine) {
	    DPRINT(Debug,9,(&Debug,
			    "set_schedule_action: action for (fd=%d) already cleared\n", 
			    fd)); 
	    return;
	}

	actions = safe_realloc(actions, (++actions_count) * 
			       sizeof (struct actions));	
	actions[ptr].timeout_sec  = 0;
	actions[ptr].next_timeout = 0;
	actions[ptr].busy         = 0;

	actions[ptr].timeout_sec = 0;
	actions[ptr].read_act    = no_action_routine;
	actions[ptr].write_act   = no_action_routine;
	actions[ptr].timeout_act = no_action_routine;
	actions[ptr].data        = NULL;

	DPRINT(Debug,4,(&Debug,
			"** Adding action %d  (fd=%d)\n", ptr,fd)); 
    }


    actions[ptr].fd          = fd;

    actions[ptr].schedule_act  = routine;
    actions[ptr].schedule_data = data;
    
    if (no_action_routine == actions[ptr].read_act &&
	no_action_routine == actions[ptr].write_act &&
	no_action_routine == actions[ptr].timeout_act &&
	actions[ptr].schedule_act == no_schedule_routine) {
	
	if (actions[ptr].busy) {
	    DPRINT(Debug,4,(&Debug,
			    "** Action %d busy (fd=%d): remove skipped\n", 
			    ptr, actions[ptr].fd)); 
	} else
	    remove_action(ptr);
    }
}			     


void clear_action(fd)
     int fd;
{
    set_schedule_action(fd,no_schedule_routine,NULL);
    change_action(fd,0,
		  no_action_routine,no_action_routine,no_action_routine,
		  NULL);
}

int have_actions()
{
    return actions_count;
}

#if POLL_METHOD == 1
/* --------------------- select() ------------------------------------------ */

static int real_wait P_((action_routine * action,
			 time_t wait_timeout,time_t next_timeout));

static int real_wait(action,wait_timeout,next_timeout)
     action_routine * action;
     time_t wait_timeout, next_timeout;
{
    int result = 0;
    int done = 0;
    int err = 0;

    char *Y = "action";
    if (action == no_action_routine)
	Y = "no action";
    else if (action == ANY_ACTION)
	Y = "any action";
	     
    DPRINT(Debug,20,(&Debug,
		     "real_wait: [select] %s %p, wait_timeout=%ld, next_timeout=%ld (actions_count=%d)\n", 	       
		     Y,
		     action,
		     (long) wait_timeout, (long) next_timeout, actions_count)); 

    do {
	int i, n = 0;
	fd_set read_set;
	fd_set write_set;
	fd_set exp_set;
	struct timeval timeout, *timeout_val = NULL;
	int ret;
	time_t current;

	FD_ZERO(&read_set);
	FD_ZERO(&write_set);
	FD_ZERO(&exp_set);

	DPRINT(Debug,20,(&Debug,
			 "real_wait: actions:"));
	for (i = 0; i < actions_count; i++) {
	    if (n <= actions[i].fd)
		n = actions[i].fd+1;

	    DPRINT(Debug,20,(&Debug,
			     " (fd=%d",actions[i].fd));
	    FD_SET(actions[i].fd,&exp_set);

	    if (actions[i].busy) {
		DPRINT(Debug,20,(&Debug,
				 " busy!"));
	    } else {
		if (actions[i].read_act != no_action_routine) {
		    FD_SET(actions[i].fd,&read_set);
		    DPRINT(Debug,20,(&Debug,
				     " READ"));
		}
	    
		if (actions[i].write_act != no_action_routine) {
		    FD_SET(actions[i].fd,&write_set);
		    DPRINT(Debug,20,(&Debug,
				     " WRITE"));
		}	
		if (actions[i].timeout_act != no_action_routine) {
		    DPRINT(Debug,20,(&Debug,
				     " TIMEOUT"));
		}
	    }
	    DPRINT(Debug,20,(&Debug, ")"));
	}
	DPRINT(Debug,20,(&Debug, "\n"));	

	if (next_timeout > 0) {
	    int diff = next_timeout - time(NULL);

	    DPRINT(Debug,20,(&Debug, "real_wait: diff=%d\n", diff));
	    
	    if (diff <= 0) 
		timeout.tv_sec = 0;
	    else
		timeout.tv_sec = diff;
	    timeout.tv_usec    = 0;
	    timeout_val        = &timeout;
	}

	DPRINT(Debug,20,(&Debug, "real_wait: n = %d, read set=", n));
	for (i = 0; i < n; i++) {
	    if (FD_ISSET(i,&read_set)) {
		DPRINT(Debug,20,(&Debug, " %d", i));
	    }
	}
	DPRINT(Debug,20,(&Debug, ", write set="));
	for (i = 0; i < n; i++) {
	    if (FD_ISSET(i,&write_set)) {
		DPRINT(Debug,20,(&Debug, " %d", i));
	    }
	}
	DPRINT(Debug,20,(&Debug,", exception set="));
	for (i = 0; i < n; i++) {
	    if (FD_ISSET(i,&exp_set)) {
		DPRINT(Debug,20,(&Debug, " %d", i));
	    }
	}
	DPRINT(Debug,20,(&Debug, ", timeout="));
	if (timeout_val) {
	    DPRINT(Debug,20,(&Debug, " sec=%ld usec=%ld\n", 
			     (long) timeout_val->tv_sec,
			     (long) timeout_val->tv_usec));
	} else {
	    DPRINT(Debug,20,(&Debug, "NULL\n")); 
	}

	wait_can_signal = 1;
#ifdef _HPUX_SOURCE
	ret = select(n,(int *)&read_set,(int *)&write_set,
		     (int *)&exp_set,timeout_val);
#else
	ret = select(n,&read_set,&write_set,&exp_set,timeout_val);
#endif
	wait_can_signal = 0;

	if (ret < 0) {
	    err = errno;
	    DPRINT(Debug,20,(&Debug,
			     "select: %s (errno %d)\n",
			     error_description(err),err));
	    result = 0;
	    break;
	}

	current = time(NULL);
	DPRINT(Debug,20,(&Debug, "real_wait: select=%d, current=%ld (time)\n",
			 ret,(long)current));	

#ifdef USE_DLOPEN
	if (ret > 0) {
	    /* If we just timed out, it is quite predictable
	       so no seeding 
	    */
	    union xxx_rand {
		struct collection {
		    struct timeval XX;   /* someone may modify this */
		    time_t  X;
		    fd_set read_set;
		    fd_set write_set;
		    int r;
		} X;
		char bytes [sizeof (struct collection)];
	    } A;

	    if (timeout_val)
		A.X.XX = *timeout_val;
	    A.X.X         = current;
	    memcpy( &(A.X.read_set), &(read_set), sizeof read_set);
	    memcpy( &(A.X.write_set), &(write_set), sizeof write_set);
	    A.X.r         = ret;

	    seed_rand_bits(A.bytes, sizeof A, 
			   4 /* entropy bits */ );
	}
#endif


	if (ret > 0) {
	    for (i = 0; i < actions_count; i++) {
		if (!actions[i].busy) {
		    actions[i].busy = 1;
		    if (FD_ISSET(actions[i].fd,&read_set) ||
			FD_ISSET(actions[i].fd,&exp_set)) {
			int val; 
			DPRINT(Debug,20,(&Debug, 
					 "real_wait: running read_act for %d (action %d)\n",
					 actions[i].fd,i));
			val  = actions[i].read_act(actions[i].fd,
						   actions[i].data);
			DPRINT(Debug,20,(&Debug,
					 "real_wait: [%d].read_act=%d\n",i,val));
			if (action == actions[i].read_act ||
			    action == ANY_ACTION) {
			    done = 1;
			    result = 1;
			}
			if (!val)
			    actions[i].read_act = no_action_routine;
		    }
		    
		    if (FD_ISSET(actions[i].fd,&write_set) ||
			FD_ISSET(actions[i].fd,&exp_set)) {
			int val;
			DPRINT(Debug,20,(&Debug,
					 "real_wait: running write_act for %d (action %d)\n",
					 actions[i].fd,i));
			val = actions[i].write_act(actions[i].fd,
						   actions[i].data);
			DPRINT(Debug,20,(&Debug,
					 "real_wait: [%d].write_act=%d\n",i,val));
			if (action == actions[i].write_act ||
			    action == ANY_ACTION) {
			    done = 1;
			    result = 1;
			}
			if (!val)
			    actions[i].write_act = no_action_routine;
		    }
		    actions[i].busy = 0;
		}
		if (FD_ISSET(actions[i].fd,&exp_set)) {
		    DPRINT(Debug,20,(&Debug,
				     "real_wait: [%d] exception\n",i));
		    actions[i].write_act = no_action_routine;
		    actions[i].read_act = no_action_routine;
		    done = 1;
		    result = 1;
		}
	    }
	}

	for (i = 0; i < actions_count; i++) {
	    if (actions[i].next_timeout &&
		actions[i].next_timeout <= current) {

		if (actions[i].busy) {
		    DPRINT(Debug,20,(&Debug, 
				     "real_wait: %d busy (action %d) -- timout skipped\n",
				     actions[i].fd,i));
		} else {		
		    int val;
		    actions[i].busy = 1;

		    DPRINT(Debug,20,(&Debug,
				     "real_wait: running timeout_act for %d (action %d)\n",
			       actions[i].fd,i));
		    val = actions[i].timeout_act(actions[i].fd,
						 actions[i].data);
		    DPRINT(Debug,20,(&Debug, 
				     "real_wait: [%d].timeout_act=%d\n",i,val));
		    if (action == actions[i].timeout_act ||
			action == ANY_ACTION) {
			done = 1;
			result = 1;
		    }
		    if (!val) {
			actions[i].timeout_act  = no_action_routine;
			actions[i].next_timeout = 0;
		    } else {
			actions[i].next_timeout = 
			    current + actions[i].timeout_sec;
		    }		
		    actions[i].busy = 0;
		}
	    }
	}

	if (next_timeout > 0 && next_timeout <= current) {
	    done = 1;
	    if (result == 0)
		result = -1;
	}

	if (wait_timeout > 0 && wait_timeout <= current) {
	    done = 1;
	    result = 1;
	}
	DPRINT(Debug,20,(&Debug,
			 "real_wait: done=%d, result=%d\n",done,result));

    } while(!done);

    
    DPRINT(Debug,20,(&Debug,
		     "real_wait=%d (errno=%d)\n",result,err));
    errno = err;
    return result;
}

#endif

#if POLL_METHOD == 2
/* --------------------- poll() -------------------------------------------- */

/* For MAXINT */
#include <values.h>

static int real_wait P_((action_routine * action,
			 time_t wait_timeout,time_t next_timeout));

static int real_wait(action,wait_timeout,next_timeout)
     action_routine * action;
     time_t wait_timeout, next_timeout;
{
    int result = 0;
    int done = 0;
    int err = 0;
    struct pollfd  * polls = NULL;
    int polls_count = 0;
    char *Y = "action";

    if (action == no_action_routine)
	Y = "no action";
    else if (action == ANY_ACTION)
	Y = "any action";

    DPRINT(Debug,20,(&Debug,
		     "real_wait: [poll] %s %p, wait_timeout=%ld, next_timeout=%ld (actions_count=%d)\n", 	       
		     Y,
		     action,
		     (long) wait_timeout, (long) next_timeout, actions_count)); 

    do {
	int i;
	int ret;
	time_t current;
	int timeout = -1;

	if (polls_count != actions_count) {
	    polls = safe_realloc(polls,
				 actions_count * sizeof(struct pollfd));
	    polls_count = actions_count;
	    bzero(polls,actions_count * sizeof(struct pollfd));
	    DPRINT(Debug,20,(&Debug,
			     "real_wait: polls=%p, polls_count=%d\n",
			     polls,polls_count));	    
	}


	DPRINT(Debug,20,(&Debug,
			 "real_wait: actions:"));
	for (i = 0; i < actions_count; i++) {

	    DPRINT(Debug,20,(&Debug, " (fd=%d",actions[i].fd));
	    polls[i].fd     = actions[i].fd;
	    polls[i].events = 0;
	    polls[i].revents = 0;

	    if (actions[i].busy) {
		DPRINT(Debug,20,(&Debug, " busy!"));
	    } else {
		if (actions[i].read_act != no_action_routine) {
		    polls[i].events |= POLLIN | POLLHUP;
		    DPRINT(Debug,20,(&Debug, " READ"));
		}
	    
		if (actions[i].write_act != no_action_routine) {
		    polls[i].events |= POLLOUT;
		    DPRINT(Debug,20,(&Debug, " WRITE"));
		}	
		if (actions[i].timeout_act != no_action_routine) {
		    DPRINT(Debug,20,(&Debug, " TIMEOUT"));
		}
	    }
	    DPRINT(Debug,20,(&Debug, ")"));
	}
        DPRINT(Debug,20,(&Debug, "\n"));	

	if (next_timeout > 0) {
	    int diff = next_timeout - time(NULL);

	    DPRINT(Debug,20,(&Debug, "real_wait: diff=%d\n", diff));
	    
	    if (diff <= 0) 
		timeout = -1;
	    else if (diff < MAXINT / 1000)
		timeout = diff * 1000;
	    else
		timeout = MAXINT;
	}

	DPRINT(Debug,20,(&Debug,"real_wait: polls_count = %d, polls=", 
			 polls_count));
	for (i = 0; i < polls_count; i++)
	    DPRINT(Debug,10,(&Debug, " {fd=%d events=%d}", 
			     polls[i].fd, polls[i].events));


	DPRINT(Debug,20,(&Debug, ", timeout=%d\n", 
			 timeout));

	wait_can_signal = 1;
	ret = poll(polls,polls_count,timeout);
	wait_can_signal = 0;

	if (ret < 0) {
	    err = errno;
	    DPRINT(Debug,20,(&Debug,
			"select: %s (errno %d)\n",
			error_description(err),err));
	    result = 0;
	    break;
	}

	current = time(NULL);
	DPRINT(Debug,20,(&Debug, "real_wait: poll=%d, current=%ld\n",
			 ret,(long)current));	

#ifdef USE_DLOPEN
	if (ret > 0) {
	    /* If we just timed out, it is quit epredictable
	       so no seeding 
	    */
	    union xxx_rand {
                struct collection {
		    int    r;
		    time_t t;
		} X;
		char bytes [sizeof (struct collection)];
	    } A;

	    A.r = ret;
	    A.t = current;

	    seed_rand_bits(A.bytes, sizeof A, 
			   4 /* entroby bits */ );

	}
#endif

	if (ret > 0) {
	    for (i = 0; i < polls_count && i < actions_count; i++) {
		if (polls[i].revents) {
		    DPRINT(Debug,20,(&Debug, 
				     "real_wait: polls[%d]: fd=%d revents=%d\n",
				     i,polls[i].fd,polls[i].revents));
		}
		if (!actions[i].busy) {
		    actions[i].busy = 1;
		    if (polls[i].revents && (POLLIN|POLLHUP|POLLERR)) {
			int val; 
			DPRINT(Debug,20,(&Debug,
					 "real_wait: running read_act for %d (action %d)\n",
					 actions[i].fd,i));
			val  = actions[i].read_act(actions[i].fd,
						   actions[i].data);
		       DPRINT(Debug,20,(&Debug,
					"real_wait: [%d].read_act=%d\n",i,val));
			if (action == actions[i].read_act ||
			    action == ANY_ACTION) {
			    done = 1;
			    result = 1;
			}
			if (!val)
			    actions[i].read_act = no_action_routine;
		    }
		    
		    if (polls[i].revents && (POLLOUT|POLLERR)) {
			int val;
			DPRINT(Debug,20,(&Debug,
					 "real_wait: running write_act for %d (action %d)\n",
					 actions[i].fd,i));
			val = actions[i].write_act(actions[i].fd,
						   actions[i].data);
			DPRINT(Debug,20,(&Debug,
					 "real_wait: [%d].write_act=%d\n",i,val));
			if (action == actions[i].write_act ||
			    action == ANY_ACTION) {
			    done = 1;
			    result = 1;
			}
			if (!val)
			    actions[i].write_act = no_action_routine;
		    }
		    actions[i].busy = 0;
		}
		if (polls[i].revents & POLLERR) {
		    DPRINT(Debug,20,(&Debug,
				     "real_wait: [%d] POLLERR\n",i));
		    actions[i].write_act = no_action_routine;
		    actions[i].read_act = no_action_routine;
		    done = 1;
		    result = 1;
		}
	    }
	}

	for (i = 0; i < actions_count; i++) {
	    if (actions[i].next_timeout &&
		actions[i].next_timeout <= current) {

		if (actions[i].busy) {
		    DPRINT(Debug,20,(&Debug,
				     "real_wait: %d busy (action %d) -- timout skipped\n",
				     actions[i].fd,i));
		} else {		
		    int val;
		    actions[i].busy = 1;

		    DPRINT(Debug,20,(&Debug,
				     "real_wait: running timeout_act for %d (action %d)\n",
				     actions[i].fd,i));
		    val = actions[i].timeout_act(actions[i].fd,
						 actions[i].data);
		    DPRINT(Debug,20,(&Debug,
				     "real_wait: [%d].timeout_act=%d\n",i,val));
		    if (action == actions[i].timeout_act ||
			    action == ANY_ACTION) {
			done = 1;
			result = 1;
		    }
		    if (!val) {
			actions[i].timeout_act  = no_action_routine;
			actions[i].next_timeout = 0;
		    } else {
			actions[i].next_timeout = 
			    current + actions[i].timeout_sec;
		    }		
		    actions[i].busy = 0;
		}
	    }
	}

	if (next_timeout > 0 && next_timeout < current) {
	    done = 1;
	    if (result == 0)
		result = -1;
	}

	if (wait_timeout > 0 && wait_timeout <= current) {
	    done = 1;
	    result = 1;
	}
	DPRINT(Debug,20,(&Debug,
			 "real_wait: done=%d, result=%d\n",done,result));

    } while(!done);

    
    if (polls)
	free(polls);

    DPRINT(Debug,20,(&Debug,
		     "real_wait=%d (errno=%d)\n",result,err));
    errno = err;
    return result;
}

#endif


#if POLL_METHOD
/* ----------- GENERIC ---------------------------------------------------- */

static int wait_for_something P_((action_routine * action,int seconds));
static int wait_for_something(action,seconds)
     action_routine * action;
     int seconds; 
{
    time_t next_timeout = 0;
    time_t wait_timeout = 0;    
    int result,err = 0;
    int done = 0;
    int i;

    if (seconds >= 0) 
	wait_timeout = time(NULL) + seconds;

    do {
	next_timeout = wait_timeout;
	
	for (i = 0; i < actions_count; i++) {
	    if (!actions[i].busy) {
		actions[i].busy = 1;

		switch(actions[i].schedule_act(actions[i].fd,
					       actions[i].schedule_data)) {
		case schedule_remove:
		    DPRINT(Debug,4,(&Debug,
			      "** Schedule action %d (fd=%d): Remove pending\n",
			      i,actions[i].fd)); 

		    clear_action(i);
		    break;
		case schedule_reconsider:
		    DPRINT(Debug,4,(&Debug,
				    "** Schedule action %d (fd=%d): Reconsider pending\n",
				    i,actions[i].fd)); 
		    next_timeout = time(NULL);
		    break;
		case schedule_have_action:
		    DPRINT(Debug,4,(&Debug,
				    "** Schedule action %d (fd=%d): Action filled\n",
				    i,actions[i].fd));
		    next_timeout = time(NULL);
		    if (ANY_ACTION == action ||
			no_action_routine != action &&
			(action == actions[i].read_act ||
			 action == actions[i].write_act ||
			 action == actions[i].timeout_act)
			) {
			DPRINT(Debug,4,(&Debug,
					"** Schedule action %d (fd=%d): Ready\n",
					i,actions[i].fd));
			done = 1;
		    }
                    break;
		}

		actions[i].busy = 0;
	    }
	}

	for (i = 0; i < actions_count; i++) {
	    if (!actions[i].busy &&
		actions[i].timeout_act != no_action_routine)
		if (!next_timeout || next_timeout > actions[i].next_timeout)
		    next_timeout =  actions[i].next_timeout;
	}

	if (next_timeout < 0) {
	    DPRINT(Debug,1,(&Debug,
			    "** Next timeout %d < 0 -- setting to current time\n",
			    next_timeout));
	    next_timeout = time(NULL);
	}

	
	result = real_wait(action,wait_timeout,next_timeout);
	err = errno;

	for (i = 0; i < actions_count; i++) {
	    if (no_action_routine == actions[i].read_act &&
		no_action_routine == actions[i].write_act &&
		no_action_routine == actions[i].timeout_act &&
		actions[i].schedule_act == no_schedule_routine) {
		if (actions[i].busy) {
		    DPRINT(Debug,4,(&Debug,"** Action %d busy (fd=%d): remove skipped\n", 
				    i, actions[i].fd)); 
		} else
		    remove_action(i);
		break;
	    }
	}
	
    } while (-1 == result && !done);

    errno = err;
    return result;
}

int wait_for_action(action)
     action_routine * action;
{
    int ret;

    DPRINT(Debug,4,(&Debug,
		    "wait_for_action:    START | action=%p\n",action)); 
    ret=wait_for_something(action,-1);
    DPRINT(Debug,4,(&Debug,
		    "wait_for_action=%d  END (errno=%d)\n",
		    ret,errno)); 

    return ret;
}

int wait_for_timeout(seconds)
     int seconds;
{
    int ret;

    DPRINT(Debug,4,(&Debug,
	      "wait_for_timeout:    START | seconds=%d\n",seconds)); 
    ret = wait_for_something(no_action_routine,seconds);
    DPRINT(Debug,4,(&Debug,
		    "wait_for_timeout=%d  END (errno=%d) \n",
		    ret,errno)); 
    return ret;
}

int wait_for_action_or_timeout(action,seconds)
     action_routine * action;
     int seconds;
{
    int ret;

    DPRINT(Debug,4,(&Debug,
		    "wait_for_action_or_timeout:    START | action=%p seconds=%d\n",
		    action,seconds)); 
    ret = wait_for_something(action,seconds);
    DPRINT(Debug,4,(&Debug,
		    "wait_for_action_or_timeout=%d  END (errno=%d)\n",
		    ret,errno)); 
    return ret;
}

int wait_for_any_action()
{
    int ret;

    DPRINT(Debug,4,(&Debug,"wait_for_any_action:    START\n")); 
    ret=wait_for_something(ANY_ACTION,-1);
    DPRINT(Debug,4,(&Debug,"wait_for_any_action=%d  END (errno=%d)\n",
	      ret,errno)); 

    return ret;
}

int wait_for_any_action_or_timeout(seconds)
     int seconds;
{
    int ret;
    
    DPRINT(Debug,4,(&Debug,"wait_for_any_action_or_timeout:    START | seconds=%d\n",seconds)); 
    ret = wait_for_something(ANY_ACTION,seconds);
    DPRINT(Debug,4,(&Debug,"wait_for_any_action_or_timeout=%d  END (errno=%d) \n",
	      ret,errno)); 
    return ret;
}


#endif

/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1