btmux/autom4te.cache/
btmux/doc/.svn/
btmux/event/.svn/
btmux/game/.svn/
btmux/game/bin/.svn/
btmux/game/data/.svn/
btmux/game/logs/.svn/
btmux/game/maps/
btmux/game/maps/.svn/
btmux/game/maps/.svn/prop-base/
btmux/game/maps/.svn/props/
btmux/game/maps/.svn/text-base/
btmux/game/maps/.svn/wcprops/
btmux/game/mechs/
btmux/game/mechs/.svn/
btmux/game/mechs/.svn/prop-base/
btmux/game/mechs/.svn/props/
btmux/game/mechs/.svn/text-base/
btmux/game/mechs/.svn/wcprops/
btmux/game/text/.svn/
btmux/include/.svn/
btmux/misc/
btmux/misc/.svn/
btmux/misc/.svn/prop-base/
btmux/misc/.svn/props/
btmux/misc/.svn/text-base/
btmux/misc/.svn/wcprops/
btmux/python/
btmux/python/.svn/
btmux/python/.svn/prop-base/
btmux/python/.svn/props/
btmux/python/.svn/text-base/
btmux/python/.svn/wcprops/
btmux/src/.svn/prop-base/
btmux/src/.svn/props/
btmux/src/.svn/text-base/
btmux/src/.svn/wcprops/
btmux/src/hcode/.svn/
btmux/src/hcode/btech/
btmux/src/hcode/btech/.svn/
btmux/src/hcode/btech/.svn/prop-base/
btmux/src/hcode/btech/.svn/props/
btmux/src/hcode/btech/.svn/text-base/
btmux/src/hcode/btech/.svn/wcprops/
btmux/src/hcode/include/.svn/
/*
 * bsd.c 
 */

/*
 * $Id: bsd.c 275 2005-11-06 22:24:48Z murrayma $ 
 */
#include "copyright.h"
#include "config.h"

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <signal.h>
#include <errno.h>

#include "mudconf.h"
#include "db.h"
#include "file_c.h"
#include "externs.h"
#include "interface.h"
#include "flags.h"
#include "powers.h"
#include "alloc.h"
#include "command.h"
#include "slave.h"
#include "attrs.h"
#include "rbtree.h"
#include <errno.h>
#include "logcache.h"

#ifdef DEBUG_BSD
#ifndef DEBUG
#define DEBUG
#endif
#endif
#include "debug.h"

#if SQL_SUPPORT
#include "sqlchild.h"
#endif

#ifdef __CYGWIN__
#undef WEXITSTATUS
#define WEXITSTATUS(stat) (((*((int *) &(stat))) >> 8) & 0xff)
#endif

void accept_slave_input(int fd, short event, void *arg);
pid_t slave_pid = -1;
int slave_socket = -1;

#ifdef SOLARIS
extern const int _sys_nsig;
#define NSIG _sys_nsig
#endif

extern void dispatch(void);
extern void dump_restart_db(void);
extern void dump_database_internal(int);
extern char *silly_atr_get(dbref, int);
extern void send_channel(char *chan, char *str);
extern void ChangeSpecialObjects(int i);

struct event listen_sock_ev;
struct event slave_sock_ev;
struct event sqlslave_sock_ev;

int mux_bound_socket = -1;
int ndescriptors = 0;

DESC *descriptor_list = NULL;

void slave_destruct();
void mux_release_socket();
void make_nonblocking(int s);

DESC *initializesock(int, struct sockaddr_in *);
DESC *new_connection(int);
int process_output(DESC *);
int process_input(DESC *);

void set_lastsite(DESC * d, char *lastsite)
{
    char buf[LBUF_SIZE];

    if (d->player) {
        if (!lastsite)
            lastsite = silly_atr_get(d->player, A_LASTSITE);
        strcpy(buf, lastsite);
        atr_add_raw(d->player, A_LASTSITE, buf);
    }
}


void flush_sockets();

void shutdown_services() {
    flush_sockets();
#ifdef SQL_SUPPORT
    sqlchild_destruct();
#endif
#ifdef ARBITRARY_LOGFILES
    logcache_destruct();
#endif
    slave_destruct();
}


/*
 * get a result from the slave 
 */

static int get_slave_result() {
    char *buf;
    char *token;
    char *os;
    char *userid;
    char *host;
    int local_port, remote_port;
    char *p;
    DESC *d;
    int len;

    buf = alloc_lbuf("slave_buf");

    len = read(slave_socket, buf, LBUF_SIZE - 1);
    if (len < 0) {
        if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
            free_lbuf(buf);
            return (-1);
        }
        close(slave_socket);
        slave_socket = -1;
        event_del(&slave_sock_ev);
        slave_pid = -1;
        free_lbuf(buf);
        return (-1);
    } else if (len == 0) {
        free_lbuf(buf);
        return (-1);
    }
    buf[len] = '\0';

    token = alloc_lbuf("slave_token");
    os = alloc_lbuf("slave_os");
    userid = alloc_lbuf("slave_userid");
    host = alloc_lbuf("slave_host");

    if (sscanf(buf, "%s %s", host, token) != 2) {
        free_lbuf(buf);
        free_lbuf(token);
        free_lbuf(os);
        free_lbuf(userid);
        free_lbuf(host);
        return (0);
    }
    p = strchr(buf, '\n');
    *p = '\0';
    for (d = descriptor_list; d; d = d->next) {
        if (strcmp(d->addr, host))
            continue;
        if (d->flags & DS_IDENTIFIED)
            continue;
        if (mudconf.use_hostname) {
            StringCopyTrunc(d->addr, token, 50);
            d->addr[50] = '\0';
            if (d->player) {
                if (d->username[0])
                    set_lastsite(d, tprintf("%s@%s", d->username,
                                d->addr));
                else
                    set_lastsite(d, d->addr);

            }
        }
    }

    if (sscanf(p + 1, "%s %d , %d : %s : %s : %s", host, &remote_port,
                &local_port, token, os, userid) != 6) {
        free_lbuf(buf);
        free_lbuf(token);
        free_lbuf(os);
        free_lbuf(userid);
        free_lbuf(host);
        return (0);
    }
    for (d = descriptor_list; d; d = d->next) {
        if (ntohs((d->address).sin_port) != remote_port)
            continue;
        if (d->flags & DS_IDENTIFIED)
            continue;
        StringCopyTrunc(d->username, userid, 10);
        d->username[10] = '\0';
        set_lastsite(d, tprintf("%s@%s", d->username, d->addr));
        free_lbuf(buf);
        free_lbuf(token);
        free_lbuf(os);
        free_lbuf(userid);
        free_lbuf(host);
        return (0);
    }
    free_lbuf(buf);
    free_lbuf(token);
    free_lbuf(os);
    free_lbuf(userid);
    free_lbuf(host);
    return (0);
}

void boot_slave() {
    int sv[2];
    int i;
    int maxfds;

#ifdef HAVE_GETDTABLESIZE
    maxfds = getdtablesize();
#else
    maxfds = sysconf(_SC_OPEN_MAX);
#endif

    if (slave_socket != -1) {
        slave_destruct();
    }

    handle_errno(socketpair(AF_UNIX, SOCK_DGRAM, 0, sv));
    
    slave_pid = fork();
    switch (slave_pid) {
        case -1:
            close(sv[0]);
            close(sv[1]);
            return;

        case 0:
            if(mux_bound_socket > 0) {
               mux_release_socket();
            }
                        /*
                         * * child  
                         */
            close(sv[0]);
            close(0);
            close(1);
            if (dup2(sv[1], 0) == -1) {
                _exit(1);
            }
            if (dup2(sv[1], 1) == -1) {
                _exit(1);
            }
            for (i = 3; i < maxfds; ++i) {
                close(i);
            }
            execlp("bin/slave", "slave", NULL);
            _exit(1);
            break;
        default:
            break;
    }
    close(sv[1]);

    if (fcntl(sv[0], F_SETFL, O_NONBLOCK) == -1) {
        close(sv[0]);
        slave_socket = -1;
        log_perror("NET", "FAIL", NULL, "fcntl(slave_socket, F_SETFL, O_NONBLOCK)");
    }
    
    dprintk("slave initialized pid = %d, fd = %d.", slave_pid, sv[0]);
    slave_socket = sv[0];
    event_set(&slave_sock_ev, slave_socket, EV_READ | EV_PERSIST, 
            accept_slave_input, NULL);
    event_add(&slave_sock_ev, NULL);
}

void slave_destruct() {
    int status;
    dprintk("nuking slave.");
    if(slave_pid != -1) {
        if(!kill(slave_pid, SIGTERM))
            wait(&status);
        else
            perror("killing slave");
        slave_pid = -1; 
    } else {
        dprintk("slave_destruct() called without functioning slave.");
    }
    if(slave_socket != -1) {
        event_del(&slave_sock_ev);
        close(slave_socket);
        slave_socket = -1;
    } else {
        dprintk("slave_destruct() called without functioning slave_socket!");
    }
    dprintk("slave shutdown.");
}

int bind_mux_socket(int port) {
    int s, opt;
    struct sockaddr_in server;

    s = socket(AF_INET, SOCK_STREAM, 0);
    if(s < 0) {
        log_perror("NET", "FAIL", NULL, "creating master socket");
        exit(3);
    }
    opt = 1;
    if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt,
                sizeof(opt)) < 0) {
        log_perror("NET", "FAIL", NULL, "setsockopt");
    }
    if(fcntl(s, F_SETFD, FD_CLOEXEC) < 0) {
        log_perror("LOGCACHE", "FAIL", NULL, "fcntl(fd, F_SETFD, FD_CLOEXEC)");
        close(s);
        abort();
    }
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(port);
    if(bind(s, (struct sockaddr *) &server, sizeof(server))) {
        log_perror("NET", "FAIL", NULL, "bind");
        close(s);
        exit(4);
    }
    dprintk("connection socket raised and bound, %d", s);
    listen(s, 5);
    return s;
}

void mux_release_socket() {
    dprintk("releasing mux main socket.");
    event_del(&listen_sock_ev);
    close(mux_bound_socket); 
    mux_bound_socket = -1;
}

static int eradicate_broken_fd(void)
{
    struct stat statbuf;
    DESC *d;

    DESC_ITER_ALL(d) {
        if (fstat(d->descriptor, &statbuf) < 0) {
            /* An invalid player connection... eject, eject, eject. */
            STARTLOG(LOG_PROBLEMS, "ERR", "EBADF") {
                log_text("Broken descriptor ");
                log_number(d->descriptor);
                log_text(" for player ");
                log_name(d->player);
                ENDLOG;
            }
            shutdownsock(d, R_SOCKDIED);
        }
    }
    if (slave_socket != -1 && fstat(slave_socket, &statbuf) < 0) {
        STARTLOG(LOG_PROBLEMS, "ERR", "EBADF") {
            log_text("Broken descriptor for DNS slave: ");
            log_number(slave_socket);
            ENDLOG;
        }
        boot_slave();
    }
    if (mux_bound_socket != -1 && fstat(mux_bound_socket, &statbuf) < 0) {
        STARTLOG(LOG_PROBLEMS, "ERR", "EBADF") {
            log_text("Broken descriptor for our main port: ");
            log_number(slave_socket);
            ENDLOG;
        }
        mux_bound_socket = -1;
        return -1;
    }
    return 0;
}

void accept_client_input(int fd, short event, void *arg) {
    DESC *connection = (DESC *)arg;

    if(connection->flags & DS_AUTODARK) {
        connection->flags &= ~DS_AUTODARK;
        s_Flags(connection->player, Flags(connection->player) & ~DARK);
    }

    if(!process_input(connection)) {
        shutdownsock(connection, R_SOCKDIED);
    }
}

void bsd_write_callback(struct bufferevent *bufev, void *arg) {
    dprintk("write.");
}

void bsd_read_callback(struct bufferevent *bufev, void *arg) {
    dprintk("read.");
}

void bsd_error_callback(struct bufferevent *bufev, short whut, void *arg) {
    dprintk("error %d", whut);
}

void accept_new_connection(int fd, short event, void *arg) {
    DESC *newfd;

    newfd = new_connection(fd);
}

void accept_slave_input(int fd, short event, void *arg) {
    while (get_slave_result() == 0);
}


#ifndef HAVE_GETTIMEOFDAY
#define get_tod(x)	{ (x)->tv_sec = time(NULL); (x)->tv_usec = 0; }
#else
#define get_tod(x)	gettimeofday(x, (struct timezone *)0)
#endif

void shovechars(int port) {
    struct timeval last_slice, current_time, next_slice, slice_timeout;

    mudstate.debug_cmd = (char *) "< shovechars >";

    dprintk("shovechars starting, sock is %d.", mux_bound_socket);

    if(mux_bound_socket < 0) {
        signal(SIGPIPE, SIG_IGN);
        mux_bound_socket = bind_mux_socket(port);
    }
    
    event_set(&listen_sock_ev, mux_bound_socket, EV_READ | EV_PERSIST, 
            accept_new_connection, NULL);
    event_add(&listen_sock_ev, NULL);
    get_tod(&last_slice);

    while (mudstate.shutdown_flag == 0) {
        get_tod(&current_time);
        last_slice = update_quotas(last_slice, current_time);

        process_commands();
        if (mudstate.shutdown_flag)
            break;

        /*
         * test for events 
         */

        dispatch();

        /*
         * any queued robot commands waiting? 
         */

        next_slice = msec_add(last_slice, mudconf.timeslice);
        slice_timeout = timeval_sub(next_slice, current_time);

        /* run event loop */

        if(event_loop(EVLOOP_ONCE) < 0) {
            perror("event_loop");
            exit(0);
        }

        /*
         * run robot commands
         */

        if (mudconf.queue_chunk)
            do_top(mudconf.queue_chunk);
    }
}

DESC *new_connection(int sock) {
    int newsock;
    char *buff, *buff1, *cmdsave;
    DESC *d;
    struct sockaddr_in addr;
    unsigned int addr_len;
    int len;
    char *buf;

    make_nonblocking(sock);
    cmdsave = mudstate.debug_cmd;
    mudstate.debug_cmd = (char *) "< new_connection >";
    addr_len = sizeof(struct sockaddr);

    newsock = accept(mux_bound_socket, (struct sockaddr *)&addr, &addr_len);
    dprintk("accept() result is %d.", newsock);
    if (newsock < 0)
        return 0;

    if (site_check(addr.sin_addr, mudstate.access_list) == H_FORBIDDEN) {
        STARTLOG(LOG_NET | LOG_SECURITY, "NET", "SITE") {
            buff = alloc_mbuf("new_connection.LOG.badsite");
            sprintf(buff, "[%d/%s] Connection refused.  (Remote port %d)",
                    newsock, inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
            log_text(buff);
            free_mbuf(buff);
            ENDLOG;
        }
        fcache_rawdump(newsock, FC_CONN_SITE);

        shutdown(newsock, 2);
        close(newsock);
        errno = 0;
        d = NULL;
    } else {
        buff = alloc_mbuf("new_connection.address");
        buf = alloc_lbuf("new_connection.write");
        StringCopy(buff, inet_ntoa(addr.sin_addr));

        /*
         * Ask slave process for host and username 
         */
        if ((slave_socket != -1) && mudconf.use_hostname) {
            sprintf(buf, "%s\n%s,%d,%d\n", inet_ntoa(addr.sin_addr),
                    inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
                    mudconf.port);
            len = strlen(buf);
            if (WRITE(slave_socket, buf, len) < 0) {
                slave_destruct();
            }
        }
        free_lbuf(buf);
        STARTLOG(LOG_NET, "NET", "CONN") {
            buff1 = alloc_mbuf("new_connection.LOG.open");
            sprintf(buff1, "[%d/%s] Connection opened (remote port %d)",
                    newsock, buff, ntohs(addr.sin_port));
            log_text(buff1);
            free_mbuf(buff1);
            ENDLOG;
        }
        d = initializesock(newsock, &addr);

        mudstate.debug_cmd = cmdsave;
        free_mbuf(buff);
    }
    mudstate.debug_cmd = cmdsave;
    return (d);
}

/*
 * Disconnect reasons that get written to the logfile 
 */

static const char *disc_reasons[] = {
    "Unspecified",
    "Quit",
    "Inactivity Timeout",
    "Booted",
    "Remote Close or Net Failure",
    "Game Shutdown",
    "Login Retry Limit",
    "Logins Disabled",
    "Logout (Connection Not Dropped)",
    "Too Many Connected Players"
};

/*
 * Disconnect reasons that get fed to A_ADISCONNECT via announce_disconnect 
 */

static const char *disc_messages[] = {
    "unknown",
    "quit",
    "timeout",
    "boot",
    "netdeath",
    "shutdown",
    "badlogin",
    "nologins",
    "logout"
};

void shutdownsock(DESC *d, int reason) {
    char *buff, *buff2;
    time_t now;
    int i, num;
    DESC *dtemp;

    if ((reason == R_LOGOUT) &&
            (site_check((d->address).sin_addr,
                        mudstate.access_list) == H_FORBIDDEN))
        reason = R_QUIT;

    if (d->flags & DS_CONNECTED) {

        /*
         * Do the disconnect stuff if we aren't doing a LOGOUT * * *
         * * * * (which keeps the connection open so the player can *
         * * connect * * * * to a different character). 
         */

        if (reason != R_LOGOUT) {
            fcache_dump(d, FC_QUIT);
            STARTLOG(LOG_NET | LOG_LOGIN, "NET", "DISC") {
                buff = alloc_mbuf("shutdownsock.LOG.disconn");
                sprintf(buff, "[%d/%s] Logout by ", d->descriptor,
                        d->addr);
                log_text(buff);
                log_name(d->player);
                sprintf(buff, " <Reason: %s>", disc_reasons[reason]);
                log_text(buff);
                free_mbuf(buff);
                ENDLOG;
            }
        } else {
            STARTLOG(LOG_NET | LOG_LOGIN, "NET", "LOGO") {
                buff = alloc_mbuf("shutdownsock.LOG.logout");
                sprintf(buff, "[%d/%s] Logout by ", d->descriptor,
                        d->addr);
                log_text(buff);
                log_name(d->player);
                sprintf(buff, " <Reason: %s>", disc_reasons[reason]);
                log_text(buff);
                free_mbuf(buff);
                ENDLOG;
            }
        }

        /*
         * If requested, write an accounting record of the form: * *
         * * * * * Plyr# Flags Cmds ConnTime Loc Money [Site]
         * <DiscRsn>  * *  * Name 
         */

        STARTLOG(LOG_ACCOUNTING, "DIS", "ACCT") {
            now = mudstate.now - d->connected_at;
            buff = alloc_lbuf("shutdownsock.LOG.accnt");
            buff2 =
                decode_flags(GOD, Flags(d->player), Flags2(d->player),
                        Flags3(d->player));
            sprintf(buff, "%d %s %d %d %d %d [%s] <%s> %s", d->player,
                    buff2, d->command_count, (int) now, Location(d->player),
                    Pennies(d->player), d->addr, disc_reasons[reason],
                    Name(d->player));
            log_text(buff);
            free_lbuf(buff);
            free_sbuf(buff2);
            ENDLOG;
        } 
        announce_disconnect(d->player, d, disc_messages[reason]);
    } else {
        if (reason == R_LOGOUT)
            reason = R_QUIT;
            STARTLOG(LOG_SECURITY | LOG_NET, "NET", "DISC") {
                buff = alloc_mbuf("shutdownsock.LOG.neverconn");
                sprintf(buff,
                        "[%d/%s] Connection closed, never connected. <Reason: %s>",
                        d->descriptor, d->addr, disc_reasons[reason]);
                log_text(buff);
                free_mbuf(buff);
            ENDLOG;
        }
    }
    
    clearstrings(d);
    if (reason == R_LOGOUT) {
        d->flags &= ~DS_CONNECTED;
        d->connected_at = mudstate.now;
        d->retries_left = mudconf.retry_limit;
        d->command_count = 0;
        d->timeout = mudconf.idle_timeout;
        d->player = 0;
        d->doing[0] = '\0';
        d->hudkey[0] = '\0';
        d->quota = mudconf.cmd_quota_max;
        d->last_time = 0;
        d->host_info =
            site_check((d->address).sin_addr,
                    mudstate.access_list) | site_check((d->address).sin_addr,
                        mudstate.suspect_list);
        d->input_tot = d->input_size;
        d->output_tot = 0;
        welcome_user(d);
    } else {
        event_del(&d->sock_ev);
        shutdown(d->descriptor, 2);
        close(d->descriptor);
        bufferevent_free(d->sock_buff);

        freeqs(d);
        *d->prev = d->next;
        if (d->next)
            d->next->prev = d->prev;

        /*
         * Is this desc still in interactive mode? 
         */
        if (d->program_data != NULL) {
            num = 0;
            DESC_ITER_PLAYER(d->player, dtemp) num++;

            if (num == 0) {
                for (i = 0; i < MAX_GLOBAL_REGS; i++) {
                    free_lbuf(d->program_data->wait_regs[i]);
                }
                free(d->program_data);
            }
        }

        free(d);
        ndescriptors--;
    }
}

void make_nonblocking(int s) {
    long flags=0;

    if(fcntl(s, F_GETFL, &flags) < 0) {
        log_perror("NET", "FAIL", "make_nonblocking", "fcntl F_GETFL");
    }
    flags |= O_NONBLOCK;
    if(fcntl(s, F_SETFL, flags) < 0) {
        log_perror("NET", "FAIL", "make_nonblocking", "fcntl F_SETFL");
    }
    flags = 1;
    if(setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(flags)) < 0) {
        log_perror("NET", "FAIL", "make_nonblocking", "setsockopt NDELAY");
    }
}

void make_blocking(int s) {
    long flags=0;

    if(fcntl(s, F_GETFL, &flags) < 0) {
        log_perror("NET", "FAIL", "make_blocking", "fcntl F_GETFL");
    }
    flags &= ~O_NONBLOCK;
    if(fcntl(s, F_SETFL, flags) < 0) {
        log_perror("NET", "FAIL", "make_blocking", "fcntl F_SETFL");
    }
    flags = 0;
    if(setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(flags)) < 0) {
        log_perror("NET", "FAIL", "make_blocking", "setsockopt NDELAY");
    }
}
extern int fcache_conn_c;

DESC *initializesock(int s, struct sockaddr_in *a) {
    DESC *d;

    ndescriptors++;
#if 0
    d = alloc_desc("init_sock");
#endif
    d = malloc(sizeof(DESC));
    memset(d, 0, sizeof(DESC));
    
    d->descriptor = s;
    d->flags = 0;
    d->connected_at = mudstate.now;
    d->retries_left = mudconf.retry_limit;
    d->command_count = 0;
    d->timeout = mudconf.idle_timeout;
    d->host_info =
        site_check((*a).sin_addr,
                mudstate.access_list) | site_check((*a).sin_addr,
                    mudstate.suspect_list);
    d->player = 0;		/*
                         * be sure #0 isn't wizard.  Shouldn't be. 
                         */
    d->chokes = 0;

    d->addr[0] = '\0';
    d->doing[0] = '\0';
    d->hudkey[0] = '\0';
    d->username[0] = '\0';
    make_nonblocking(s);
    d->output_prefix = NULL;
    d->output_suffix = NULL;
    d->output_size = 0;
    d->output_tot = 0;
    d->output_lost = 0;
    d->input_head = NULL;
    d->input_tail = NULL;
    d->input_size = 0;
    d->input_tot = 0;
    d->input_lost = 0;
    d->raw_input = NULL;
    d->raw_input_at = NULL;
    d->quota = mudconf.cmd_quota_max;
    d->program_data = NULL;
    d->last_time = 0;
    d->address = *a;		/*
                             * added 5/3/90 SCG 
                             */
    if (descriptor_list)
        descriptor_list->prev = &d->next;
    d->hashnext = NULL;
    d->next = descriptor_list;
    d->prev = &descriptor_list;
    StringCopyTrunc(d->addr, inet_ntoa(a->sin_addr), 50);
    descriptor_list = d;

    d->sock_buff = bufferevent_new(d->descriptor, bsd_write_callback, 
            bsd_read_callback, bsd_error_callback, NULL);
    bufferevent_disable(d->sock_buff, EV_READ);
    bufferevent_enable(d->sock_buff, EV_WRITE);
    event_set(&d->sock_ev, d->descriptor, EV_READ | EV_PERSIST, 
            accept_client_input, d);
    event_add(&d->sock_ev, NULL);

    welcome_user(d);
    return d;
}

extern int muxevent_tick;
int last_bug2 = -1;
int last_bug = -1;
int last_bugc = 0;		/* If 4+ bugs per second, and/or 3 bugs per 2sec, *bang* */

int fatal_bug() {
    last_bugc++;
    if (last_bugc == 4)
        return 1;
    if (last_bug2 == (last_bug - 1) && last_bugc == 2)
        return 1;
    return 0;
}

int process_input(DESC *d) {
    static char buf[LBUF_SIZE];
    int got, in, lost;
    char *p, *pend, *q, *qend;
    char *cmdsave;

    cmdsave = mudstate.debug_cmd;
    mudstate.debug_cmd = (char *) "< process_input >";

    got = in = read(d->descriptor, buf, (sizeof buf - 1));
    if (got <= 0)  {
        if(errno == EINTR) return 1;
        else return 0;
    }

    buf[got] = 0;
    if (!d->raw_input) {
        d->raw_input = (CBLK *) alloc_lbuf("process_input.raw");
        d->raw_input_at = d->raw_input->cmd;
    }
    p = d->raw_input_at;
    pend = d->raw_input->cmd + LBUF_SIZE - sizeof(CBLKHDR) - 1;
    lost = 0;
    for (q = buf, qend = buf + got; q < qend; q++) {
        if (*q == '\n') {
            *p = '\0';
            if (p > d->raw_input->cmd) {
                save_command(d, d->raw_input);
                d->raw_input = (CBLK *) alloc_lbuf("process_input.raw");

                p = d->raw_input_at = d->raw_input->cmd;
                pend = d->raw_input->cmd + LBUF_SIZE - sizeof(CBLKHDR) - 1;
            } else {
                in -= 1;	/*
                             * for newline 
                             */
            }
        } else if ((*q == '\b') || (*q == 127)) {
            if (*q == 127)
                queue_string(d, "\b \b");
            else
                queue_string(d, " \b");
            in -= 2;
            if (p > d->raw_input->cmd)
                p--;
            if (p < d->raw_input_at)
                (d->raw_input_at)--;
        } else if (p < pend && ((isascii(*q) && isprint(*q)) || *q < 0)) {
            *p++ = *q;
        } else {
            in--;
            if (p >= pend)
                lost++;
        }
    }
    if (p > d->raw_input->cmd) {
        d->raw_input_at = p;
    } else {
        free_lbuf(d->raw_input);
        d->raw_input = NULL;
        d->raw_input_at = NULL;
    }
    d->input_tot += got;
    d->input_size += in;
    d->input_lost += lost;
    mudstate.debug_cmd = cmdsave;
    return 1;
}

void flush_sockets() {
    int null = 0;
    DESC *d, *dnext;
    DESC_SAFEITER_ALL(d, dnext) {
        dprintk("%d output evbuffer misalign: %d, totallen: %d, off: %d", 
                    d->descriptor,
                    d->sock_buff->output->misalign,
                    d->sock_buff->output->totallen,
                    d->sock_buff->output->off);
        if(d->chokes) {
#if TCP_CORK
            setsockopt(d->descriptor, IPPROTO_TCP, TCP_CORK, &null, sizeof(null));
#else
#ifdef TCP_NOPUSH
            setsockopt(d->descriptor, IPPROTO_TCP, TCP_NOPUSH, &null, sizeof(null));
#endif
#endif
            d->chokes = 0;
        }   
        if(EVBUFFER_LENGTH(d->sock_buff->output)) {
            dprintk("forcing write.");
            evbuffer_write(d->sock_buff->output, d->descriptor);
        }
        fsync(d->descriptor);
    }
}

void close_sockets(int emergency, char *message) {
    DESC *d, *dnext;

    DESC_SAFEITER_ALL(d, dnext) {
        if (emergency) {
            WRITE(d->descriptor, message, strlen(message));
            if (shutdown(d->descriptor, 2) < 0)
                log_perror("NET", "FAIL", NULL, "shutdown");
            dprintk("shutting down fd %d", d->descriptor);
            dprintk("output evbuffer misalign: %d, totallen: %d, off: %d", 
                    d->sock_buff->output->misalign,
                    d->sock_buff->output->totallen,
                    d->sock_buff->output->off);
            fsync(d->descriptor);
            event_loop(EVLOOP_ONCE);
            event_del(&d->sock_ev);
            bufferevent_free(d->sock_buff);
            close(d->descriptor);
        } else {
            queue_string(d, message);
            queue_write(d, "\r\n", 2);
            shutdownsock(d, R_GOING_DOWN);
        }
    }
    close(mux_bound_socket);
    event_del(&listen_sock_ev);
}

void emergency_shutdown(void)
{
    close_sockets(1, (char *) "Going down - Bye.\n");
}


/*
 * ---------------------------------------------------------------------------
 * * Signal handling routines.
 */

#ifndef SIGCHLD
#define SIGCHLD SIGCLD
#endif

#ifdef HAVE_STRUCT_SIGCONTEXT
static RETSIGTYPE sighandler(int, int, struct sigcontext);
#else
static RETSIGTYPE sighandler(int);
#endif

/* *INDENT-OFF* */

NAMETAB sigactions_nametab[] = {
    {(char *)"exit",	3,	0,	SA_EXIT},
    {(char *)"default",	1,	0,	SA_DFLT},
    { NULL,			0,	0,	0}};

/* *INDENT-ON* */

void set_signals(void)
{
#if 0
    signal(SIGALRM, sighandler);
#endif
    signal(SIGCHLD, sighandler);
#if 0
    signal(SIGHUP, sighandler);
    signal(SIGINT, sighandler);
    signal(SIGQUIT, sighandler);
    signal(SIGTERM, sighandler);
#endif
    signal(SIGPIPE, SIG_IGN);
#if 0
    signal(SIGUSR1, sighandler);
    signal(SIGUSR2, sighandler);
    signal(SIGTRAP, sighandler);
#ifdef SIGXCPU
    signal(SIGXCPU, sighandler);
#endif

    signal(SIGILL, sighandler);
#ifdef __linux__
    signal(SIGFPE, SIG_IGN);
#else
    signal(SIGFPE, sighandler);
#endif
    signal(SIGSEGV, sighandler);
    signal(SIGABRT, sighandler);
#ifdef SIGFSZ
    signal(SIGXFSZ, sighandler);
#endif
#ifdef SIGEMT
    signal(SIGEMT, sighandler);
#endif
#ifdef SIGBUS
    signal(SIGBUS, sighandler);
#endif
#ifdef SIGSYS
    signal(SIGSYS, sighandler);
#endif
#endif
}

static void unset_signals()
{
    int i;

    for (i = 0; i < NSIG; i++)
        signal(i, SIG_DFL);
    abort();
}

static void check_panicking(int sig) {
    int i;

    /*
     * If we are panicking, turn off signal catching and resignal 
     */

    if (mudstate.panicking) {
        for (i = 0; i < NSIG; i++)
            signal(i, SIG_DFL);
        kill(getpid(), sig);
    }
    mudstate.panicking = 1;
}

void log_signal(const char *signame) {
    STARTLOG(LOG_PROBLEMS, "SIG", "CATCH") {
        log_text((char *) "Caught signal ");
        log_text((char *) signame);
        ENDLOG;
    }
}


void log_commands(int sig)
{
}

#ifdef HAVE_STRUCT_SIGCONTEXT
static RETSIGTYPE sighandler(sig, code, scp)
    int sig;
    int code;
    struct sigcontext *scp;

#else
static RETSIGTYPE sighandler(sig)
    int sig;

#endif
{
#ifdef SYS_SIGLIST_DECLARED
#define signames sys_siglist
#else
    static const char *signames[] = {
        "SIGZERO", "SIGHUP", "SIGINT", "SIGQUIT",
        "SIGILL", "SIGTRAP", "SIGABRT", "SIGEMT",
        "SIGFPE", "SIGKILL", "SIGBUS", "SIGSEGV",
        "SIGSYS", "SIGPIPE", "SIGALRM", "SIGTERM",
        "SIGURG", "SIGSTOP", "SIGTSTP", "SIGCONT",
        "SIGCHLD", "SIGTTIN", "SIGTTOU", "SIGIO",
        "SIGXCPU", "SIGXFSZ", "SIGVTALRM", "SIGPROF",
        "SIGWINCH", "SIGLOST", "SIGUSR1", "SIGUSR2"
    };

#endif

    char buff[32];

#ifdef HAVE_UNION_WAIT
    union wait stat = {0};

#else
    int stat;

#endif

    switch (sig) {
        case SIGUSR1:
            do_restart(1, 1, 0);
            break;
        case SIGALRM:		/*
                             * Timer 
                             */
            mudstate.alarm_triggered = 1;
            break;
        case SIGCHLD:		/*
                             * Change in child status 
                             */
#ifndef SIGNAL_SIGCHLD_BRAINDAMAGE
            signal(SIGCHLD, sighandler);
#endif
#ifdef HAVE_WAIT3
            while (wait3(&stat, WNOHANG, NULL) > 0);
#else
            wait(&stat);
#endif
            /* Did the child exit? */

            if (WEXITSTATUS(stat) == 8)
                exit(0);

            mudstate.dumping = 0;
            break;
        case SIGHUP:		/*
                             * Perform a database dump 
                             */
            log_signal(signames[sig]);
            mudstate.dump_counter = 0;
            break;
        case SIGINT:		/*
                             * Log + ignore 
                             */
            log_signal(signames[sig]);
            break;
        case SIGQUIT:		/*
                             * Normal shutdown 
                             */
        case SIGTERM:
#ifdef SIGXCPU
        case SIGXCPU:
#endif
            check_panicking(sig);
            log_signal(signames[sig]);
            log_commands(sig);
            sprintf(buff, "Caught signal %s, exiting.", signames[sig]);
            raw_broadcast(0, buff);
            dump_database_internal(DUMP_KILLED);
            exit(0);
            break;
        case SIGILL:		/*
                             * Panic save + restart 
                             */
        case SIGFPE:
        case SIGSEGV:
        case SIGTRAP:
#ifdef SIGXFSZ
        case SIGXFSZ:
#endif
#ifdef SIGEMT
        case SIGEMT:
#endif
#ifdef SIGBUS
        case SIGBUS:
#endif
#ifdef SIGSYS
        case SIGSYS:
#endif
            /*
             * Try our best to dump a core first 
             */
            if (!fork()) {
                unset_signals();
                return;
            }
            check_panicking(sig);
            log_signal(signames[sig]);
            report();
            if (mudconf.sig_action != SA_EXIT) {
                char outdb[128];
                char indb[128];

                log_commands(sig);
                raw_broadcast(0,
                        "Game: Fatal signal %s caught, restarting with previous database.",
                        signames[sig]);

                /* Don't sync first. Using older db. */

                dump_database_internal(DUMP_CRASHED);

                shutdown_services();
                
                if (mudconf.compress_db) {
                    sprintf(outdb, "%s.Z", mudconf.outdb);
                    sprintf(indb, "%s.Z", mudconf.indb);
                    rename(outdb, indb);
                } else {
                    rename(mudconf.outdb, mudconf.indb);
                }
                if (mudconf.have_specials)
                    ChangeSpecialObjects(0);
                dump_restart_db();
                execl("bin/netmux", "netmux", mudconf.config_file, NULL);
                break;
            } else {
                unset_signals();
                signal(sig, SIG_DFL);
                exit(1);
            }
        case SIGABRT:		/*
                             * Coredump. 
                             */
            check_panicking(sig);
            log_signal(signames[sig]);
            report();

            unset_signals();
            signal(sig, SIG_DFL);
            exit(1);

    }
    signal(sig, sighandler);
    mudstate.panicking = 0;
    return;
}