summaryrefslogtreecommitdiffstats
path: root/daemon/gemd.c
diff options
context:
space:
mode:
Diffstat (limited to 'daemon/gemd.c')
-rw-r--r--daemon/gemd.c1066
1 files changed, 1066 insertions, 0 deletions
diff --git a/daemon/gemd.c b/daemon/gemd.c
new file mode 100644
index 0000000..379145a
--- /dev/null
+++ b/daemon/gemd.c
@@ -0,0 +1,1066 @@
+/*-------------------------------------------------------*/
+/* util/gemd.c ( NTHU CS MapleBBS Ver 3.00 ) */
+/*-------------------------------------------------------*/
+/* target : BBS gopher daemon */
+/* create : 96/11/20 */
+/* update : 97/03/29 */
+/*-------------------------------------------------------*/
+/* syntax : gemd */
+/*-------------------------------------------------------*/
+/* notice : single process concurrent server */
+/*-------------------------------------------------------*/
+
+
+#include "bbs.h"
+
+
+#include <netinet/tcp.h>
+#include <sys/wait.h>
+#include <sys/resource.h>
+
+
+#define WATCH_DOG
+#define SERVER_USAGE
+
+
+#define GEMD_LOGFILE "run/gemd.log"
+#define GEMD_PIDFILE "run/gemd.pid"
+
+
+#define GEMD_PERIOD (60 * 30)
+#define GEMD_TIMEOUT (60 * 10)
+
+
+#define TCP_QLEN 3
+#define TCP_BUFSIZ (256 * 14)
+#define TCP_LINSIZ 256
+#define TCP_RCVSIZ 512
+
+
+/* ----------------------------------------------------- */
+/* client connection structure */
+/* ----------------------------------------------------- */
+
+
+typedef struct Agent
+{
+ struct Agent *anext;
+ int state;
+ int sock;
+ int sno;
+ time_t tbegin; /* 建立 connection 的時間 */
+ time_t uptime;
+
+ FILE *stream;
+ char zone[64];
+
+ char pool[TCP_BUFSIZ]; /* buffered I/O pool */
+ int locus;
+ int xdata;
+} Agent;
+
+
+#define ap_log(key, sno, msg) fprintf(flog, "%s\t[%d] %s\n", key, sno, msg);
+
+
+/* ----------------------------------------------------- */
+/* connection state */
+/* ----------------------------------------------------- */
+
+
+#define CS_FREE 0x00
+#define CS_READ 0x01 /* reading command */
+#define CS_FILE 0x02 /* writing file */
+#define CS_INDEX 0x03 /* writing index */
+#define CS_GPLUS 0x04 /* writing index (gopher 2.0+) */
+#define CS_FLUSH 0x05 /* flush data */
+
+
+/* ----------------------------------------------------- */
+/* operation log and debug information */
+/* ----------------------------------------------------- */
+
+
+extern int errno;
+
+
+static FILE *flog; /* log file descriptor */
+static int gline;
+
+
+#ifdef WATCH_DOG
+# define MYDOG gline = __LINE__
+#else
+# define MYDOG /* NOOP */
+#endif
+
+
+static void
+logit(key, msg)
+ char *key;
+ char *msg;
+{
+ time_t now;
+ struct tm *p;
+
+ time(&now);
+ p = localtime(&now);
+ fprintf(flog, "%02d/%02d %02d:%02d:%02d %-7s%s\n",
+ p->tm_mon + 1, p->tm_mday,
+ p->tm_hour, p->tm_min, p->tm_sec, key, msg);
+}
+
+
+static inline void
+log_open()
+{
+ FILE *fp;
+
+ umask(077);
+
+ if (fp = fopen(GEMD_PIDFILE, "w"))
+ {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+
+ flog = fopen(GEMD_LOGFILE, "w");
+ logit("START", "gemd (gopher) daemon");
+}
+
+
+/*-------------------------------------------------------*/
+/* BRD shm 部分須與 cache.c 相容 */
+/*-------------------------------------------------------*/
+
+
+static BCACHE *bshm;
+
+
+static void
+init_bshm()
+{
+ /* itoc.030727: 在開啟 bbsd 之前,應該就要執行過 account,
+ 所以 bshm 應該已設定好 */
+
+ bshm = shm_new(BRDSHM_KEY, sizeof(BCACHE));
+
+ if (bshm->uptime <= 0) /* bshm 未設定完成 */
+ exit(0);
+}
+
+
+/* itoc.030708: 加上檢查看板權限的部分,以免有人亂踹 */
+
+static int
+allow_brdname(brdname)
+ char *brdname;
+{
+ BRD *bhdr, *tail;
+
+ bhdr = bshm->bcache;
+ tail = bhdr + bshm->number;
+
+ do
+ {
+ if (!strcmp(bhdr->brdname, brdname))
+ {
+ if (!bhdr->readlevel)
+ return 1;
+ break;
+ }
+ } while (++bhdr < tail);
+
+ return 0;
+}
+
+
+static int
+allow_path(fpath)
+ char *fpath;
+{
+ char *brdname, *str;
+ int ret;
+
+ if (!strncmp(fpath, "brd/", 4))
+ {
+ brdname = fpath + 4;
+ if (str = strchr(brdname, '/'))
+ {
+ *str = '\0';
+ ret = allow_brdname(brdname);
+ *str = '/';
+ return ret;
+ }
+ }
+
+ return 1;
+}
+
+
+/* ----------------------------------------------------- */
+/* server side stuff */
+/* ----------------------------------------------------- */
+
+
+static char *
+str_copy(dst, src)
+ char *dst;
+ char *src;
+{
+ int ch;
+
+ while (ch = *src++)
+ *dst++ = ch;
+ return dst;
+}
+
+
+static FILE *
+gem_open(fpath)
+ char *fpath;
+{
+ FILE *fp;
+ int fd;
+ struct stat st;
+
+ if (!allow_path(fpath))
+ return NULL;
+
+ fp = NULL;
+ if ((fd = open(fpath, O_RDONLY)) >= 0)
+ {
+ if (fstat(fd, &st) || !S_ISREG(st.st_mode) ||
+ (st.st_size <= 0) || !(fp = fdopen(fd, "r")))
+ {
+ close(fd);
+ }
+ }
+ return fp;
+}
+
+
+/* ----------------------------------------------------- */
+/* transform ASCII file to text-mail format */
+/* ----------------------------------------------------- */
+
+
+static void
+gem_file(ap)
+ Agent *ap;
+{
+ char *pool, *head, *tail;
+ FILE *fp;
+ int cc;
+
+ fp = ap->stream;
+ pool = ap->pool;
+ head = pool + ap->locus;
+ tail = pool + TCP_BUFSIZ - TCP_LINSIZ;
+
+ while (head < tail)
+ {
+ if (!fgets(head, TCP_LINSIZ, fp))
+ {
+ fclose(fp);
+ ap->stream = NULL;
+ ap->state = CS_FLUSH;
+
+ *head++ = '.';
+ *head++ = '\r';
+ *head++ = '\n';
+ break;
+ }
+
+ while (cc = *head)
+ {
+ if (cc == '\n')
+ {
+ *head++ = '\r';
+ *head++ = '\n';
+ break;
+ }
+ head++;
+ }
+ }
+ ap->locus = head - pool;
+}
+
+
+/* ----------------------------------------------------- */
+/* transform FOLDER to gopher index format */
+/* ----------------------------------------------------- */
+
+
+static void
+gem_index(ap)
+ Agent *ap;
+{
+ char *pool, *head, *tail, *xname, *zone;
+ int cc, state, xmode;
+ FILE *fp;
+ HDR hdr;
+
+ fp = ap->stream;
+ zone = ap->zone;
+ pool = ap->pool;
+ head = pool + ap->locus;
+ tail = pool + TCP_BUFSIZ - TCP_LINSIZ;
+ state = ap->state;
+
+ while (head < tail)
+ {
+ if (fread(&hdr, sizeof(hdr), 1, fp) != 1)
+ {
+ fclose(fp);
+ ap->stream = NULL;
+ ap->state = CS_FLUSH;
+
+ *head++ = '.';
+ *head++ = '\r';
+ *head++ = '\n';
+ break;
+ }
+
+ xmode = hdr.xmode;
+ if ((xmode & GEM_RESTRICT) || (hdr.title[0] == '#'))
+ continue;
+
+ if (state == CS_GPLUS)
+ head = str_copy(head, "+INFO: ");
+
+ *head++ = cc = (xmode & GEM_FOLDER) ? '1' : '0';
+
+ head = str_copy(head, hdr.title);
+ *head++ = '\t';
+
+ xname = hdr.xname;
+
+ *head++ = cc;
+ *head++ = '/';
+
+ if (xmode & GEM_BOARD)
+ {
+ brd_fpath(head, xname, FN_DIR);
+ head += strlen(head);
+ }
+ else
+ {
+ head = str_copy(head, zone);
+
+ cc = *xname;
+ if (cc != '@')
+ cc = xname[7];
+ *head++ = cc;
+ *head++ = '/';
+
+ head = str_copy(head, xname);
+ }
+
+ head = str_copy(head, "\t" MYHOSTNAME "\t70\r\n");
+ }
+ ap->locus = head - pool;
+}
+
+
+/* ----------------------------------------------------- */
+/* parse the command, check security & serve it */
+/* ----------------------------------------------------- */
+
+
+static int
+agent_serve(ap)
+ Agent *ap;
+{
+ char *data, *str, *zone;
+ int cc;
+ FILE *fp;
+
+ data = ap->pool;
+ if (strstr(data, "..") || strstr(data, "//"))
+ return CS_FREE;
+
+ switch (*data)
+ {
+ case '0':
+ data += 2;
+ if (*data == '/')
+ return CS_FREE;
+ cc = CS_FILE;
+ break;
+
+ case '1':
+ if (data[1])
+ {
+ data += 2;
+ if (*data == '/')
+ return CS_FREE;
+ cc = CS_INDEX;
+ break;
+ }
+
+ case 0:
+ data = ".DIR";
+ cc = CS_INDEX;
+ break;
+
+ case '\t':
+ data = ".DIR";
+ cc = CS_GPLUS;
+ break;
+
+ default:
+ return CS_FREE;
+ }
+
+ fp = gem_open(data);
+
+ zone = ap->zone;
+ *zone = '\0';
+
+ if (!fp && cc == CS_INDEX && *data != '.' && strlen(data) <= BNLEN)
+ {
+ /* map "xyz" to "brd/xyz/.DIR" and try it once again */
+
+ strcpy(zone, data);
+ brd_fpath(data, zone, FN_DIR);
+ fprintf(flog, "MAP\t[%d] %s -> %s\n", ap->sno, zone, data);
+ fp = gem_open(data);
+ }
+
+ if (!fp)
+ return CS_FREE;
+
+ ap->stream = fp;
+ if (cc != CS_FILE)
+ {
+ ap_log("DIR", ap->sno, data);
+ if (str = strrchr(data, '/'))
+ {
+ if (str[1] != '.')
+ str--;
+ else
+ str++;
+
+ while (data < str)
+ {
+ *zone++ = *data++;
+ }
+ }
+ *zone = '\0';
+ ap_log("ZONE", ap->sno, ap->zone);
+ }
+ else
+ {
+ ap_log("FILE", ap->sno, data);
+ }
+ return cc;
+}
+
+
+/* ----------------------------------------------------- */
+/* send output to client */
+/* ----------------------------------------------------- */
+
+
+static int
+agent_send(ap)
+ Agent *ap;
+{
+ int sock, len, cc;
+ char *data;
+
+ sock = ap->sock;
+ len = ap->locus;
+ data = ap->pool;
+ cc = send(sock, data, len, 0);
+
+ if (cc < 0)
+ {
+ cc = errno;
+ if (cc != EWOULDBLOCK)
+ {
+ ap_log("send", ap->sno, strerror(cc));
+ return 0;
+ }
+
+ return -1; /* would block, so leave it to do later */
+ }
+
+ if (cc == 0)
+ return -1;
+
+ len -= cc;
+ ap->xdata += cc;
+ ap->locus = len;
+
+ if (len)
+ {
+ memcpy(data, data + cc, len);
+ return cc;
+ }
+
+ if (ap->state == CS_FLUSH)
+ {
+ ap->state = CS_FREE;
+ return 0;
+ }
+
+ return cc;
+}
+
+
+/* ----------------------------------------------------- */
+/* receive request from client */
+/* ----------------------------------------------------- */
+
+
+static int
+agent_recv(ap)
+ Agent *ap;
+{
+ int cc;
+ char *pool, *data;
+
+ pool = ap->pool;
+ data = pool + ap->locus;
+ cc = recv(ap->sock, data, TCP_RCVSIZ, 0);
+
+ if (cc <= 0)
+ {
+ cc = errno;
+ if (cc != EWOULDBLOCK)
+ {
+ ap_log("RECV", ap->sno, strerror(cc));
+ return 0;
+ }
+
+ return -1; /* would block, so leave it to do later */
+ }
+
+ data[cc] = '\0';
+
+ while (cc = *data)
+ {
+ if (cc == '\r' || cc == '\n')
+ {
+ /* remove tailing '/' from path */
+
+ if ((data > pool + 1) && data[-1] == '/' && data[-2] != '/')
+ data--;
+
+ *data = '\0';
+
+ ap_log("CMD", ap->sno, pool);
+
+ ap->state = cc = agent_serve(ap);
+
+ if (cc == CS_FREE)
+ return 0;
+
+ if (cc == CS_GPLUS)
+ {
+ strcpy(pool, "+-1\r\n");
+ ap->locus = 5;
+ }
+ else
+ {
+ ap->locus = 0;
+ }
+
+ return 1;
+ }
+ data++;
+ }
+
+ if ((ap->locus = data - pool) > TCP_RCVSIZ)
+ {
+ ap_log("HACK", ap->sno, "buffer overflow");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* ----------------------------------------------------- */
+/* close a connection & release its resource */
+/* ----------------------------------------------------- */
+
+
+static void
+agent_fire(ap)
+ Agent *ap;
+{
+ int sock;
+ FILE *fp;
+
+ sock = ap->sock;
+ shutdown(sock, 2);
+ close(sock);
+
+ if (fp = ap->stream)
+ fclose(fp);
+
+ fprintf(flog, "%s\t[%d] T%d D%d\n",
+ ap->state == CS_FREE ? "BYE" : "END",
+ ap->sno, time(0) - ap->tbegin, ap->xdata);
+}
+
+
+/* ----------------------------------------------------- */
+/* accept a new connection */
+/* ----------------------------------------------------- */
+
+
+static inline int
+agent_accept()
+{
+ int sock;
+ int value;
+
+ /* gopher do not care remote host / user name */
+
+ for (;;)
+ {
+ sock = accept(0, NULL, NULL);
+ if (sock > 0)
+ break;
+
+ sock = errno;
+ if (sock != EINTR)
+ {
+ logit("ACCEPT", strerror(sock));
+ return -1;
+ }
+
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0);
+ }
+
+ value = 1;
+ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &value, sizeof(value));
+ return sock;
+}
+
+
+/* ----------------------------------------------------- */
+/* server core routines */
+/* ----------------------------------------------------- */
+
+
+static void
+servo_daemon(inetd)
+ int inetd;
+{
+ int fd, value;
+ char buf[80];
+ struct sockaddr_in fsin;
+ struct linger ld;
+#ifdef HAVE_RLIMIT
+ struct rlimit limit;
+#endif
+
+ /*
+ * More idiot speed-hacking --- the first time conversion makes the C
+ * library open the files containing the locale definition and time zone.
+ * If this hasn't happened in the parent process, it happens in the
+ * children, once per connection --- and it does add up.
+ */
+
+ time((time_t *) &value);
+ gmtime((time_t *) &value);
+ strftime(buf, 80, "%d/%b/%Y:%H:%M:%S", localtime((time_t *) &value));
+
+#ifdef HAVE_RLIMIT
+ /* --------------------------------------------------- */
+ /* adjust the resource limit */
+ /* --------------------------------------------------- */
+
+ limit.rlim_cur = limit.rlim_max = 4 * 1024 * 1024;
+ setrlimit(RLIMIT_FSIZE, &limit);
+ setrlimit(RLIMIT_DATA, &limit);
+
+#ifdef SOLARIS
+#define RLIMIT_RSS RLIMIT_AS /* Thor.981206: port for solaris 2.6 */
+#endif
+
+ setrlimit(RLIMIT_RSS, &limit);
+
+ limit.rlim_cur = limit.rlim_max = 0;
+ setrlimit(RLIMIT_CORE, &limit);
+#endif
+
+ /* --------------------------------------------------- */
+ /* detach process */
+ /* --------------------------------------------------- */
+
+ close(1);
+ close(2);
+
+ if (inetd)
+ return;
+
+ close(0);
+
+ if (fork())
+ exit(0);
+
+ setsid();
+
+ if (fork())
+ exit(0);
+
+ /* --------------------------------------------------- */
+ /* setup socket */
+ /* --------------------------------------------------- */
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+
+ value = 1;
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &value, sizeof(value));
+
+#if 0
+ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char *) &value, sizeof(value));
+#endif
+
+ ld.l_onoff = ld.l_linger = 0;
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld));
+
+ memset((char *) &fsin, 0, sizeof(fsin));
+ fsin.sin_family = AF_INET;
+ fsin.sin_port = htons(GEMD_PORT);
+ fsin.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if (bind(fd, (struct sockaddr *) & fsin, sizeof(fsin)) ||
+ listen(fd, TCP_QLEN))
+ exit(1);
+}
+
+
+#ifdef SERVER_USAGE
+static void
+servo_usage()
+{
+ struct rusage ru;
+
+ if (getrusage(RUSAGE_SELF, &ru))
+ return;
+
+ fprintf(flog, "\n[Server Usage]\n\n"
+ "user time: %.6f\n"
+ "system time: %.6f\n"
+ "maximum resident set size: %lu P\n"
+ "integral resident set size: %lu\n"
+ "page faults not requiring physical I/O: %d\n"
+ "page faults requiring physical I/O: %d\n"
+ "swaps: %d\n"
+ "block input operations: %d\n"
+ "block output operations: %d\n"
+ "messages sent: %d\n"
+ "messages received: %d\n"
+ "signals received: %d\n"
+ "voluntary context switches: %d\n"
+ "involuntary context switches: %d\n"
+ "gline: %d\n\n",
+
+ (double) ru.ru_utime.tv_sec + (double) ru.ru_utime.tv_usec / 1000000.0,
+ (double) ru.ru_stime.tv_sec + (double) ru.ru_stime.tv_usec / 1000000.0,
+ ru.ru_maxrss,
+ ru.ru_idrss,
+ ru.ru_minflt,
+ ru.ru_majflt,
+ ru.ru_nswap,
+ ru.ru_inblock,
+ ru.ru_oublock,
+ ru.ru_msgsnd,
+ ru.ru_msgrcv,
+ ru.ru_nsignals,
+ ru.ru_nvcsw,
+ ru.ru_nivcsw,
+ gline);
+
+ fflush(flog);
+}
+#endif
+
+
+static void
+reaper()
+{
+ while (waitpid(-1, NULL, WNOHANG | WUNTRACED) > 0);
+}
+
+
+static void
+sig_trap(sig)
+ int sig;
+{
+ char buf[80];
+
+ sprintf(buf, "[%d] at %d", sig, gline);
+ logit("EXIT", buf);
+ fclose(flog);
+ exit(0);
+}
+
+
+/* ----------------------------------------------------- */
+/* signal routines */
+/* ----------------------------------------------------- */
+
+
+static void
+servo_signal()
+{
+ struct sigaction act;
+
+ /* sigblock(sigmask(SIGPIPE)); */ /* Thor.981206: 統一 POSIX 標準用法 */
+
+ /* act.sa_mask = 0; */ /* Thor.981105: 標準用法 */
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+
+ act.sa_handler = sig_trap;
+ sigaction(SIGBUS, &act, NULL);
+ sigaction(SIGSEGV, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+
+ act.sa_handler = reaper;
+ sigaction(SIGCHLD, &act, NULL);
+
+#ifdef SERVER_USAGE
+ act.sa_handler = servo_usage;
+ sigaction(SIGPROF, &act, NULL);
+#endif
+
+ /* Thor.981206: lkchu patch: 統一 POSIX 標準用法 */
+ /* 在此借用 sigset_t act.sa_mask */
+ sigaddset(&act.sa_mask, SIGPIPE);
+ sigprocmask(SIG_BLOCK, &act.sa_mask, NULL);
+
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int sock, nfds, state, servo_sno;
+ time_t uptime, tcheck;
+ Agent **FBI, *Scully, *Mulder, *agent;
+ fd_set rset, wset, xset;
+ struct timeval tv;
+
+ state = 0;
+
+ while ((nfds = getopt(argc, argv, "hid")) != -1)
+ {
+ switch (nfds)
+ {
+ case 'i':
+ state = 1;
+ break;
+
+ case 'd':
+ break;
+
+ case 'h':
+ default:
+
+ fprintf(stderr, "Usage: %s [options]\n"
+ "\t-i start from inetd with wait option\n"
+ "\t-d debug mode\n"
+ "\t-h help\n",
+ argv[0]);
+ exit(0);
+ }
+ }
+
+ servo_daemon(state);
+
+ setgid(BBSGID);
+ setuid(BBSUID);
+ chdir(BBSHOME);
+
+ log_open();
+ init_bshm();
+
+ chdir("gem");
+ servo_signal();
+
+ tcheck = time(0) + GEMD_PERIOD;
+ Scully = Mulder = NULL;
+ servo_sno = 0;
+
+ for (;;)
+ {
+ uptime = time(0);
+ if (tcheck < uptime)
+ {
+ tcheck = uptime - GEMD_TIMEOUT;
+
+ for (FBI = &Scully; agent = *FBI;)
+ {
+ if (agent->uptime < tcheck)
+ {
+ agent_fire(agent);
+
+ *FBI = agent->anext;
+
+ agent->anext = Mulder;
+ Mulder = agent;
+ }
+ else
+ {
+ FBI = &(agent->anext);
+ }
+ }
+
+ fflush(flog);
+ tcheck = uptime + GEMD_PERIOD;
+ }
+
+ /* ------------------------------------------------- */
+ /* Set up the fdsets */
+ /* ------------------------------------------------- */
+
+ FD_ZERO(&rset);
+ FD_ZERO(&wset);
+ FD_ZERO(&xset);
+
+ FD_SET(0, &rset);
+ nfds = 0;
+
+ for (agent = Scully; agent; agent = agent->anext)
+ {
+ sock = agent->sock;
+ state = agent->state;
+
+ if (nfds < sock)
+ nfds = sock;
+
+ if (state == CS_READ)
+ {
+ FD_SET(sock, &rset);
+ }
+ else
+ {
+ FD_SET(sock, &wset);
+
+ if (state == CS_FILE)
+ gem_file(agent);
+ else if (state == CS_INDEX || state == CS_GPLUS)
+ gem_index(agent);
+ }
+
+ FD_SET(sock, &xset);
+ }
+
+ /* Thor.981206: for reservation future bug */
+ tv.tv_sec = GEMD_PERIOD;
+ tv.tv_usec = 0;
+
+ nfds = select(nfds + 1, &rset, &wset, &xset, &tv);
+
+ if (nfds == 0)
+ {
+ continue;
+ }
+
+ if (nfds < 0)
+ {
+ sock = errno;
+ if (sock != EINTR)
+ logit("select", strerror(sock));
+ continue;
+ }
+
+ /* ------------------------------------------------- */
+ /* serve active agents */
+ /* ------------------------------------------------- */
+
+ uptime = time(0);
+
+ for (FBI = &Scully; agent = *FBI;)
+ {
+ sock = agent->sock;
+
+ if (FD_ISSET(sock, &wset))
+ {
+ state = agent_send(agent);
+ }
+ else if (FD_ISSET(sock, &rset))
+ {
+ state = agent_recv(agent);
+ }
+ else if (FD_ISSET(sock, &xset))
+ {
+ state = 0;
+ }
+ else
+ {
+ state = -1;
+ }
+
+ if (state == 0) /* fire this agent */
+ {
+ agent_fire(agent);
+
+ *FBI = agent->anext;
+
+ agent->anext = Mulder;
+ Mulder = agent;
+
+ continue;
+ }
+
+ if (state > 0)
+ {
+ agent->uptime = uptime;
+ }
+
+ FBI = &(agent->anext);
+ }
+
+ /* ------------------------------------------------- */
+ /* serve new connection */
+ /* ------------------------------------------------- */
+
+ if (FD_ISSET(0, &rset))
+ {
+ sock = agent_accept();
+ if (sock <= 0)
+ continue;
+
+ if (agent = Mulder)
+ {
+ Mulder = agent->anext;
+ }
+ else
+ {
+ agent = (Agent *) malloc(sizeof(Agent));
+ }
+
+ *FBI = agent;
+
+ /* variable initialization */
+
+ agent->anext = NULL;
+ agent->state = CS_READ;
+ agent->sock = sock;
+ agent->sno = ++servo_sno;
+ agent->tbegin = uptime;
+ agent->uptime = uptime;
+ agent->stream = NULL;
+ agent->locus = 0;
+ agent->xdata = 0;
+
+ fprintf(flog, "CONN\t[%d] %s\n", servo_sno, Btime(&agent->tbegin));
+ }
+
+ /* ------------------------------------------------- */
+ /* tail of main loop */
+ /* ------------------------------------------------- */
+ }
+}