/* *------------------------------------------------------------------ * * $Source: /afs/net.mit.edu/dapps/project/techinfodev/src/srv_ti/RCS/server.c,v $ * $Revision: 1.6 $ * $Date: 92/08/28 16:09:47 $ * $State: Exp $ * $Author: ark $ * $Locker: ark $ * * $Log: server.c,v $ * Revision 1.6 92/08/28 16:09:47 ark * Summer 1992 final version * * Revision 1.5 92/08/12 14:18:26 ark * New version of 'o' transaction tells what output format to use; * this is a hack to allow old (but broken) clients to still work. * * Revision 1.4 92/08/11 13:46:54 ark * Bug fixes: 'n' transaction leaving file descriptors open; * 'b' transaction now disallows searches on empty strings. * * Revision 1.3 92/08/06 18:44:32 ark * Admin command "B" no longer crashes when there are many connections. * * Revision 1.2 92/08/04 16:28:12 ark * Test production version 8/4/92 * * Revision 1.1 92/07/22 11:09:29 ark * Saber loads quietly; ANSI use standardized; command line options; no behavioral changes * * Revision 1.0 92/07/10 12:34:00 ark * Initial revision * * Revision 1.1 91/07/15 10:40:39 thorne * Initial revision * *------------------------------------------------------------------ */ #ifndef lint #ifndef SABER static char *rcsid_foo_c = "$Header: /afs/net.mit.edu/dapps/project/techinfodev/src/srv_ti/RCS/server.c,v 1.6 92/08/28 16:09:47 ark Exp Locker: ark $"; #endif #endif lint /* * 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. */ #include #include #include #include #include #include #include #include "network.h" #include "inet.h" /*#include "web.h" */ #include "pdb.h" /* #include Ulriix ??? */ CONN conntab[FD_SETSIZE]; fd_set read_fds; fd_set write_fds; fd_set except_fds; #define sock_readable(s) FD_ISSET(s, &read_fds) #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 datastruct_load(int unloadfirst); extern void save_datastruct(void); extern void remove_send(TOSEND *ptr); extern void remove_rec(TOREC *ptr); extern void remove_helper(WAITFORHELPER *ptr); extern void log_trans(char *str); extern void nio_rec_more(TOREC *ptr); extern void catch_child(void); extern void catch_hup(void); 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 : */ 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]; conn->c_socket = inet_accept_connection(listen_sock, &hostname, &conn->c_portnum); 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; active_socks = select(FD_SETSIZE,&read_fds, &write_fds, &except_fds, BLOCK); 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); } FD_ZERO(&read_fds); log_trans("Starting server"); printf("TechInfo server ready.\n"); fflush(stdout); num_connection = 0; signal(SIGPIPE, broken_pipe); umask(022); fflush(stdout); server_loop(); }