/* * IMC2 - an inter-mud communications protocol * * imc-events.c: IMC event handling * * Copyright (C) 1996,1997 Oliver Jowett <oliver@jowett.manawatu.planet.co.nz> * * 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; either version 2 of the License, or * (at your option) any later version. * * 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 (see the file COPYING); if not, write to the * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include <stdlib.h> #include <stdio.h> #include <stdarg.h> #include <string.h> #include <ctype.h> #include <unistd.h> #include <sys/time.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <fcntl.h> #include <errno.h> #include "imc.h" /* * I needed to split up imc.c (2600 lines and counting..) so this file now * contains: * * - event support functions * - event callbacks */ /* * event support functions */ /* we use size-limited recycle lists, since events will be being * queued/unqueued quite often */ imc_event *imc_event_list, *imc_event_free; static int event_freecount; imc_event *imc_new_event(void) { imc_event *p; if (imc_event_free) { p=imc_event_free; imc_event_free=p->next; event_freecount--; } else p=imc_malloc(sizeof(imc_event)); p->when=0; p->callback=NULL; p->data=NULL; p->next=NULL; p->timed=0; return p; } void imc_free_event(imc_event *p) { if (event_freecount>10) /* pick a number, any number */ { imc_free(p, sizeof(imc_event)); } else { p->next=imc_event_free; imc_event_free=p; event_freecount++; } } void imc_add_event(int when, void (*callback)(void *), void *data, int timed) { imc_event *p, *scan, **last; p=imc_new_event(); p->when=imc_now+when; p->callback=callback; p->data=data; p->timed=timed; for (last=&imc_event_list, scan=*last; scan; last=&scan->next, scan=*last) if (scan->when >= p->when) break; p->next=scan; *last=p; } void imc_cancel_event(void (*callback)(void *), void *data) { imc_event *p, *p_next, **last; for (last=&imc_event_list, p=*last; p; p=p_next) { p_next=p->next; if ((!callback || p->callback==callback) && p->data==data) { *last=p_next; imc_free_event(p); } else last=&p->next; } } void imc_run_events(time_t newtime) { imc_event *p; void (*callback)(void *); void *data; while(imc_event_list) { p=imc_event_list; if (p->when > newtime) break; imc_event_list=p->next; callback=p->callback; data=p->data; imc_now=p->when; imc_free_event(p); if (callback) (*callback)(data); else imc_logerror("imc_run_events: NULL callback"); } imc_now=newtime; } int imc_next_event(void (*callback)(void *), void *data) { imc_event *p; for (p=imc_event_list; p; p=p->next) if (p->callback==callback && p->data==data) return p->when - imc_now; return -1; } /* * Events */ void ev_imc_optimize(void *data) { /* This one's mine! This will do the switch to the less trafficky hub, once each time imc reloads, and then each 24 hours. -- Scion */ if (!imc_is_router) /* don't wanna do it if we -are- a hub :) */ { imc_logstring("Auto-optimizing hub connections."); if (strcmp(global_hubname, "NULL")) { /* we got a hub, let's connect to it. */ imc_send_autoconnect(global_hubname); strcpy(global_hubname, "NULL"); /* erase the record of the hub so we can start over */ } } } /* need optimize poll as seperate event - shogar */ void ev_imc_pollforhub(void *data) { if (!imc_is_router) /* don't wanna do it if we -are- a hub :) */ { imc_send_info_request(); imc_add_event(3600, ev_imc_pollforhub, NULL, 1); /* every 12 hours */ imc_add_event(3660, ev_imc_optimize, NULL, 1); /* give em a minutes to reply */ } } /* expire the imc_reminfo entry pointed at by data */ void ev_expire_reminfo(void *data) { imc_reminfo *r=(imc_reminfo *)data; r->type=IMC_REMINFO_EXPIRED; } /* drop the imc_reminfo entry */ void ev_drop_reminfo(void *data) { imc_cancel_event(NULL, data); imc_delete_reminfo((imc_reminfo *)data); } /* send a keepalive, and queue the next keepalive event */ void ev_keepalive(void *data) { imc_send_keepalive(); } void ev_request_keepalive(void *data) { imc_request_keepalive(); } /* maybe shrink the input buffer of the connection 'data' */ void ev_shrink_input(void *data) { imc_connect *c=(imc_connect *)data; int len; int newsize; void *newbuf; len=strlen(c->inbuf)+1; newsize=c->insize/2; if (len<=newsize) { if (newsize<IMC_MINBUF) /* huh? should never happen.. */ return; /* shrink the buffer one step */ newbuf=imc_malloc(newsize); strcpy(newbuf, c->inbuf); imc_free(c->inbuf, c->insize); c->inbuf=newbuf; c->insize=newsize; if (newsize<=IMC_MINBUF) return; } imc_add_event(IMC_SHRINKTIME, ev_shrink_input, c, 0); } /* maybe shrink the output buffer of the connection 'data' */ void ev_shrink_output(void *data) { imc_connect *c=(imc_connect *)data; int len; int newsize; void *newbuf; len=strlen(c->outbuf)+1; newsize=c->outsize/2; if (len<=newsize) { if (newsize<IMC_MINBUF) /* huh? should never happen.. */ return; /* shrink the buffer one step */ newbuf=imc_malloc(newsize); strcpy(newbuf, c->outbuf); imc_free(c->outbuf, c->outsize); c->outbuf=newbuf; c->outsize=newsize; if (newsize<=IMC_MINBUF) return; } imc_add_event(IMC_SHRINKTIME, ev_shrink_output, c, 0); } /* try a reconnect to the given imc_info */ void ev_reconnect(void *data) { imc_info *info=(imc_info *)data; if (!info->connection) { if ((info->flags & IMC_RECONNECT) || (info->flags & IMC_HUB) || (info->flags & IMC_MAIN_HUB)) { if (!imc_connect_to(info->name)) { /* routers should never do reconnects to muds - shogar */ /* and limit RECONNECTs to 5 attempts until successful connect */ /* or they connect to us until we reboot, then we give them a second */ /* chance - shogar */ if(!imc_is_router || (info->flags & IMC_HUB)) { if(info->connect_attempts < 4) imc_setup_reconnect(info); else { /* Set them to "dead_hub" so they get deleted at next reboot. -- Scion */ info->flags=imc_flagvalue("dead_hub", imc_connection_flags); } } } } } } /* handle a spam event for counter 1 */ void ev_spam1(void *data) { imc_connect *c=(imc_connect *)data; if (c->spamcounter1 > IMC_SPAM1MAX) { if (c->spamtime1 < 0 || ++c->spamtime1 >= IMC_SPAM1TIME) c->spamtime1=-IMC_SPAM1TIME; } else { if (c->spamtime1<0) c->spamtime1++; } c->spamcounter1=0; if (c->spamtime1>0) imc_add_event(IMC_SPAM1INTERVAL, ev_spam1, c, 0); else if (c->spamtime1<0) imc_add_event(IMC_SPAM1INTERVAL, ev_spam1, c, 1); } /* handle a spam event for counter 2 */ void ev_spam2(void *data) { imc_connect *c=(imc_connect *)data; if (c->spamcounter2 > IMC_SPAM2MAX) { if (c->spamtime2 < 0 || ++c->spamtime2 >= IMC_SPAM2TIME) c->spamtime2=-IMC_SPAM2TIME; } else if (c->spamtime2<0) c->spamtime2++; c->spamcounter2=0; if (c->spamtime2>0) imc_add_event(IMC_SPAM1INTERVAL, ev_spam2, c, 0); else if (c->spamtime2<0) imc_add_event(IMC_SPAM1INTERVAL, ev_spam2, c, 1); }