/*
* server.c
* S. Thorne
* This is the entry point of the Techinfo server. It loads the web and gets
* connections. It contains the main server loop.
*/
/*
Copyright (C) 1989 by the Massachusetts Institute of Technology
Export of this software from the United States of America is assumed
to require a specific license from the United States Government.
It is the responsibility of any person or organization contemplating
export to obtain such a license before exporting.
WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
distribute this software and its documentation for any purpose and
without fee is hereby granted, provided that the above copyright
notice appear in all copies and that both that copyright notice and
this permission notice appear in supporting documentation, and that
the name of M.I.T. not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission. M.I.T. makes no representations about the suitability of
this software for any purpose. It is provided "as is" without express
or implied warranty.
#define conn_writeable(cn) FD_ISSET(conntab[cn].c_socket,&write_fds)
#define conn_except(cn) FD_ISSET(conntab[cn].c_socket,&except_fds)
#define conn_readable(cn) sock_readable(conntab[cn].c_socket)
#define conn_established(cn) (conntab[cn].c_socket != -1)
#define conn_wait_write(cn) (conntab[cn].c_ptr) /* is the connection
waiting for data? */
#define conn_wait_recv(cn) (conntab[cn].c_recptr) /* is the connection
sending a file over? */
#define conn_timedout(cn) (conntab[cn].c_flags & C_TIMEOUT)
#define conn_provider(cn) (conntab[cn].c_flags & C_PROVIDER)
#define POLL &tv
#define BLOCK 0
#define ADAY 86400 /* this should go in a .h file, but which? -- lam */
static int num_connection;
static int listen_sock;
int PENDING_SHUTDOWN = FALSE;
int ALT_BANNER = FALSE;
char *shutdown_msg;
char *alt_banner;
short todaysdate = 0; /* will be properly initialized later */
static int alarmwentoff;
extern int PROV; /* the socket # of the current provider */
char *msglist[100];
int debug = 0; /* no debugging is default */
void catch_alarm(void)
{
alarmwentoff = 1;
}
/* load the file of error messages. error file has structure of
<error #>:<error text> */
int
load_msgs(void)
{
FILE *fopen(), *msgfile;
char msgline[101];
int pos = 0;
int len;
msgfile = fopen(MSG_FILE, "r");
if (!msgfile) {
perror(MSG_FILE);
puts("Could not find the message file");
exit(1);
}
while (fgets(msgline, 100, msgfile) != '\0') {
len = strlen(msgline);
msgline[len - 1] = '\0';
msglist[pos] = (char *) domalloc((unsigned int) len);
strcpy(msglist[pos],msgline);
pos++;
}
fclose(msgfile);
return 0; /* close file */
}
/* write the transaction to the log file */
void
log_session(char *line)
{
int dfd;
if ((dfd = open(LOG_FILE, O_APPEND | O_CREAT | O_WRONLY, 0644)) == 0)
printf("error logging transaction\n");
write(dfd, line, strlen(line));
close(dfd);
return;
}
void
dump_conntab(void)
{
int i,fd;
char line[200];
fd = open("conntab",O_CREAT|O_TRUNC|O_WRONLY, 0644);
for (i = 0; i < FD_SETSIZE; i++) {
if (conn_established(i)){
sprintf(line,"conn # %d, sock = %d, hostname = %s\n",i,conntab[i].c_socket,
conntab[i].c_hostname);
write(fd,line,strlen(line));}
}
close(fd);
}
/* Close the connection on socket c, saving the web if it was connected to
a provider.
Note: Should there be a modified flag in the provider's socket info to
determine whether or not we need to save the web?
*/
void
close_connection(int c)
{
time_t secs;
char line[120];
int rc;
time(&secs);
if (debug) {
printf("Closing connection to %s, port %d, socket #%d.\n",
conntab[c].c_hostname,
conntab[c].c_portnum,
conntab[c].c_socket);
fflush(stdout);
}
sprintf(line,"%d %d %s %.24s, active %.1f minutes - SERVER\n",
conntab[c].c_portnum,
conntab[c].c_socket,
conntab[c].c_hostname,
ctime(&secs),
(float) ((secs - conntab[c].c_made) /60.0));
log_session(line);
if (conntab[c].c_ptr != NULL)
remove_send(conntab[c].c_ptr);
if (conntab[c].c_recptr != NULL)
remove_rec(conntab[c].c_recptr);
if (conntab[c].c_hptr != NULL)
remove_helper(conntab[c].c_hptr); /* send KILL to child */
do_free(conntab[c].c_hostname);
do_free(conntab[c].c_uid);
do_free(conntab[c].c_type);
FD_CLR(conntab[c].c_socket, &read_fds); /* not needed now ? */
FD_CLR(conntab[c].c_socket, &write_fds);
FD_CLR(conntab[c].c_socket, &except_fds);
rc = close(conntab[c].c_socket);
if (rc < 0) {
sprintf(line,"Connection close failed on socket %d\n",conntab[c].c_socket);
printf(line); fflush(stdout);
log_session(line);
}
conntab[c].c_socket = -1;
conntab[c].c_ptr = NULL;
conntab[c].c_recptr = NULL;
conntab[c].c_hptr = NULL;
num_connection--;
}
/*
* Find the connection using the given socket, and mark it as having
* timed out, so the server can close it off next time around.
*/
void
sock_timeout(int sock)
{
int i;
for (i = 0; i < FD_SETSIZE; i++) {
if (conntab[i].c_socket == sock) {
conntab[i].c_flags |= C_TIMEOUT;
break;
}
}
printf("Sock #%d marked for timeout termination.\n",
conntab[i].c_socket);
fflush(stdout);
}
void
server_shutdown()
{
int i;
save_datastruct(); /* do this for production only? */
log_trans("Shuting down the server");
puts("\nServer shutdown, closing connections:");
for (i = 0; i < FD_SETSIZE; i++) /* Close Connections */
if (conn_established(i))
close_connection(i);
close(listen_sock);
puts("Server has shut down -- Exiting.");
exit(0);
}
int
hdl_connect(int c)
{
CONN *conn = &conntab[c];
int quit;
if (debug) {
printf("\nHandling transaction to %s, port %d, socket #%d.\n",
conn->c_hostname,
conn->c_portnum,
conn->c_socket);
fflush(stdout);
}
quit = hdl_transact(conn);
time(&conn->c_last);
return quit;
}
void
make_new_connection(int listen_sock)
{
CONN *conn;
char *hostname,line[100],*cp;
int i;
time_t secs;
time(&secs);
for (i = 0; i < FD_SETSIZE; i++)
if (!conn_established(i))
break;
if (i == FD_SETSIZE) {
printf("TechInfo server: reached connection limit of %d.\n",
FD_SETSIZE);
fflush(stdout);
return;
}
conn = &conntab[i];
if (conn->c_socket == -1) {
puts("accept_connection failed");
return;
}
if (PENDING_SHUTDOWN) /* don't accept new connections */
{ /* send alternate message and close connection down */
if (shutdown_msg)
{
write(conn->c_socket, shutdown_msg, strlen(shutdown_msg));
write(conn->c_socket, EOM, EOM_LEN);
}
else
{
write(conn->c_socket, NAK_BANNER, strlen(NAK_BANNER));
write(conn->c_socket, EOM, EOM_LEN);
}
/* possibly sleep ?? */
close(conn->c_socket);
conn->c_socket = -1;
return;
}
conn->c_hostname = (char *) domalloc((unsigned int) strlen(hostname) + 1);
strcpy(conn->c_hostname, hostname);
conn->c_uid = (char *) domalloc((unsigned int) 25);
*conn->c_uid = '\0';
conn->c_flags = 0;
conn->c_type = NULL;
conn->c_last = time(&conn->c_made);
conn->c_ptr = NULL;
conn->c_recptr = NULL;
conn->c_hptr = NULL;
conn->c_trans_cnt = 0;
/* Added 8/12/92 ark -- Set initial output format to that defined in network.h
Change with the 'o' transaction */
conn->c_output_fmt = DEFAULT_OUTPUT_FORMAT;
num_connection++;
if (debug) {
printf("Accepted new connection to %s, thru port %d, over socket #%d.\n",
conn->c_hostname,
conn->c_portnum,
conn->c_socket);
fflush(stdout);
}
if (ALT_BANNER) {
write(conn->c_socket, alt_banner, strlen(alt_banner));
write(conn->c_socket, EOM, EOM_LEN);
}
else {
write(conn->c_socket, BANNER, strlen(BANNER));
write(conn->c_socket, EOM, EOM_LEN);
}
if (fcntl(conn->c_socket, F_SETFL, FNDELAY) == -1)
perror("fcntl");
/*
* We should turn blocking on BEFORE write the banner! Those
* writes shouldn't be direct syscalls either.
*/
if (conn->c_type == NULL)
cp = "unknown";
else
cp = conn->c_type;
sprintf(line,"%d %d %s %s %.24s - SERVER\n",
conn->c_portnum,
conn->c_socket,
conn->c_hostname,
cp,
ctime(&secs));
log_session(line);
}
int Standalone = 1;
static int i, handled;
static time_t secs;
int ActiveJobs = 0; /* Number of pending sends & recvs
set int netio.c */
static CONN *conn;
extern int max_stale_time; /* in shutdown mode wait until all conn have been
inactive for this many minutes */
void
server_loop(void)
{
int active_socks, show_connect = 0, do_shutdown;
char line[200];
struct timeval tv;
tv.tv_sec = 0; /* For our select when it only polls */
tv.tv_usec = 0;
alarmwentoff = 1;
signal (SIGALRM, catch_alarm);
for (;;) {
if (alarmwentoff) {
alarmwentoff = 0;
save_datastruct();
alarm(60); /* set alarm for 1 minute */
}
if (PENDING_SHUTDOWN)
do_shutdown = TRUE;
if (!ActiveJobs)
show_connect++;
if (show_connect && debug && num_connection) {
printf("\nCurrent connections:");
fflush(stdout);
}
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&except_fds);
FD_SET(listen_sock, &read_fds);
time(&secs);
for (i = 0; i < FD_SETSIZE; i++) {
if (!conn_established(i))
continue;
conn = &conntab[i];
if (conn_wait_write(i))
FD_SET(conn->c_socket, &write_fds);
else
FD_SET(conn->c_socket, &read_fds);
FD_SET(conn->c_socket, &except_fds);
if (!show_connect)
continue;
if (debug) {
printf("\n#%2d %s.%d\tactive %.1f, last %.1f",
conn->c_socket,
conn->c_hostname,
conn->c_portnum,
(float) (secs - conn->c_made) / 60.0,
(float) (secs - conn->c_last) / 60.0);
fflush(stdout);
}
if (conn->c_ptr != NULL | conn->c_recptr != NULL) {
if (((secs - conn->c_last) / 60.0) > 30.0)
close_connection(i); /* this conn asked for something 30 minutes
ago and still hasn't gotten it... */
}
if (PENDING_SHUTDOWN)
if (((secs - conn->c_last) / 60.0) < max_stale_time)
do_shutdown = FALSE;
if (((secs - conn->c_last) / 60.0) > 700.0)
close_connection(i); /* close connections which
timed out */
}
if (PENDING_SHUTDOWN && do_shutdown)
server_shutdown();
if (show_connect && debug) {
if (num_connection) {
printf("\n%d connection%s currently established.\n",
num_connection, num_connection == 1 ? "" : "s");
fflush(stdout);
}
/* else
puts(" None"); */
}
if (debug) {
if (ActiveJobs)
printf("%s for activity...",
ActiveJobs ? "Checking" : "Waiting");
if (ActiveJobs)
printf("(%d asleep)...", ActiveJobs);
fflush(stdout);
}
show_connect = 0;
if (active_socks < 0) {
if (errno != EINTR)
perror("select");
continue;
}
if (debug) {
if (!active_socks && ActiveJobs) {
printf("none...continuing our %d active processes...\n",
ActiveJobs);
continue;
}
printf("%d socket%s reported with new activity.\n",
active_socks, active_socks == 1 ? "" : "s");
fflush(stdout);
}
handled = 0;
/*
* First, see if we can establish any new connections. Takes
* only first one off queue even if there are more.
*/
if (sock_readable(listen_sock))
{
make_new_connection(listen_sock);
handled++;
show_connect++;
}
/*
* Now handle requests from all connections that have sent
* them.
*/
todaysdate = time(0) / ADAY; /* outside loop; used by other modules */
for (i = 0; i < FD_SETSIZE; i++) {
if (!conn_established(i))
continue;
if (conn_timedout(i)) {
if (conn_readable(i))
handled++;
close_connection(i), show_connect++;
continue;
}
if (conn_except(i)) {
printf("Exceptional condition on socket %d, closing connection.\n", i);
fflush(stdout);
handled++;
close_connection(i);
continue;
}
if (conn_wait_recv(i)){ /* if we're in the middle of
getting a file */
nio_rec_more(conntab[i].c_recptr);
handled++;
continue;
}
else if (conn_writeable(i))
{ /* finish writing */
handled++;
send_more(conntab[i].c_ptr);
}
else if (conn_readable(i))
{
handled++;
if (hdl_connect(i) || conntab[i].c_trans_cnt > MAX_TRANS)
{
close_connection(i), show_connect++;
if (conntab[i].c_trans_cnt > MAX_TRANS) {
sprintf(line,"MAX TRANS REACHED... closing sock %d - %s\n",
conntab[i].c_socket,conntab[i].c_hostname);
log_session(line);
}
}
continue;
}
}
/*
* We have a very major problem if we thought we needed to do
* something, and nothing was done, cause this can mean we're
* headed into an infinte loop. So we close everyone off if
* this happens (although it never should...)
*/
if (!handled) {
fprintf(stderr, "TechInfo server: Very major client plexing problem.\n");
for (i = 0; i < FD_SETSIZE; i++)
if (conn_established(i))
close_connection(i);
}
}
}
void
broken_pipe()
{
puts("SIGPIPE - No one to read our write.");
close_connection(i);
}
main(int argc, char **argv)
{
extern int menu;
extern int disptype;
int pips_port = PIPS_PORT;
char c;
int errorflag = 0, temp_port = 0;
extern int optind;
extern char *optarg;
while ((c = getopt(argc, argv, "dp:")) != EOF)
switch (c) {
case 'd':
debug++;
break;
case 'p':
if (optarg)
temp_port = atoi(optarg);
break;
default:
errorflag++;
}
if (errorflag)
{
printf("Usage: %s [-p port] [-d]\n", argv[0]);
exit(1);
}
if (temp_port)
if (temp_port > 1000)
pips_port = temp_port;
else
{
puts("Must use a port number greater than 1000");
exit(1);
}
menu = NOMENU;
disptype = SIMPLE;
setuid(SERVER_UID); /* ti_serve */
printf("Uid set to %d\n",getuid());
if (access(PROV_FILE, F_OK)) {
puts("Could not find the provider file");
exit(1);
}
if (access(ADMIN_FILE, F_OK)) {
puts("Could not find the admin file");
exit(1);
}
if (access(NEXTID_FILE, F_OK)) {
puts("Could not find the next_id file");
exit(1);
}
if (access(VER_FILE, F_OK)) {
puts("Could not find the version file");
exit(1);
}
if (access(SOURCE_FILE, F_OK)) {
puts("Could not find the source file");
exit(1);
}
if (access(SERVER_FILE, F_OK)) {
puts("Could not find the server file");
exit(1);
}
for (i = 0; i < FD_SETSIZE; i++)
conntab[i].c_socket = -1;
signal(SIGINT, server_shutdown);
signal(SIGTERM, server_shutdown);
signal(SIGCHLD, catch_child);
signal(SIGHUP, catch_hup);
SERVER = TRUE;
printf("\n%s\n", BANNER); fflush(stdout);
time(&secs);
load_msgs(); /* load the message list from disk */
printf("Started %.24s, pid = %d\n\n", ctime(&secs), getpid());
fflush(stdout);
listen_sock = inet_make_listner(pips_port);
if (listen_sock < 0) {
puts("Unable to accept connections!");
exit(1);
}
if (!(int) datastruct_load(0)) {
puts("Fatal error: datastruct_load failed");
exit(1);
}