#include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <ctype.h> #include <time.h> #include <netdb.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/wait.h> #include <sys/stat.h> #include <sys/ipc.h> #include <sys/shm.h> #include <unistd.h> #include <netinet/in.h> #include <fcntl.h> #include <errno.h> #include <arpa/inet.h> #include "externs.h" #include "db.h" #include "config.h" #if defined(SYSV) || defined(HPUX) || defined(SYSV_MAYBE) #define mklinebuf(fp) setvbuf(fp, NULL, _IOLBF, 0) #else #define mklinebuf(fp) setlinebuf(fp) #endif static void init_args P((int, char *[])); static void init_io P((void)); static void set_signals P((void)); static void save_uptime P((void)); static signal_type bailout P((int)); static signal_type do_sig_reboot P((int)); static signal_type do_sig_shutdown P((int)); static void process_commands P((void)); static int do_command P((DDATA *, char *)); static void check_connect P((DDATA *, char *)); static void do_notify_connect P((OBJ *, char, time_t)); static void do_main_engine P((void)); static DDATA *new_connection P((void)); static int process_input P((DDATA *)); static void close_sockets P((void)); static void open_sockets P((void)); static DDATA *initializesock P((int, char *, int)); static void get_uptime P((void)); #ifdef CATCH_SIGSEGV static signal_type do_sig_segv P((int)); #endif /* CATCH_SIGSEGV */ DDATA *descriptor_list = NULL; unsigned long total_input = 0, total_output = 0; int exit_status; int shutdown_flag = 0; int quiet_reboot = 0; int num_reboots = 0; int num_crashes = 0; int con_since_reboot = 0; int con_since_boot = 0; time_t maze_up_time; time_t since_reboot; time_t next_dump; time_t next_mail_clear; time_t now; struct timed_reboot_struct timed_reboot = { 0, 0, "" }; static int sock = -1; static int topd = 0; static int sig_caught; static bool descriptor_list_modified; const char *NullFile = "logs/null"; extern int reserved; extern FILE *db_read_ptr; #ifdef USE_DEV_URANDOM extern int rand_fd; #endif /* USE_DEV_URANDOM */ fd_set input_set, output_set; static void free_everything() { extern char *swlm; stack_free(swlm); free_global_config(); free_mlists(); free_todomotd(); free_pages(); free_cmdav(); free_channels(); free_database(); } int main(int argc, char *argv[]) { read_global_config_file(); init_args(argc, argv); init_io(); printf("--------------------------------\n"); printf("MAZE online (pid = %d)\n", getpid()); init_timer(); open_sockets(); read_host_lockouts(); if(init_game(config.db_name) < 0) { log_error(tprintf("Couldn't load %s!", config.db_name)); exit_nicely(136); } set_signals(); time(&since_reboot); get_uptime(); now = time(NULL); next_dump = now+config.dump_interval; next_mail_clear = now+config.old_mail_interval; do_main_engine(); alarm(0); /* Shut off timers */ #ifdef USE_DEV_URANDOM if(rand_fd != -1) close(rand_fd); #endif /* USE_DEV_URANDOM */ if(exit_status == 1) /* Reboot */ { log_important("MAZE rebooting."); notify_all("MAZE rebooting.", NULL); } else { log_important("Shutting down normally."); notify_all("MAZE shutting down.", NULL); } flush_all_output(); close_sockets(); if(exit_status != 1) unlink("logs/socket_table"); do_haltall(root_obj); save_todomotd(0); save_todomotd(1); save_sc_funcs(); free_sc_funcs(); write_host_lockouts(); plugin_shutdown(); dump_database(); free_everything(); fcntl(sock, F_SETFD, 1); if(sig_caught > 0) log_important(tprintf("Shutting down due to signal %d", sig_caught)); if(exit_status == 1) /* Reboot */ { num_reboots++; close_logs(); save_uptime(); stack_report(); wait(0); execv("../bin/netmaze", argv); unlink("logs/socket_table"); _exit(exit_status); } stack_report(); exit_nicely(exit_status); return(0); } static void get_uptime() { FILE *fp; char buf[4096]; if((fp = fopen("logs/uptime", "r")) == NULL) { maze_up_time = time(NULL); return; } fgets(buf, sizeof(buf), fp); maze_up_time = atol(buf); fgets(buf, sizeof(buf), fp); quiet_reboot = atoi(buf); fgets(buf, sizeof(buf), fp); num_reboots = atoi(buf); fgets(buf, sizeof(buf), fp); num_crashes = atoi(buf); fgets(buf, sizeof(buf), fp); con_since_boot = atoi(buf); fclose(fp); unlink("logs/uptime"); } void save_uptime() { FILE *fp; if ( (fp = fopen("logs/uptime", "w")) == NULL ) return; fprintf(fp, "%ld\n", maze_up_time); fprintf(fp, "%d\n", quiet_reboot); fprintf(fp, "%d\n", num_reboots); fprintf(fp, "%d\n", num_crashes); fprintf(fp, "%d\n", con_since_boot); fclose(fp); } static void init_args(int argc, char *argv[]) { /* Change database? */ if(argc > 1) { --argc; stack_free(config.db_name); SET(config.db_name, *++argv); } /* Change port number? */ if(argc > 1) { --argc; config.inet_port = atoi(*++argv); } } static void init_io() { int fd; const char *stdout_logfile = "logs/out.log"; fclose(stdin); /* Open a link to the log file */ fd = open(stdout_logfile, O_WRONLY | O_CREAT | O_APPEND, 0644); if(fd < 0) { perror("open()"); log_error(tprintf("error opening %s for writing", stdout_logfile)); exit_nicely(136); } close(fileno(stdout)); if(dup2(fd, fileno(stdout)) == -1) { perror("dup2()"); log_error("error converting standard output to logfile"); } mklinebuf(stdout); /* Attempt to convert standard error to logfile */ close(fileno(stderr)); if(dup2(fd, fileno(stderr)) == -1) { perror("dup2()"); printf("error converting standard error to logfile\n"); } mklinebuf(stderr); /* This logfile reference is no longer needed */ close(fd); /* Save a file descriptor */ reserved = open(NullFile, O_RDWR, 0); } #ifndef BSD_REAP #ifndef SIGCLD static void handle_sigchld() { int status; while(wait3(&status, WNOHANG, 0) > 0); } #endif #endif static void set_signals() { signal(SIGINT, bailout); /* Comment these out so we get a core dump */ /* signal(SIGQUIT, bailout); signal(SIGILL, bailout); signal(SIGTRAP, bailout); signal(SIGIOT, bailout); signal(SIGEMT, bailout); signal(SIGFPE, bailout); signal(SIGBUS, bailout); signal(SIGSEGV, bailout); signal(SIGSYS, bailout); signal(SIGXCPU, bailout); signal(SIGXFSZ, bailout); signal(SIGVTALRM, bailout); signal(SIGUSR2, bailout); */ #ifdef BSD_REAP #ifdef SIGCLD signal(SIGCLD, handle_sigchld); #else signal(SIGCHLD, handle_sigchld); #endif #else #ifdef SIGCLD signal(SIGCLD, SIG_IGN); #else signal(SIGCHLD, handle_sigchld); #endif #endif /* Let's catch SIGSEGV and make it just reboot the place after a delay */ /* The delay is there because if it continuously crashes it would chew */ /* up an indefinite amount of resources without the delay. */ #ifdef CATCH_SIGSEGV signal(SIGSEGV, do_sig_segv); #endif /* CATCH_SIGSEGV */ /* Signal to reboot the MAZE */ signal(SIGUSR1, do_sig_reboot); signal(SIGHUP, do_sig_reboot); /* Signal to shutdown the MAZE */ signal(SIGTERM, do_sig_shutdown); } void raw_notify(OBJ *player, char *msg, bool new_line) { DDATA *d; for(d = descriptor_list;d;d = d->next) { if(check_state(d, STATE_CONNECTED) && d->player == player) { if(IS(player, TYPE_PLAYER, PLAYER_ANSI)) queue_string(d, tprintf("%s\33[37;0m", color(msg, ADD)), new_line); else queue_string(d, color(msg, REMOVE), new_line); } } } void flush_all_output() { DDATA *d; for(d = descriptor_list;d;d = d->next) process_output(d); } static void do_main_engine() { struct sockaddr_in servaddr; struct timeval timeout; fd_set input_set, output_set; int ready; DDATA *d, *dnext; int opt = 1; log_io(tprintf("Starting up on port %d", config.inet_port)); if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { log_error(tprintf("socket() - %s\n", strerror(errno))); exit(1); } if(setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0) { log_error(tprintf("setsockopt() - %s\n", strerror(errno))); exit(1); } if(sock >= topd) topd = sock+1; memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(config.inet_port); if(bind(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) { log_error(tprintf("bind() - %s\n", strerror(errno))); exit(1); } listen(sock, 5); load_db(); if(db_read_ptr) fclose(db_read_ptr); plugin_init(); plugin_startup(); /* Stick stuff to run once here */ while(!shutdown_flag) { descriptor_list_modified = 0; run_softcode(); process_commands(); if(shutdown_flag) break; /* Test for events */ dispatch(); FD_ZERO(&input_set); FD_ZERO(&output_set); FD_SET(sock, &input_set); for(d = descriptor_list;d;d = d->next) { FD_SET(d->descriptor, &input_set); if(d->output) FD_SET(d->descriptor, &output_set); } now = time(NULL); /* Setup timeout for select() */ /* If there's softcode running make the timeout almost nothing */ timeout.tv_sec = (code_list)?0:1; timeout.tv_usec = 1; if((ready = select(topd, &input_set, &output_set, NULL, &timeout)) <= 0) { if(ready < 0) if(errno != EINTR) log_error(tprintf("select() - %s\n", strerror(errno))); continue; } now = time(NULL); /* sock will be in the input set if there's a new connection */ if(FD_ISSET(sock, &input_set)) if(!(d = new_connection())) continue; for(d = descriptor_list;d;d = dnext) { dnext = d->next; if(FD_ISSET(d->descriptor, &input_set)) if(!process_input(d)) { shutdownsock(d); continue; } if(FD_ISSET(d->descriptor, &output_set)) if(!process_output(d)) shutdownsock(d); } } } static bool chk_max_users(int sock) { DDATA *d; int ctr = 0; char *msg = tprintf("Sorry, the maximum of %d users has been reached.\r\n", config.max_users); if(!config.max_users) return(1); for(d = descriptor_list;d;d = d->next) ctr++; if(ctr >= config.max_users) { write(sock, msg, strlen(msg)); log_io("New connection was denied access. max_users has been reached."); close(sock); return(0); } return(1); } DDATA *new_connection() { struct sockaddr_in servaddr; int size = sizeof(struct sockaddr_in); DDATA *newd; int newsock; char *s; if((newsock = accept(sock, (struct sockaddr *)&servaddr, &size)) < 0) { log_error(tprintf("accept() - %s\n", strerror(errno))); return((DDATA *)NULL); } if(!chk_max_users(newsock)) return(NULL); if(!chk_host_lockout(newsock, inet_ntoa(servaddr.sin_addr))) return(NULL); newd = initializesock(newsock, inet_ntoa(servaddr.sin_addr), STATE_WAITCONNECT); s = ctime(&now); *(s+strlen(s)-1) = '\0'; log_io(tprintf("CONNECT: descriptor %d, host %s@%s, time: %s", newd->descriptor, newd->user, newd->addr, s)); return(newd); } static int process_input(DDATA *d) { char buf[1024], input[1024]; int got; char *p, *i; struct io_queue *io, *newio; if((got = read(d->descriptor, buf, sizeof(buf))) <= 0) return(0); if(got >= MAX_COMMAND_LEN) *(buf+MAX_COMMAND_LEN) = '\0'; for(p = buf, i = input;*p && p-buf <= got;p++) { if(isprint(*p)) { *i++ = *p; continue; } if(*p != '\n') continue; *i = '\0'; newio = (struct io_queue *)stack_alloc(sizeof(struct io_queue), 1, 0); newio->next = NULL; if(!d->input) d->input = newio; else { for(io = d->input;io->next;io = io->next); io->next = newio; } SET(newio->text, input); total_input += strlen(input); d->total_input += strlen(input); i = input; while(*(p+1) && !isprint(*(p+1)) && *p != '\n') p++; } return(1); } float get_input(DDATA *d) { if(!d) return((float)total_input/(float)(now-since_reboot)); return((float)(d->total_input)/(float)(now-since_reboot)); } float get_output(DDATA *d) { if(!d) return((float)total_output/(float)(now-since_reboot)); return((float)(d->total_output)/(float)(now-since_reboot)); } bool process_output(DDATA *d) { struct io_queue *io, *ionext; char *flushed_msg = "*** OUTPUT FLUSHED ***\r\n"; /* Hold their output if they're in a different mode (i.e. @paste) */ if(!d->output || d->mode != MODE_NORMAL) return(1); if(d->flushed) { write(d->descriptor, flushed_msg, strlen(flushed_msg)); total_output += strlen(flushed_msg); d->total_output += strlen(flushed_msg); d->flushed = 0; } for(io = d->output;io;io = ionext) { ionext = io->next; if(config.max_server_io) if(get_input(NULL)+get_output(NULL) > (float)(config.max_server_io)) { d->output = io; return(1); } if(config.max_player_io) if(get_input(d)+get_output(d) > (float)(config.max_player_io)) { d->output = io; return(1); } if(write(d->descriptor, io->text, strlen(io->text)) < 0) { if(errno == EWOULDBLOCK) return(1); return(0); } total_output += strlen(io->text); d->total_output += strlen(io->text); d->output_size -= strlen(io->text); stack_free(io->text); stack_free(io); } d->output = NULL; return(1); } static unsigned long output_queue_size(DDATA *d) { struct io_queue *io; unsigned long total = 0L; for(io = d->output;io;io = io->next) total += strlen(io->text); return(total); } void queue_string(DDATA *d, char *str, bool newline) { struct io_queue *io, *newio; char buf[4096]; if(d->flushed || (output_queue_size(d)+strlen(str)+2 > config.max_output && d->output)) { d->output_size -= strlen(d->output->text); stack_free(d->output->text); newio = d->output; d->output = d->output->next; d->flushed = 1; } else newio = (struct io_queue *)stack_alloc(sizeof(struct io_queue), 1, 0); newio->next = NULL; if(!d->output) d->output = newio; else { for(io = d->output;io->next;io = io->next); io->next = newio; } sprintf(buf, "%s%s", str, (newline)?"\r\n":""); SET(newio->text, buf); d->output_size += strlen(buf); } static void free_io_queue(DDATA *des) { struct io_queue *ionext; while(des->input) { ionext = des->input->next; stack_free(des->input->text); stack_free(des->input); des->input = ionext; } while(des->output) { ionext = des->output->next; stack_free(des->output->text); stack_free(des->output); des->output = ionext; } } static void hostname_lookup_shutdown(DDATA *d) { if(d->lookup_pid) { kill(d->lookup_pid, SIGKILL); shmdt((char *)d->update_flag); shmdt(d->user); shmdt(d->addr); } else { stack_free(d->update_flag); stack_free(d->user); stack_free(d->addr); } } void shutdownsock(DDATA *des) { OBJ *guest_player = NULL; DDATA *d, *dprev = NULL; descriptor_list_modified = 1; free_io_queue(des); if(des->lookup_pid) { log_io(tprintf("Killing hostname lookup for descriptor %d (pid %d)", des->descriptor, des->lookup_pid)); kill(des->lookup_pid, SIGKILL); } if(check_state(des, STATE_CONNECTED)) { if(des->player) { if(Guest(des->player)) guest_player = des->player; log_io(tprintf("DISCONNECT descriptor %d player %s. Session time: %s", des->descriptor, unparse_object(des->player, des->player), format_time(now-des->connected_at))); if(!(*atr_get(des->player, "LHIDE"))) com_send(config.connect_channel, NULL, tprintf("* DISCONNECT: %s(#%d)", name(des->player), des->player->dbref)); com_send(config.adm_connect_channel, NULL, tprintf("* DISCONNECT: %s(#%d) (descriptor %d)", name(des->player), des->player->dbref, des->descriptor)); announce_disconnect(des); } } else { log_io(tprintf("DISCONNECT: descriptor %d never connected", des->descriptor)); if(des->create_info) { stack_free(des->create_info->name); if(des->create_info->password) { stack_free(des->create_info->password); if(des->create_info->passverify) stack_free(des->create_info->passverify); } stack_free(des->create_info); } } if(FD_ISSET(des->descriptor, &input_set)) FD_CLR(des->descriptor, &input_set); if(FD_ISSET(des->descriptor, &output_set)) FD_CLR(des->descriptor, &output_set); free_hashtable(des); /* Remove descriptor from descriptor_list */ for(d = descriptor_list;d;d = d->next) { if(d == des) break; dprev = d; } if(!d) return; if(dprev) dprev->next = d->next; else descriptor_list = d->next; hostname_lookup_shutdown(des); close(des->descriptor); stack_free(des); if(!guest_player) return; for(d = descriptor_list;d;d = d->next) if(d->player && d->player == guest_player) break; if(!d) destroy_guest(guest_player); } static void close_sockets() { FILE *fp; DDATA *d, *dnext; if(!(fp = fopen("logs/socket_table", "w"))) return; /* Screwed */ fprintf(fp, "%d\n", sock); fcntl(sock, F_SETFD, 0); /* Make it so sock doesn't close */ for(d = descriptor_list;d;d = dnext) { dnext = d->next; if((check_state(d, STATE_CONNECTED) || check_state(d, STATE_WAITINPUT)) && d->player) { fprintf(fp, "%010d %010d %010ld %010ld %010d\n", d->descriptor, d->mode, d->connected_at, d->last_time, d->player->dbref); fcntl(d->descriptor, F_SETFD, 0); /* Don't close this */ hostname_lookup_shutdown(d); free_hashtable(d); stack_free(d); } else shutdownsock(d); } descriptor_list = NULL; fclose(fp); } static void open_sockets() { FILE *fp; char buf[4096]; DDATA *d, *nextd, *oldlist; int desc; struct sockaddr_in in; int len; if(!(fp = fopen("logs/socket_table", "r"))) return; /* Must not be a reboot */ fgets(buf, sizeof(buf), fp); sock = atoi(buf); fcntl(sock, F_SETFD, 1); /* Return it to its normal state */ while(fgets(buf, sizeof(buf), fp)) { desc = atoi(buf); len = sizeof(struct sockaddr_in); fcntl(desc, F_SETFD, 1); /* Return it to its normal state */ getpeername(desc, (struct sockaddr *)&in, &len); d = initializesock(desc, inet_ntoa(in.sin_addr), STATE_RELOADCONNECT); d->mode = atoi(buf+11); d->connected_at = atol(buf+22); d->last_time = atol(buf+33); d->player_ref = atoi(buf+44); } /* Flip the descriptor list back the right way */ oldlist = descriptor_list; descriptor_list = NULL; for(d = oldlist;d;d = nextd) { nextd = d->next; d->next = descriptor_list; descriptor_list = d; } close(sock); /* do_main_engine() will open this back up */ fclose(fp); unlink("logs/socket_table"); } static void ip_to_hostname(char *hostname) { struct hostent *hent; struct in_addr addr; if(!inet_aton(hostname, &addr)) return; if((hent = gethostbyaddr((char *)&(addr.s_addr), sizeof(addr.s_addr), AF_INET))) { /* At this point 'hostname' points to 100 bytes of shared memory */ strncpy(hostname, hent->h_name, 99); hostname[99] = '\0'; } } static void lookup_hostname(DDATA *d) { int shared_mem_id1, shared_mem_id2, shared_mem_id3; char *shared_mem_ptr1, *shared_mem_ptr2; int *shared_mem_ptr3; int *des, *flg; char *temp; struct shmid_ds ds; shared_mem_id1 = shmget(IPC_PRIVATE, sizeof(char)*100, 0660); shared_mem_id2 = shmget(IPC_PRIVATE, sizeof(char)*100, 0660); shared_mem_id3 = shmget(IPC_PRIVATE, sizeof(int)*2, 0660); if(shared_mem_id1 == -1 || shared_mem_id2 == -1 || shared_mem_id3 == -1) { log_error("Couldn't allocate shared memory!"); if(shared_mem_id3 == -1) { shmctl(shared_mem_id1, IPC_RMID, &ds); shmctl(shared_mem_id2, IPC_RMID, &ds); } if(shared_mem_id2 == -1) shmctl(shared_mem_id1, IPC_RMID, &ds); *(d->update_flag) = 0; return; } shared_mem_ptr1 = shmat(shared_mem_id1, 0, 0); shared_mem_ptr2 = shmat(shared_mem_id2, 0, 0); shared_mem_ptr3 = (int *)shmat(shared_mem_id3, 0, 0); temp = d->addr; d->addr = shared_mem_ptr1; strcpy(d->addr, temp); stack_free(temp); temp = d->user; d->user = shared_mem_ptr2; strcpy(d->user, temp); stack_free(temp); flg = d->update_flag; d->update_flag = shared_mem_ptr3; *(d->update_flag) = *flg; stack_free(flg); des = shared_mem_ptr3+1; *des = d->descriptor; if(!(d->lookup_pid = fork())) { ip_to_hostname(d->addr); strcpy(d->user, ident_id(*des, 60)); if(*(d->update_flag) != -1) *(d->update_flag) = 1; exit(0); } shmctl(shared_mem_id1, IPC_RMID, &ds); shmctl(shared_mem_id2, IPC_RMID, &ds); shmctl(shared_mem_id3, IPC_RMID, &ds); } DDATA *initializesock(int des, char *addr, int state) { DDATA *d; d = (DDATA *)stack_alloc(sizeof(DDATA), 1, 0); d->descriptor = des; d->state = find_state(state); d->create_info = NULL; d->mode = MODE_NORMAL; /* Calling function is responsible */ d->input = NULL; d->output = NULL; d->total_input = 0; d->total_output = 0; d->output_size = 0; d->flushed = 0; d->player = NULL; SET(d->user, "???"); SET(d->addr, addr); d->update_flag = (int *)stack_alloc(sizeof(int), 1, 0); *(d->update_flag) = (state == STATE_RELOADCONNECT)?-1:0; d->lookup_pid = 0; d->connected_at = time(0); d->last_time = time(0); d->hashtable = NULL; fcntl(des, F_SETFL, O_NONBLOCK); d->next = descriptor_list; descriptor_list = d; if(des >= topd) topd = des+1; lookup_hostname(d); if(d->state->func) d->state->func(d, ""); return(d); } void show_file(DDATA *d, char *filename) { FILE *fp; char buf[4096]; if(!(fp = fopen(filename, "r"))) return; while(fgets(buf, sizeof(buf), fp)) { *last_char(buf) = '\0'; queue_string(d, color(tprintf("%s|n|", buf), ADD), 1); } fclose(fp); } static void process_commands() { DDATA *d, *dnext; int nprocessed; struct io_queue *io, *ionext; bool cont; do { nprocessed = 0; for(d = descriptor_list;d;d = dnext) { cont = 0; if(!d->input) { dnext = d->next; continue; } for(io = d->input;io;io = ionext) { ionext = io->next; nprocessed++; if(!do_command(d, io->text)) { show_file(d, config.leave_msg_file); shutdownsock(d); /* This should flag descriptor_list_modified */ } if(descriptor_list_modified) { descriptor_list_modified = 0; dnext = descriptor_list; cont = 1; break; } stack_free(io->text); stack_free(io); } /* Clear the stack after every command */ stack_unalloc(); if(cont) continue; dnext = d->next; d->input = NULL; } } while(nprocessed); } static int do_command(DDATA *d, char *command) { time_t was_idle = now-d->last_time; if(is_pasting(d)) { add_more_paste(d, command); return(1); } if(!*command) return(1); d->last_time = now; /* See if what they typed should be regarded as input */ if(check_input(d, command)) return(1); if(!strcmp(command, QUIT_COMMAND)) return(0); if(!check_state(d, STATE_CONNECTED)) { check_connect(d, command); return(1); } if(was_idle > 1800) do_notify_connect(d->player, 1, was_idle); process_command(d, d->player, command, NULL); if(was_idle > 300) check_mail(d->player, 1); return(1); } int check_password(OBJ *player, char *pass) { if(!strcmp(Pass(player), pass) || !strcmp(crypt(pass, "XX"), Pass(player))) { return(1); } return(0); } void emergency_shutdown() { log_error("Emergency shutdown."); shutdown_flag = 1; exit_status = 136; close_sockets(); } static void check_connect(DDATA *d, char *msg) { OBJ *player; int notified = 0; int connects; if(!d->state->func || !d->state->func(d, msg)) return; player = d->player; con_since_boot++; con_since_reboot++; log_io(tprintf("CONNECTED: %s%s on descriptor %d%s", name(player), (Guest(player))?" |+W|(GUEST)|n|":"", d->descriptor, (*atr_get(player, "LHIDE"))?" |+W|(HIDDEN)":"")); if(!(*atr_get(player, "LHIDE"))) com_send(config.connect_channel, NULL, tprintf("* CONNECT: %s(#%d)", name(player), player->dbref)); com_send(config.adm_connect_channel, NULL, tprintf("* CONNECT: %s(#%d) (descriptor %d, host %s@%s)%s", name(player), player->dbref, d->descriptor, d->user, d->addr, (*atr_get(player, "LHIDE"))?" |+W|(HIDDEN)":"")); /* Stop multiple connections */ if(get_class(player) == CLASS_CITIZEN) { DDATA *d2, *d2next; for(d2 = descriptor_list;d2;d2 = d2next) { d2next = d2->next; if(d2 == d) continue; if(check_state(d2, STATE_CONNECTED)) continue; if(d2->player == player) { set_state(d, STATE_CONNECTED); d->player = player; do_notify_connect(player, 0, 0); notified++; queue_string(d2, "You're getting booted!", 1); process_output(d2); shutdownsock(d2); } } } set_state(d, STATE_CONNECTED); d->connected_at = now; if(!notified) do_notify_connect(player, 0, 0); /* Give players a message on connection */ show_file(d, config.connect_msg_file); announce_connect(d); if(!(Guest(player))) { my_atr_add(player, "CONNECTS", 1); /* Show the player the +motd */ /* if(num_motd) if(atoi(atr_get(player, "MOTD"))) { notify(player, ""); do_motd(player, "", ""); notify(player, ""); } */ connects = atoi(atr_get(player, "CONNECTS")); if(connects == 1) notify(player, "This is your first time here. Welcome!"); else notify(player, tprintf("You've connected %d times. Welcome back!", connects)); /* Notify player if they have mail */ notify(player, ""); check_mail(player, 0); notify(player, ""); } else { notify(player, tprintf("Welcome to %s; your name is %s", config.maze_name, player->name)); } do_look_around(player); } int boot_off(OBJ *player) { DDATA *d; for(d = descriptor_list;d;d = d->next) { if(check_state(d, STATE_CONNECTED) && d->player == player) { process_output(d); shutdownsock(d); return(1); } } return(0); } static signal_type bailout(int sig) { char message[1024]; sprintf(message, "BAILOUT: caught signal %d", sig); panic(message); exit_nicely(136); return; } #ifdef CATCH_SIGSEGV static signal_type do_sig_segv(int i) { char *argv[] = {"../bin/netmaze", NULL}; log_sensitive("SIGSEGV: Server crash. rebooting server in 10 seconds."); notify_all(tprintf("Server crash. %s rebooting in 10 seconds.", config.maze_name), NULL); log_important("MAZE rebooting."); notify_all("MAZE rebooting.", NULL); log_important(tprintf("Shutting down due to signal %d", i)); flush_all_output(); alarm(0); /* Shut off timers */ sleep(10); /* Wait 10 seconds */ close_sockets(); do_haltall(root_obj); save_todomotd(0); save_todomotd(1); save_sc_funcs(); free_sc_funcs(); dump_database(); free_everything(); fcntl(sock, F_SETFD, 1); quiet_reboot = 1; num_crashes++; close_logs(); save_uptime(); /* Do this to dump a core */ if(!fork()) { signal(SIGSEGV, SIG_DFL); kill(getpid(), SIGSEGV); exit(0); } wait(0); execv("../bin/netmaze", argv); unlink("logs/socket_table"); _exit(exit_status); } #endif /* CATCH_SIGSEGV */ static signal_type do_sig_reboot(int i) { log_sensitive("REBOOT: by external source"); log_important("REBOOT: by external source"); exit_status = 1; shutdown_flag = 1; return; } static signal_type do_sig_shutdown(int i) { log_sensitive("SHUTDOWN: by external source"); log_important("SHUTDOWN: by external source"); exit_status = 0; shutdown_flag = 1; return; } static void do_notify_connect(OBJ *player, char flag, time_t idle) { OBJ *o; char *p, *b; char buf[2048]; int found = 0; char *str; DDATA *d; if(flag == 1) { log_important(tprintf("%s unidled after %s", name(player), format_time(idle))); str = "|+W|Unidle:"; } else if(flag == 2) { for(d = descriptor_list;d;d = d->next) if(check_state(d, STATE_CONNECTED)) if(d->player == player) found++; /* Don't notify anyone if they're connected more than once */ if(found > 1) return; str = "|+W|Disconnect:"; } else { for(d = descriptor_list;d;d = d->next) if(check_state(d, STATE_CONNECTED)) if(d->player == player) found++; if(found > 1) str = "|+W|Reconnect:"; else str = "|+W|Connect:"; } for(o = player_list;o;o = o->next) { if(o == player) continue; found = 0; p = atr_get(o, "NOTIFY"); if(!string_compare(p, "all")) { if(could_doit(o, player, "LHIDE")) found = 1; } else { while(*p) { b = buf; while(*p && isspace(*p)) p++; while(*p && !isspace(*p)) *b++ = *p++; *b = '\0'; if(!*buf) break; if(match_player(NULL, buf) == player) { if(could_doit(o, player, "LHIDE") && !controls(player, o, POW_WHO)) found = 1; break; } } } if(found) { if(idle) notify(o, tprintf("|+W|%s %s |+W|(%s)", str, name(player), format_time(idle))); else notify(o, tprintf("|+W|%s %s", str, name(player))); } } } void announce_connect(DDATA *d) { OBJ *player = d->player; OBJ *loc = player->location; char *s, *t; time_t tt; int connect_again = 0; DDATA *des; plugin_login(d); /* Tell everyone a new player has connected. */ if(!atoi(atr_get(player, "CONNECTS"))) { if(!Guest(player)) { notify_all("|C+|Welcome a new lamb to the slaughter!", player); notify_all(tprintf("|W+|-- |B+|%s has entered the realm of %s.", name(player), config.maze_name), player); } } for(des = descriptor_list;des;des = des->next) if(des == d) connect_again++; if(!(player->flags & PLAYER_INVISIBLE)) { if(connect_again > 1) notify_in(loc, player, tprintf("%s has reconnected.", name(player))); else notify_in(loc, player, tprintf("%s has connected.", name(player))); } if(!Guest(player)) { tt = atol(atr_get(player, "LASTCONN")); if(tt == 0L) s = "no previous login"; else { s = ctime(&tt); s[strlen(s)-1] = 0; } if(*atr_get(player, "LASTSITE")) t = tprintf(" |+W|from |+Y|%s", atr_get(player, "LASTSITE")); else t = ""; notify(player, tprintf("|+B|Last login|+W|: |+C|%s%s", s, t)); if(*atr_get(player, "BADLOGINS")) { t = atr_get(player, "BADLOGINS"); notify(player, "\n|+R|There were failed login attempts on your character|+W|:"); while((s = parse_up(&t, ' '))) notify(player, tprintf("|R|%s", s)); notify(player, ""); atr_clr(player, "BADLOGINS"); } tt = now; atr_add(player, "LASTCONN", tprintf("%ld", tt)); /* Do this regardless of d->update_flag. If the hostname is still */ /* updating, this will temporarily fill it with their IP address and */ /* be later updated with their userid and hostname by */ /* chk_hostname_update() */ atr_add(player, "LASTSITE", tprintf("%s@%s", d->user, d->addr)); } if(!connect_again) { did_it(player, player, NULL, NULL, "PCONN"); did_it(player, player->location, NULL, NULL, "PCONN"); } } void announce_disconnect(DDATA *des) { OBJ *player = des->player; OBJ *loc = player->location; int num; char buf[4096]; DDATA *d; int partial_disconnect; plugin_logout(des); for(num = 0, d = descriptor_list;d;d = d->next) if(check_state(d, STATE_CONNECTED) && d->player && d->player == player) num++; if(num < 3 && !shutdown_flag) partial_disconnect = 0; else partial_disconnect = 1; atr_add(player, "LASTDISC", tprintf("%ld", now)); do_notify_connect(player, 2, 0); if(is_pasting(des)) remove_paste(des); if(partial_disconnect) sprintf(buf, "%s has partially disconnected.", name(player)); else sprintf(buf, "%s has disconnected.", name(player)); if(!(player->flags & PLAYER_INVISIBLE)) notify_in(loc, player, buf); if(!partial_disconnect) { did_it(player, player, NULL, NULL, "PDISC"); did_it(player, player->location, NULL, NULL, "PDISC"); } } void info_io(OBJ *player) { float throughput; char buf[4096]; notify(player, "|+Y|I/O stats since last reboot:"); if(now-since_reboot == 0) throughput = 0; else throughput = (float)total_input/(float)(now-since_reboot); notify(player, tprintf("|+B|Total Input|+W|: |+C|%s bytes |+W|(%s bytes/second)", comma(tprintf("%ld", total_input)), comma(tprintf("%.2f", throughput)))); if(now-since_reboot == 0) throughput = 0; else throughput = (float)total_output/(float)(now-since_reboot); notify(player, tprintf("|+B|Total Output|+W|: |+C|%s bytes |+W|(%s bytes/second)", comma(tprintf("%ld", total_output)), comma(tprintf("%.2f", throughput)))); if(config.max_server_io) sprintf(buf, "%s bytes/second", comma(tprintf("%d", config.max_server_io))); else strcpy(buf, "NO LIMIT"); notify(player, tprintf("\n|+B|Maximum Server Throughput|+W|: |+C|%s", buf)); if(config.max_player_io) sprintf(buf, "%s bytes/second", comma(tprintf("%d", config.max_player_io))); else strcpy(buf, "NO LIMIT"); notify(player, tprintf("|+B|Maximum Player Throughput|+W|: |+C|%s", buf)); } void chk_hostname_update() { DDATA *d; char *newuser, *newaddr; int *newflag; for(d = descriptor_list;d;d = d->next) { if(*(d->update_flag) != 0) { if(*(d->update_flag) == 1) { if(check_state(d, STATE_CONNECTED)) log_io(tprintf("Host for %s (descriptor %d): %s@%s", name(d->player), d->descriptor, d->user, d->addr)); else log_io(tprintf("Host for descriptor %d: %s@%s", d->descriptor, d->user, d->addr)); } *(d->update_flag) = 0; d->lookup_pid = 0; /* Free the shared memory areas */ SET(newuser, d->user); shmdt(d->user); d->user = newuser; SET(newaddr, d->addr); shmdt(d->addr); d->addr = newaddr; newflag = (int *)stack_alloc(sizeof(int), 1, 0); *newflag = *(d->update_flag); shmdt((char *)d->update_flag); d->update_flag = newflag; if(d->player && !Guest(d->player)) atr_add(d->player, "LASTSITE", tprintf("%s@%s", d->user, d->addr)); } } }