#include #include #include #include "defs.h" #include INC_UNISTD #include "struct.h" #include "data.h" #include "ip.h" #include "proto.h" /* * queue.c: Wait queue handling routines * The waitq is handled in a deterministic manner by a double linked * list. Extra redundancy is built into the code, to account for the * unexpected. All ERROR messages should never appear in the log file. * If these occur, the linked list structure has been violated somehow. * * written by Michael Kantner, October 1994 * updated: * mjk - March 1995 - added extra redundancy, fixed a tiny bug. * */ /* Function headers, for when the server becomes ansi (or -Wall) */ int queues_init(void); int queue_add(int w_queue); int queue_exit(int waitindex); int queue_update(int w_queue); int queues_purge(void); int queue_setname(int w_queue, char *name); /* * Set up the the wait queues initially for pickup game */ int queues_init(void) { register int i; /* Initialize all waitings */ for (i=0;ifirst == -1) { lqueue->first = i; } else{ waiting[lqueue->last].next = i; lwait->previous = lqueue->last; } lqueue->last = i; lwait->next = -1; lwait->count = lqueue->count++; lwait->process = getpid(); lwait->w_queue = w_queue; if (ip_hide(ip)) { strcpy(lwait->ip, "127.0.0.1"); strcpy(lwait->host, "hidden"); } else { STRNCPY(lwait->ip, ip, sizeof(lwait->ip)); STRNCPY(lwait->host, host, sizeof(lwait->host)); } ip_lookup(ip, lwait->host, lwait->host, NULL, NULL, NULL, sizeof(lwait->host)); return i; } /* * Remove an ntserv from waiting (either into game or quit) */ int queue_exit(int waitindex) { struct pqueue *lqueue; struct queuewait *lwait=&(waiting[waitindex]); int w_queue = lwait->w_queue; if ((w_queue < 0) || (w_queue >= MAXQUEUE)){ ERROR(1,("queue_exit: Invalid queue number %i\n",w_queue)); return 0; } lqueue = &queues[w_queue]; if (lwait->previous == -1){ lqueue->first = lwait->next; } else{ waiting[lwait->previous].next = lwait->next; } if (lwait->next == -1){ lqueue->last = lwait->previous; } else{ waiting[lwait->next].previous = lwait->previous; } /* Clear the lwait for redundancy */ lwait->previous = -1; lwait->next = -1; lwait->count = -1; lwait->w_queue = -1; lwait->process = 0; lwait->inuse = 0; queue_update(w_queue); /* Update everyone's count */ ip_waitpid(); return 1; } /* * Set the name of the queue in a safe manner. */ int queue_setname(int w_queue, char *name) { if ((w_queue < 0) || (w_queue >= MAXQUEUE)){ ERROR(1,("queue_update: Invalid queue number %i\n",w_queue)); return 0; } STRNCPY(queues[w_queue].q_name,name,QNAMESIZE-1); queues[w_queue].q_name[QNAMESIZE-1] = '\0'; return 1; } /* * Update all of the waiting counts for a given queue */ int queue_update(int w_queue) { struct pqueue *lqueue; int currentindex = -1; struct queuewait *current; u_int lcount = 0; if ((w_queue < 0) || (w_queue >= MAXQUEUE)){ ERROR(1,("queue_update: Invalid queue number %i\n",w_queue)); return 0; } lqueue = &queues[w_queue]; currentindex = lqueue->first; while (currentindex != -1){ current = &(waiting[currentindex]); /* There can be at most MAXWAITING people waiting, but players */ /* can exit and enter during this check. However. this should */ /* rarely happen during normal usage, so it is a good check. */ /* The people at the end will be caught the next time through. */ if (lcount >= MAXWAITING){ /* This is redundant */ ERROR(1,("queues_update: wait count more than MAXWAITING\n")); break; /* Exit from the while loop */ } if (current->inuse == 0){ /* This is redundant */ ERROR(1,("queues_update: waitq element is not in use\n")); break; /* Exit from the while loop */ } if (current->process == 0){ /* This is redundant */ ERROR(1,("queues_update: waitq element is process 0\n")); break; /* Exit from the while loop */ } /* The waitq element appears valid */ current->count=lcount++; currentindex = current->next; } lqueue->count=lcount; return 1; } /* * Remove dead processes from the queue */ int queues_purge(void) { register int i; int killcount = 0, next = -1; struct pqueue *lqueue; struct queuewait *lwait; for (i=0, lqueue=&(queues[0]); ifirst; while (next != -1){ lwait = &(waiting[next]); /* There can be at most MAXWAITING people waiting, but players */ /* can exit and enter during this check. However. this should */ /* rarely happen during normal usage, so it is a good check.*/ if (killcount++ >= MAXWAITING){ /* This is redundant */ ERROR(1,("queues_purge: trying to kill more than MAXWAITING\n")); return 0; /* Exit from queues_purge */ } if (lwait->inuse == 0){ /* This is redundant */ ERROR(1,("queues_purge: waitq element is not in use\n")); continue; /* Check the next queue */ } if (lwait->process == 0){ /* This is redundant */ ERROR(1,("queues_purge: waitq element is process 0\n")); continue; /* Check the next queue */ } /* The waitq element appears valid */ next = lwait->next; if (kill(lwait->process, 0) < 0){ if (errno == ESRCH){ /* The process is not a valid number */ ERROR(1,("queues_purge: removing dead waitQ player\n")); queue_exit(lwait->mynum); } else if (errno == EINVAL){ /* The value of 0 is not acceptable!!! */ ERROR(1,("queues_purge: EINVAL kill error!\n")); } else if (errno == EPERM){ /* Permission error */ ERROR(1,("queues_purge: EPERM kill error (pid %i)!\n", lwait->process)); } else{ /* Unexpected error value */ ERROR(1,("queues_purge: Unknown kill error %i\n",errno)); } } } if (lqueue->free_slots > lqueue->max_slots){ /* The queue has more free slots than allowed. */ /* This is typically caused by ghostbusting and such */ /* Ideally, this never happens, but occasionally it does */ ERROR(1,("queues_purge: reducing the free_slots to max_slots\n")); lqueue->free_slots = lqueue->max_slots; } } return 1; }