ldmud-3.2.9/doc/
ldmud-3.2.9/doc/efun/
ldmud-3.2.9/mud/
ldmud-3.2.9/mud/heaven7/
ldmud-3.2.9/mud/heaven7/lib/
ldmud-3.2.9/mud/lp-245/
ldmud-3.2.9/mud/lp-245/banish/
ldmud-3.2.9/mud/lp-245/doc/
ldmud-3.2.9/mud/lp-245/doc/examples/
ldmud-3.2.9/mud/lp-245/doc/sefun/
ldmud-3.2.9/mud/lp-245/log/
ldmud-3.2.9/mud/lp-245/obj/Go/
ldmud-3.2.9/mud/lp-245/players/lars/
ldmud-3.2.9/mud/lp-245/room/death/
ldmud-3.2.9/mud/lp-245/room/maze1/
ldmud-3.2.9/mud/lp-245/room/sub/
ldmud-3.2.9/mud/lp-245/secure/
ldmud-3.2.9/mud/morgengrauen/
ldmud-3.2.9/mud/morgengrauen/lib/
ldmud-3.2.9/mud/sticklib/
ldmud-3.2.9/mud/sticklib/src/
ldmud-3.2.9/mudlib/uni-crasher/
ldmud-3.2.9/pkg/
ldmud-3.2.9/pkg/debugger/
ldmud-3.2.9/pkg/diff/
ldmud-3.2.9/pkg/misc/
ldmud-3.2.9/src/autoconf/
ldmud-3.2.9/src/bugs/
ldmud-3.2.9/src/bugs/MudCompress/
ldmud-3.2.9/src/bugs/b-020916-files/
ldmud-3.2.9/src/bugs/doomdark/
ldmud-3.2.9/src/bugs/ferrycode/ferry/
ldmud-3.2.9/src/bugs/ferrycode/obj/
ldmud-3.2.9/src/bugs/psql/
ldmud-3.2.9/src/done/
ldmud-3.2.9/src/done/order_alist/
ldmud-3.2.9/src/done/order_alist/obj/
ldmud-3.2.9/src/done/order_alist/room/
ldmud-3.2.9/src/gcc/
ldmud-3.2.9/src/gcc/2.7.0/
ldmud-3.2.9/src/gcc/2.7.1/
ldmud-3.2.9/src/hosts/
ldmud-3.2.9/src/hosts/GnuWin32/
ldmud-3.2.9/src/hosts/amiga/NetIncl/
ldmud-3.2.9/src/hosts/amiga/NetIncl/netinet/
ldmud-3.2.9/src/hosts/amiga/NetIncl/sys/
ldmud-3.2.9/src/hosts/i386/
ldmud-3.2.9/src/hosts/msdos/byacc/
ldmud-3.2.9/src/hosts/msdos/doc/
ldmud-3.2.9/src/hosts/os2/
ldmud-3.2.9/src/hosts/win32/
ldmud-3.2.9/src/util/
ldmud-3.2.9/src/util/erq/
ldmud-3.2.9/src/util/indent/hosts/next/
ldmud-3.2.9/src/util/xerq/
ldmud-3.2.9/src/util/xerq/lpc/
ldmud-3.2.9/src/util/xerq/lpc/www/
/*---------------------------------------------------------------------------
 * XErq - Main module.
 * (C) Copyright 1995 by Brian Gerst.
 * (C) Copyright 2001 by Brian Gerst, Frank Kirschner, Lars Duening.
 *---------------------------------------------------------------------------
 * This module implements the main() function with the central loop, plus
 * a bunch of utility functions.
 *
 * Dispatch of the various ERQ requests is implemented with a lookup table
 * holding the addresses of the request functions.
 *---------------------------------------------------------------------------
 */

#include "defs.h"

#include "random.c" /* Compile the driver's random module */

/*-------------------------------------------------------------------------*/
void (*erq_table[])(char *, int)
  = { erq_rlookup
    , erq_execute
    , erq_fork
    , erq_auth
    , erq_spawn
    , erq_send
    , erq_kill
    , erq_open_udp
    , erq_open_tcp
    , erq_listen
    , erq_accept
    , erq_lookup
#ifdef USE_IPV6
    , erq_rlookupv6
#endif
};
  /* Dispatchtable for the ERQ request functions.
   * Arguments are (message, msg_len).
   */

#ifndef USE_IPV6
#    define ERQ_REQUEST_MAX ERQ_LOOKUP
#else
#    define ERQ_REQUEST_MAX ERQ_RLOOKUPV6
#endif

/*-------------------------------------------------------------------------*/
const char * erq_dir = ERQ_DIR;
  /* The filename of the directory with the ERQ executables. */

child_t *childs;
  /* List of active children. The main loop will remove _EXITED children.
   */

socket_t *sockets;
  /* List of opened sockets, including those to communicate with spawned
   * commands.
   */

retry_t *retries;
  /* List of function calls to retry at a later point of time.
   */

equeue_t *stdout_queue;
  /* List of messages pending to write to stdout.
   */

int in_select;
  /* TRUE while erq is in select() - during this time sig_child()
   * can write its replies directly.
   */

int seq_number;
  /* The last sequence number assigned to ticket, incremented
   * in <seq_interval>s, initialized with a random number.
   */

int seq_interval;
  /* The interval used to increment seq_number, initialized with
   * an odd random number.
   */

pid_t master_pid;
  /* The pid of the 'master' erq process.
   */

/* When spawning a short-running child process, it used to happen that the
 * process finishes before the parent even manages to put the pid into
 * the child control structure; causing sig_child() not to find the child
 * structure when it gets the signal.
 * To avoid this, the spawning code employs a synchronisation scheme to
 * make sure that the child doesn't execute before the parent completed
 * initialisation.
 *
 * For ERQ_FORKed children (which are not synchronized), and as a fallback
 * solution in case the synchronisation fails and the child terminates
 * prematurely, sig_child() stores the relevant data in these globals for
 * the main process to evaluate. The calling pattern (one ERQ
 * command per select() round) guarantees that there can be only one such
 * 'unfinished' child control structure at a time.
 */

volatile int pending_sig;
  /* Set to true if these variables hold data of for an unaccounted SIG_CLD.
   */

volatile wait_status_t pending_status;
volatile pid_t pending_pid;
  /* The status and pid for the pending SIG_CLD, valid only while pending_sig
   * is TRUE.
   */

/*-------------------------------------------------------------------------*/
char *
time_stamp (void)

/* Return a textual representation of the current time
 * in the form "YYYY.MM.DD HH:MM:SS [xerq]".
 * Result is a pointer to a static buffer.
 *
 * Putting this function in strfuns is not a good idea, because
 * it is need by almost every module anyway.
 */

{
    time_t t;
    static char result[27+20];
    struct tm *tm;
    int pid;

    t = time(NULL);
    tm = localtime(&t);
    pid = getpid();
    if (pid == master_pid)
        strftime(result, sizeof(result), "%Y.%m.%d %H:%M:%S [xerq]", tm);
    else
    {
        strftime(result, sizeof(result), "%Y.%m.%d %H:%M:%S [xerq:", tm);
        sprintf(result+26, "%d]", pid);
    }
    return result;
} /* time_stamp() */

/*-------------------------------------------------------------------------*/
int
main(int argc, char *argv[])

/* The main program and -loop of the ERQ.
 */

{
    int num;

    master_pid = getpid();

    /* Print information about this daemon to help debugging */
    {
        fprintf(stderr, "%s XERQ %s: Path '%s', debuglevel %d\n"
                      , time_stamp(), __DATE__, argv[0], ERQ_DEBUG
               );
    }

    /* Quick and dirty commandline parser */
    {
        int is_forked = 0;
        int i;

        for (i = 1; i < argc; i++)
        {
            if (!strcmp(argv[i], "--forked"))
                is_forked = 1;
            else if (!strcmp(argv[i], "--execdir"))
            {
                if (i+1 >= argc)
                {
                    fprintf(stderr, "%s Missing value for --execdir.\n"
                                  , time_stamp());
                    die();
                }
                erq_dir = argv[i+1];
                i++;
            }
            else
            {
                fprintf(stderr, "%s Unknown argument '%s'.\n"
                              , time_stamp(), argv[i]);
                die();
            }
        }
        /* Check if we have been forked off the driver */
        if (is_forked)
        {
            write(1, "1", 1); /* indicate sucessful fork/execl */
            fprintf(stderr, "%s Demon started\n", time_stamp() );
        }
        else
        {
            fprintf(stderr, "%s Dynamic attachement unimplemented\n"
                          , time_stamp());
            die();
        }
    }

    /* Initialize */

    in_select = 0;
    pending_sig = 0;

    signal(SIGCLD, sig_child);
    signal(SIGPIPE, SIG_IGN);

    sockets = NULL;
    childs = NULL;
    retries = NULL;
    stdout_queue = NULL;

    randomize(time(0));
    seq_number = get_ticket();
    seq_interval = get_ticket() | 1; /* make sure it is odd */

#ifdef DETACH
    /* Detach from console */
    num = open("/dev/tty", O_RDWR);
    if (num >= 0) {
        ioctl(num, TIOCNOTTY, 0);
        close(num);
    }
#endif

    /* The main loop */

    while(1)
    {
        fd_set read_fds, write_fds;
        int num_fds;
        child_t *chp;
        retry_t *rtp, **rtpp;
        socket_t *sp;
        struct timeval timeout;

        /* Clean up the list of children (may close some sockets) */

        for (chp = childs; chp;)
        {
            child_t *this = chp;

            chp = chp->next;

            /* If there is a pending SIG_CLD for this child, handle it.
             * This is to be expected for CHILD_FORK children.
             */
            if (pending_sig && this->pid == pending_pid)
            {
                if (this->type != CHILD_FORK)
                    fprintf(stderr, "%s Pending SIG_CLD for pid %d delivered.\n"
                                  , time_stamp(), pending_pid);
                this->status = pending_status;
                this->pid = pending_pid;
                pending_sig = 0;
            }

            if (this->status == CHILD_EXITED)
            {
                XPRINTF((stderr, "%s Child %p exited.\n", time_stamp(), this));
                remove_child(this); /* will also unlink it from the list */
            }
        }

        /* look for sockets to select on */

        FD_ZERO(&read_fds);
        FD_ZERO(&write_fds);

        FD_SET(0, &read_fds);
        if (stdout_queue)
            FD_SET(1, &write_fds);

        num_fds = 2;
        for (sp = sockets; sp; sp = sp->next)
        {
            switch(sp->type)
            {
            case SOCKET_WAIT_CONNECT:
            case SOCKET_WAIT_AUTH:
                FD_SET(sp->fd, &write_fds);
                FD_SET(sp->fd, &read_fds);
                if (sp->fd >= num_fds)
                    num_fds=sp->fd+1;
                break;

            default:
                FD_SET(sp->fd, &read_fds);
                if (sp->fd >= num_fds)
                    num_fds=sp->fd+1;
                break;

            case SOCKET_WAIT_ACCEPT:
                /* do nothing */;
                /* Without the ; above, Metrowerks Codewarrior reports
                 * an error :-( */
            }

            if (sp->queue)
                FD_SET(sp->fd, &write_fds);
        } /* for (sockets) */

        /* Scan the list of pending retries for the soonest one.
         * Put the time till then into timeout.
         * (If the list is empty, select() will receive NULL for timeout).
         */
        if (retries)
        {
            time_t t;

            t = retries->time;
            for (rtp = retries; rtp; rtp = rtp->next)
            {
                if (rtp->time < t)
                    t = rtp->time;
            }
            timeout.tv_sec = t - time(NULL);
            timeout.tv_usec = 0;
            XPRINTF((stderr, "%s Soonest retry_t: in %ld seconds.\n"
                           , time_stamp(), (long)timeout.tv_sec));
        }

#if ERQ_DEBUG > 1
        fprintf(stderr, "%s select()\n", time_stamp());
#endif
        in_select = 1; /* so sig_child() can write reply directly */
        num = select(num_fds, &read_fds, &write_fds, 0, retries ? &timeout : 0);
        in_select = 0; /* don't want sig_child() writing now */

#if ERQ_DEBUG > 1
        fprintf(stderr, "%s select() returns %d, time() %ld\n"
                      , time_stamp(), num, (long)time(NULL));
#endif
#if ERQ_DEBUG > 0
        if (num < 0)
        {
            int myerrno = errno;
            fprintf(stderr, "%s select() errno = %d", time_stamp(), errno);
            errno = myerrno;
            perror(" ");
        }
#endif

        /* Is stdout ready to write? Then flush the queue. */
        if (FD_ISSET(1, &write_fds))
        {
            XPRINTF((stderr, "%s stdout_queue ready for flush.\n", time_stamp()));
            flush_queue(&stdout_queue, 1, 0);
        }

        /* Check for retries */
        for (rtpp = &retries; *rtpp; )
        {
            rtp = *rtpp;
            if (rtp->time <= time(NULL))
            {
                XPRINTF((stderr, "%s Call retry %p (time %ld)\n"
                               , time_stamp(), rtp, (long)rtp->time));
                (*(rtp->func))(rtp->mesg, read_32(rtp->mesg));
                *rtpp = rtp->next;
                free(rtp);
            }
            else
            {
                rtpp = &rtp->next;
            }
        }

        /* check for input from driver */
        if (FD_ISSET(0, &read_fds))
        {
            XPRINTF((stderr, "%s New command from driver.\n", time_stamp()));
            erq_cmd();
        }

        /* Handle the ready sockets.
         * Remember that read_socket() may close the socket.
         */

        for (sp = sockets; sp; )
        {
            socket_t *this = sp;
            int rc;

            sp = sp->next;

            rc = 0;

            if (FD_ISSET(this->fd, &read_fds))
            {
                XPRINTF((stderr, "%s Socket %p ready for reading.\n"
                               , time_stamp(), this));
                rc = read_socket(this, 0);
            }

            if (!rc && FD_ISSET(this->fd, &write_fds))
            {
                XPRINTF((stderr, "%s Socket %p ready for writing.\n"
                               , time_stamp(), this));
                (void)read_socket(this, 1);
            }
        }
    } /* while(1) */

    /* NOTREACHED */

    return 0;
} /* main() */

/*-------------------------------------------------------------------------*/
void
erq_cmd (void)

/* There is data ready from the driver - read and execute it when complete.
 * The function maintains a static buffer for the data read - incomplete
 * messages are buffered until they are complete.
 */

{
    static char buf[ERQ_MAX_SEND];
    static int pos = 0;
      /* Position in buf[]. If it extends beyond the end of buf,
       * it is because the message is too long and the function
       * is in the process of skipping the extraneous characters.
       */

    int len, mesg_len;
    char request;

    /* Clear the buffer so that errors can be detected more easily */
    memset(buf, 0, sizeof(buf));

    /* Read the message header */
    if (pos < 9)
    {
        len = read(0, buf+pos, 9-pos);
        if (len <= 0)
        {
            perror("[xerq] read");
            die();
        }
        XPRINTF((stderr, "%s Read %d of the missing %d header bytes.\n"
                       , time_stamp(), len, 9-pos));
        pos += len;
        if (pos < 9)
            return;
    }

    mesg_len = read_32(buf);
    if (mesg_len > sizeof(buf))
    {
        /* This doesn't happen in a functioning system */
        fprintf(stderr
               , "%s Received too long packet: %d bytes.\n"
               , time_stamp(), mesg_len);
        die();
    }

    /* Get the rest of the message */

    if (pos < mesg_len)
    {
        len = read(0, buf+pos, mesg_len-pos);
        if (len <= 0)
        {
            perror("read");
            die();
        }
        XPRINTF((stderr, "%s Read %d of the missing %d message bytes.\n"
                       , time_stamp(), len, mesg_len-pos));
        pos += len;
        if (pos < mesg_len)
            return;
    }

    XPRINTF((stderr, "%s Message complete.\n", time_stamp()));
    pos = 0; /* Message complete */

    /* Branch on the request */
    request = buf[8];
    if (request <= ERQ_REQUEST_MAX)
    {
#if ERQ_DEBUG > 0
        char *mesg, *mesgs[]={
            "rlookup","execute","fork","auth","spawn","send","kill",
            "open_udp","open_tcp","listen","accept","lookup","rlookupv6"};
        mesg=mesgs[(int)request];
        fprintf(stderr, "%s command: %s\n", time_stamp(), mesg);
#endif
        (*erq_table[(int)request])(buf, mesg_len);
    }
    else
        bad_request(buf);
} /* erq_cmd() */

/*-------------------------------------------------------------------------*/
void
die(void)

/* Terminate the ERQ with status 1.
 */

{
    fprintf(stderr, "%s Demon exiting.\n", time_stamp());
    exit(1);
} /* die() */

/*-------------------------------------------------------------------------*/
#ifndef _AIX
void
sig_child()
#else
void
sig_child(int sig)
#endif

/* A child process exited - update its child structure.
 */

{
    wait_status_t status;
    pid_t pid;
    struct child_s *chp;

    pid = wait(&status);

#if ERQ_DEBUG > 0
    fprintf(stderr, "%s [sigchild] pid=%d status=%d\n"
                  , time_stamp(), pid, status);
#endif

    /* Look for the child and mark it as exited */
    for (chp = childs; chp; chp = chp->next)
    {
        if (chp->pid != pid)
            continue;

        chp->status = CHILD_EXITED;
        chp->return_code = status;
#if ERQ_DEBUG > 0
        fprintf(stderr, "%s [sigchild] Caught SIGCLD for pid %d, child %p.\n"
                      , time_stamp(), pid, chp);
#endif
        if (in_select)
            remove_child(chp); /* safe to do it from here */
        /*  if we're in select, we know we're not going to be messing up
            the main loop with stuff we're doing here */
        break;
    }

    if (!chp)
    {
        /* There is no valid child. Maybe we caught the signal before
         * the child structure was complete (this can happen especially
         * with short-lived CHILD_FORK sub processes).
         */
        if (pending_sig)
        {
            fprintf(stderr, "%s [sigchild] SIGCLD for pid %d not delivered.\n"
                          , time_stamp(), pending_pid);
        }

#if ERQ_DEBUG > 0
        fprintf(stderr, "%s [sigchild] SIGCLD for unknown pid %d received.\n"
                      , time_stamp(), pid);
#endif

        pending_status = status;
        pending_pid = pid;
        pending_sig = 1;
    }

    /* Restore the signal handler */
    signal(SIGCLD, sig_child);
} /* sig_child() */

/*-------------------------------------------------------------------------*/
void
add_retry (void (*func)(char *, int), char *mesg, int len, int t)

/* Add a new retry: function <func> is to be executed in <t> seconds
 * with (<mesg>, <len>) as arguments.
 */

{
    struct retry_s *retry;

    retry = malloc(sizeof(struct retry_s)+len);
    XPRINTF((stderr, "%s New retry %p: %d seconds, func %p, data %p:%d\n"
                   , time_stamp(), retry, t, func, mesg, len));
    retry->time = time(NULL)+t;
    retry->func = func;
    memcpy(&retry->mesg, mesg, len);
    retry->next = retries;
    retries = retry;
} /* add_retry() */

/*-------------------------------------------------------------------------*/
void
bad_request (char *mesg)

/* ERQ received a bad message in <mesg> - print some diagnostics.
 */

{
    fprintf(stderr, "%s Bad request %d\n", time_stamp(), mesg[8]);
    fprintf(stderr, "%s %x %x %x %x %x %x %x %x %x\n", time_stamp(),
        mesg[0], mesg[1], mesg[2], mesg[3], mesg[4],
        mesg[5], mesg[6], mesg[7], mesg[8]);
    fprintf(stderr, "%s %c %c %c %c %c %c %c %c %c\n", time_stamp(),
        mesg[0], mesg[1], mesg[2], mesg[3], mesg[4],
        mesg[5], mesg[6], mesg[7], mesg[8]);
    reply1(get_handle(mesg), "", 0);
} /* bad_request() */

/*-------------------------------------------------------------------------*/
void
reply1 (int32 handle, const void *data, int32 len)

/* Compose a reply message from <handle> and the <len> bytes of <data>
 * and send it back to the driver.
 */

{
    char reply[ERQ_MAX_REPLY];

    write_32(reply,   len+8);
    write_32(reply+4, handle);
    memcpy(reply+8, data, len);
    write1(reply, len+8);
} /* reply1() */

/*-------------------------------------------------------------------------*/
void
reply1keep (int32 handle, const void *data, int32 len)

/* Compose a reply message from <handle> and the <len> bytes of <data>
 * and send it back to the driver. The message will be an _KEEP_HANDLE
 * message.
 */

{
    char reply[ERQ_MAX_REPLY];

    write_32(reply,   len+12);
    write_32(reply+4, ERQ_HANDLE_KEEP_HANDLE);
    write_32(reply+8, handle);
    memcpy(reply+12, data, len);
    write1(reply, len+12);
} /* reply1keep() */

/*-------------------------------------------------------------------------*/
void
replyn (int32 handle, int keep, int num, ...)

/* Compose and send to the driver a replymessage for <handle> with
 * the <num> data arguments concatenated as body. If <keep> is true,
 * a _KEEP_HANDLE message is composed.
 *
 * Each data argument is a tuple (char *data, int len).
 */

{
    char reply[ERQ_MAX_REPLY];
    char *p;
    int total;
    va_list va;

    /* Determine the size of the header */
    total = (keep ? 12 : 8);
    p = reply+total;

    /* Catenate the data arguments */
    va_start(va, num);
    while (num-- > 0 && total < ERQ_MAX_REPLY)
    {
        char *data;
        int len;

        data = va_arg(va, char *);
        len = va_arg(va, int);
        if (total + len > ERQ_MAX_REPLY)
        {
            fprintf(stderr, "%s Too much data in replyn(): %d bytes omitted.\n"
                          , time_stamp(), total + len - ERQ_MAX_REPLY);
            len = ERQ_MAX_REPLY - total;
        }
        memcpy(p, data, len);
        p += len;
        total += len;
    }
    va_end(va);

    if (num > 0)
    {
        fprintf(stderr, "%s Too much data in replyn(): Remaining %d "
                        "data blocks omitted.\n"
                      , time_stamp(), num);
    }

    /* Create the header */
    write_32(reply, total);
    if (keep)
    {
        write_32(reply+4, ERQ_HANDLE_KEEP_HANDLE);
        write_32(reply+8, handle);
    }
    else
    {
        write_32(reply+4, handle);
    }

    /* Send the reply */
    write1(reply, total);
} /* replyn() */

/*-------------------------------------------------------------------------*/
void
reply_errno (int32 handle)

/* Send a (errcode, errno) message to the driver for <handle>.
 */

{
    char mesg[2];

    switch(errno)
    {
    case EWOULDBLOCK:
#if EAGAIN != EWOULDBLOCK
    case EAGAIN:
#endif
        mesg[0] = ERQ_E_WOULDBLOCK;
        break;

    case EPIPE:
        mesg[0] = ERQ_E_PIPE;
        break;

    default:
        mesg[0] = ERQ_E_UNKNOWN;
        break;
    }
    mesg[1] = errno;
    reply1(handle, mesg, 2);
} /* reply_errno() */

/*-------------------------------------------------------------------------*/
int
writen (int fd, char *mesg, int len, struct equeue_s **qpp)

/* Send or queue the message <mesg> (length <len> bytes) to <fd>.
 * If *<qpp> is non-NULL, the message is queued immediately.
 * Otherwise, the function tries to send as much of the message
 * as possible, and the queues whatever is left.
 */

{
    int l = 0;

    XPRINTF((stderr, "%s writen(%d, %p:%d, %p (-> %p) ))\n"
                   , time_stamp(), fd, mesg, len, qpp, *qpp));
    if (!(*qpp))
    {
        /* Send as much of the message as possible */
        do
            l = write(fd, mesg, len);
        while (l == -1 && errno == EINTR);
        XPRINTF((stderr, "%s   Wrote %d bytes.\n", time_stamp(), l));
        if (l < 0 || l == len)
            return l;
        mesg += l;
        len -= l;
    }

    if (!len)
        return 0;

    XPRINTF((stderr, "%s   Queuing data %p:%d\n", time_stamp(), mesg, len));
    add_to_queue(qpp, mesg, len, 0);
    return l;
} /* writen() */

/*-------------------------------------------------------------------------*/
void
write1 (void *mesg, int len)

/* Write the <len> bytes of <mesg> to stdout, ie to the driver.
 */

{
    int l;

    l = writen(1, mesg, len, &stdout_queue);
    if (l < 0)
    {
        int myerrno = errno;
        fprintf(stderr, "%s Error occurred on driver socket, errno=%d",
                time_stamp(), errno);
        errno = myerrno;
        perror(" ");
        die();
    }
#if ERQ_DEBUG > 0
    if (l != len)
        fprintf( stderr
               , "%s Driver-erq socket blocked, queueing %d bytes\n"
               , time_stamp(), len);
#endif
} /* write1() */

/***************************************************************************/