/************************************************************************ Realms of Aurealis James Rhone aka Vall of RoA comm.c Heavily based on comm.c from CircleMUD3.0. Additions include external editor code along with all the functions associated with it. RoAOLC also needs some mods to this file with roaolc_menu checks thru- out. 5/1/97 - Heavy mods to allow communication with first generation RoAClients. -jtrhone 5/19/98 - more mods have been added and mostly moved to clicomm.c -jtrhone ******** Heavily modified and expanded ******** *** BE AWARE OF ALL RIGHTS AND RESERVATIONS *** ******** Heavily modified and expanded ******** All rights reserved henceforth. Please note that no guarantees are associated with any code from Realms of Aurealis. All code which has been released to the general public has been done so with an 'as is' pretense. RoA is based on both Diku and CircleMUD and ALL licenses from both *MUST* be adhered to as well as the RoA license. *** Read, Learn, Understand, Improve *** *************************************************************************/ #define __COMM_C__ #include "sysdep.h" #include "conf.h" #include <sys/socket.h> #include <sys/wait.h> #include <sys/resource.h> #include <netinet/in.h> #include <arpa/telnet.h> #include <arpa/inet.h> #include <netdb.h> #include <signal.h> #include <errno.h> #include <fcntl.h> #include <sys/stat.h> #include <unistd.h> #include "structures.h" #include "utils.h" #include "comm.h" #include "clicomm.h" #include "interpreter.h" #include "acmd.h" #include "handler.h" #include "db.h" #include "house.h" #include "identd/ident.h" #include "screen.h" #include "roaolc.h" #include "lists.h" #include "global.h" #include "plshop.h" #include "darkenelf.h" #include "descmenu.h" /* externs */ extern int restrict; extern int mini_mud; extern FILE *player_fl; extern int DFLT_PORT; extern char *DFLT_DIR; /* local globals */ dsdata *descriptor_list = NULL; /* master desc list of normal descriptors */ dsdata *cdesc_list = NULL; /* master desc list of client descriptors */ cldesc *prelim_list = NULL; /* global list of preliminary clients... */ struct txt_block *bufpool = 0; /* pool of large output buffers */ int buf_largecount = 0; /* # of large buffers which exist */ int buf_overflows = 0; /* # of overflows of output */ int buf_switches = 0; /* # of switches from small to large buf */ int circle_shutdown = 0; /* clean shutdown */ int circle_reboot = 0; /* reboot the game after a shutdown */ int max_players = 0; /* max descriptors available */ int tics = 0; /* for extern checkpointing */ struct timeval null_time; /* zero-valued time structure */ int MASTER_DESCRIPTOR; /* assigned to mother_desc for exshell.c */ int CLIENT_DESCRIPTOR; /* assigned to client_desc for exshell.c */ /* internal functions */ int get_from_q(struct txt_q *queue, char *dest, int *aliased); void init_game(int port); void setup_checkpoint(void); void signal_setup(void); void game_loop(int mother_desc, int client_desc); void respond_to_input(void); int init_socket(int port); int new_descriptor(int s); int get_max_players(void); int process_output(dsdata *t); int process_input(dsdata *t); void close_socket(dsdata *d); void flush_queues(dsdata *d); void nonblock(int s); void block(int s); int perform_subst(dsdata *t, char *orig, char *subst); int perform_alias(dsdata *d, char *orig); void record_usage(void); void make_prompt(dsdata *point); void check_idle_passwords(void); void send_auction(char *messg); struct timeval timediff(struct timeval *a, struct timeval *b); void update_vt100_bars(void); void handle_heartbeat_stuff(int *real_pulse); /* extern fcnts */ void boot_db(void); void boot_world(void); void zone_update(void); void affect_update(void); /* In spells.c */ void point_update(void); /* In limits.c */ void mobile_activity(BOOL doubletime); void magical_activity(void); void perform_violence(void); void update_client_stats(BOOL combat); void show_string(dsdata *d, char *input); int isbanned(char *hostname); void another_hour(void); void string_add(dsdata *d, char *str, BOOL term); extern void jump_to_ritual(chdata *ch); extern void rand_rite_messg(chdata *p); extern int update_who_html(void); extern int update_stats_html(void); extern void update_goss_html(void); extern void do_auction_update(void); extern void room_activity(BOOL doubletime); extern void zone_activity(void); extern void info_broadcast(void); extern void check_arena_state(BOOL award); extern void free_the_mud(void); extern void do_arena_update(void); extern void mortal_save(void); extern void save_board_config(void); // MUST be linked up to the libdesc library to use this -roa ACMD(do_identify) { chdata *vict; IDENT *idinfo; char *argu = argument; skip_spaces(&argu); if (!(vict = get_char_vis(ch, argu))) { send_to_char("Nobody by that name around.\n\r",ch); return; } if (!vict->desc) { send_to_char("There is no link to identify.\n\r",ch); return; } idinfo = ident_lookup(vict->desc->descriptor, 5); sprintf(buf, "%%6Identity information on %%B%s%%0:\n\r", GET_NAME(vict)); S2C(); if (!idinfo) { send_to_char("Remote host not running identd or connection timed out.\n\r",ch); return; } sprintf(buf, " %%5Username%%0: %s\n\r", idinfo->identifier); S2C(); sprintf(buf, " %%5Host%%0: %s\n\r", vict->desc->host); S2C(); sprintf(buf, " %%5Remote OS%%0: %s\n\r", idinfo->opsys); S2C(); sprintf(buf, " %%5Remote CharSet%%0: %s\n\r", idinfo->charset); S2C(); sprintf(buf, " %%5Lport%%0: %d\n\r", idinfo->lport); S2C(); sprintf(buf, " %%5Fport%%0: %d\n\r", idinfo->fport); S2C(); // fixed mem leak here...7/21/98 -jtrhone ident_free(idinfo); } /* ********************************************************************* * main game loop and related stuff * ********************************************************************* */ int main(int argc, char **argv) { int port; char buf[512]; int pos = 1; char *dir; port = DFLT_PORT; dir = DFLT_DIR; while ((pos < argc) && (*(argv[pos]) == '-')) { switch (*(argv[pos] + 1)) { case 'd': if (*(argv[pos] + 2)) dir = argv[pos] + 2; else if (++pos < argc) dir = argv[pos]; else { log("Directory arg expected after option -d."); exit(1); } break; case 'm': mini_mud = TRUE; log("Running in minimized mode."); break; case 'r': restrict = 1; log("Restricting mud -- no new players allowed (wizlock 1)."); break; case 'w': restrict = 71; log("Restricting mud -- no mortals allowed (wizlock 71)."); break; default: sprintf(buf, "SYSERR: Unknown option -%c in argument string.", *(argv[pos] + 1)); log(buf); break; } pos++; } if (pos < argc) { if (!isdigit(*argv[pos])) { fprintf(stderr, "Usage: %s [-c] [-m] [-q] [-r] [-d pathname] [port #]\n", argv[0]); exit(1); } else if ((port = atoi(argv[pos])) <= 1024) { fprintf(stderr, "Illegal port number.\n"); exit(1); } } if (chdir(dir) < 0) { perror("Fatal error changing to data directory"); exit(1); } sprintf(buf, "Using %s as data directory.", dir); log(buf); sprintf(buf, "Main Server on port %d.", port); log(buf); init_game(port); return 0; } /* Init sockets, run game, and cleanup sockets */ void init_game(int port) { int mother_desc; int client_desc; srandom(time(0)); // make sure we run with these ENV vars set right... 2/12/98 -jtrhone log("Initializing environment."); system("rm -f misc/*.soklock >& /dev/null"); putenv("TERM=vt100"); log("Opening mother connection."); mother_desc = init_socket(port); // this will also now fork off a cli_router... for client connections... client_desc = init_client_socket(port + 1); /* testing jtrhone -roa*/ MASTER_DESCRIPTOR = mother_desc; CLIENT_DESCRIPTOR = client_desc; max_players = get_max_players(); boot_db(); log("Signal trapping."); signal_setup(); log("Entering game loop."); game_loop(mother_desc, client_desc); log("Closing all mother connections."); while (descriptor_list) close_socket(descriptor_list); close(mother_desc); close(client_desc); fclose(player_fl); // ignore SIGCHILD on shutdown signal(SIGCHLD, SIG_IGN); // if we brought one up, kill it... if (cli_router_pid > 0) kill(cli_router_pid, SIGTERM); // now remove local socket 5/19/98 -jtrhone sprintf(buf, "Removing %s.", local_sockname); log(buf); unlink(local_sockname); // cleanup... system("rm -f misc/*.soklock >& /dev/null"); if (circle_reboot) { log("Rebooting."); exit(52); /* what's so great about HHGTTG, anyhow? */ } log("Normal termination of game."); } /* * init_socket sets up the mother descriptor - creates the socket, sets * its options up, binds it, and listens. */ int init_socket(int port) { int s, opt; struct sockaddr_in sa; /* * Should the first argument to socket() be AF_INET or PF_INET? I don't * know, take your pick. PF_INET seems to be more widely adopted, and * Comer (_Internetworking with TCP/IP_) even makes a point to say that * people erroneously use AF_INET with socket() when they should be using * PF_INET. However, the man pages of some systems indicate that AF_INET * is correct; some such as ConvexOS even say that you can use either one. * All implementations I've seen define AF_INET and PF_INET to be the same * number anyway, so ths point is (hopefully) moot. */ if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) { perror("Create socket"); exit(1); } #if defined(SO_SNDBUF) opt = LARGE_BUFSIZE + GARBAGE_SPACE; if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0) { perror("setsockopt SNDBUF"); exit(1); } #endif #if defined(SO_REUSEADDR) opt = 1; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0) { perror("setsockopt REUSEADDR"); exit(1); } #endif #if defined(SO_LINGER) { struct linger ld; ld.l_onoff = 0; ld.l_linger = 0; if (setsockopt(s, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld)) < 0) { perror("setsockopt LINGER"); exit(1); } } #endif sa.sin_family = AF_INET; sa.sin_port = htons(port); sa.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) { perror("bind"); close(s); exit(1); } nonblock(s); listen(s, 5); return s; } int get_max_players(void) { int max_descs = 0; char *method; #ifdef OS2 return MAX_PLAYERS; #else /* * First, we'll try using getrlimit/setrlimit. This will probably work * on most systems. */ #if defined (RLIMIT_NOFILE) || defined (RLIMIT_OFILE) #if !defined(RLIMIT_NOFILE) #define RLIMIT_NOFILE RLIMIT_OFILE #endif { struct rlimit limit; /* find the limit of file descs */ method = "rlimit"; if (getrlimit(RLIMIT_NOFILE, &limit) < 0) { perror("calling getrlimit"); exit(1); } /* set the current to the maximum */ limit.rlim_cur = limit.rlim_max; if (setrlimit(RLIMIT_NOFILE, &limit) < 0) { perror("calling setrlimit"); exit(1); } #ifdef RLIM_INFINITY if (limit.rlim_max == RLIM_INFINITY) max_descs = MAX_PLAYERS + NUM_RESERVED_DESCS; else max_descs = MIN(MAX_PLAYERS + NUM_RESERVED_DESCS, limit.rlim_max); #else max_descs = MIN(MAX_PLAYERS + NUM_RESERVED_DESCS, limit.rlim_max); #endif } #elif defined (OPEN_MAX) || defined(FOPEN_MAX) #if !defined(OPEN_MAX) #define OPEN_MAX FOPEN_MAX #endif method = "OPEN_MAX"; max_descs = OPEN_MAX; /* Uh oh.. rlimit didn't work, but we have * OPEN_MAX */ #elif defined (POSIX) /* * Okay, you don't have getrlimit() and you don't have OPEN_MAX. Time to * use the POSIX sysconf() function. (See Stevens' _Advanced Programming * in the UNIX Environment_). */ method = "POSIX sysconf"; errno = 0; if ((max_descs = sysconf(_SC_OPEN_MAX)) < 0) { if (errno == 0) max_descs = MAX_PLAYERS + NUM_RESERVED_DESCS; else { perror("Error calling sysconf"); exit(1); } } #else /* if everything has failed, we'll just take a guess */ max_descs = MAX_PLAYERS + NUM_RESERVED_DESCS; #endif /* now calculate max _players_ based on max descs */ max_descs = MIN(MAX_PLAYERS, max_descs - NUM_RESERVED_DESCS); if (max_descs <= 0) { sprintf(buf, "Non-positive max player limit! (Set at %d using %s).", max_descs, method); log(buf); exit(1); } sprintf(buf, "Setting player limit to %d using %s.", max_descs, method); log(buf); return max_descs; #endif /* OS2 */ } // Now handle second list of connections, from client descriptor // use the same select, accept new client connections as well now // as well as poll thru available client connections and // parsing thru client headers -RoA // now select on list of cldescs (preliminary clients before login) as well 5/20/98 -jtrhone void game_loop(int mother_desc, int client_desc) { fd_set input_set, output_set, exc_set; struct timeval last_time, now, timespent, timeout, opt_time; dsdata *d, *next_d; cldesc *c, *next_c; int pulse = 0, maxdesc; /* initialize various time values */ null_time.tv_sec = 0; null_time.tv_usec = 0; opt_time.tv_usec = OPT_USEC; opt_time.tv_sec = 0; gettimeofday(&last_time, (struct timezone *) 0); while (!circle_shutdown) { /* Sleep if we don't have any connections */ if (!descriptor_list && !cdesc_list && !prelim_list) { log("No connections. Going to sleep."); // null out entire input set (read_set) -roa FD_ZERO(&input_set); // stick our mother into the input set -roa FD_SET(mother_desc, &input_set); // stick client in there too, but mother should always be trigged // first, just a safety check i guess if mother is plugged FD_SET(client_desc, &input_set); if (select(MAX(mother_desc, client_desc)+1, &input_set, (fd_set *) 0, (fd_set *) 0, NULL) < 0) { if (errno == EINTR) log("Waking up to process signal."); else perror("Select coma"); } else log("New connection. Waking up."); gettimeofday(&last_time, (struct timezone *) 0); } /* Set up the input, output, and exception sets for select(). */ // clear out every set, init them to NULL -roa FD_ZERO(&input_set); FD_ZERO(&output_set); FD_ZERO(&exc_set); // put our mother, client_desc into the input set FD_SET(mother_desc, &input_set); FD_SET(client_desc, &input_set); // high end of the descriptors we scan starts at mother_desc -roa // in theory, client is higher, but it could have rolled maxdesc = MAX(mother_desc, client_desc); // now, go thru list, find the high end of the range of descriptors // and stick each of them into our read,write,and exception sets -roa Descriptors(d) { if (d->descriptor > maxdesc) maxdesc = d->descriptor; FD_SET(d->descriptor, &input_set); FD_SET(d->descriptor, &output_set); FD_SET(d->descriptor, &exc_set); } // now scan RoA client descriptor list and insert for (d = cdesc_list; d; d = d->next_client) { if (d->cdesc > maxdesc) maxdesc = d->cdesc; FD_SET(d->cdesc, &input_set); FD_SET(d->cdesc, &output_set); FD_SET(d->cdesc, &exc_set); } // now, go thru cldesc list as well, lookin for max... 5/20/98 -jtrhone for (c = prelim_list; c; c = c->next) { if (c->desc > maxdesc) maxdesc = c->desc; FD_SET(c->desc, &input_set); FD_SET(c->desc, &output_set); FD_SET(c->desc, &exc_set); } do { errno = 0; /* clear error condition */ /* figure out for how long we have to sleep */ gettimeofday(&now, (struct timezone *) 0); timespent = timediff(&now, &last_time); timeout = timediff(&opt_time, ×pent); /* sleep (regardless!) until the next 0.1 second tick */ if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, &timeout) < 0) if (errno != EINTR) { perror("Select sleep"); exit(1); } } while (errno); /* record the time for the next pass */ gettimeofday(&last_time, (struct timezone *) 0); /* poll (without blocking) for new input, output, and exceptions */ // we are scanning the i,o,e sets for changes in their state // those sets contain every player descriptor in the game including mother if (select(maxdesc + 1, &input_set, &output_set, &exc_set, &null_time) < 0) { perror("Select poll"); return; } /* If there are new connections waiting, accept them. */ // ok, mama is in the input_set, someone is knockin at her door -roa if (FD_ISSET(mother_desc, &input_set)) new_descriptor(mother_desc); // client monitor has a new connection, go getem one // no longer insert into client list, insert now into prelim list // upon successful process_prelim_input and login, this desc will be // put into client list if (FD_ISSET(client_desc, &input_set)) new_prelim_socket(client_desc); // anybody in the exception set, we wanna rip from the game // and remove them from the input and output sets -roa for (d = descriptor_list; d; d = next_d) { next_d = d->next; if (FD_ISSET(d->descriptor, &exc_set)) { FD_CLR(d->descriptor, &input_set); FD_CLR(d->descriptor, &output_set); close_socket(d); } } // kick client exceptions out for (d = cdesc_list; d; d = next_d) { next_d = d->next_client; if (FD_ISSET(d->cdesc, &exc_set)) { FD_CLR(d->cdesc, &input_set); FD_CLR(d->cdesc, &output_set); close_client_socket(d); } } // kick preliminary client exceptions out 5/20/98 -jtrhone for (c = prelim_list; c; c = next_c) { next_c = c->next; if (FD_ISSET(c->desc, &exc_set)) { FD_CLR(c->desc, &input_set); FD_CLR(c->desc, &output_set); close_prelim_socket(c); } } // process all descriptors which are in our input_set for (d = descriptor_list; d; d = next_d) { next_d = d->next; if (FD_ISSET(d->descriptor, &input_set)) if (process_input(d) < 0) close_socket(d); } // take care of client input for (d = cdesc_list; d; d = next_d) { next_d = d->next_client; if (FD_ISSET(d->cdesc, &input_set)) if (process_client_input(d) < 0) close_client_socket(d); } // take care of prelim client input for (c = prelim_list; c; c = next_c) { next_c = c->next; if (FD_ISSET(c->desc, &input_set)) if (process_prelim_client(c) < 0) close_prelim_socket(c); } // now each descriptor has had its input stuck onto a queue // lets go thru and yank a chunk of each players queue and // decipher it and process it respond_to_input(); /* send queued output out to the operating system (ultimately to user) */ // ok, now take care of those descriptors in our output_set -roa for (d = descriptor_list; d; d = next_d) { next_d = d->next; if (FD_ISSET(d->descriptor, &output_set) && *(d->output)) if (process_output(d) < 0) close_socket(d); else d->prompt_mode = 1; } // now process output from mud to client // doesn't do much since client output isn't buffered (yet)... for (d = cdesc_list; d; d = next_d) { next_d = d->next_client; if (FD_ISSET(d->cdesc, &output_set)) if (process_client_output(d) < 0) close_client_socket(d); } // wax CON_CLOSEd folks, this will also wax client connections for (d = descriptor_list; d; d = next_d) { next_d = d->next; if (STATE(d) == CON_CLOSE) close_socket(d); } /* give each descriptor an appropriate prompt */ Descriptors(d) if (d->prompt_mode) { make_prompt(d); d->prompt_mode = 0; } // Note: pulse only updates every 0.10 seconds because we force // the mud to sleep for that amount of time with the select statement // above as we send it NULL i,o,e sets and a timeval of 0.10 secs -roa pulse++; handle_heartbeat_stuff(&pulse); tics++; /* tics since last checkpoint signal */ } // if we have logged, close the file ptr 5/13/98 -jtrhone if (memfp) fclose(memfp); // ok, were out of the main loop, ready to shutdown // lets free a ton of stuff so we can track down memory leaks // with the dmalloc libraries easier -roa free_the_mud(); #ifndef DMALLOC_DISABLE #ifdef DEBUG_MAX dmalloc_log_stats(); dmalloc_log_unfreed(); #endif #endif } // no more nanny call or STATE() macros..., if they in a descriptor menu, call it 7/14/98 -jtrhone void respond_to_input(void) { dsdata *d, *next_d; chdata *player; char comm[MAX_INPUT_LENGTH]; char combuf[256]; int aliased; for (d = descriptor_list; d; d = next_d) { next_d = d->next; player = d->character; // check for arena and ritualistic things first if (D_CHECK(d) && IS_PC(player)) { if (IN_ARENA(player) && !ZONE_FLAGGED(world[player->in_room].zone, Z_ARENA)) { REMOVE_BIT(PLR_FLAGS(player), PLR_ARENA); check_arena_state(TRUE); } if (IN_A_RITUAL(player) && DELAY_TYPE(player)) { if (!(d->wait % 60)) /* every 60 pulses show messg */ rand_rite_messg(player); if ((--d->wait) <= 0) /* ready to finish ritual? */ { d->wait = 1; jump_to_ritual(player); } continue; // dont take input from this guy yet } } aliased = FALSE; if ((--(d->wait) <= 0) && get_from_q(&d->input, comm, &aliased)) { // Record time of last entered command... 03/21/98 -callahan d->last_time = time(0); if (d->character) { /* pull char back from void if necessary */ d->character->specials.timer = 0; if (!d->connected && GET_WAS_IN(d->character) != NOWHERE) { if (!IN_NOWHERE(d->character)) char_from_room(d->character); char_to_room(d->character, GET_WAS_IN(d->character)); GET_WAS_IN(d->character) = NOWHERE; act("$n has returned.", TRUE, d->character, 0, 0, TO_ROOM); } } d->wait = 1; d->prompt_mode = 1; // if they are rerouted... we basically ignore them... if (d->rerouted) { if (d->character) sprintf(combuf, "SYSUPD: %s rerouted input in comm.c", GET_NAME(d->character)); else sprintf(combuf, "SYSUPD: Unknown char rerouted in comm.c."); mudlog(combuf, BUG, LEV_IMM, TRUE); continue; } else if (d->str) /* writing boards, mail, etc. */ string_add(d, comm, FALSE); else if (d->showstr_count) /* reading something w/ pager */ show_string(d, comm); else if (d->descmenu) /* in descmenus */ { d->idle_tics = 0; (*d->descmenu)(d, comm); } else if (d->roaolc_menu) /* in RoA OLC */ { if (d->character) (*d->roaolc_menu)(d->character, comm); else mudlog("SYSERR: NonCharacter menu access in comm.c", BRF,LEV_IMM,TRUE); } else { /* else: we're playing normally */ if (aliased) /* to prevent recursive aliases */ d->prompt_mode = 0; else { /* run it through aliasing system */ if (d->character) { if (perform_alias(d, comm)) get_from_q(&d->input, comm, &aliased); } else { mudlog("SYSERR: NonCharacter perform_alias attempt in comm.c", BRF,LEV_IMM,TRUE); } } /* send it to interpreter */ command_interpreter(d->character, comm); if (VT100(d->character)) updatescr(d->character); } } } } // updated room_activity for rproc dtime 6/6/98 -jtrhone void handle_heartbeat_stuff(int *real_pulse) { int pulse = *real_pulse; static int mins_since_crashsave = 0; static BOOL dtime = FALSE; static BOOL rdtime = FALSE; extern void room_save_all(void); if (!(pulse % PULSE_PASSWORD)) check_idle_passwords(); if (!(pulse % PULSE_ZONE)) { zone_update(); zone_activity(); } if (!(pulse % PULSE_MAGIC)) magical_activity(); // in mobact.c for now -roa if (!(pulse % PULSE_ROOM)) { room_activity(rdtime); rdtime = !rdtime; } if (!(pulse % PULSE_MOBILE)) { mobile_activity(dtime); dtime = !dtime; } // do the normal violence thing if (!(pulse % PULSE_VIOLENCE)) { perform_violence(); // for all those players who are vt100 and need an update update_vt100_bars(); // for all those peeps who WANT stat data every 2 seconds... 8/3/98 -jtrhone update_client_stats(TRUE); } /* do per tick i guess? per hour */ if (!(pulse % (SECS_PER_MUD_HOUR * PASSES_PER_SEC))) { another_hour(); affect_update(); point_update(); fflush(player_fl); max_on = MAX(max_on, update_who_html()); update_stats_html(); update_goss_html(); do_auction_update(); do_arena_update(); info_broadcast(); // update these every tick, regardless if they fighting 8/4/98 -jtrhone update_client_stats(FALSE); } /* 1 minute */ if (auto_save) if (!(pulse % (60 * PASSES_PER_SEC))) if (++mins_since_crashsave >= autosave_time) { mins_since_crashsave = 0; mortal_save(); House_save_all(); room_save_all(); } /* 5 minutes */ if (!(pulse % (300 * PASSES_PER_SEC))) record_usage(); /* 30 minutes */ if (pulse >= (30 * 60 * PASSES_PER_SEC)) { *real_pulse = 0; // save config files every half hour... -jtrhone 11/25/97 save_configuration(); save_board_config(); save_plshops(); } } /* ****************************************************************** * general utility stuff (for local use) * ****************************************************************** */ /* * new code to calculate time differences, which works on systems * for which tv_usec is unsigned (and thus comparisons for something * being < 0 fail). Based on code submitted by ss@sirocco.cup.hp.com. */ /* * code to return the time difference between a and b (a-b). * always returns a nonnegative value (floors at 0). */ struct timeval timediff(struct timeval *a, struct timeval *b) { struct timeval rslt; if (a->tv_sec < b->tv_sec) return null_time; else if (a->tv_sec == b->tv_sec) { if (a->tv_usec < b->tv_usec) return null_time; else { rslt.tv_sec = 0; rslt.tv_usec = a->tv_usec - b->tv_usec; return rslt; } } else { /* a->tv_sec > b->tv_sec */ rslt.tv_sec = a->tv_sec - b->tv_sec; if (a->tv_usec < b->tv_usec) { rslt.tv_usec = a->tv_usec + 1000000 - b->tv_usec; rslt.tv_sec--; } else rslt.tv_usec = a->tv_usec - b->tv_usec; return rslt; } } void record_usage(void) { int sockets_connected = 0, sockets_playing = 0; dsdata *d; char buf[256]; Descriptors(d) { sockets_connected++; if (!d->connected) sockets_playing++; } sprintf(buf, "nusage: %-3d sockets connected, %-3d sockets playing", sockets_connected, sockets_playing); log(buf); if (0) { struct rusage ru; getrusage(0, &ru); sprintf(buf, "rusage: user time: %d sec, system time: %d sec, max res size: %ld", ru.ru_utime.tv_sec, ru.ru_stime.tv_sec, ru.ru_maxrss); log(buf); } } /* * Turn off echoing (specific to telnet client) */ void echo_off(dsdata *d) { char off_string[] = { (char) IAC, (char) WILL, (char) TELOPT_ECHO, (char) 0, }; send_to_q(off_string, d); } /* * Turn on echoing (specific to telnet client) */ void echo_on(dsdata *d) { char on_string[] = { (char) IAC, (char) WONT, (char) TELOPT_ECHO, (char) TELOPT_NAOFFD, (char) TELOPT_NAOCRD, (char) 0, }; send_to_q(on_string, d); } // at predetermined interval, go thru descriptor list and updatescr void update_vt100_bars(void) { dsdata *d = descriptor_list; dsdata *next_d; for ( ; d; d = next_d) { next_d = d->next; if (D_CHECK(d) && IS_PC(d->character) && VT100(d->character) && d->character->pc_specials->needs_update) updatescr(d->character); } } // give our peeps a prompt if they need one void make_prompt(dsdata *d) { char prompt[MAX_INPUT_LENGTH+1]; // dont give em no prompt if they shelled out -roa if (d->rerouted) return; else if (d->character && INCHAT(d->character)) { write_to_descriptor(d->descriptor, ">"); return; } else if (d->showstr_count) // they using the pager type code? { sprintf(prompt, "\r[ Return to continue, (q)uit, (r)efresh, (b)ack, or page number (%d/%d) ]", d->showstr_page, d->showstr_count); write_to_descriptor(d->descriptor, prompt); if (d->character && IS_PC(d->character)) d->character->pc_specials->needs_update = FALSE; } else if (d->descmenu_prompt) // they have descmenu? { write_to_descriptor(d->descriptor, d->descmenu_prompt); if (d->character && IS_PC(d->character)) d->character->pc_specials->needs_update = FALSE; } else if (d->menu_prompt) // they have an RoAOLC menu? { write_to_descriptor(d->descriptor, d->menu_prompt); if (d->character && IS_PC(d->character)) d->character->pc_specials->needs_update = FALSE; } else if (d->str) // they in the old fashioned editor? { write_to_descriptor(d->descriptor, "] "); if (d->character && IS_PC(d->character)) d->character->pc_specials->needs_update = FALSE; } else if (D_CHECK(d) && !VT100(d->character)) { display_prompt(d->character, prompt); write_to_descriptor(d->descriptor, prompt); if (d->character && IS_PC(d->character)) d->character->pc_specials->needs_update = FALSE; } } // throw a chunk of text on a queue void write_to_q(char *txt, struct txt_q *queue, int aliased) { struct txt_block *neww; CREATE(neww, struct txt_block, 1); CREATE(neww->text, char, strlen(txt) + 1); strcpy(neww->text, txt); neww->aliased = aliased; /* queue empty? */ if (!queue->head) { neww->next = NULL; queue->head = queue->tail = neww; } else { queue->tail->next = neww; queue->tail = neww; neww->next = NULL; } } /* used to be a macro, BLAH, i had to throw a thing in here, easier as a function now -jtrhone */ void send_to_q(char *messg, dsdata *d) { if (!messg || !*messg) return; if (d->character && IS_PC(d->character) && VT100(d->character)) d->character->pc_specials->needs_update = TRUE; write_to_output(messg, d); } // yank a chunk off the queue int get_from_q(struct txt_q *queue, char *dest, int *aliased) { struct txt_block *tmp; /* queue empty? */ if (!queue->head) return 0; tmp = queue->head; strcpy(dest, queue->head->text); *aliased = queue->head->aliased; queue->head = queue->head->next; free(tmp->text); free(tmp); return 1; } /* Empty the queues before closing connection */ void flush_queues(dsdata *d) { int dummy; if (d->large_outbuf) { d->large_outbuf->next = bufpool; bufpool = d->large_outbuf; } while (get_from_q(&d->input, buf2, &dummy)); } /* Add a new string to a players output queue */ void write_to_output(const char *txt, dsdata *t) { int size; size = strlen(txt); /* if were in the overflow state already, ignore this new output */ if (t->bufptr < 0) return; /* if we have enough space, just write to buffer and thats it! */ if (t->bufspace >= size) { strcpy(t->output + t->bufptr, txt); t->bufspace -= size; t->bufptr += size; return; } /* * If were already using the large buffer, or if even the large buffer * is too small to handle this new text, chuck the text and switch to the * overflow state. */ if (t->large_outbuf || ((size + strlen(t->output)) > LARGE_BUFSIZE)) { t->bufptr = -1; buf_overflows++; mudlog("SYSUPD: Buf overflow noted.",BUG,LEV_IMM,TRUE); return; } buf_switches++; /* if the pool has a buffer in it, grab it */ if (bufpool != NULL) { t->large_outbuf = bufpool; bufpool = bufpool->next; } else { /* else create a new one */ CREATE(t->large_outbuf, struct txt_block, 1); CREATE(t->large_outbuf->text, char, LARGE_BUFSIZE); buf_largecount++; } strcpy(t->large_outbuf->text, t->output); /* copy to big buffer */ t->output = t->large_outbuf->text; /* make big buffer primary */ strcat(t->output, txt); /* now add new text */ /* calculate how much space is left in the buffer */ t->bufspace = LARGE_BUFSIZE - 1 - strlen(t->output); /* set the pointer for the next write */ t->bufptr = strlen(t->output); } /* ****************************************************************** * socket handling * ****************************************************************** */ // set up descriptor, and descriptor struct int new_descriptor(int s) { int desc, sockets_connected = 0, i; unsigned long addr; static int last_desc = 0; dsdata *newd; struct sockaddr_in peer; struct hostent *from; /* accept the new connection */ i = sizeof(peer); if ((desc = accept(s, (struct sockaddr *) &peer, &i)) < 0) { perror("accept"); return -1; } /* keep it from blocking */ nonblock(desc); /* make sure we have room for it */ Descriptors(newd) sockets_connected++; if (sockets_connected >= max_players) { sprintf(buf, "Sorry, %s is full right now... please try again later!\n\r", shortmudname); write_to_descriptor(desc, buf); close(desc); return 0; } /* create a new descriptor */ CREATE(newd, dsdata, 1); memset((char *) newd, 0, sizeof(dsdata)); /* find the sitename */ if (nameserver_is_slow || !(from = gethostbyaddr((char *) &peer.sin_addr, sizeof(peer.sin_addr), AF_INET))) { /* resolution failed */ if (!nameserver_is_slow) perror("gethostbyaddr"); /* find the numeric site address */ addr = ntohl(peer.sin_addr.s_addr); sprintf(newd->host, "%03u.%03u.%03u.%03u", (int) ((addr & 0xFF000000) >> 24), (int) ((addr & 0x00FF0000) >> 16), (int) ((addr & 0x0000FF00) >> 8), (int) ((addr & 0x000000FF))); } else { strncpy(newd->host, from->h_name, 30); *(newd->host + 30) = '\0'; } /* determine if the site is banned */ if (isbanned(newd->host) == BAN_ALL) { close(desc); sprintf(buf2, "Connection attempt denied from [%s]", newd->host); mudlog(buf2, CMP, LEV_GOD, TRUE); free(newd); return 0; } /* initialize descriptor data */ newd->descriptor = desc; newd->pos = -1; newd->wait = 1; newd->output = newd->small_outbuf; newd->bufspace = SMALL_BUFSIZE - 1; newd->login_time = time(0); newd->last_time = time(0); // For idle-monitoring... 03/21/98 -callahan newd->zone_stimer = -1; newd->room_stimer = -1; newd->mob_stimer = -1; STATE(newd) = CON_DESCMENU; // we need a number to reference this descriptor for wiz commands // this has nothing to do with the socket or descriptor stuff -roa if (++last_desc == 1000) { last_desc = 1; mudlog("SYSUPD: 1000 connections reached. Reset to 1.", BUG, LEV_IMM, TRUE); } newd->desc_num = last_desc; /* prepend to our list of descriptors list */ newd->next = descriptor_list; descriptor_list = newd; // jump to getname menu in descmenu.c descmenu_jump(newd, getname); return 0; } // each character has a buffer of output waiting to be processed // (normally), lets process a chunk and send it out int process_output(dsdata *t) { static char i[LARGE_BUFSIZE + GARBAGE_SPACE]; static int result; /* we may need this \r\n for later -- see below */ strcpy(i, "\r\n"); /* now, append the 'real' output */ str_cpy(i + 2, t->output, LARGE_BUFSIZE + GARBAGE_SPACE -2, "process_output"); /* if we're in the overflow state, notify the user */ if (t->bufptr < 0) strcat(i, "**OVERFLOW**"); /* add the extra CRLF if the person isn't in compact mode */ if (D_CHECK(t) && !PRF_FLAGGED(t->character, PRF_COMPACT)) strcat(i + 2, "\r\n"); /* * now, send the output. If this is an 'interruption', use the prepended * CRLF, otherwise send the straight output sans CRLF. */ if (!t->prompt_mode) result = write_to_descriptor(t->descriptor, i); else result = write_to_descriptor(t->descriptor, i + 2); if (t->character && IS_PC(t->character)) t->character->pc_specials->needs_update = FALSE; /* handle snooping: prepend "% " and send to snooper */ if (t->snoop_by) { send_to_q("% ", t->snoop_by); send_to_q(t->output, t->snoop_by); send_to_q("%%", t->snoop_by); } /* * if we were using a large buffer, put the large buffer on the buffer pool * and switch back to the small one */ if (t->large_outbuf) { t->large_outbuf->next = bufpool; bufpool = t->large_outbuf; t->large_outbuf = NULL; t->output = t->small_outbuf; } /* reset total bufspace back to that of a small buffer */ t->bufspace = SMALL_BUFSIZE - 1; t->bufptr = 0; *(t->output) = '\0'; return result; } int write_to_descriptor(int desc, char *txt) { int sofar, thisround, total; total = strlen(txt); sofar = 0; do { thisround = write(desc, txt + sofar, total - sofar); if (thisround < 0) { perror("Write to socket"); return(-1); } sofar += thisround; } while (sofar < total); return(0); } int process_input(dsdata *t) { int sofar, thisround, begin, squelch, i, k, flag, failed_subst = 0; char tmp[MAX_INPUT_LENGTH+2], buffer[MAX_INPUT_LENGTH + 60]; char somebuf[MAX_STRING_LENGTH]; // if they is rerouted, dump the input off into cyberspacia -roa if (t->rerouted) { read(t->descriptor, somebuf, MAX_STRING_LENGTH); return 0; } sofar = 0; flag = 0; begin = strlen(t->buf); /* Read in some stuff */ do { if ((thisround = read(t->descriptor, t->buf + begin + sofar, MAX_STRING_LENGTH - (begin + sofar) - 1)) > 0) sofar += thisround; else if (thisround < 0) if (errno != EWOULDBLOCK) { perror("Read1 - ERROR"); return(-1); } else break; else { log("EOF encountered on socket read. (connection broken by peer)"); return(-1); } } while (!ISNEWL(*(t->buf + begin + sofar - 1))); *(t->buf + begin + sofar) = 0; /* if no newline is contained in input, return without proc'ing */ for (i = begin; !ISNEWL(*(t->buf + i)); i++) if (!*(t->buf + i)) return(0); /* input contains 1 or more newlines; process the stuff */ // this is normal case for (i = 0, k = 0; *(t->buf + i); ) { if (!ISNEWL(*(t->buf + i)) && !(flag = (k >= (MAX_INPUT_LENGTH - 2)))) if (*(t->buf + i) == '\b') /* backspace */ if (k) { /* more than one char ? */ if (*(tmp + --k) == '$') k--; i++; } else i++; /* no or just one char.. Skip backsp */ else if (isascii(*(t->buf + i)) && isprint(*(t->buf + i))) { /* trans char, double for '$' (printf) */ if ((*(tmp + k) = *(t->buf + i)) == '$') *(tmp + ++k) = '$'; k++; i++; } else i++; else { *(tmp + k) = 0; if (*tmp == '!') strcpy(tmp, t->last_input); else if (*tmp == '^') { if (!(failed_subst = perform_subst(t, t->last_input, tmp))) strcpy(t->last_input, tmp); } else strcpy(t->last_input, tmp); if (!failed_subst) write_to_q(tmp, &t->input, 0); if (t->snoop_by) { send_to_q("% ", t->snoop_by); send_to_q(tmp, t->snoop_by); send_to_q("\n\r", t->snoop_by); } if (flag) { sprintf(buffer, "Line too long. Truncated to:\n\r%s\n\r", tmp); if (write_to_descriptor(t->descriptor, buffer) < 0) return(-1); /* skip the rest of the line */ for (; !ISNEWL(*(t->buf + i)); i++) ; } /* find end of entry */ for (; ISNEWL(*(t->buf + i)); i++) ; /* squelch the entry from the buffer */ for (squelch = 0; ; squelch++) if ((*(t->buf + squelch) = *(t->buf + i + squelch)) == '\0') break; k = 0; i = 0; } } return(1); } /* * perform substitution for the '^..^' csh-esque syntax * orig is the orig string (i.e. the one being modified. * subst contains the substition string, i.e. "^telm^tell" */ int perform_subst(dsdata *t, char *orig, char *subst) { char neww[MAX_INPUT_LENGTH + 5]; char *first, *second, *strpos; /* * first is the position of the beginning of the first string (the one * to be replaced */ first = subst + 1; /* now find the second '^' */ if (!(second = strchr(first, '^'))) { send_to_q("Invalid substitution.\r\n", t); return 1; } /* terminate "first" at the position of the '^' and make 'second' point * to the beginning of the second string */ *(second++) = '\0'; /* now, see if the contents of the first string appear in the original */ if (!(strpos = strstr(orig, first))) { send_to_q("Invalid substitution.\r\n", t); return 1; } /* now, we construct the new string for output. */ /* first, everything in the original, up to the string to be replaced */ strncpy(neww, orig, (strpos - orig)); neww[(strpos - orig)] = '\0'; /* now, the replacement string */ strncat(neww, second, (MAX_INPUT_LENGTH - strlen(neww) - 1)); /* now, if there's anything left in the original after the string to * replaced, copy that too. */ if (((strpos - orig) + strlen(first)) < strlen(orig)) strncat(neww, strpos + strlen(first), (MAX_INPUT_LENGTH - strlen(neww) - 1)); /* terminate the string in case of an overflow from strncat */ neww[MAX_INPUT_LENGTH - 1] = '\0'; strcpy(subst, neww); return 0; } void cleanup_after_linkloss(dsdata *d) { extern void wax_room(rmdata *r); extern void wax_object(obdata *o); extern void wax_mobile(chdata *ch); if (OBJ_EDITTED(d->character)) { wax_object(OBJ_EDITTED(d->character)); FREENULL(OBJ_EDITTED(d->character)); sprintf(buf, "SYSUPD: Freeing editted object on %s.", GET_NAME(d->character)); mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(d->character)), TRUE); } if (ROOM_EDITTED(d->character)) { wax_room(ROOM_EDITTED(d->character)); // free it, since it's stuck on a character FREENULL(ROOM_EDITTED(d->character)); sprintf(buf, "SYSUPD: Freeing editted room on %s.", GET_NAME(d->character)); mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(d->character)), TRUE); } if (MOB_EDITTED(d->character)) { wax_mobile(MOB_EDITTED(d->character)); FREENULL(MOB_EDITTED(d->character)); sprintf(buf, "SYSUPD: Freeing editted mobile on %s.", GET_NAME(d->character)); mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(d->character)), TRUE); } if (PSTR1(d->character)) { FREENULL(PSTR1(d->character)); sprintf(buf, "SYSUPD: Freeing PSTR1 on %s.", GET_NAME(d->character)); mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(d->character)), TRUE); } if (PSTR2(d->character)) { FREENULL(PSTR2(d->character)); sprintf(buf, "SYSUPD: Freeing PSTR2 on %s.", GET_NAME(d->character)); mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(d->character)), TRUE); } FREENULL(d->character->tmp_skills); } void close_socket(dsdata *d) { dsdata *temp; if (d->cdesc) close_client_socket(d); close(d->descriptor); flush_queues(d); /* Forget snooping */ if (d->snooping) d->snooping->snoop_by = NULL; if (d->snoop_by) { send_to_q("Your victim is no longer among us.\r\n", d->snoop_by); d->snoop_by->snooping = NULL; } if (d->room_snooping) remove_room_snooper(&world[d->room_snooping], d); if (d->character) { if (STATE(d) == CON_PLAYING) { // MUST check to see if this guy had spawned process off in some editor // if so, kill its child if (d->child_pid > 0) { sprintf(buf, "Closing child shell: %s.", GET_NAME(d->character)); mudlog(buf, BRF, MAX(LEV_IMM, GET_INVIS_LEV(d->character)),TRUE); if ( kill(d->child_pid, SIGKILL) < 0 ) { perror("shell kill:"); sprintf(buf, "%%1%%BSYSWAR%%0: Error in child kill, recommend immediate shutdown."); mudlog(buf, BRF, LEV_IMM,TRUE); } d->child_pid = -1; } REMOVE_BIT(PLR_FLAGS(d->character), PLR_BUILDING | PLR_WRITING | PLR_MAILING); save_char(d->character, NOWHERE); act("$n has lost $s soul.", TRUE, d->character, 0, 0, TO_ROOM); sprintf(buf, "Closing link to: %s.", GET_NAME(d->character)); mudlog(buf, NRM, MAX(LEV_IMM, GET_INVIS_LEV(d->character)), TRUE); // unallocate any unneeded memory cleanup_after_linkloss(d); d->character->desc = NULL; } else { sprintf(buf, "Losing player: %s.", GET_NAME(d->character) ? GET_NAME(d->character) : "<null>"); mudlog(buf, CMP, MAX(LEV_IMM, GET_INVIS_LEV(d->character)), TRUE); free_char(d->character); } } else mudlog("Losing descriptor without char.", CMP, LEV_IMM, TRUE); /* JE 2/22/95 -- part of my unending quest to make switch stable */ if (d->original && d->original->desc) d->original->desc = NULL; REMOVE_FROM_LIST(d, descriptor_list, next); FREENULL(d->showstr_head); if (d->showstr_count) FREENULL(d->showstr_vector); free(d); } // updated to kick idle prelim clients 5/20/98 -jtrhone void check_idle_passwords(void) { dsdata *d, *next_d; cldesc *c, *next_c; for (d = descriptor_list; d; d = next_d) { next_d = d->next; if (DESCMENU_HANDLER(d) != (void *)getpasswd && DESCMENU_HANDLER(d) != (void *)getnewpasswd && DESCMENU_HANDLER(d) != (void *)confirmnewpasswd && DESCMENU_HANDLER(d) != (void *)getname && DESCMENU_HANDLER(d) != (void *)confirmname) continue; if (!d->idle_tics) { d->idle_tics++; continue; } else { sprintf(buf, "SYSUPD: Descriptor #%d timed out.",d->desc_num); mudlog(buf, BUG, LEV_IMM, FALSE); echo_on(d); send_to_q("\r\nIdle-timed out... please reconnect.\r\n", d); STATE(d) = CON_CLOSE; } } for (c = prelim_list; c; c = next_c) { next_c = c->next; if (++(c->timer) > 1) close_prelim_socket(c); } } /* * I tried to universally convert Circle over to POSIX compliance, but * alas, some systems are still straggling behind and don't have all the * appropriate defines. In particular, NeXT 2.x defines O_NDELAY but not * O_NONBLOCK. Krusty old NeXT machines! (Thanks to Michael Jones for * this and various other NeXT fixes.) */ #ifndef O_NONBLOCK #define O_NONBLOCK O_NDELAY #endif void nonblock(int s) { int flags; flags = fcntl(s, F_GETFL, 0); flags |= O_NONBLOCK; if (fcntl(s, F_SETFL, flags) < 0) { perror("Fatal error executing nonblock (comm.c)"); log("fatal error executing nonblock comm.c"); exit(1); } } void block(int s) { int flags; flags = fcntl(s, F_GETFL, 0); REMOVE_BIT(flags,O_NONBLOCK); if (fcntl(s, F_SETFL, flags) < 0) { perror("Fatal error executing block (comm.c)"); log("fatal error executing block comm.c"); exit(1); } } /* ****************************************************************** * signal-handling functions (formerly signals.c) * ****************************************************************** */ /* * This is an implementation of signal() using sigaction() for portability. * (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced * Programming in the UNIX Environment_. We are specifying that all system * calls _not_ be automatically restarted for uniformity, because BSD systems * do not restart select(), even if SA_RESTART is used. * * Note that NeXT 2.x is not POSIX and does not have sigaction; therefore, * I just define it to be the old signal. If your system doesn't have * sigaction either, you can use the same fix. * * SunOS Release 4.0.2 (sun386) needs this too, according to Tim Aldric. */ #ifndef POSIX #define my_signal(signo, func) signal(signo, func) #else sigfunc *my_signal(int signo, sigfunc * func) { struct sigaction act, oact; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0; #ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT; /* SunOS */ #endif if (sigaction(signo, &act, &oact) < 0) return SIG_ERR; return oact.sa_handler; } #endif /* NeXT */ RETSIGTYPE checkpointing(int sig) { if (!tics) { log("SYSERR: CHECKPOINT shutdown: tics not updated"); /* abort(); */ sleep(20); } else tics = 0; setup_checkpoint(); } RETSIGTYPE unrestrict_game(int sig) { extern struct ban_list_element *ban_list; extern int num_invalid; mudlog("Received SIGUSR2 - completely unrestricting game (emergent)", BRF, LEV_IMM, TRUE); ban_list = NULL; restrict = 0; num_invalid = 0; my_signal(SIGUSR2, unrestrict_game); } RETSIGTYPE hupsig(int sig) { // ignore all signals now... 3/6/98 -jtrhone default_sigs(); log("Rcvd SIGHUP, SIGINT, or SIGTERM. Freeing up and Shutting down..."); circle_shutdown = TRUE; } /* * set up the deadlock-protection so that the MUD aborts itself if it gets * caught in an infinite loop for more than 8 minutes. Doesn't work with * OS/2. */ void setup_checkpoint(void) { struct itimerval itime; struct timeval interval; interval.tv_sec = 480; interval.tv_usec = 0; itime.it_interval = interval; itime.it_value = interval; setitimer(ITIMER_VIRTUAL, &itime, NULL); my_signal(SIGVTALRM, checkpointing); } void signal_setup(void) { extern RETSIGTYPE grim_reaper(int sig_received); /* * user signal 2: unrestrict game. Used for emergencies if you lock * yourself out of the MUD somehow. (Duh...) */ my_signal(SIGUSR2, unrestrict_game); setup_checkpoint(); /* just to be on the safe side: */ my_signal(SIGHUP, hupsig); my_signal(SIGINT, hupsig); my_signal(SIGTERM, hupsig); my_signal(SIGPIPE, SIG_IGN); my_signal(SIGALRM, SIG_IGN); // RoA -jtrhone, lets make a grim_reaper to take care of errant children my_signal(SIGCHLD, grim_reaper); } // for children that shouldn't exit any special way... void default_sigs(void) { my_signal(SIGHUP, SIG_DFL); my_signal(SIGINT, SIG_DFL); my_signal(SIGTERM, SIG_DFL); // however, have the children ignore these my_signal(SIGCHLD, SIG_IGN); my_signal(SIGVTALRM, SIG_IGN); } /* **************************************************************** * Public routines for system-to-player-communication * *******************************************************************/ void send_to_char(char *messg, chdata *ch) { char cstr[MAX_STRING_LENGTH]; if (ch->desc && messg) { color_decode(ch, messg, cstr); send_to_q(cstr, ch->desc); } } void send_to_room_snoopers(char *messg, int room, chdata *ch, int type) { dsdata *s, *next_s; for (s = RSNOOPER(&world[room]); s; s=next_s) { next_s = s->next_rsnooper; if (s->character) { if (type == TO_SNOOP || (type == TO_SNOOP_HIDE && ch && can_see(s->character, ch))) { send_to_char("%B%5**%0 ",s->character); send_to_char(messg, s->character); } } else remove_room_snooper(&world[room], s); } } void send_auction(char *messg) { dsdata *i; char cstr[MAX_STRING_LENGTH]; if (messg) for (i = descriptor_list; i; i = i->next) { if (D_CHECK(i) && SEND_OK(i->character) && !PRF_FLAGGED(i->character, PRF_NOAUCT) && !ROOM_FLAGGED2(i->character->in_room, SOUNDPROOF)) { color_decode(i->character, messg, cstr); send_to_q(cstr, i); } } } void send_to_arena(char *messg) { dsdata *i; char cstr[MAX_STRING_LENGTH]; if (messg) for (i = descriptor_list; i; i = i->next) if (D_CHECK(i) && SEND_OK(i->character) && !PRF2_FLAGGED(i->character, PRF2_NOARENA)) { color_decode(i->character, messg, cstr); send_to_q(cstr, i); } } void send_to_all(char *messg) { dsdata *i; char cstr[MAX_STRING_LENGTH]; if (messg) for (i = descriptor_list; i; i = i->next) if (D_CHECK(i) && SEND_OK(i->character)) { color_decode(i->character, messg, cstr); send_to_q(cstr, i); } } void send_to_outdoor(char *messg) { dsdata *i; char cstr[MAX_STRING_LENGTH]; if (messg) for (i = descriptor_list; i; i = i->next) if (D_CHECK(i) && !INVALID_ROOM(i->character->in_room) && AWAKE(i->character) && SEND_OK(i->character) && OUTSIDE(i->character) && !ROOM_FLAGGED(i->character->in_room, NO_WEATHER)) { color_decode(i->character, messg, cstr); send_to_q(cstr, i); } } void send_to_except(char *messg, chdata *ch) { dsdata *i; if (messg) for (i = descriptor_list; i; i = i->next) if (ch->desc != i && !i->connected) send_to_q(messg, i); } void send_to_room(char *messg, int room) { chdata *i; for (i = world[room].people; i; i = i->next_in_room) send_to_char(messg, i); send_to_room_snoopers(messg, room, NULL, TO_SNOOP); } /* send to ppl in room who arent building/writing/mailing */ void send_to_room_not_busy(char *messg, int room) { chdata *i; char mesg[MAX_STRING_LENGTH -10]; str_cpy(mesg, messg, MAX_STRING_LENGTH-10, "send_to_room_not_busy"); str_cat(mesg, "\n\r", MAX_STRING_LENGTH-10, "send_to_room_not_busy"); for (i = world[room].people; i; i = i->next_in_room) if (AWAKE(i) && SEND_OK(i)) send_to_char(mesg, i); send_to_room_snoopers(mesg, room, NULL, TO_SNOOP); } /* send to ppl in zone (outside) who arent building/writing/mailing */ void send_to_zone_outside(char *messg, int zone) { int r; for (r = 0; r < top_of_world; r++) if (world[r].zone == zone && WEATHER_ROOM(r)) send_to_room_not_busy(messg, r); } void send_to_room_except(char *messg, int room, chdata *ch) { chdata *i; for (i = world[room].people; i; i = i->next_in_room) if (i != ch) send_to_char(messg, i); } void send_to_room_except_two(char *messg, int room, chdata *ch1, chdata *ch2) { chdata *i; for (i = world[room].people; i; i = i->next_in_room) if (i != ch1 && i != ch2) send_to_char(messg, i); } char *ACTNULL = "<NULL>"; #define CHECK_NULL(pointer, expression) \ if ((pointer) == NULL) i = ACTNULL; else i = (expression); /* higher-level communication: the act() function */ void perform_act(char *orig, chdata *ch, obdata *obj, void *vict_obj, chdata *to, int type) { register char *i = NULL, *buf; static char lbuf[MAX_STRING_LENGTH]; char cstr[MAX_STRING_LENGTH]; char *debug_string; buf = lbuf; debug_string = orig; // so we can log violating strings from beginning -roa for (;;) { if (*orig == '$') { switch (*(++orig)) { case 'n': i = PERS(ch, to); break; case 'N': CHECK_NULL(vict_obj, PERS((chdata *) vict_obj, to)); break; case 'm': i = HMHR(ch); break; case 'M': CHECK_NULL(vict_obj, HMHR((chdata *) vict_obj)); break; case 's': i = HSHR(ch); break; case 'S': CHECK_NULL(vict_obj, HSHR((chdata *) vict_obj)); break; case 'e': i = HSSH(ch); break; case 'E': CHECK_NULL(vict_obj, HSSH((chdata *) vict_obj)); break; case 'o': CHECK_NULL(obj, OBJN(obj, to)); break; case 'O': CHECK_NULL(vict_obj, OBJN((obdata *) vict_obj, to)); break; case 'p': CHECK_NULL(obj, OBJS(obj, to)); break; case 'P': CHECK_NULL(vict_obj, OBJS((obdata *) vict_obj, to)); break; case 'a': CHECK_NULL(obj, SANA(obj)); break; case 'A': CHECK_NULL(vict_obj, SANA((obdata *) vict_obj)); break; case 'T': CHECK_NULL(vict_obj, (char *) vict_obj); break; case 'F': CHECK_NULL(vict_obj, fname((char *) vict_obj)); break; case '$': i = "$"; break; default: mudlog("SYSERR: Illegal $-code to act():", BRF, LEV_IMM, FALSE); sprintf(buf1, "SYSERR: (%s)", debug_string); mudlog(buf1, BRF, LEV_IMM, FALSE); return; break; } while ((*buf = *(i++))) buf++; orig++; } else if (!(*(buf++) = *(orig++))) break; } *(--buf) = '\r'; *(++buf) = '\n'; *(++buf) = '\0'; CCAP(lbuf); // roa snoop/zone/world additions -jtrhone switch (type) { case TO_SNOOP: case TO_SNOOP_HIDE: send_to_room_snoopers(lbuf, to->in_room, to, type); return; default: break; } if (!INCHAT(to)) { color_decode(to, lbuf, cstr); send_to_q(cstr, to->desc); } } #define SENDOK(ch) ((ch)->desc && (AWAKE(ch) || sleep) && SEND_OK((ch))) void act(char *str, int hide_invisible, chdata *ch, obdata *obj, void *vict_obj, int type) { chdata *to; static int sleep; if (!str || !*str) return; /* * Warning: the following TO_SLEEP code is a hack. * * I wanted to be able to tell act to deliver a message regardless of sleep * without adding an additional argument. TO_SLEEP is 128 (a single bit * high up). It's ONLY legal to combine TO_SLEEP with one other TO_x * command. It's not legal to combine TO_x's with each other otherwise. */ /* check if TO_SLEEP is there, and remove it if it is. */ if ((sleep = (type & TO_SLEEP))) type &= ~TO_SLEEP; if (type == TO_CHAR) { if (ch && SENDOK(ch)) perform_act(str, ch, obj, vict_obj, ch, type); return; } if (type == TO_VICT) { if ((to = (chdata *) vict_obj) && SENDOK(to)) perform_act(str, ch, obj, vict_obj, to, type); return; } /* ASSUMPTION: at this point we know type must be TO_NOTVICT or TO_ROOM */ if (ch && !IN_NOWHERE(ch)) to = world[ch->in_room].people; else if (obj && !IN_NOWHERE(obj)) to = world[obj->in_room].people; else { log("SYSERR: no valid target to act()!"); return; } if (type == TO_ROOM) // send to room snoopers, one time { if (hide_invisible) perform_act(str, ch, obj, vict_obj, ch, TO_SNOOP_HIDE); else perform_act(str, ch, obj, vict_obj, ch, TO_SNOOP); } switch(type) { case TO_ROOM: case TO_NOTVICT: for (; to; to = to->next_in_room) if (SENDOK(to) && !(hide_invisible && ch && !can_see(to, ch)) && (to != ch) && (type == TO_ROOM || (to != vict_obj))) perform_act(str, ch, obj, vict_obj, to, type); return; case TO_ZONE_MORTLOG: for (to=character_list; to && ch; to=to->next) if (SENDOK(to) && !(hide_invisible && ch && !can_see(to, ch)) && (to != ch) && PRF_FLAGGED(to, PRF_MORTLOG) && world[to->in_room].zone == world[ch->in_room].zone) perform_act(str, ch, obj, vict_obj, to, type); return; case TO_WORLD_MORTLOG: for (to=character_list; to && ch; to=to->next) if (SENDOK(to) && !(hide_invisible && ch && !can_see(to, ch)) && (to != ch) && PRF_FLAGGED(to, PRF_MORTLOG)) perform_act(str, ch, obj, vict_obj, to, type); return; } }