/*
* bsd.c
*/
/*
* $Id: bsd.c,v 1.30.2.1 2000/04/25 01:37:44 cvs Exp $
*/
#include "copyright.h"
#include "autoconf.h"
#ifdef VMS
#include "multinet_root:[multinet.include.sys]file.h"
#include "multinet_root:[multinet.include.sys]ioctl.h"
#include "multinet_root:[multinet.include]errno.h"
#else
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/wait.h>
#endif
#include <sys/stat.h>
#include <signal.h>
#include "mudconf.h"
#include "config.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"
#ifndef NSIG
extern const int _sys_nsig;
#define NSIG _sys_nsig
#endif
#ifdef CONCENTRATE
extern struct descriptor_data *ccontrol;
extern void FDECL(send_killconcid, (DESC *));
extern long NDECL(make_concid);
#endif
extern void NDECL(dispatch);
extern void NDECL(dump_restart_db);
extern void FDECL(dump_database_internal, (int));
extern void FDECL(fork_and_dump, (int));
int sock;
int ndescriptors = 0;
int maxd = 0;
DESC *descriptor_list = NULL;
pid_t slave_pid;
int slave_socket = -1;
DESC *FDECL(initializesock, (int, struct sockaddr_in *));
DESC *FDECL(new_connection, (int));
int FDECL(process_output, (DESC *));
int FDECL(process_input, (DESC *));
/*
* 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;
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 (mudconf.use_hostname) {
StringCopyTrunc(d->addr, token, 50);
d->addr[50] = '\0';
if (d->player != 0) {
if (d->username[0])
atr_add_raw(d->player, A_LASTSITE, tprintf("%s@%s",
d->username, d->addr));
else
atr_add_raw(d->player, A_LASTSITE, 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;
StringCopyTrunc(d->username, userid, 10);
d->username[10] = '\0';
if (d->player != 0) {
atr_add_raw(d->player, A_LASTSITE, 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) {
close(slave_socket);
slave_socket = -1;
}
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) {
return;
}
/*
* set to nonblocking
*/
if (fcntl(sv[0], F_SETFL, FNDELAY) == -1) {
close(sv[0]);
close(sv[1]);
return;
}
slave_pid = vfork();
switch (slave_pid) {
case -1:
close(sv[0]);
close(sv[1]);
return;
case 0: /*
* * 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);
}
close(sv[1]);
if (fcntl(sv[0], F_SETFL, FNDELAY) == -1) {
close(sv[0]);
return;
}
slave_socket = sv[0];
STARTLOG(LOG_ALWAYS, "NET", "SLAVE")
log_text((char *) "DNS lookup slave started on fd ");
log_number(slave_socket);
ENDLOG
}
int make_socket(port)
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");
}
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(port);
if (!mudstate.restarting)
if (bind(s, (struct sockaddr *)&server, sizeof(server))) {
log_perror("NET", "FAIL", NULL, "bind");
close(s);
exit(4);
}
listen(s, 5);
return s;
}
void shovechars(port)
int port;
{
fd_set input_set, output_set;
struct timeval last_slice, current_time, next_slice, timeout, slice_timeout;
int found, check;
DESC *d, *dnext, *newd;
int avail_descriptors, maxfds;
struct stat fstatbuf;
#define CheckInput(x) FD_ISSET(x, &input_set)
#define CheckOutput(x) FD_ISSET(x, &output_set)
mudstate.debug_cmd = (char *)"< shovechars >";
if (!mudstate.restarting) {
sock = make_socket(port);
}
if (!mudstate.restarting)
maxd = sock + 1;
get_tod(&last_slice);
#ifdef HAVE_GETDTABLESIZE
maxfds = getdtablesize();
#else
maxfds = sysconf(_SC_OPEN_MAX);
#endif
avail_descriptors = maxfds - 7;
while (mudstate.shutdown_flag == 0) {
get_tod(¤t_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?
*/
timeout.tv_sec = que_next();
timeout.tv_usec = 0;
next_slice = msec_add(last_slice, mudconf.timeslice);
slice_timeout = timeval_sub(next_slice, current_time);
FD_ZERO(&input_set);
FD_ZERO(&output_set);
/*
* Listen for new connections if there are free descriptors
*/
if (ndescriptors < avail_descriptors) {
FD_SET(sock, &input_set);
}
/*
* Listen for replies from the slave socket
*/
if (slave_socket != -1) {
FD_SET(slave_socket, &input_set);
}
/*
* Mark sockets that we want to test for change in status
*/
DESC_ITER_ALL(d) {
if (!d->input_head)
FD_SET(d->descriptor, &input_set);
if (d->output_head)
FD_SET(d->descriptor, &output_set);
}
/*
* Wait for something to happen
*/
found = select(maxd, &input_set, &output_set, (fd_set *) NULL,
&timeout);
if (found < 0) {
if (errno == EBADF) {
/* This one is bad, as it results in a
* spiral of doom, unless we can figure
* out what the bad file descriptor is
* and get rid of it.
*/
log_perror("NET", "FAIL",
"checking for activity", "select");
DESC_ITER_ALL(d) {
if (fstat(d->descriptor, &fstatbuf) < 0) {
/* It's a player. Just toss the
* connection.
*/
STARTLOG(LOG_PROBLEMS, "ERR", "EBADF")
log_text((char *) "Bad descriptor ");
log_number(d->descriptor);
ENDLOG
shutdownsock(d, R_SOCKDIED);
}
}
if ((slave_socket != -1) &&
(fstat(slave_socket, &fstatbuf) < 0)) {
/* Try to restart the slave, since
* it presumably died.
*/
STARTLOG(LOG_PROBLEMS, "ERR", "EBADF")
log_text((char *) "Bad slave descriptor ");
log_number(slave_socket);
ENDLOG
boot_slave();
}
if ((mudstate.sql_socket != -1) &&
(fstat(mudstate.sql_socket, &fstatbuf) < 0)) {
/* Just mark it dead. */
STARTLOG(LOG_PROBLEMS, "ERR", "EBADF")
log_text((char *) "Bad SQL descriptor ");
log_number(mudstate.sql_socket);
ENDLOG
mudstate.sql_socket = -1;
}
} else if (errno != EINTR) {
log_perror("NET", "FAIL",
"checking for activity", "select");
}
continue;
}
/*
* if !found then time for robot commands
*/
if (!found) {
if (mudconf.queue_chunk)
do_top(mudconf.queue_chunk);
continue;
} else {
do_top(mudconf.active_q_chunk);
}
/*
* Get usernames and hostnames
*/
if (slave_socket != -1 &&
FD_ISSET(slave_socket, &input_set)) {
while (get_slave_result() == 0) ;
}
/*
* Check for new connection requests
*/
if (CheckInput(sock)) {
newd = new_connection(sock);
if (!newd) {
check = (errno && (errno != EINTR) &&
(errno != EMFILE) &&
(errno != ENFILE));
if (check) {
log_perror("NET", "FAIL", NULL,
"new_connection");
}
} else {
if (newd->descriptor >= maxd)
maxd = newd->descriptor + 1;
}
}
/*
* Check for activity on user sockets
*/
DESC_SAFEITER_ALL(d, dnext) {
/*
* Process input from sockets with pending input
*/
if (CheckInput(d->descriptor)) {
/*
* Undo autodark
*/
if (d->flags & DS_AUTODARK) {
d->flags &= ~DS_AUTODARK;
s_Flags(d->player,
Flags(d->player) & ~DARK);
}
/*
* Process received data
*/
#ifdef CONCENTRATE
if (!(d->cstatus & C_REMOTE))
#endif
if (!process_input(d)) {
shutdownsock(d, R_SOCKDIED);
continue;
}
}
/*
* Process output for sockets with pending output
*/
if (CheckOutput(d->descriptor)) {
if (!process_output(d)) {
#ifdef CONCENTRATE
if (!(d->cstatus & C_CCONTROL))
#endif
shutdownsock(d, R_SOCKDIED);
}
}
}
}
}
#ifdef BROKEN_GCC_PADDING
char *inet_ntoa(in)
struct in_addr in;
{
/* gcc 2.8.1 does not correctly pass/return structures which are smaller
* than 16 bytes, but are not 8 bytes. Structures get padded at the
* wrong end. gcc is consistent with itself, but if you try to link
* with library files, there's a problem. This particularly affects
* Irix 6, but also affects other 64-bit targets.
*
* There may be little/big-endian problems with this, too.
*/
static char buf[MBUF_SIZE];
long a = in.s_addr;
sprintf(buf, "%d.%d.%d.%d",
(int) ((a >> 24) & 0xff),
(int) ((a >> 16) & 0xff),
(int) ((a >> 8) & 0xff),
(int) (a & 0xff));
return buf;
}
#endif /* BROKEN_GCC_PADDING */
DESC *new_connection(sock)
int sock;
{
int newsock;
char *buff, *buff1, *cmdsave;
DESC *d;
struct sockaddr_in addr;
int addr_len, len;
char *buf;
cmdsave = mudstate.debug_cmd;
mudstate.debug_cmd = (char *)"< new_connection >";
addr_len = sizeof(struct sockaddr);
newsock = accept(sock, (struct sockaddr *)&addr, &addr_len);
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) {
close(slave_socket);
slave_socket = -1;
}
}
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(d, reason)
DESC *d;
int reason;
{
char *buff, *buff2;
time_t now;
int i, num, ncon;
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) {
if (reason != R_SOCKDIED) {
/* If the socket died, there's no reason to
* display the quit file.
*/
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, " <%s: %d cmds, %d bytes in, %d bytes out, %d secs>",
disc_reasons[reason], d->command_count,
d->input_tot, d->output_tot,
(int) (time(NULL) - d->connected_at));
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, " <%s: %d cmds, %d bytes in, %d bytes out, %d secs>",
disc_reasons[reason], d->command_count,
d->input_tot, d->output_tot,
(int) (time(NULL) - d->connected_at));
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> ",
d->player, buff2, d->command_count, (int) now,
Location(d->player), Pennies(d->player),
d->addr, disc_reasons[reason]);
log_text(buff);
log_text(Name(d->player));
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
}
process_output(d);
clearstrings(d);
if (reason == R_LOGOUT) {
/* If this was our only connection, get out of interactive
* mode.
*/
if (d->program_data) {
ncon = 0;
DESC_ITER_PLAYER(d->player, dtemp) ncon++;
if (ncon == 0) {
for (i = 0; i < MAX_GLOBAL_REGS; i++) {
free_lbuf(d->program_data->wait_regs[i]);
}
XFREE(d->program_data, "do_prog");
d->program_data = NULL;
atr_clr(d->player, A_PROGCMD);
}
}
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->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 {
#ifdef CONCENTRATE
if (!(d->cstatus & C_REMOTE)) {
if (d->cstatus & C_CCONTROL) {
register struct descriptor_data *k;
for (k = descriptor_list; k; k = k->next)
if (k->parent == d)
shutdownsock(k, R_QUIT);
}
#endif
shutdown(d->descriptor, 2);
close(d->descriptor);
#ifdef CONCENTRATE
} else {
register struct descriptor_data *k;
for (k = descriptor_list; k; k = k->next)
if (d->parent == k)
send_killconcid(d);
}
#endif
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]);
}
XFREE(d->program_data, "do_prog");
d->program_data = NULL;
atr_clr(d->player, A_PROGCMD);
}
}
free_desc(d);
#ifdef CONCENTRATE
if (!(d->cstatus & C_REMOTE))
#endif
ndescriptors--;
}
}
void make_nonblocking(s)
int s;
{
#ifdef HAVE_LINGER
struct linger ling;
#endif
#ifdef FNDELAY
if (fcntl(s, F_SETFL, FNDELAY) == -1) {
log_perror("NET", "FAIL", "make_nonblocking", "fcntl");
}
#else
if (fcntl(s, F_SETFL, O_NDELAY) == -1) {
log_perror("NET", "FAIL", "make_nonblocking", "fcntl");
}
#endif
#ifdef HAVE_LINGER
ling.l_onoff = 0;
ling.l_linger = 0;
if (setsockopt(s, SOL_SOCKET, SO_LINGER,
(char *)&ling, sizeof(ling)) < 0) {
log_perror("NET", "FAIL", "linger", "setsockopt");
}
#endif
}
DESC *initializesock(s, a)
int s;
struct sockaddr_in *a;
{
DESC *d;
if (s == slave_socket) {
/* Whoa. We shouldn't be allocating this. If we got this
* descriptor, our connection with the slave must have
* died somehow. We make sure to take note appropriately.
*/
STARTLOG(LOG_ALWAYS, "ERR", "SOCK")
log_text((char *) "Player descriptor clashes with slave fd ");
log_number(slave_socket);
ENDLOG
slave_socket = -1;
}
if (s == mudstate.sql_socket) {
/* We shouldn't be allocating this either, for the same reason. */
STARTLOG(LOG_ALWAYS, "ERR", "SOCK")
log_text((char *) "Player descriptor clashes with SQL server fd ");
log_number(slave_socket);
ENDLOG
mudstate.sql_socket = -1;
}
ndescriptors++;
d = alloc_desc("init_sock");
d->descriptor = s;
#ifdef CONCENTRATE
d->concid = make_concid();
d->cstatus = 0;
d->parent = 0;
#endif
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->addr[0] = '\0';
d->doing[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->output_head = NULL;
d->output_tail = NULL;
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;
welcome_user(d);
return d;
}
int process_output(d)
DESC *d;
{
TBLOCK *tb, *save;
int cnt;
char *cmdsave;
cmdsave = mudstate.debug_cmd;
mudstate.debug_cmd = (char *)"< process_output >";
tb = d->output_head;
#ifdef CONCENTRATE
if (d->cstatus & C_REMOTE) {
static char buf[10];
static char obuf[2048];
int buflen, k, j;
sprintf(buf, "%d ", d->concid);
buflen = strlen(buf);
bcopy(buf, obuf, buflen);
j = buflen;
while (tb != NULL) {
for (k = 0; k < tb->hdr.nchars; k++) {
obuf[j++] = tb->hdr.start[k];
if (tb->hdr.start[k] == '\n') {
if (d->parent)
queue_write(d->parent, obuf, j);
bcopy(buf, obuf, buflen);
j = buflen;
}
}
d->output_size -= tb->hdr.nchars;
save = tb;
tb = tb->hdr.nxt;
free(save);
d->output_head = tb;
if (tb == NULL)
d->output_tail = NULL;
}
if (j > buflen)
queue_write(d, obuf + buflen, j - buflen);
return 1;
} else {
#endif
while (tb != NULL) {
while (tb->hdr.nchars > 0) {
cnt = WRITE(d->descriptor, tb->hdr.start,
tb->hdr.nchars);
if (cnt < 0) {
mudstate.debug_cmd = cmdsave;
if (errno == EWOULDBLOCK)
return 1;
return 0;
}
d->output_size -= cnt;
tb->hdr.nchars -= cnt;
tb->hdr.start += cnt;
}
save = tb;
tb = tb->hdr.nxt;
free(save);
d->output_head = tb;
if (tb == NULL)
d->output_tail = NULL;
}
#ifdef CONCENTRATE
}
#endif
mudstate.debug_cmd = cmdsave;
return 1;
}
int process_input(d)
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);
if (got <= 0) {
mudstate.debug_cmd = cmdsave;
return 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 - sizeof(CBLKHDR) - 1 + LBUF_SIZE;
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 - sizeof(CBLKHDR) - 1 + LBUF_SIZE;
} 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)) {
*p++ = *q;
} else {
in--;
if (p >= pend)
lost++;
}
}
if (in < 0) /* backspace and delete by themselves */
in = 0;
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 close_sockets(emergency, message)
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");
close(d->descriptor);
} else {
queue_string(d, message);
queue_write(d, "\r\n", 2);
shutdownsock(d, R_GOING_DOWN);
}
}
close(sock);
}
void NDECL(emergency_shutdown)
{
close_sockets(1, (char *)"Going down - Bye");
}
/*
* ---------------------------------------------------------------------------
* * Signal handling routines.
*/
#ifndef SIGCHLD
#define SIGCHLD SIGCLD
#endif
#if defined(HAVE_STRUCT_SIGCONTEXT) && !defined(GLIBC_BRAINDAMAGE)
static RETSIGTYPE FDECL(sighandler, (int, int, struct sigcontext *));
#else
static RETSIGTYPE FDECL(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 NDECL(set_signals)
{
sigset_t sigs;
/* We have to reset our signal mask, because of the possibility
* that we triggered a restart on a SIGUSR1. If we did so, then
* the signal became blocked, and stays blocked, since control
* never returns to the caller; i.e., further attempts to send
* a SIGUSR1 would fail.
*/
sigfillset(&sigs);
sigprocmask(SIG_UNBLOCK, &sigs, NULL);
signal(SIGALRM, sighandler);
signal(SIGCHLD, sighandler);
signal(SIGHUP, sighandler);
signal(SIGINT, sighandler);
signal(SIGQUIT, sighandler);
signal(SIGTERM, sighandler);
signal(SIGPIPE, SIG_IGN);
signal(SIGUSR1, sighandler);
signal(SIGUSR2, sighandler);
signal(SIGTRAP, sighandler);
#ifdef SIGXCPU
signal(SIGXCPU, sighandler);
#endif
signal(SIGFPE, SIG_IGN);
signal(SIGILL, sighandler);
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
}
static void unset_signals()
{
int i;
for (i = 0; i < NSIG; i++)
signal(i, SIG_DFL);
fprintf(stderr, "ABORT! bsd.c, called unset_signals().\n");
abort();
}
static void check_panicking(sig)
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(signame)
const char *signame;
{
STARTLOG(LOG_PROBLEMS, "SIG", "CATCH")
log_text((char *)"Caught signal ");
log_text((char *)signame);
ENDLOG
}
#if defined(HAVE_STRUCT_SIGCONTEXT) && !defined(GLIBC_BRAINDAMAGE)
static RETSIGTYPE sighandler(sig, code, scp)
int sig, 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];
#if defined(HAVE_UNION_WAIT) && defined(NEED_WAIT3_DCL)
union wait stat;
#else
int stat;
#endif
switch (sig) {
case SIGUSR1:
log_signal(signames[sig]);
do_restart(GOD, GOD, 0);
break;
case SIGUSR2:
fork_and_dump(DUMP_FLATFILE);
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((int *) &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]);
sprintf(buff, "GAME: Caught signal %s, exiting.",
signames[sig]);
raw_broadcast(0, buff);
dump_database_internal(4);
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
check_panicking(sig);
log_signal(signames[sig]);
report();
if (mudconf.sig_action != SA_EXIT) {
char outdb[128];
char indb[128];
raw_broadcast(0, "GAME: Fatal signal %s caught, restarting with previous database.", signames[sig]);
/* Don't sync first. Using older db. */
CLOSE;
dump_database_internal(1);
shutdown(slave_socket, 2);
kill(slave_pid, SIGKILL);
/*
* Try our best to dump a core first
*/
if (!fork()) {
unset_signals();
exit(1);
}
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);
}
alarm(0);
dump_restart_db();
execl(mudconf.exec_path, mudconf.exec_path,
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;
#ifdef VMS
return 1;
#else
return;
#endif
}