/**************************************************************** * C-Dirt 3.0beta3 * * (C) 1999 G. Castrataro * ****************************************************************/ #include "main.h" void save_pid(void) { char pidfile[50]; FILE *pidptr; sprintf (pidfile, "%s/%s.%d", data_dir, PID_FILE, mud_port); if ((pidptr = FOPEN(pidfile, "w"))) { fprintf(pidptr, "%d\n", getpid()); FCLOSE(pidptr); } } void rm_pid(void) { char pidfile[50]; sprintf(pidfile, "%s/%s.%d", data_dir, PID_FILE, mud_port); unlink(pidfile); } int main (int argc, char **argv, char **ep) { int x; envp = ep; progname = argv[0]; srand48(time(NULL)); /* init random numbers */ srand((int) time(NULL)); get_options (argc, argv); /* Parse command line */ if (!old_proc_num) fprintf(stderr, "%s Daemon Loading", VERSION); if (!quiet) { if (data_dir == NULL) { fprintf (stderr, " C-Dirt Daemon Error: data_dir is a NULL value.\n"); fprintf (stderr, " Halting Daemon!\n"); exit (1); } fprintf (stderr, "\n Data Directory: %s\n", data_dir); fprintf (stderr, " Maximum Players: %d\n", max_players); fprintf (stderr, " Port Selected: %d\n", mud_port); fprintf (stderr, " Clear System Log: %s\n", clear_syslog_file ?"Yes":"No"); fprintf (stderr, " Kill Other MUD: %s\n", kill_other_mud ?"Yes":"Ask User"); } chdir (data_dir); for (x = 0 ; x < MAX_FDS ; x++) sock_fds[x] = -1; if (!old_proc_num) { check_pid(); x = xmain (); } else x = xmain_reboot (); rm_pid(); if (x < 0) mudlog ("BOOTUP: Abnormal termination of mud"); else mudlog ("BOOTUP: Normal termination of mud"); return(0); } int msock(int port) { int s; struct sockaddr_in server; struct hostent *h; int opt; s = socket(AF_INET, SOCK_STREAM, 0); if (s == -1) { fprintf(stderr, "(Bootup): Error creating stream socket\n"); exit(1); } opt = 1; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) == -1) { fprintf(stderr, "(Bootup): Error in setsockopt\n"); exit(2); } if (!(h = gethostbyname(_HOSTNAME_))) { fprintf(stderr, "Unable to bind to hostname you chose. Run configure.\n"); exit(3); } server.sin_family = AF_INET; server.sin_port = htons (port); bcopy (h->h_addr_list[0], &(server.sin_addr), h->h_length); if (bind (s, (struct sockaddr *) & server, sizeof (server))) { fprintf(stderr, "(Bootup): Error binding stream socket\n"); close(s); exit(4); } listen (s, 5); return s; } int xmain () { if (open_logfile(clear_syslog_file) || bootstrap() || go_background()) return(-1); main_socket = msock(mud_port); numresets = 0; numreboots = 0; numcrashes = 0; /* Initialize MUD Daemon Startup Time */ time (&last_startup); /* Main program loop */ main_loop(main_socket); mudlog ("&+WSYSTEM:&N Closing listening socket"); close(main_socket); return(0); } /* BSD style daemonizing */ int go_background () { int pid; if (!stay_foreground) { if ((pid = fork())) { fflush (stdout); exit(3); } fclose (stdin); fclose (stdout); #ifdef _LINUX_ setpgrp (); #else setpgrp (pid, pid); #endif } save_pid(); setsignals(); return(0); } /* check if the pid exists, if so, ask to kill it */ void check_pid(void) { FILE *pidptr; char c; int oldpid; char pidfile[50]; sprintf (pidfile, "%s/%s.%d", data_dir, PID_FILE, mud_port); if (!(pidptr = FOPEN(pidfile, "r"))) return; fscanf(pidptr, "%d", &oldpid); FCLOSE(pidptr); if (kill(oldpid, SIGCONT) == -1) { if (errno == EPERM) { fprintf(stderr, "...\nAberd already running under a different user.\n"); exit(4); } } else { fprintf(stderr, "...\nAberd is already running, kill it? "); if (tolower(c = getchar()) == 'y') { kill(oldpid, SIGTERM); while(!access(pidfile, F_OK)) /* sleep until other mud dies */ sleep(1); fprintf(stderr, " Other MUD killed, loading"); } else { fprintf(stderr, "Aborted aberd loading.\n"); exit(5); } } } void get_options (int argc, char **argv) { char *s; int x; if (argc == 1) { stay_foreground = False; clear_syslog_file = False; mud_port = PORT; max_players = 32; return; } while (--argc > 0) { s = *++argv; if (*s++ != '-') { usage (); exit (0); } x = *s++; switch (x) { case 'a': break; case 'h': usage (); exit(0); case 'H': fullusage (); exit(0); case 'p': if (*s != '\0' || (--argc > 0 && *(s = *++argv) != '\0')) { if ((mud_port = atoi (s)) < 1000 || mud_port > 65535) { mud_port = PORT + 5; } } break; case 'f': stay_foreground = True; break; case 'k': kill_other_mud = True; break; case 'c': clear_syslog_file = True; break; case 'v': quiet = False; break; case 'V': printf ("\nC-Dirt Version Information\n"); printf ("-------------------------\n"); printf ("%s (%s/%s)\n", VERSION, _ARCH_, _OS_); printf ("1999, G. Castrataro\n\n"); printf ("See credits for contributions\n"); exit (0); break; case 'u': update = 1; case 'r': if (*s != '\0' || (--argc > 0 && *(s = *++argv) != '\0')) old_proc_num = atoi (s); break; case 'n': if (*s != '\0' || (--argc > 0 && *(s = *++argv) != '\0')) { if ((max_players = atoi (s)) < 1 || max_players > 1000) { max_players = 32; } } break; case 'd': if (*s != '\0' || (--argc > 0 && *(s = *++argv) != '\0')) { data_dir = s; } break; default: usage (); exit (6); } } if (argc > 0) { usage (); exit (7); } } void usage (void) { fprintf (stderr, "usage: %s [-p port] [-d path] [-n #] [-f] [-k] [-c] [-v] [-V] [-o] [-H]\n", progname); fprintf (stderr, "(%s -H for detailed help)\n", progname); } void fullusage (void) { fprintf (stderr, "usage: %s [-p port] [-d path] [-n #] [-f] [-k] [-c] [-v] [-V] [-o] [-H]\n", progname); fprintf (stderr, " -p port : Alternate port to attach C-Dirt to.\n"); fprintf (stderr, " -d path : Path to data files.\n"); fprintf (stderr, " -n # : Maximum number of players (Default is 32).\n"); fprintf (stderr, " -f : Run C-Dirt in the foreground.\n"); fprintf (stderr, " -k : Automatically kill any other C-Dirt Daemons that are running.\n"); fprintf (stderr, " -c : Automatically clear system log.\n"); fprintf (stderr, " -v : Run in verbose startup mode.\n"); fprintf (stderr, " -V : Display version information.\n"); fprintf (stderr, " -o : Automatically open MUD.\n\n"); } Boolean is_identing(int fd) { int i; for (i = 0 ; i < max_players ; i++) if (resfd(i) == fd) return(True); return(False); } void set_fd(int fd, Boolean output) { if (fd == -1) return; if (output) FD_SET(fd, &output_set); if (fd >= width) width = fd + 1; FD_SET(fd, &input_set); } extern void dnsboot(void); void main_loop (int listen_socket) { int i, fd, plx, fds; struct timeval timeout, slice; time (&global_clock); time (&last_autosave); time (&last_healall); breset = False; if (!old_proc_num) mudlog ("&+WSYSTEM:&N C-Dirt %s Booted (PID: %d)", VERSION, getpid()); else mudlog ("&+WSYSTEM:&N %s Successful, C-Dirt Daemon Restarted", update ? "Update" : "Reboot"); if (clear_syslog_file) mudlog ("&+WSYSTEM:&N System Log Cleared"); if (!update) { last_reset = global_clock; qdresetflgs(); for (i = 0; i < numzon; i++) { move_pouncie (); reset_zone (i); } scatter_potions(); } setup_globals(-1); timeout.tv_sec = 2; timeout.tv_usec = 0; #ifdef ABERCHAT if (mud_port == PORT) /* do not run on test mud */ aberchat_boot(); #endif dnsboot(); while (1) { FD_ZERO(&input_set); /* zero out all fd_sets */ FD_ZERO(&output_set); FD_ZERO(&exception_set); set_fd(listen_socket, False); set_fd(aberfd, aber_output); set_fd(dnsfd, dns_output); for (plx = 0 ; plx < max_players ; plx++) /* add player fds */ if (is_conn(plx) && !linkdead(plx)) { if (resolved(plx) && ident(plx) && limbo(plx)) enter_game(plx); set_fd(fildes(plx), output(plx)); if (!ident(plx)) set_fd(players[plx].resfd, players[plx].respos != -1); } if (crashing) flush_mud(False); fds = select(width, &input_set, &output_set, &exception_set, &timeout); if (fds == -1) { if (errno == EINTR) continue; else if (errno == EBADF) { progerror ("select"); rm_pid(); exit(8); } } gettimeofday(&slice, (struct timezone *) NULL); timeout.tv_usec = 1000000 - slice.tv_usec; timeout.tv_sec = (slice.tv_sec + 1) % 2; if (!fds) { on_timer(); continue; } for (fd = 0 ; fds > 0; fd++) { if (FD_ISSET (fd, &exception_set)) { mudlog ("SOCKET: Exception pending with fd = %d", fd); fds--; } if (FD_ISSET (fd, &input_set)) { if (fd == listen_socket) new_connection(fd); else if (fd == aberfd) aberchat_readpacket(fd); else if (fd == dnsfd) read_dns(fd); else if (is_identing(fd)) read_ident(fd); else read_packet(fd); fds--; } if (FD_ISSET (fd, &output_set)) { if (fd == aberfd) aberchat_writepacket(fd); else if (fd == dnsfd) send_dns(fd); else if (is_identing(fd)) send_ident(fd); else write_packet(fd); fds--; } } } } void write_packet(int fd) { int len, n_wrote, me; me = find_pl_index(fd); if (me < 0) return; len = out_write(me) - out_read(me); if (len > M_BUFFLEN) len = M_BUFFLEN; n_wrote = write(fd, out_read(me), len); #ifdef IO_STATS bytes_sent += n_wrote; #endif if (*(out_read(me) + n_wrote)) /* more bytes to send */ out_read(me) += n_wrote; else { if (out_size(me) > M_BUFFLEN) { /* shrink buffer */ FREE(out_buffer(me)); out_size(me) = M_BUFFLEN; out_buffer(me) = NEW(char, M_BUFFLEN); } out_write(me) = out_buffer(me); out_read(me) = out_buffer(me); output(me) = False; if (hasquit(me)) { setup_globals(me); sock_msg("&+C%H &+Wclosed"); close_sock(fd); } } } void close_sock(int fd) { int i, new_width; setup_globals(find_pl_index(fd)); shutdown(fd, 2); if (cur_player->iamon) remove_from_game(); free_player(); cur_player->iamon = False; cur_player->is_conn = False; sock_fds[fd] = -1; for (i = 0, new_width = 0 ; i < width ; i++) if (sock_fds[i] != -1) new_width = i; width = new_width; close(fd); } char *test_multi_connects (struct in_addr *in) { static char inet_str[16]; int host_connects; int i; strcpy(inet_str, inet_ntoa(*in)); host_connects = 0; for (i = 0, host_connects = 0 ; i < max_players ; i++) if (is_conn(i) && *(ip_addr(i)) && !strcmp(strchr(ip_addr(i), '.'), strchr(inet_str, '.'))) host_connects++; if (host_connects > MAX_CONNECTS || !strcmp(inet_str, "0.0.0.0")) return NULL; else return inet_str; } void closemsg(int fd, char *msg) { write(fd, msg, strlen(msg)); shutdown(fd, 2); close(fd); } void new_connection (int listen_socket) { int plx; int fd; int sin_len; struct sockaddr_in sin; char *ip_addr; bzero ((char *) &sin, sizeof (struct sockaddr_in)); sin_len = sizeof (struct sockaddr_in); if ((fd = accept (listen_socket, (struct sockaddr *) &sin, &sin_len)) < 0) return; if (fcntl (fd, F_SETFL, FNDELAY) == -1) return; if ((ip_addr = test_multi_connects(&sin.sin_addr)) == NULL) { closemsg(fd, "Too many connects from your domain.\n"); return; } if ((plx = find_free_player_slot ()) < 0) { sock_msg("Connection refused (MUD full): %s", ip_addr); closemsg(fd, "\nSorry, but this mud is full, please come back later.\n"); return; } setup_io (plx, fd); setup_globals (plx); #ifdef IO_STATS sock_conns++; #endif strcpy(ip_addr(plx), ip_addr); strcpy(hostname(plx), ip_addr); strcpy(username(plx), ip_addr); port(plx) = (int) ntohs(sin.sin_port); setpname (mynum, "<Logging In>"); dns_output = True; ident_player(); setup_globals(-1); } void setup_io(int plx, int fd) { sock_fds[fd] = plx; rplrs[plx].fil_des = fd; players[plx].resfd = -1; out_buffer(plx) = NEW (char, M_BUFFLEN); out_size(plx) = M_BUFFLEN; out_read(plx) = out_buffer(plx); out_write(plx) = out_buffer(plx); inp_ptr(plx) = inp_buffer(plx); *inp_buffer(plx) = *out_buffer(plx) = 0; ignore_input(plx) = False; is_conn(plx) = True; limbo(plx) = True; } void read_packet (int fd) { char *ptr, *buffptr, *buff_start; int num_read, me; me = find_pl_index(fd); /* real player giving input */ if (me < 0) return; setup_globals(me); buff_start = inp_buffer(me); buffptr = inp_ptr(me); if ((num_read = read (fd, buffptr, CUTOFF_LEN - (buffptr - buff_start))) < 1) { if (!num_read) /* 0-byte packets = connection cut */ quit_player(-1); else quit_player(errno); return; } #ifdef IO_STATS bytes_read += num_read; #endif *(buffptr + num_read) = 0; if (!isascii(*buffptr)) { /* if (!isprint(*buffptr) && !isspace(*buffer)) { telnetresponse */ inp_ptr(me) = inp_buffer(me); return; } /* lines below are for the end of a spam of text */ else if (num_read + (buffptr - buff_start) < CUTOFF_LEN) { if (ignore_input(me) && strchr(buffptr, '\n')) { inp_ptr(me) = inp_buffer(me); ignore_input(me) = False; return; } } else { /* packet too big */ ptr = buff_start + CUTOFF_LEN; if (!ignore_input(me)) { /* first oversize packet */ *ptr = 0; bprintf ("&+B(&*Maximum Input Exceeded&+B)\n" "&+B(&*Input Cut Off After: &+W%s&+B)\n", ptr - 16); strcat(buff_start, "\r\n"); ignore_input(me) = True; } else { inp_ptr(me) = inp_buffer(me); /* don't add to buff */ return; } } if (!strchr(buff_start, '\n') && !strchr(buff_start, '\r')) inp_ptr(me) += num_read; else if (limbo(me)) /* player is resolvin, susp */ inp_ptr(me) += num_read; else if ((!inp_handler(me) || !phandler(me))) { bprintf("\tInternal Error Has Occurred: Lost Your Input Handler"); quit_player(-3); } else do_packet(me, buff_start); setup_globals(-1); } /* this function deals with a complete & valid packet which may have multiple lines of text separated by \n, \r, \r\n or \n\r */ void do_packet(int me, char *packet) { static char buff[MAX_COM_LEN]; static char *bptr, *tptr; /* buff ptr, text ptr */ static int plx; if (snooped(me)) { for (plx = 0 ; plx < max_players ; plx++) if (snooptarget(plx) == me) { setup_globals(plx); bprintf("\n&+r[&+WSnoop: %s&+r]&N %s", pname(me), packet); } setup_globals(me); } if (islogged(me)) write_plr_log(packet); /* copy command to buffer, exec command, repeat */ for (tptr = packet, bptr = buff ; *tptr ; tptr++) { if (!iscntrl(*tptr)) *bptr++ = *tptr; else if (*tptr == '\n' || *tptr == '\r' ) { if (*(tptr + 1) == '\n' || *(tptr + 1) == '\r') tptr++; *bptr = 0; exec_cmd(me, buff); bptr = buff; } else if (*tptr == '\b' && bptr > buff) bptr--; } inp_ptr(me) = inp_buffer(me); } /* actually execute the mud command */ void exec_cmd(int me, char *text) { #ifdef LOG_INPUT if (plogptr) { fprintf(plogptr, "(%s) %s:%s\n", time2ascii(global_clock) + 11, pname(mynum), text); fflush(plogptr); } #endif if (!inp_handler(me)) return; if (!ptstflg (me, PFL_IDLE)) plast_cmd (me) = global_clock; prlast_cmd (me) = global_clock; in_cmd(me) = True; (phandler(me))(text); if (me != real_mynum) setup_globals(me); in_cmd(me) = False; if (!hasquit(me)) bprintf("%s", cur_player->cprompt); }