]> jfr.im git - irc/quakenet/newserv.git/commitdiff
Added files that weren't in the initial import.
authorChris Porter <redacted>
Tue, 17 May 2005 11:46:00 +0000 (12:46 +0100)
committerChris Porter <redacted>
Tue, 17 May 2005 11:46:00 +0000 (12:46 +0100)
18 files changed:
core/Makefile [new file with mode: 0644]
core/config.c [new file with mode: 0644]
core/config.h [new file with mode: 0644]
core/error.c [new file with mode: 0644]
core/error.h [new file with mode: 0644]
core/events-epoll.c [new file with mode: 0644]
core/events-kqueue.c [new file with mode: 0644]
core/events-poll.c [new file with mode: 0644]
core/events.h [new file with mode: 0644]
core/hooks.c [new file with mode: 0644]
core/hooks.h [new file with mode: 0644]
core/main.c [new file with mode: 0644]
core/modules.c [new file with mode: 0644]
core/modules.h [new file with mode: 0644]
core/schedule.c [new file with mode: 0644]
core/schedule.h [new file with mode: 0644]
core/schedulealloc.c [new file with mode: 0644]
helpmod2/helpmod.db.old [new file with mode: 0644]

diff --git a/core/Makefile b/core/Makefile
new file mode 100644 (file)
index 0000000..0d5e94f
--- /dev/null
@@ -0,0 +1,18 @@
+
+default: all
+
+events.c:
+       rm -f events.c
+       ln -s events-poll.c events.c
+
+kqueue:
+       rm -f events.c
+       ln -s events-kqueue.c events.c
+
+epoll:
+       rm -f events.c
+       ln -s events-epoll.c events.c
+
+.PHONY: all
+all: events.c main.o events.o schedule.o hooks.o error.o modules.o config.o schedulealloc.o
+
diff --git a/core/config.c b/core/config.c
new file mode 100644 (file)
index 0000000..4127bca
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * config.c:
+ *
+ * Facilities for handling the config file
+ */
+
+#define __USE_GNU
+#include <string.h>
+
+#include "../lib/sstring.h"
+#include "../lib/array.h"
+#include "error.h"
+#include "config.h"
+#include <stdio.h>
+
+char *theconfig;
+
+typedef struct {
+  sstring *key;
+  array values;
+} configitem;
+
+typedef struct {
+  sstring *modulename;
+  array items;
+} configsection;
+
+array sections;
+
+void rehashconfig() {
+  Error("config",ERR_INFO,"Rehashing config file.");
+  freeconfig();
+  initconfig(NULL);
+}
+
+void freeconfig() {
+  int i,j,k;
+  configsection *sects;
+  configitem *items;
+  sstring **values;
+  
+  sects=(configsection *)(sections.content);
+  for (i=0;i<sections.cursi;i++) {
+    items=(configitem *)(sects[i].items.content);
+    for (j=0;j<sects[i].items.cursi;j++) {
+      freesstring(items[j].key);
+      values=(sstring **)(items[j].values.content);
+      for (k=0;k<items[j].values.cursi;k++) {
+        freesstring(values[k]);
+      }
+      array_free(&(items[j].values));
+    }
+    array_free(&(sects[i].items));
+    freesstring(sects[i].modulename);
+  }
+  array_free(&sections);
+}
+
+void initconfig(char *filename) {
+  FILE *fp;
+  configsection *sects=NULL;
+  configitem *items=NULL;
+  sstring **values=NULL;
+  char buf[255];
+  char *cp;
+  int si=-1;
+  int ii,j;
+  int matched;
+
+  if (filename==NULL) {
+    filename=theconfig;
+  } else {
+    theconfig=filename;
+  }
+  
+  array_init((&sections),sizeof(configsection));
+  
+  if ((fp=fopen(filename,"r"))==NULL) {
+    Error("core",ERR_FATAL,"Couldn't load config file.");
+    exit(1);
+  }
+  
+  while (!feof(fp)) {
+    /* Read in a line */
+    fgets(buf,255,fp);
+    /* Check we got something */
+    if (feof(fp))
+      break;
+  
+    /* Allow some comment chars */
+    if (buf[0]=='#' || buf[0]==';' || (buf[0]=='/' && buf[1]=='/'))
+      continue;
+
+    /* Blow away the line ending */
+    for (cp=buf;*cp;cp++)
+      if (*cp=='\n' || *cp=='\r') {
+        *cp='\0';
+        break;
+      }
+
+    /* Check it's long enough */
+    if (strlen(buf)<3)
+      continue;
+      
+    if (buf[0]=='[') {
+      /* New section (possibly) -- hunt for the ']' */
+      for (cp=&(buf[2]);*cp;cp++) {
+        if (*cp==']') {
+          si=array_getfreeslot(&sections);
+          sects=(configsection *)(sections.content);         
+          array_init(&(sects[si].items),sizeof(configitem));
+          array_setlim1(&(sects[si].items),10);
+          *cp='\0';
+          sects[si].modulename=getsstring(&(buf[1]),255);
+          break;
+        }
+      }
+    } else {
+      /* Ignore if we're not in a valid section */
+      if (si<0)
+        continue;
+        
+      for (cp=buf;*cp;cp++) {
+        if (*cp=='=') {
+          *cp='\0';
+          matched=0;
+          for (ii=0;ii<sects[si].items.cursi;ii++) {
+            if (!strcmp(items[ii].key->content,buf)) {
+              /* Another value for an existing key */
+              j=array_getfreeslot(&(items[ii].values));
+              values=(sstring **)(items[ii].values.content);
+              values[j]=getsstring(cp+1,512);
+              matched=1;
+            }
+          }
+          
+          if (matched==0) {
+            /* New key */
+            ii=array_getfreeslot(&(sects[si].items));
+            items=(configitem *)(sects[si].items.content);
+            items[ii].key=getsstring(buf,512);
+            array_init(&(items[ii].values),sizeof(sstring *));
+            array_setlim1(&(items[ii].values),5);
+            j=array_getfreeslot(&(items[ii].values));
+            values=(sstring **)(items[ii].values.content);
+            values[j]=getsstring(cp+1,512); /* looks nasty but is OK, this char is '=' 
+                                             * and we know 'buf' is null-terminated */
+          }
+          break;
+        }
+      }
+    }
+  }
+  
+  fclose(fp);
+}
+
+void dumpconfig() {
+  int i,j,k;
+  configsection *sects;
+  configitem *items;
+  sstring **values;
+  
+  printf("Dumping complete configuration database.\n");
+  printf("Total sections: %d\n",sections.cursi);
+  
+  sects=(configsection *)(sections.content);
+  for (i=0;i<sections.cursi;i++) {
+    printf ("\nSection %02d: [%s] has %d items\n",i,sects[i].modulename->content,sects[i].items.cursi);
+    items=(configitem *)(sects[i].items.content);
+    for(j=0;j<sects[i].items.cursi;j++) {
+      printf("  Item %02d: [%s] has %d values\n",j,items[j].key->content,items[j].values.cursi);
+      values=(sstring **)(items[j].values.content);
+      for (k=0;k<items[j].values.cursi;k++) {
+        printf("    Value %2d: [%s]\n",k,values[k]->content);
+      }
+    }
+  }
+  
+  printf("\n\nEnd of configuration database.\n");
+}  
+
+/*
+ * Two routes for extacting config info:
+ *
+ *  - getconfigitem() is for keys which can only meaningfully have one value.
+ *    It returns the last value for that key (so the config file has "last
+ *    value overrides" semantics.
+ *  - getconfigitems() is for keys which can have multiple values, it returns
+ *    a pointer to the array of values.
+ */
+array *getconfigitems(char *module, char *key) {
+  int i,j;
+  configsection *sects;
+  configitem *items;
+  
+  sects=(configsection *)(sections.content);
+  for (i=0;i<sections.cursi;i++) {
+    if (!strcmp(module,sects[i].modulename->content)) {
+      /* Found the module */
+      items=(configitem *)(sects[i].items.content);
+      for (j=0;j<sects[i].items.cursi;j++) {
+        if (!strcmp(key,items[j].key->content)) {
+          return (&items[j].values);
+        }
+      }
+      return NULL;
+    }
+  }  
+  return NULL;
+}
+
+sstring *getconfigitem(char *module, char *key) {
+  array *a;
+  sstring **values;
+  
+  if ((a=getconfigitems(module,key))==NULL) {
+    return NULL;
+  }
+  
+  values=(sstring **)(a->content);
+  return values[(a->cursi-1)];
+}
+
+sstring *getcopyconfigitem(char *module, char *key, char *defaultvalue, int len) {
+  sstring *ss;
+  
+  ss=getconfigitem(module,key);
+  if (ss!=NULL) {
+    return getsstring(ss->content,len);
+  } else {
+    return getsstring(defaultvalue,len);
+  }  
+}  
diff --git a/core/config.h b/core/config.h
new file mode 100644 (file)
index 0000000..f576681
--- /dev/null
@@ -0,0 +1,17 @@
+/* config.h */
+
+#ifndef __CONFIG_H
+#define __CONFIG_H
+
+#include "../lib/array.h"
+#include "../lib/sstring.h"
+
+void freeconfig();
+void initconfig(char *filename);
+void dumpconfig();
+void rehashconfig();
+array *getconfigitems(char *module, char *key);
+sstring *getconfigitem(char *module, char *key);
+sstring *getcopyconfigitem(char *module, char *key, char *defaultvalue, int len);
+
+#endif
diff --git a/core/error.c b/core/error.c
new file mode 100644 (file)
index 0000000..fc7505c
--- /dev/null
@@ -0,0 +1,47 @@
+/* error.c */
+
+#include <stdarg.h>
+#include <time.h>
+#include <stdio.h>
+#include "error.h"
+
+char *sevtostring(int severity) {
+  switch(severity) {
+    case ERR_DEBUG:
+      return "debug";
+    
+    case ERR_INFO:
+      return "info";
+      
+    case ERR_WARNING:
+      return "warning";
+    
+    case ERR_ERROR:
+      return "error";
+      
+    case ERR_FATAL:
+      return "fatal error";
+      
+    default:
+      return "unknown error";
+  }
+}
+
+void Error(char *source, int severity, char *reason, ... ) {
+  char buf[512];
+  va_list va;
+  struct tm *tm;
+  time_t now;
+  char timebuf[100];
+  
+  va_start(va,reason);
+  vsnprintf(buf,512,reason,va);
+  va_end(va);
+  
+  if (severity>ERR_DEBUG) {
+    now=time(NULL);
+    tm=gmtime(&now);
+    strftime(timebuf,100,"%Y-%m-%d %H:%M:%S",tm);
+    fprintf(stderr,"[%s] %s(%s): %s\n",timebuf,sevtostring(severity),source,buf);
+  }
+}
diff --git a/core/error.h b/core/error.h
new file mode 100644 (file)
index 0000000..8922514
--- /dev/null
@@ -0,0 +1,14 @@
+/* error.h:
+ *
+ * Error flagging routines 
+ */
+
+
+#define ERR_DEBUG    0
+#define ERR_INFO     1
+#define ERR_WARNING  2
+#define ERR_ERROR    3
+#define ERR_FATAL    4
+
+void Error(char *source, int severity, char *reason, ... );
+
diff --git a/core/events-epoll.c b/core/events-epoll.c
new file mode 100644 (file)
index 0000000..d7a96f6
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * events.c: the event handling core, poll() version
+ */
+
+#include <stdio.h>
+#include <sys/poll.h>
+#include <sys/epoll.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include "../lib/array.h"
+#include "events.h"
+#include "error.h"
+#include "hooks.h"
+
+/* We need to track the handler for each fd - epoll() can 
+ * return an fd or a pointer but not both :(.  We'll keep the
+ * fd in the epoll_data structure for simplicity. */
+
+typedef struct {
+  FDHandler          handler;
+} reghandler;
+
+reghandler *eventhandlers;
+
+unsigned int maxfds;
+
+int eventadds;
+int eventdels;
+int eventexes;
+
+/* How many fds are currently registered */
+int regfds;
+int epollfd;
+
+void eventstats(int hooknum, void *arg);
+
+void inithandlers() {
+  regfds=0;
+  eventadds=eventdels=eventexes=0;
+  maxfds=STARTFDS;
+  eventhandlers=(reghandler *)malloc(maxfds*sizeof(reghandler));
+  memset(eventhandlers,0,maxfds*sizeof(reghandler));
+
+  /* Get an epoll FD */
+  if ((epollfd=epoll_create(STARTFDS))<0) {
+    Error("events",ERR_FATAL,"Unable to initialise epoll.");
+  }
+
+  registerhook(HOOK_CORE_STATSREQUEST, &eventstats);
+}
+
+/*
+ * checkindex():
+ *  Given the number of a new file descriptor, makes sure that the array
+ * will be big enough to deal with it.
+ */ 
+
+void checkindex(unsigned index) {
+  int oldmax=maxfds;
+  
+  if (index<maxfds) {
+    return;
+  }
+
+  while (maxfds<=index) {
+    maxfds+=GROWFDS;
+  }
+
+  eventhandlers=(reghandler *)realloc((void *)eventhandlers,maxfds*sizeof(reghandler));
+  memset(&eventhandlers[oldmax],0,(maxfds-oldmax)*sizeof(reghandler));
+}
+
+/*
+ * polltoepoll():
+ *  Converts a poll-style event variable to an epoll one.
+ */
+unsigned int polltoepoll(short events) {
+  unsigned int epe=EPOLLERR|EPOLLHUP; /* Default event mask */
+  
+  if (events & POLLIN)  epe |= EPOLLIN;
+  if (events & POLLOUT) epe |= EPOLLOUT;
+  if (events & POLLPRI) epe |= EPOLLPRI;
+
+  return epe;
+}
+
+short epolltopoll(unsigned int events) {
+  short e=0;
+  
+  if (events & EPOLLIN)  e |= POLLIN;
+  if (events & EPOLLOUT) e |= POLLOUT;
+  if (events & EPOLLERR) e |= POLLERR;
+  if (events & EPOLLPRI) e |= POLLPRI;
+  if (events & EPOLLHUP) e |= POLLHUP;
+
+  return e;
+}  
+
+/* 
+ * registerhandler():
+ *  Add an fd to the epoll array.
+ */
+
+int registerhandler(int fd, short events, FDHandler handler) {
+  struct epoll_event epe;
+
+  checkindex(fd);
+
+  /* Check that it's not already registered */
+  if (eventhandlers[fd].handler!=NULL) {
+    Error("events",ERR_WARNING,"Attempting to register already-registered fd %d.",fd);
+    return 1;
+  }  
+   
+  eventhandlers[fd].handler=handler;
+
+  epe.data.fd=fd;
+  epe.events=polltoepoll(events);
+
+  if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &epe)) {
+    Error("events",ERR_WARNING,"Error %d adding fd %d to epoll (events=%d)",errno,fd,epe.events);
+    return 1;
+  }
+
+  eventadds++;
+  regfds++;
+  return 0;
+}
+
+/*
+ * deregisterhandler():
+ *  Remove an fd from the poll() array.
+ *
+ * The poll() version can't save any time if doclose is set, so
+ * we just do the same work and then close the socket if it's set.
+ * 
+ * Now O(1)
+ */
+
+
+int deregisterhandler(int fd, int doclose) {
+  struct epoll_event epe;
+  
+  checkindex(fd);
+  
+  /* Check that the handler exists */
+  if (eventhandlers[fd].handler==NULL) {
+    Error("events",ERR_WARNING,"Attempt to deregister unregistered fd: %d",fd);
+    return 1;
+  }
+      
+  eventhandlers[fd].handler=NULL;
+
+  if (epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &epe)) {
+    Error("events",ERR_WARNING,"Error deleting FD %d from epoll: %d",fd,errno);
+  }
+
+  if (doclose) {
+    close(fd);
+  }
+
+  eventdels++;
+  regfds--;
+  
+  return 0;
+}
+
+/*
+ * handleevents():
+ *  Call epoll_wait() and handle and call appropiate handlers
+ *  for any sockets that come up.
+ */
+
+int handleevents(int timeout) {
+  int i,res;
+  struct epoll_event epes[100];
+
+  res=epoll_wait(epollfd, epes, 100, timeout);
+
+  if (res<0) {
+    Error("events",ERR_WARNING,"Error in epoll_wait(): %d",errno);
+    return 1;
+  }
+  
+  for (i=0;i<res;i++) {
+    (eventhandlers[epes[i].data.fd].handler)(epes[i].data.fd, epolltopoll(epes[i].events));
+    eventexes++;
+  }  
+
+  return 0;
+}   
+
+void eventstats(int hooknum, void *arg) {
+  char buf[512];
+  int level=(int) arg;
+  
+  if (level>5) {
+    sprintf(buf,"Events  :%7d fds registered,   %7d fds deregistered",eventadds,eventdels);
+    triggerhook(HOOK_CORE_STATSREPLY,(void *)buf);
+    sprintf(buf,"Events  :%7d events triggered,  %6d fds active",eventexes,regfds);
+    triggerhook(HOOK_CORE_STATSREPLY,(void *)buf);
+  }
+}
diff --git a/core/events-kqueue.c b/core/events-kqueue.c
new file mode 100644 (file)
index 0000000..849b20b
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * events.c: the event handling core, kqueue() version
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "../lib/array.h"
+#include "events.h"
+#include "error.h"
+#include "hooks.h"
+
+/*
+ * OK, for the kqueue() version we just keep an array (indexed by fd)
+ * of what we put in the kqueue() so we can remove it later.  This is only
+ * required because the deregisterhandler() call doesn't include the 
+ * "events" field, thus the kqueue filter used is unknown.
+ *
+ * We have a seperate fixed array addqueue() for stuff we're adding to
+ * the queue; this gets flushed if it's full or held over until we
+ * next call handleevents().
+ */
+
+#define UPDATEQUEUESIZE    100
+
+struct kevent  addqueue[UPDATEQUEUESIZE];
+struct kevent *eventfds;
+
+unsigned int maxfds;
+unsigned int updates;
+
+int kq;
+
+int eventadds;
+int eventdels;
+int eventexes;
+
+/* How many fds are currently registered */
+int regfds;
+
+void eventstats(int hooknum, void *arg);
+
+void inithandlers() {
+  regfds=0;
+  updates=0;
+  eventadds=eventdels=eventexes=0;
+  maxfds=0;
+  eventfds=NULL;
+  kq=kqueue();
+  registerhook(HOOK_CORE_STATSREQUEST, &eventstats);
+}
+
+/*
+ * checkindex():
+ *  Given the number of a new file descriptor, makes sure that the array
+ * will be big enough to deal with it.
+ */ 
+
+void checkindex(unsigned index) {
+  int oldmax=maxfds;
+  
+  if (index<maxfds) {
+    return;
+  }
+
+  while (maxfds<=index) {
+    maxfds+=GROWFDS;
+  }
+
+  eventfds=(struct kevent *)realloc((void *)eventfds,maxfds*sizeof(struct kevent));
+  memset(&eventfds[oldmax],0,(maxfds-oldmax)*sizeof(struct kevent));
+}
+
+/* 
+ * registerhandler():
+ *  Create a kevent structure and put it on the list to be added next 
+ *  time kevent() is called.  If that list is full, we call kevent() 
+ *  to flush it first.
+ *
+ * We pass the handler in as the udata field to the kernel, this way
+ * we don't have to hunt for it when the kernel throws the kevent back
+ * at us.
+ */
+
+int registerhandler(int fd, short events, FDHandler handler) {
+  checkindex(fd);
+   
+  /* Check that it's not already registered */
+  if (eventfds[fd].filter!=0) {
+    return 1;
+  }
+  
+  eventfds[fd].ident=fd;
+  if (events & POLLIN) {
+    eventfds[fd].filter=EVFILT_READ;
+  } else {
+    eventfds[fd].filter=EVFILT_WRITE;
+  }
+  eventfds[fd].flags=EV_ADD;
+  eventfds[fd].fflags=0;
+  eventfds[fd].data=0;
+  eventfds[fd].udata=(void *)handler;
+
+/*  Error("core",ERR_DEBUG,"Adding fd %d filter %d",fd,eventfds[fd].filter); */
+  
+  if (updates>=UPDATEQUEUESIZE) {
+    kevent(kq, addqueue, updates, NULL, 0, NULL);
+    updates=0;
+  }
+  
+  addqueue[updates++]=eventfds[fd];
+  
+  eventadds++;
+  regfds++;
+  return 0;
+}
+
+/*
+ * deregisterhandler():
+ *  Removes the fd's kevent from the kqueue.  Note that if we're 
+ *  going to be closing the fd, it will automatically be removed
+ *  from the queue so we don't have to do anything except the close().
+ */
+
+int deregisterhandler(int fd, int doclose) {
+
+  if (!doclose) {
+    if (updates>=UPDATEQUEUESIZE) {
+      kevent(kq, addqueue, updates, NULL, 0, NULL);
+      updates=0;
+    }
+  
+    eventfds[fd].flags=EV_DELETE;
+    addqueue[updates++]=eventfds[fd];
+
+/*    Error("core",ERR_DEBUG,"Deleting fd %d filter %d",fd,eventfds[fd].filter); */
+  } else {
+    close(fd);
+  }
+
+  regfds--;
+  eventdels++;
+  eventfds[fd].filter=0;
+
+  return 0;
+}
+
+/*
+ * handleevents():
+ *  Call kevent() and handle and call appropiate handlers
+ *  for any sockets that come up.
+ *
+ * This is O(n) in the number of fds returned, rather than the number 
+ * of fds polled as in the poll() case.
+ */
+
+int handleevents(int timeout) {
+  int i,res;
+  struct timespec ts;
+  struct kevent theevents[100];
+  short revents;
+  
+  ts.tv_sec=(timeout/1000);
+  ts.tv_nsec=(timeout%1000)*1000000;
+
+  res=kevent(kq, addqueue, updates, theevents, 100, &ts);
+  updates=0;
+  
+  if (res<0) {
+    return 1;
+  }
+  
+  for (i=0;i<res;i++) {
+    revents=0;
+    
+    /* If EV_ERROR is set it's a failed addition of a kevent to the queue.
+     * This shouldn't happen so we flag a warning. */
+    if(theevents[i].flags & EV_ERROR) {
+      Error("core",ERR_WARNING,"Got EV_ERROR return from kqueue: fd %d filter %d error %d",theevents[i].ident,theevents[i].filter,theevents[i].data); 
+    } else {    
+      /* Otherwise, translate the result into poll() format.. */
+      if(theevents[i].filter==EVFILT_READ) {
+        if (theevents[i].data>0) {
+          revents|=POLLIN;
+        }
+      } else {
+        if (theevents[i].data>0) {
+          revents|=POLLOUT;
+        }
+      }
+      
+      if (theevents[i].flags & EV_EOF || theevents[i].data<0) {
+        revents|=POLLERR;
+      }
+      
+      /* Call the handler */
+      ((FDHandler)(theevents[i].udata))(theevents[i].ident, revents);
+      eventexes++;
+    }
+  }  
+  return 0;
+}   
+
+void eventstats(int hooknum, void *arg) {
+  char buf[512];
+  int level=(int) arg;
+  
+  if (level>5) {
+    sprintf(buf,"Events  :%7d fds registered,   %7d fds deregistered",eventadds,eventdels);
+    triggerhook(HOOK_CORE_STATSREPLY,(void *)buf);
+    sprintf(buf,"Events  :%7d events triggered,  %6d fds active",eventexes,regfds);
+    triggerhook(HOOK_CORE_STATSREPLY,(void *)buf);
+  }
+}
+
diff --git a/core/events-poll.c b/core/events-poll.c
new file mode 100644 (file)
index 0000000..acb27ab
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * events.c: the event handling core, poll() version
+ */
+
+#include <stdio.h>
+#include <sys/poll.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "../lib/array.h"
+#include "events.h"
+#include "error.h"
+#include "hooks.h"
+
+typedef struct {
+  FDHandler handler;
+  int fdarraypos;
+} reghandler;
+
+/*
+ * OK, new data structure format for here to make everything
+ * faster.  Two fixed static arrays, one of struct pollfds
+ * (for feeding to poll) in no particular order.  Second array
+ * has an array of reghandler structs; this is indexed by FD for 
+ * quick reference...
+ */
+
+struct pollfd *eventfds;
+reghandler *eventhandlers;
+
+unsigned int maxfds;
+
+int eventadds;
+int eventdels;
+int eventexes;
+
+/* How many fds are currently registered */
+int regfds;
+
+void eventstats(int hooknum, void *arg);
+
+void inithandlers() {
+  regfds=0;
+  eventadds=eventdels=eventexes=0;
+  maxfds=STARTFDS;
+  eventfds=(struct pollfd *)malloc(maxfds*sizeof(struct pollfd));
+  memset(eventfds,0,maxfds*sizeof(struct pollfd));
+  eventhandlers=(reghandler *)malloc(maxfds*sizeof(reghandler));
+  memset(eventhandlers,0,maxfds*sizeof(reghandler));
+  registerhook(HOOK_CORE_STATSREQUEST, &eventstats);
+}
+
+/*
+ * checkindex():
+ *  Given the number of a new file descriptor, makes sure that the arrays
+ * will be big enough to deal with it.
+ */ 
+
+void checkindex(unsigned index) {
+  int oldmax=maxfds;
+  
+  if (index<maxfds) {
+    return;
+  }
+
+  while (maxfds<=index) {
+    maxfds+=GROWFDS;
+  }
+
+  eventfds=(struct pollfd *)realloc((void *)eventfds,maxfds*sizeof(struct pollfd));
+  memset(&eventfds[oldmax],0,maxfds-oldmax);
+  eventhandlers=(reghandler *)realloc((void *)eventhandlers,maxfds*sizeof(reghandler));
+  memset(&eventhandlers[oldmax],0,maxfds-oldmax);
+}
+
+/* 
+ * registerhandler():
+ *  Add an fd to the poll() array.
+ *
+ * Now O(1)
+ */
+
+int registerhandler(int fd, short events, FDHandler handler) {
+  checkindex(fd);
+
+  /* Check that it's not already registered */
+  if (eventhandlers[fd].handler!=NULL) {
+    return 1;
+  }  
+   
+  eventhandlers[fd].handler=handler;
+  eventhandlers[fd].fdarraypos=regfds;
+
+  eventfds[regfds].fd=fd;
+  eventfds[regfds].events=events;
+  eventfds[regfds].revents=0;
+
+  eventadds++;
+  regfds++;
+  return 0;
+}
+
+/*
+ * deregisterhandler():
+ *  Remove an fd from the poll() array.
+ *
+ * The poll() version can't save any time if doclose is set, so
+ * we just do the same work and then close the socket if it's set.
+ * 
+ * Now O(1)
+ */
+
+
+int deregisterhandler(int fd, int doclose) {
+  int oldfdpos;
+  int lastreggedfd;
+  
+  /* Check that the handler exists */
+  if (eventhandlers[fd].handler==NULL)
+    return 1;
+    
+  /* We need to rearrange the fds array slightly to handle the delete */
+  eventdels++;
+  regfds--;
+  if (regfds>0) {
+    oldfdpos=eventhandlers[fd].fdarraypos;
+    lastreggedfd=eventfds[regfds].fd;
+    if (lastreggedfd!=fd) {
+      /* We need to move the "lastreggedfd" into "oldfdpos" */
+      memcpy(&eventfds[oldfdpos],&eventfds[regfds],sizeof(struct pollfd));
+      eventhandlers[lastreggedfd].fdarraypos=oldfdpos;
+    }
+  }
+  
+  eventhandlers[fd].handler=NULL;
+  eventhandlers[fd].fdarraypos=-1;
+
+  if (doclose) {
+    close(fd);
+  }
+
+  return 0;
+}
+
+/*
+ * handleevents():
+ *  Call poll() and handle and call appropiate handlers
+ *  for any sockets that come up.
+ *
+ * Unavoidably O(n)
+ */
+
+int handleevents(int timeout) {
+  int i,res;
+
+  for (i=0;i<regfds;i++) {
+    eventfds[i].revents=0;
+  }
+  
+  res=poll(eventfds,regfds,timeout);
+  if (res<0) {
+    return 1;
+  }
+  
+  for (i=0;i<regfds;i++) {
+    if(eventfds[i].revents>0) {
+      (eventhandlers[eventfds[i].fd].handler)(eventfds[i].fd, eventfds[i].revents);
+      eventexes++;
+    }
+  }  
+  return 0;
+}   
+
+void eventstats(int hooknum, void *arg) {
+  char buf[512];
+  int level=(int) arg;
+  
+  if (level>5) {
+    sprintf(buf,"Events  :%7d fds registered,   %7d fds deregistered",eventadds,eventdels);
+    triggerhook(HOOK_CORE_STATSREPLY,(void *)buf);
+    sprintf(buf,"Events  :%7d events triggered,  %6d fds active",eventexes,regfds);
+    triggerhook(HOOK_CORE_STATSREPLY,(void *)buf);
+  }
+}
+
diff --git a/core/events.h b/core/events.h
new file mode 100644 (file)
index 0000000..6986898
--- /dev/null
@@ -0,0 +1,16 @@
+/* events.h */
+
+#ifndef __EVENTS_H
+#define __EVENTS_H
+
+#define STARTFDS    1000
+#define GROWFDS     500
+
+typedef void (*FDHandler)(int,short);
+
+void inithandlers();
+int registerhandler(int fd, short events, FDHandler handler);
+int deregisterhandler(int fd, int doclose);
+int handleevents(int timeout);
+
+#endif
diff --git a/core/hooks.c b/core/hooks.c
new file mode 100644 (file)
index 0000000..b79aa24
--- /dev/null
@@ -0,0 +1,68 @@
+/* hooks.c */
+
+#include "hooks.h"
+#include <assert.h>
+#include "../lib/array.h"
+
+array hooks[HOOKMAX];
+
+void inithooks() {
+  int i;
+  
+  for (i=0;i<HOOKMAX;i++) {
+    array_init(&(hooks[i]),sizeof(HookCallback));
+    array_setlim1(&(hooks[i]),2);
+    array_setlim2(&(hooks[i]),2);
+  }
+}
+
+int registerhook(int hooknum, HookCallback callback) {
+  int i;
+  HookCallback *hcbs;
+  
+  if (hooknum>HOOKMAX)
+    return 1;
+    
+  hcbs=(HookCallback *)(hooks[hooknum].content);
+  for(i=0;i<hooks[hooknum].cursi;i++)
+    if(hcbs[i]==callback)
+      return 1;
+
+  i=array_getfreeslot(&hooks[hooknum]);
+  hcbs=(HookCallback *)(hooks[hooknum].content);
+  hcbs[i]=callback;
+  
+  return 0;
+}
+
+int deregisterhook(int hooknum, HookCallback callback) {
+  int i;
+  HookCallback *hcbs;
+  
+  if (hooknum>HOOKMAX)
+    return 1;
+    
+  hcbs=(HookCallback *)(hooks[hooknum].content);
+
+  for(i=0;i<hooks[hooknum].cursi;i++)
+    if(hcbs[i]==callback) {
+      array_delslot(&(hooks[hooknum]),i);
+      return 0;
+    }
+
+  return 1;
+}
+  
+void triggerhook(int hooknum, void *arg) {
+  int i;
+  HookCallback *hcbs;
+  
+  if (hooknum>HOOKMAX)
+    return;
+    
+  hcbs=(HookCallback *)(hooks[hooknum].content);
+  for (i=0;i<hooks[hooknum].cursi;i++) {
+    (hcbs[i])(hooknum, arg);
+  }
+}
+  
diff --git a/core/hooks.h b/core/hooks.h
new file mode 100644 (file)
index 0000000..483574f
--- /dev/null
@@ -0,0 +1,60 @@
+/* hooks.h */
+
+#ifndef __HOOKS_H
+#define __HOOKS_H
+
+#define HOOKMAX 5000
+
+/* This is the authoritative registry of all known hook numbers */
+
+#define HOOK_CORE_REHASH             0
+#define HOOK_CORE_STATSREQUEST       1
+#define HOOK_CORE_STATSREPLY         2
+
+#define HOOK_IRC_CONNECTED         100
+#define HOOK_IRC_DISCON            101
+#define HOOK_IRC_SENDBURSTSERVERS  102
+#define HOOK_IRC_SENDBURSTNICKS    103
+#define HOOK_IRC_SENDBURSTBURSTS   104
+
+#define HOOK_SERVER_NEWSERVER      200  /* Argument is number of new server */
+#define HOOK_SERVER_LOSTSERVER     201  /* Argument is number of lost server */
+
+#define HOOK_NICK_NEWNICK          300  /* Argument is nick* */
+#define HOOK_NICK_RENAME           301  /* Argument is nick* */
+#define HOOK_NICK_LOSTNICK         302  /* Argument is nick* */
+#define HOOK_NICK_WHOISCHANNELS    303  /* Argument is nick*[2] (sender, target) */
+#define HOOK_NICK_ACCOUNT          304  /* Argument is nick* */
+#define HOOK_NICK_QUIT             305  /* Argument is void*[2] (nick, reason) */
+#define HOOK_NICK_SETHOST          306  /* Argument is nick* */
+
+#define HOOK_CHANNEL_BURST         400  /* Argument is channel pointer */
+#define HOOK_CHANNEL_CREATE        401  /* Argument is void*[2] (channel, nick) */
+#define HOOK_CHANNEL_JOIN          402  /* Argument is void*[2] (channel, nick) */
+#define HOOK_CHANNEL_PART          403  /* Argument is void*[3] (channel, nick, reason) */
+#define HOOK_CHANNEL_KICK          404  /* Argument is void*[4] (channel, kicked, kicker, reason) ** KICKER COULD BE NULL ***/
+#define HOOK_CHANNEL_TOPIC         405  /* Argument is void*[2] (channel, nick) ** NICK COULD BE NULL ** */
+#define HOOK_CHANNEL_MODECHANGE    406  /* Argument is void*[3] (channel, nick, flags) ** NICK COULD BE NULL ** */
+#define HOOK_CHANNEL_BANSET        407  /* Argument is void*[2] (channel, nick) ** NICK COULD BE NULL **, ban will be first ban on channel */
+#define HOOK_CHANNEL_BANCLEAR      408  /* Argument is void*[2] (channel, nick) ** NICK COULD BE NULL **, ban will be gone.  XXX - could we care what the ban was? */
+#define HOOK_CHANNEL_OPPED         409  /* Argument is void*[3] (channel, nick, target) ** NICK COULD BE NULL ** */
+#define HOOK_CHANNEL_DEOPPED       410  /* Argument is void*[3] (channel, nick, target) ** NICK COULD BE NULL ** */
+#define HOOK_CHANNEL_VOICED        411  /* Argument is void*[3] (channel, nick, target) ** NICK COULD BE NULL ** */
+#define HOOK_CHANNEL_DEVOICED      412  /* Argument is void*[3] (channel, nick, target) ** NICK COULD BE NULL ** */
+
+#define HOOK_CHANNEL_NEWCHANNEL    413  /* Argument is channel pointer */
+#define HOOK_CHANNEL_LOSTCHANNEL   414  /* Argument is channel pointer */
+
+#define HOOK_CHANNEL_NEWNICK       415  /* Argument is void*[2] (channel, nick) */
+#define HOOK_CHANNEL_LOSTNICK      416  /* Argument is void*[2] (channel, nick) */
+
+#define HOOK_CHANSERV_DBLOADED     500  /* No arg */
+
+typedef void (*HookCallback)(int, void *);
+
+void inithooks();
+int registerhook(int hooknum, HookCallback callback);
+int deregisterhook(int hooknum, HookCallback callback);
+void triggerhook(int hooknum, void *arg);
+
+#endif
diff --git a/core/main.c b/core/main.c
new file mode 100644 (file)
index 0000000..201d0a1
--- /dev/null
@@ -0,0 +1,48 @@
+#include "../lib/sstring.h"
+#include "events.h"
+#include "schedule.h"
+#include "hooks.h"
+#include "modules.h"
+#include "config.h"
+#include "error.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/time.h>
+
+void initseed();
+
+int main(int argc, char **argv) {
+  initseed();
+  inithooks();
+  inithandlers();
+  initschedule();
+  
+  initsstring();
+  
+  if (argc>1) {
+    initconfig(argv[1]);
+  } else {  
+    initconfig("newserv.conf");
+  }
+
+  /* Loading the modules will bring in the bulk of the code */
+  initmodules();
+
+  /* Main loop */
+  for(;;) {
+    handleevents(10);  
+    doscheduledevents(time(NULL));
+  }  
+}
+
+/*
+ * seed the pseudo-random number generator, rand()
+ */
+void initseed() {
+  struct timeval t;
+
+  gettimeofday(&t, NULL);
+  srand(t.tv_usec);
+}
diff --git a/core/modules.c b/core/modules.c
new file mode 100644 (file)
index 0000000..1f435ec
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * modules.c:
+ *
+ * Provides functions for dealing with dynamic modules.
+ */
+#include <stdlib.h>
+#include <dlfcn.h>
+#include "modules.h"
+#include "../lib/array.h" 
+#include "../lib/sstring.h"
+#include "../lib/irc_string.h"
+#include "config.h"
+#include "error.h"
+#include <stdio.h>
+#include <string.h>
+
+array modules;
+
+sstring *moddir;
+sstring *modsuffix;
+
+void modulerehash() {
+  int i;
+  sstring **mods;
+  array *autoloads;
+  
+  if (moddir!=NULL)
+    freesstring(moddir);
+  
+  if (modsuffix!=NULL)
+    freesstring(modsuffix);
+  
+  moddir=getcopyconfigitem("core","moduledir",".",100);
+  modsuffix=getcopyconfigitem("core","modulesuffix",".so",5);  
+
+  /* Check for auto-load modules */
+  autoloads=getconfigitems("core","loadmodule");
+  if (autoloads!=NULL) {
+    mods=(sstring **)(autoloads->content);
+    for (i=0;i<autoloads->cursi;i++) {
+      insmod(mods[i]->content);
+    }
+  }
+}
+
+void initmodules() {
+  array_init(&modules,sizeof(module));
+  array_setlim1(&modules,5);
+  array_setlim2(&modules,10);
+  
+  moddir=NULL;
+  modsuffix=NULL;
+  modulerehash();  
+}
+
+int insmod(char *modulename) {
+  int i;
+  module *mods;
+  char buf[512];
+
+  delchars(modulename,"./\\;");
+  
+  if (isloaded(modulename)) {
+    Error("core",ERR_DEBUG,"Tried to load already loaded module: %s",modulename);
+    return 1;
+  }
+  
+  if (strlen(modulename)>100) {
+    Error("core",ERR_WARNING,"Module name too long: %s",modulename);  
+    return 1;
+  }
+  
+  i=array_getfreeslot(&modules);
+  mods=(module *)(modules.content);
+  
+  sprintf(buf,"%s/%s%s",moddir->content,modulename,modsuffix->content);
+  mods[i].handle=dlopen(buf,RTLD_NOW|RTLD_GLOBAL);
+  
+  if(mods[i].handle==NULL) {
+    Error("core",ERR_ERROR,"Loading module %s failed: %s",modulename,dlerror());
+    array_delslot(&modules,i);
+    return -1;
+  }
+  
+  mods[i].name=getsstring(modulename,MODULENAMELEN);
+
+  Error("core",ERR_INFO,"Loaded module %s OK.",modulename);
+  
+  return 0;
+}
+
+int getindex(char *modulename) {
+  int i;
+  module *mods;
+  
+  mods=(module *)(modules.content);
+  for(i=0;i<modules.cursi;i++)
+    if (!strcmp(mods[i].name->content,modulename))
+      return i;
+      
+  return -1;
+}
+
+int isloaded(char *modulename) {
+  if (getindex(modulename)==-1)
+    return 0;
+  else
+    return 1;
+}
+
+int rmmod(char *modulename) {
+  int i;
+  module *mods;
+  
+  delchars(modulename,"./\\;");
+  
+  i=getindex(modulename);
+  if (i<0)
+    return 1;
+  
+  mods=(module *)(modules.content);
+    
+  dlclose(mods[i].handle);
+  freesstring(mods[i].name);
+  array_delslot(&modules,i);
+
+  Error("core",ERR_INFO,"Removed module %s.",modulename);
+  
+  return 0;
+}    
diff --git a/core/modules.h b/core/modules.h
new file mode 100644 (file)
index 0000000..6eba231
--- /dev/null
@@ -0,0 +1,21 @@
+/* modules.h */
+
+#ifndef __MODULES_H
+#define __MODULES_H
+
+#include "../lib/sstring.h"
+
+#define MODULENAMELEN 40
+#define MODULEDESCLEN 200
+
+typedef struct {
+  sstring *name;
+  void    *handle;
+} module;
+
+void initmodules();
+int insmod(char *modulename);
+int getindex(char *modulename);
+int isloaded(char *modulename);
+int rmmod(char *modulename);
+#endif
diff --git a/core/schedule.c b/core/schedule.c
new file mode 100644 (file)
index 0000000..18219b6
--- /dev/null
@@ -0,0 +1,321 @@
+/* schedule.c */
+
+#include "schedule.h"
+#include "error.h"
+#include "hooks.h"
+#include "../lib/array.h"
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#define INITSCHEDSIZE      1000
+#define GROWSCHEDSIZE      500
+
+#undef SCHEDDEBUG
+
+schedule **events;
+int heapsize;
+int heapmax;
+
+int schedadds;
+int scheddels;
+int scheddelfast;
+int schedexes;
+
+/* Local prototypes */
+void schedulestats(int hooknum, void *arg);
+
+void initschedule() {
+  initschedulealloc();
+  events=NULL;
+  schedadds=scheddels=schedexes=scheddelfast=0;
+  registerhook(HOOK_CORE_STATSREQUEST, &schedulestats);
+  heapsize=0;
+  heapmax=INITSCHEDSIZE;
+  events=(schedule **)malloc(INITSCHEDSIZE*sizeof(schedule *));
+}
+
+void schedule_heapify(int index) {
+  int firstindex=index;
+  schedule *ep;
+  
+  /* If this node is a leaf, do nothing */  
+  if ((index*2)+1 >= heapsize) {
+    return;
+  }
+
+  /* Check left child */  
+  if (events[index]->nextschedule > events[(index*2)+1]->nextschedule) {
+    firstindex=(index*2)+1;
+  }
+
+  /* Check right (if exists) */
+  if ((index*2)+2 < heapsize) {
+    if (events[firstindex]->nextschedule > events[(index*2)+2]->nextschedule) {
+      firstindex=(index*2)+2;
+    }
+  }
+  
+  /* If both children were scheduled after us, we're done */
+  if (firstindex==index) {
+    return;
+  }
+
+  /* Swap the two pointers around in the heap */  
+  ep=events[firstindex];
+  events[firstindex]=events[index];
+  events[index]=ep;
+
+  /* Fix up the "index" field in the structures */
+  events[firstindex]->index=firstindex;
+  events[index]->index=index;
+  
+  schedule_heapify(firstindex);
+}
+
+void insertschedule (schedule *sp) {
+  int mypos,myparent;
+
+  schedadds++;
+  
+  if (heapsize>=heapmax) {
+    /* We need to grow the heap */
+    heapmax+=GROWSCHEDSIZE;
+    events=(schedule **)realloc((void *)events,heapmax*sizeof(schedule *));
+  }
+  
+  mypos=heapsize++;
+  
+  /* Travel up the heap looking for a slot for this new element */
+  /* mypos points at a (vacant) candidate space; we either put the element
+   * in this space, or pull it's parent down and try again with it's parent's space */
+  for (;;) {
+    myparent=(mypos-1)/2;
+    if (mypos==0 || (sp->nextschedule >= events[myparent]->nextschedule)) {
+      /* We reached the top, or our parent is scheduled before us -- end */
+      events[mypos]=sp;
+      sp->index=mypos;
+      break;
+    } else {
+      /* Pull the parent into this space and move up the heap */
+      events[mypos]=events[myparent];
+      events[mypos]->index=mypos;
+      mypos=myparent;
+    }
+  }
+}
+
+void schedule_remove (int index) {
+  int mypos,myparent;
+  schedule *sp;
+
+  assert(index<heapsize);
+
+#ifdef SCHEDDEBUG
+  Error("schedule",ERR_DEBUG,"schedule_remove: %d",index);
+#endif
+
+  if (index<0)
+    return;
+
+  scheddels++;  
+  heapsize--;
+  
+  /* Move the last element into the position we just deleted, then heapify 
+   * If we happen to be deleting the last element, do nothing */
+  if (index!=heapsize) {
+    events[index]->index=-1;
+    events[index]=events[heapsize];
+    events[index]->index=index;
+    schedule_heapify(index);
+    
+    /* Now we may need to float the element up the heap, similar to the insert case */
+    mypos=index;
+    for (;;) {
+      myparent=(mypos-1)/2;
+      if (mypos==0 || (events[mypos]->nextschedule >= events[myparent]->nextschedule)) {
+        break;
+      } else {
+        /* Swap the element up the tree */
+        sp=events[myparent];
+        events[myparent]=events[mypos];
+        events[mypos]=sp;
+        /* Fix up the index members */
+        events[myparent]->index=myparent;
+        events[mypos]->index=mypos;
+        
+        mypos=myparent;
+      }
+    }
+  }
+}
+  
+void *scheduleoneshot(time_t when, ScheduleCallback callback, void *arg) {
+  schedule *sp;
+  
+  sp=getschedule();
+  
+  sp->nextschedule=when;
+  sp->type=SCHEDULE_ONESHOT;
+  sp->repeatinterval=0;
+  sp->repeatcount=1;
+  sp->callback=callback;
+  sp->callbackparam=arg;
+
+  insertschedule(sp);
+  
+#ifdef SCHEDDEBUG
+  Error("schedule",ERR_DEBUG,"scheduleoneshot: (%ld, %x, %x) = %x",when, callback, arg, sp);
+#endif
+
+  return (void *)sp;
+}
+
+void *schedulerecurring(time_t first, int count, time_t interval, ScheduleCallback callback, void *arg) {
+  schedule *sp;
+
+  if (count==1) {
+    return scheduleoneshot(first, callback, arg);
+  }
+
+  sp=getschedule();
+
+  sp->nextschedule=first;
+  sp->type=SCHEDULE_REPEATING;
+  sp->repeatinterval=interval;
+  sp->repeatcount=(count-1);
+  sp->callback=callback;
+  sp->callbackparam=arg;
+  
+  insertschedule(sp);
+  
+  return (void *)sp;
+}
+
+void deleteschedule(void *sch, ScheduleCallback callback, void *arg) {
+  schedule *sp;
+  int i;
+  
+  /* New (optional) faster path: Clients can track the schedule pointer if they wish and 
+   * pass it in here for an O(1) *cough* O(lg n) delete */
+
+#ifdef SCHEDDEBUG
+  Error("schedule",ERR_DEBUG,"deleteschedule(%x,%x,%x)",sch,callback, arg);
+#endif
+
+  if (sch) {
+    sp=(schedule *)sch;
+    /* Double check the params are correct: 
+     *  because we recycle and never free schedule structs it's
+     *  actually OK to try and delete a schedule that has been executed... */
+     
+    if (sp->callback==callback && sp->callbackparam==arg) {
+      scheddelfast++;
+      schedule_remove(sp->index);
+      freeschedule(sp);
+    }
+    return;
+  }
+  
+  /* Argh, have to find it by brute force */
+
+  for(i=0;i<heapsize;i++) {
+    if ((events[i]->callback==callback) && (events[i]->callbackparam==arg)) {
+      sp=events[i];
+      schedule_remove(sp->index);
+      freeschedule(sp);
+      return;
+    }
+  }
+}
+
+void deleteallschedules(ScheduleCallback callback) {
+  schedule *sp;
+  int i;
+  
+trydel:
+  /* OK, this gets to be REALLY cheesy and stupidly slow as well */
+  
+  for(i=0;i<heapsize;i++) {
+    if (events[i]->callback==callback) {
+      sp=events[i];
+      schedule_remove(sp->index);
+      freeschedule(sp);
+      goto trydel;
+    }
+  }
+}
+
+void doscheduledevents(time_t when) {
+  void *arg;
+  ScheduleCallback sc;
+  schedule *sp;
+
+  while (heapsize && events[0] && events[0]->nextschedule <= when) {
+    /* Pick out the first element first */
+    sp=events[0];
+    sp->index=-1; /* Invalidate index so that an explicit delete doesn't screw us up */
+
+    /* Remove from the top of the heap */
+    heapsize--;
+    events[0]=events[heapsize];
+    events[0]->index=0;
+    schedule_heapify(0);
+    
+    if (sp->callback==NULL) {
+      Error("core",ERR_ERROR,"Tried to call NULL function in doscheduledevents(): (%x, %x, %x)",sp,sp->callback,sp->callbackparam);
+      continue;
+    }
+
+    /* Store the callback */
+    arg=(sp->callbackparam);
+    sc=(sp->callback);
+    
+    /* Update the structures _before_ doing the callback.. */
+    switch(sp->type) {
+    case SCHEDULE_ONESHOT:
+      freeschedule(sp);
+      break;
+        
+    case SCHEDULE_REPEATING:
+      sp->nextschedule+=sp->repeatinterval;
+      /* Repeat count:
+       *  0 for repeat forever
+       *  1 for repeat set number of times..
+       *
+       * When we schedule it for the last time, change it to a ONESHOT event
+       */
+      if (sp->repeatcount>0) {
+       sp->repeatcount--;
+       if (sp->repeatcount==0) {
+         sp->type=SCHEDULE_ONESHOT;       
+       }
+      }
+      insertschedule(sp);
+      break;
+    }
+#ifdef SCHEDDEBUG
+    Error("schedule",ERR_DEBUG,"exec schedule:(%x, %x, %x)", sp, sc, arg);
+#endif
+    (sc)(arg);
+#ifdef SCHEDDEBUG
+    Error("schedule",ERR_DEBUG,"schedule run OK");
+#endif
+    schedexes++;
+  }
+}
+
+void schedulestats(int hooknum, void *arg) {
+  int level=(int)arg;
+  char buf[512];
+
+  if (level>5) {
+    sprintf(buf,"Schedule:%7d events scheduled, %7d events executed",schedadds,schedexes);
+    triggerhook(HOOK_CORE_STATSREPLY,(void *)buf);
+    sprintf(buf,"Schedule:%7d events deleted,   %7d fast deletes (%.2f%%)",scheddels,scheddelfast,(float)(scheddelfast*100)/scheddels);
+    triggerhook(HOOK_CORE_STATSREPLY,(void *)buf);
+    sprintf(buf,"Schedule:%7d events currently in queue",heapsize);
+    triggerhook(HOOK_CORE_STATSREPLY,(void *)buf);
+  }
+}
diff --git a/core/schedule.h b/core/schedule.h
new file mode 100644 (file)
index 0000000..5b6f8fa
--- /dev/null
@@ -0,0 +1,39 @@
+/* schedule.h */
+
+#ifndef __SCHEDULE_H
+#define __SCHEDULE_H
+
+#include <time.h>
+
+#define SCHEDULE_ONESHOT    0
+#define SCHEDULE_REPEATING  1
+
+typedef void (*ScheduleCallback)(void *);
+
+typedef struct schedule {
+  time_t            nextschedule;
+  int               type;
+  int               repeatinterval;
+  int               repeatcount;
+  ScheduleCallback  callback;
+  void             *callbackparam;
+  int               index; /* Where in the array this event is currently situated */
+} schedule;
+
+
+/* schedulealloc.c */
+
+void initschedulealloc();
+schedule *getschedule();
+void freeschedule(schedule *sp);
+
+/* schedule.c */
+void initschedule();
+void sortschedule();
+void *scheduleoneshot(time_t when, ScheduleCallback callback, void *arg);
+void *schedulerecurring(time_t first, int count, time_t interval, ScheduleCallback callback, void *arg);
+void deleteschedule(void *sch, ScheduleCallback callback, void *arg);
+void deleteallschedules(ScheduleCallback callback);
+void doscheduledevents(time_t when);
+  
+#endif
diff --git a/core/schedulealloc.c b/core/schedulealloc.c
new file mode 100644 (file)
index 0000000..2929826
--- /dev/null
@@ -0,0 +1,37 @@
+/* schedulealloc.c */
+
+#include "schedule.h"
+#include <stdlib.h>
+
+#define ALLOCUNIT 100
+
+schedule *freescheds;
+
+void initschedulealloc() {
+  freescheds=NULL;
+}
+
+schedule *getschedule() {
+  int i;
+  schedule *sp;
+
+  if (freescheds==NULL) {
+    freescheds=(schedule *)malloc(ALLOCUNIT*sizeof(schedule));
+    for (i=0;i<ALLOCUNIT-1;i++) {
+      freescheds[i].callbackparam=(void *)&(freescheds[i+1]);
+    }
+    freescheds[ALLOCUNIT-1].callbackparam=NULL;
+  }
+
+  sp=freescheds;
+  freescheds=(schedule *)sp->callbackparam;
+
+  return sp;
+}
+
+void freeschedule(schedule *sp) {
+  sp->callbackparam=(void *)freescheds;
+  sp->callback=NULL;
+  freescheds=sp;
+}
+
diff --git a/helpmod2/helpmod.db.old b/helpmod2/helpmod.db.old
new file mode 100644 (file)
index 0000000..1da2528
--- /dev/null
@@ -0,0 +1,187 @@
+% H2 version 2.01 database
+% Wed Feb 23 08:58:30 2005
+
+
+% lamercontrol profile structure:
+%  X (string):
+%  Y (string):
+%  Z (int):
+lamercontrol profile
+       nazi
+       25 10
+       1 4
+       3 4 4
+       3 0.010 7
+       0 1 2
+lamercontrol profile
+       strict
+       35 15
+       2 6
+       4 5 5
+       4 0.010 10
+       0 2 3
+lamercontrol profile
+       default
+       40 20
+       3 7
+       6 7 7
+       5 0.008 10
+       1 3 5
+lamercontrol profile
+       liberal
+       50 25
+       4 9
+       8 10 10
+       7 0.006 12
+       2 5 8
+
+% channel structure:
+%  name (string):
+%  flags (integer):
+%  welcome message (string):
+%  lamercontrol profile (string):
+channel
+       #feds
+       8f03a
+       Welcome to #feds
+       default
+       0 % censor
+       17 % terms
+       takeover
+       Support for recovering stolen channels is limited as the responsibility for protecting those channels is your own. However, if you have not already done so, state the #channelname and be patient. If the operator informs you that he or she will not intervene in this case then there is no appeal and you should choose a new channel and consider what security mistakes you made which resulted in the loss of the original channel.
+       serverapp
+       You can find a server application at http://www.quakenet.org/server_application *READ IT CAREFULLY* Do not ask questions about the application form in #feds. Response time for submitted applications is typically between 2 to 4 weeks.
+       score
+       You must be in the top 5 recognised chanops to request L. Statistics for L are only recorded when you have op in the channel and there are at least 3 other people in the channel with you (who may or may not be channel ops). Previous op history is kept for approximately 10 days therefore it is possible for previous ops to have op time that greatly exceeds 2 hours, preventing you requesting L to a channel that appears unused. You will simply have to wait until the op time is exceeded or the records expire. 
+       stats
+       Please do not join #feds to question the statistical monitoring we carry out to check your L, Q or S request. Our services count according to internal rules designed to avoid network overload. Despite what you may perceive, services check these statistics much more accurately than you can. If you have failed in a request then you will simply have to try again in the future.
+       S
+       This is QuakeNet's antispam service. It can be requested from www.quakenet.org. Read the LFAQ on the site before requesting.
+       abuse
+       This channel can be used for reporting abusive users, not requesting specific punishment for the alleged crimes. Tell us where it is happening, and who is doing it, we will monitor the situation and decide how to deal with it. You may leave once the abuse has been reported. The decision of an operator is final.
+       removebot
+       Delete all CHANLEV's from the channel, and then L or Q will leave the channel. If the users are not online you will have to use their authname. Type ?? #auth for more.
+       logs
+       Log files cannot be taken as evidence by IRC operators in most situations, due to the ease with which they can be faked. As such, you should not attempt to send logs to an operator or paste large amounts of them into the channel unless asked to do so.
+       L 
+       QuakeNet's channel service L can be requested at www.quakenet.org, Read the LFAQ on the site before requesting.
+       ircops
+       IRC Operators of QuakeNet are volunteers. They do not get paid for their work, and often contribute out of their own pocket towards QuakeNet`s costs. Most of the IRC Operators have ordinary work to attend to at day and/or night time, families etc. The IRC Operators of QuakeNet are NOT required or obligated to help users at any time.
+       IPv6
+       QuakeNet does not support IPv6, and there are no plans to support IPV6 in the near future 
+       gaveowner
+       If you give another user the +n chanlev on Q or L, this user is then allowed to take away your own +n. It does not matter who originally requested the service. For this reason, you should exercise extreme caution when setting the owner on users in channels you currently control.
+       freeop 
+       Operator help for takeovers on freeop channels is not available. If you want support in protecting your channel then use the channel modes and chanlevs wisely and use them to manage your channel, not to win friends and influence.
+       business
+       QuakeNet does NOT offer any additional support to users wishing to operate, or propogate, a business on our servers. If you wish to do so in spite of this, you will NOT recieve support from our staff, and will be dealt with like any other user on the network. All business activities which make use of QuakeNet resources are at your own risk and QuakeNet in no way guarantees or warrants that a service available currently will be available on a continued basis.
+       eggdrop
+       We do not provide help with other people`s bots/eggdrops. see the bot administrator for help (a /whois of the bot will often provide information on where to find the administrator).
+       question 
+       State your question. Do not ask if you can ask a question, or if particular staff are available. Do not use 1 word per line when stating your question, and if it is about a channel, provide the #channel name. Do not paste logs or other large text items in the channel.
+       #feds
+       This channel is for matters REQUIRING the assistance of an IRC operator. Questions answered in the FAQs, or that do not require an IRC operator, will be ignored. Read the FAQs at www.quakenet.org, and if that does not help, visit #help.
+       0 0 0 0 0 0
+       0 0 0 0 0 0
+       0 0 0 0 0 0
+       0 0 0 0 0 0
+       0 0 3 0 0 0
+       0 0 0 0 0 0
+       0 0 1 0 0 0
+       0 0 0 0 0 0
+       0 0 0 0 0 0
+       0 0 0 0 0 0
+       0 0 0 0 0 0
+       0 0 0 0 0 0
+       0 0 0 0 0 0
+       0 0 0 0 0 0
+       0 0 0 0 0 0
+       0 0 0 0 0 0
+       0 0 0 0 0 0
+
+
+% account structure:
+%  name (string):
+%  level (integer):
+account
+       minimoo
+       4       0
+       0 % statistics for this channel
+account
+       Deckard
+       4       0
+       0 % statistics for this channel
+account
+       Salt
+       4       0
+       0 % statistics for this channel
+account
+       rnikkila
+       5       0
+       0 % statistics for this channel
+
+
+% ban structure:
+%  banmask (string):
+%  reason (string):
+%  expiration (int):
+
+% term structure:
+%  name (string):
+%  description (string):
+term
+       password
+       If you have forgotten your auth password use /msg q requestpassword. If you have not set your email address then the auth account is lost - an oper will NOT tell you the password for any auth account. Accounts that formely had owner on a channel will expire in 40 days at which point the next highest chanlev in the channel may use /MSG L (or Q) REQUESTOWNER. 
+term
+       P
+       All clients are scanned upon connection for open proxies. That means that the service tries to connect to the user`s computer, and if successful, try to connect back to QuakeNet, through the proxy. If successful with that, the user gets a temporary ban from QuakeNet. It is up to the user to secure his or her computer. The action of connecting to QuakeNet indicates your acceptance of this security test.
+term
+       requestop
+       You can use /msg o requestop #channel to request channel operator status in a channel that has no ops. This command will not work during a netsplit, or if you are not a recognised chanop. If you cannot get op in a channel then an operator will not assist you.
+term
+       nickserv
+       QuakeNet does not use Nickserv to reserve nicknames. As a result of this, you cannot register your nickname on QuakeNet. If a nickname is available, any user is allowed to use it. However, this does NOT apply to the nicknames of IRC Operators, Helpers and Network services, due to security related issues. If a user uses any of these nicknames, an operator may immediately disconnect the user from the network WITHOUT prior notice.
+term
+       netrider
+       If you create a channel during a netsplit which exists on the other side of the split and has +i or +k set, then you will be automatically kicked off the channel when the net rejoins with the kick message 'NetRider'. 
+term
+       expire
+       An unused L bot will expire from a channel after 20 days of inactivity (No known users joining), or if the channel usercount is insufficient over a period of time. A Q bot will expire after 40 days of inactivity. You will need to request L into the channel again if you wish to secure it. These unused services are removed during a cleanup process which typically takes place every 40 days, but may on occasion occur later.
+term
+       fixtakeover
+       Use /msg L (or Q) recover #channel to recover your channel. This command requires at least +m in your chanlev. If you are +ao only you can /msg L (or Q) invite #channel to enter the channel and remove channel modes manually. Note that if a Q ban has been set on you the invite command will not be effective.
+term
+       fishbot
+       Fishbot exists to remind our users that fish do indeed go m00! To get fishbot in your channel, do /invite fishbot #channel. To remove fishbot from your channel, just kick it. Fishbot has NO other purpose and is not part of some secret, evil conspiracy. 
+term
+       chanserv
+       You can not register channels on QuakeNet. Instead, you can request the channel service bot called 'L' to join your channel. You will first need an auth account, and then you can request the L bot using that auth account. Visit www.quakenet.org and read the General FAQ for details of auth accounts, and the L FAQ for details of the L bot.
+term
+       modes
+       QuakeNet channels have a number of channel and user modes to provide protection against harrasment, advertising and other unwelcome behaviour, and to help you manage your channel. Visit www.quakenet.org and read the IRC FAQ for a full list of the current modes.
+term
+       FAQ
+       QuakeNet FAQs can be found at www.quakenet.org. These contain information on security, channel and user modes, IRCing, channel services and our network rules.
+term
+       authhack
+       Auth accounts are not 'hacked'. Poor security on your computer, using a compromised/free BNC, messaging your auth details to someone other than Q, clicking URLs pasted in channels and running decode scripts and commands without understanding beforehand what they do is the cause of lost auths. If you want help you will have to identify HOW your auth was compromised first.
+term
+       authsecure 
+       If you are concerned that your auth account is compromised first do /MSG Q WHOIS #authname. If you are the only person authed to the account do /MSG Q WHOAMI and check the email address listed is yours (check it is EXACTLY your email address). If it is incorrect, change it with the EMAIL command. Following this you should use /MSG Q NEWPASS to change the current password. 
+term
+       #auth
+       If the user is not online at the moment you can still modify his/her account by using the user's auth nick and putting # infront of it (If user John have auth Clan^John it would be #Clan^John). You can list the auth nicks by doing /msg Q chanlev #channel or /msg L chanlev #channel.
+
+% ticket structure:
+%  channel (string)
+%  authname (string)
+%  expiration time (int)
+
+% report structure:
+%  channel reported
+%  channel reported to
+
+% global variables
+%  hstat_cycle
+globals
+       3