comm.c
1 /* ************************************************************************ 2 * File: comm.c Part of CircleMUD * 3 * Usage: Communication, socket handling, main(), central game loop * 4 * * 5 * All rights reserved. See license.doc for complete information. * 6 * * 7 * Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * 8 * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * 9 ************************************************************************ */ 10 11 #define __COMM_C__ 12 13 #include "conf.h" 14 #include "sysdep.h" 15 16 #if CIRCLE_GNU_LIBC_MEMORY_TRACK 17 # include <mcheck.h> 18 #endif 19 20 #ifdef CIRCLE_MACINTOSH /* Includes for the Macintosh */ 21 # define SIGPIPE 13 22 # define SIGALRM 14 23 /* GUSI headers */ 24 # include <sys/ioctl.h> 25 /* Codewarrior dependant */ 26 # include <SIOUX.h> 27 # include <console.h> 28 #endif 29 30 #ifdef CIRCLE_WINDOWS /* Includes for Win32 */ 31 # ifdef __BORLANDC__ 32 # include <dir.h> 33 # else /* MSVC */ 34 # include <direct.h> 35 # endif 36 # include <mmsystem.h> 37 #endif /* CIRCLE_WINDOWS */ 38 39 #ifdef CIRCLE_AMIGA /* Includes for the Amiga */ 40 # include <sys/ioctl.h> 41 # include <clib/socket_protos.h> 42 #endif /* CIRCLE_AMIGA */ 43 44 #ifdef CIRCLE_ACORN /* Includes for the Acorn (RiscOS) */ 45 # include <socklib.h> 46 # include <inetlib.h> 47 # include <sys/ioctl.h> 48 #endif 49 50 /* 51 * Note, most includes for all platforms are in sysdep.h. The list of 52 * files that is included is controlled by conf.h for that platform. 53 */ 54 55 #include "structs.h" 56 #include "utils.h" 57 #include "comm.h" 58 #include "interpreter.h" 59 #include "handler.h" 60 #include "db.h" 61 #include "house.h" 62 63 #ifdef HAVE_ARPA_TELNET_H 64 #include <arpa/telnet.h> 65 #else 66 #include "telnet.h" 67 #endif 68 69 #ifndef INVALID_SOCKET 70 #define INVALID_SOCKET (-1) 71 #endif 72 73 /* externs */ 74 extern struct ban_list_element *ban_list; 75 extern int num_invalid; 76 extern char *GREETINGS; 77 extern const char *circlemud_version; 78 extern int circle_restrict; 79 extern int mini_mud; 80 extern int no_rent_check; 81 extern FILE *player_fl; 82 extern ush_int DFLT_PORT; 83 extern const char *DFLT_DIR; 84 extern const char *DFLT_IP; 85 extern const char *LOGNAME; 86 extern int max_playing; 87 extern int nameserver_is_slow; /* see config.c */ 88 extern int auto_save; /* see config.c */ 89 extern int autosave_time; /* see config.c */ 90 extern int *cmd_sort_info; 91 92 extern struct time_info_data time_info; /* In db.c */ 93 extern char *help; 94 95 /* local globals */ 96 struct descriptor_data *descriptor_list = NULL; /* master desc list */ 97 struct txt_block *bufpool = 0; /* pool of large output buffers */ 98 int buf_largecount = 0; /* # of large buffers which exist */ 99 int buf_overflows = 0; /* # of overflows of output */ 100 int buf_switches = 0; /* # of switches from small to large buf */ 101 int circle_shutdown = 0; /* clean shutdown */ 102 int circle_reboot = 0; /* reboot the game after a shutdown */ 103 int no_specials = 0; /* Suppress ass. of special routines */ 104 int max_players = 0; /* max descriptors available */ 105 int tics = 0; /* for extern checkpointing */ 106 int scheck = 0; /* for syntax checking mode */ 107 struct timeval null_time; /* zero-valued time structure */ 108 byte reread_wizlist; /* signal: SIGUSR1 */ 109 byte emergency_unban; /* signal: SIGUSR2 */ 110 FILE *logfile = NULL; /* Where to send the log messages. */ 111 const char *text_overflow = "**OVERFLOW**\r\n"; 112 113 /* functions in this file */ 114 RETSIGTYPE reread_wizlists(int sig); 115 RETSIGTYPE unrestrict_game(int sig); 116 RETSIGTYPE reap(int sig); 117 RETSIGTYPE checkpointing(int sig); 118 RETSIGTYPE hupsig(int sig); 119 ssize_t perform_socket_read(socket_t desc, char *read_point,size_t space_left); 120 ssize_t perform_socket_write(socket_t desc, const char *txt,size_t length); 121 void echo_off(struct descriptor_data *d); 122 void echo_on(struct descriptor_data *d); 123 void circle_sleep(struct timeval *timeout); 124 int get_from_q(struct txt_q *queue, char *dest, int *aliased); 125 void init_game(ush_int port); 126 void signal_setup(void); 127 void game_loop(socket_t mother_desc); 128 socket_t init_socket(ush_int port); 129 int new_descriptor(socket_t s); 130 int get_max_players(void); 131 int process_output(struct descriptor_data *t); 132 int process_input(struct descriptor_data *t); 133 void timediff(struct timeval *diff, struct timeval *a, struct timeval *b); 134 void timeadd(struct timeval *sum, struct timeval *a, struct timeval *b); 135 void flush_queues(struct descriptor_data *d); 136 void nonblock(socket_t s); 137 int perform_subst(struct descriptor_data *t, char *orig, char *subst); 138 void record_usage(void); 139 char *make_prompt(struct descriptor_data *point); 140 void check_idle_passwords(void); 141 void heartbeat(int pulse); 142 struct in_addr *get_bind_addr(void); 143 int parse_ip(const char *addr, struct in_addr *inaddr); 144 int set_sendbuf(socket_t s); 145 void setup_log(const char *filename, int fd); 146 int open_logfile(const char *filename, FILE *stderr_fp); 147 #if defined(POSIX) 148 sigfunc *my_signal(int signo, sigfunc *func); 149 #endif 150 151 /* extern fcnts */ 152 void reboot_wizlists(void); 153 void boot_world(void); 154 void affect_update(void); /* In magic.c */ 155 void mobile_activity(void); 156 void perform_violence(void); 157 void show_string(struct descriptor_data *d, char *input); 158 int isbanned(char *hostname); 159 void weather_and_time(int mode); 160 int perform_alias(struct descriptor_data *d, char *orig, size_t maxlen); 161 void clear_free_list(void); 162 void free_messages(void); 163 void Board_clear_all(void); 164 void free_social_messages(void); 165 void Free_Invalid_List(void); 166 167 #ifdef __CXREF__ 168 #undef FD_ZERO 169 #undef FD_SET 170 #undef FD_ISSET 171 #undef FD_CLR 172 #define FD_ZERO(x) 173 #define FD_SET(x, y) 0 174 #define FD_ISSET(x, y) 0 175 #define FD_CLR(x, y) 176 #endif 177 178 179 /*********************************************************************** 180 * main game loop and related stuff * 181 ***********************************************************************/ 182 183 #if defined(CIRCLE_WINDOWS) || defined(CIRCLE_MACINTOSH) 184 185 /* 186 * Windows doesn't have gettimeofday, so we'll simulate it. 187 * The Mac doesn't have gettimeofday either. 188 * Borland C++ warns: "Undefined structure 'timezone'" 189 */ 190 void gettimeofday(struct timeval *t, struct timezone *dummy) 191 { 192 #if defined(CIRCLE_WINDOWS) 193 DWORD millisec = GetTickCount(); 194 #elif defined(CIRCLE_MACINTOSH) 195 unsigned long int millisec; 196 millisec = (int)((float)TickCount() * 1000.0 / 60.0); 197 #endif 198 199 t->tv_sec = (int) (millisec / 1000); 200 t->tv_usec = (millisec % 1000) * 1000; 201 } 202 203 #endif /* CIRCLE_WINDOWS || CIRCLE_MACINTOSH */ 204 205 206 int main(int argc, char **argv) 207 { 208 ush_int port; 209 int pos = 1; 210 const char *dir; 211 212 #if CIRCLE_GNU_LIBC_MEMORY_TRACK 213 mtrace(); /* This must come before any use of malloc(). */ 214 #endif 215 216 #ifdef CIRCLE_MACINTOSH 217 /* 218 * ccommand() calls the command line/io redirection dialog box from 219 * Codewarriors's SIOUX library 220 */ 221 argc = ccommand(&argv); 222 /* Initialize the GUSI library calls. */ 223 GUSIDefaultSetup(); 224 #endif 225 226 port = DFLT_PORT; 227 dir = DFLT_DIR; 228 229 while ((pos < argc) && (*(argv[pos]) == '-')) { 230 switch (*(argv[pos] + 1)) { 231 case 'o': 232 if (*(argv[pos] + 2)) 233 LOGNAME = argv[pos] + 2; 234 else if (++pos < argc) 235 LOGNAME = argv[pos]; 236 else { 237 puts("SYSERR: File name to log to expected after option -o."); 238 exit(1); 239 } 240 break; 241 case 'd': 242 if (*(argv[pos] + 2)) 243 dir = argv[pos] + 2; 244 else if (++pos < argc) 245 dir = argv[pos]; 246 else { 247 puts("SYSERR: Directory arg expected after option -d."); 248 exit(1); 249 } 250 break; 251 case 'm': 252 mini_mud = 1; 253 no_rent_check = 1; 254 puts("Running in minimized mode & with no rent check."); 255 break; 256 case 'c': 257 scheck = 1; 258 puts("Syntax check mode enabled."); 259 break; 260 case 'q': 261 no_rent_check = 1; 262 puts("Quick boot mode -- rent check supressed."); 263 break; 264 case 'r': 265 circle_restrict = 1; 266 puts("Restricting game -- no new players allowed."); 267 break; 268 case 's': 269 no_specials = 1; 270 puts("Suppressing assignment of special routines."); 271 break; 272 case 'h': 273 /* From: Anil Mahajan <amahajan@proxicom.com> */ 274 printf("Usage: %s [-c] [-m] [-q] [-r] [-s] [-d pathname] [port #]\n" 275 " -c Enable syntax check mode.\n" 276 " -d <directory> Specify library directory (defaults to 'lib').\n" 277 " -h Print this command line argument help.\n" 278 " -m Start in mini-MUD mode.\n" 279 " -o <file> Write log to <file> instead of stderr.\n" 280 " -q Quick boot (doesn't scan rent for object limits)\n" 281 " -r Restrict MUD -- no new players allowed.\n" 282 " -s Suppress special procedure assignments.\n", 283 argv[0] 284 ); 285 exit(0); 286 default: 287 printf("SYSERR: Unknown option -%c in argument string.\n", *(argv[pos] + 1)); 288 break; 289 } 290 pos++; 291 } 292 293 if (pos < argc) { 294 if (!isdigit(*argv[pos])) { 295 printf("Usage: %s [-c] [-m] [-q] [-r] [-s] [-d pathname] [port #]\n", argv[0]); 296 exit(1); 297 } else if ((port = atoi(argv[pos])) <= 1024) { 298 printf("SYSERR: Illegal port number %d.\n", port); 299 exit(1); 300 } 301 } 302 303 /* All arguments have been parsed, try to open log file. */ 304 setup_log(LOGNAME, STDERR_FILENO); 305 306 /* 307 * Moved here to distinguish command line options and to show up 308 * in the log if stderr is redirected to a file. 309 */ 310 log("%s", circlemud_version); 311 312 if (chdir(dir) < 0) { 313 perror("SYSERR: Fatal error changing to data directory"); 314 exit(1); 315 } 316 log("Using %s as data directory.", dir); 317 318 if (scheck) 319 boot_world(); 320 else { 321 log("Running game on port %d.", port); 322 init_game(port); 323 } 324 325 log("Clearing game world."); 326 destroy_db(); 327 328 if (!scheck) { 329 log("Clearing other memory."); 330 free_player_index(); /* db.c */ 331 free_messages(); /* fight.c */ 332 clear_free_list(); /* mail.c */ 333 free_text_files(); /* db.c */ 334 Board_clear_all(); /* boards.c */ 335 free(cmd_sort_info); /* act.informative.c */ 336 free_social_messages(); /* act.social.c */ 337 free_help(); /* db.c */ 338 Free_Invalid_List(); /* ban.c */ 339 } 340 341 log("Done."); 342 return (0); 343 } 344 345 346 347 /* Init sockets, run game, and cleanup sockets */ 348 void init_game(ush_int port) 349 { 350 socket_t mother_desc; 351 352 /* We don't want to restart if we crash before we get up. */ 353 touch(KILLSCRIPT_FILE); 354 355 circle_srandom(time(0)); 356 357 log("Finding player limit."); 358 max_players = get_max_players(); 359 360 log("Opening mother connection."); 361 mother_desc = init_socket(port); 362 363 boot_db(); 364 365 #if defined(CIRCLE_UNIX) || defined(CIRCLE_MACINTOSH) 366 log("Signal trapping."); 367 signal_setup(); 368 #endif 369 370 /* If we made it this far, we will be able to restart without problem. */ 371 remove(KILLSCRIPT_FILE); 372 373 log("Entering game loop."); 374 375 game_loop(mother_desc); 376 377 Crash_save_all(); 378 379 log("Closing all sockets."); 380 while (descriptor_list) 381 close_socket(descriptor_list); 382 383 CLOSE_SOCKET(mother_desc); 384 fclose(player_fl); 385 386 log("Saving current MUD time."); 387 save_mud_time(&time_info); 388 389 if (circle_reboot) { 390 log("Rebooting."); 391 exit(52); /* what's so great about HHGTTG, anyhow? */ 392 } 393 log("Normal termination of game."); 394 } 395 396 397 398 /* 399 * init_socket sets up the mother descriptor - creates the socket, sets 400 * its options up, binds it, and listens. 401 */ 402 socket_t init_socket(ush_int port) 403 { 404 socket_t s; 405 struct sockaddr_in sa; 406 int opt; 407 408 #ifdef CIRCLE_WINDOWS 409 { 410 WORD wVersionRequested; 411 WSADATA wsaData; 412 413 wVersionRequested = MAKEWORD(1, 1); 414 415 if (WSAStartup(wVersionRequested, &wsaData) != 0) { 416 log("SYSERR: WinSock not available!"); 417 exit(1); 418 } 419 420 /* 421 * 4 = stdin, stdout, stderr, mother_desc. Windows might 422 * keep sockets and files separate, in which case this isn't 423 * necessary, but we will err on the side of caution. 424 */ 425 if ((wsaData.iMaxSockets - 4) < max_players) { 426 max_players = wsaData.iMaxSockets - 4; 427 } 428 log("Max players set to %d", max_players); 429 430 if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) { 431 log("SYSERR: Error opening network connection: Winsock error #%d", 432 WSAGetLastError()); 433 exit(1); 434 } 435 } 436 #else 437 /* 438 * Should the first argument to socket() be AF_INET or PF_INET? I don't 439 * know, take your pick. PF_INET seems to be more widely adopted, and 440 * Comer (_Internetworking with TCP/IP_) even makes a point to say that 441 * people erroneously use AF_INET with socket() when they should be using 442 * PF_INET. However, the man pages of some systems indicate that AF_INET 443 * is correct; some such as ConvexOS even say that you can use either one. 444 * All implementations I've seen define AF_INET and PF_INET to be the same 445 * number anyway, so the point is (hopefully) moot. 446 */ 447 448 if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) { 449 perror("SYSERR: Error creating socket"); 450 exit(1); 451 } 452 #endif /* CIRCLE_WINDOWS */ 453 454 #if defined(SO_REUSEADDR) && !defined(CIRCLE_MACINTOSH) 455 opt = 1; 456 if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)) < 0){ 457 perror("SYSERR: setsockopt REUSEADDR"); 458 exit(1); 459 } 460 #endif 461 462 set_sendbuf(s); 463 464 /* 465 * The GUSI sockets library is derived from BSD, so it defines 466 * SO_LINGER, even though setsockopt() is unimplimented. 467 * (from Dean Takemori <dean@UHHEPH.PHYS.HAWAII.EDU>) 468 */ 469 #if defined(SO_LINGER) && !defined(CIRCLE_MACINTOSH) 470 { 471 struct linger ld; 472 473 ld.l_onoff = 0; 474 ld.l_linger = 0; 475 if (setsockopt(s, SOL_SOCKET, SO_LINGER, (char *) &ld, sizeof(ld)) < 0) 476 perror("SYSERR: setsockopt SO_LINGER"); /* Not fatal I suppose. */ 477 } 478 #endif 479 480 /* Clear the structure */ 481 memset((char *)&sa, 0, sizeof(sa)); 482 483 sa.sin_family = AF_INET; 484 sa.sin_port = htons(port); 485 sa.sin_addr = *(get_bind_addr()); 486 487 if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) { 488 perror("SYSERR: bind"); 489 CLOSE_SOCKET(s); 490 exit(1); 491 } 492 nonblock(s); 493 listen(s, 5); 494 return (s); 495 } 496 497 498 int get_max_players(void) 499 { 500 #ifndef CIRCLE_UNIX 501 return (max_playing); 502 #else 503 504 int max_descs = 0; 505 const char *method; 506 507 /* 508 * First, we'll try using getrlimit/setrlimit. This will probably work 509 * on most systems. HAS_RLIMIT is defined in sysdep.h. 510 */ 511 #ifdef HAS_RLIMIT 512 { 513 struct rlimit limit; 514 515 /* find the limit of file descs */ 516 method = "rlimit"; 517 if (getrlimit(RLIMIT_NOFILE, &limit) < 0) { 518 perror("SYSERR: calling getrlimit"); 519 exit(1); 520 } 521 522 /* set the current to the maximum */ 523 limit.rlim_cur = limit.rlim_max; 524 if (setrlimit(RLIMIT_NOFILE, &limit) < 0) { 525 perror("SYSERR: calling setrlimit"); 526 exit(1); 527 } 528 #ifdef RLIM_INFINITY 529 if (limit.rlim_max == RLIM_INFINITY) 530 max_descs = max_playing + NUM_RESERVED_DESCS; 531 else 532 max_descs = MIN(max_playing + NUM_RESERVED_DESCS, limit.rlim_max); 533 #else 534 max_descs = MIN(max_playing + NUM_RESERVED_DESCS, limit.rlim_max); 535 #endif 536 } 537 538 #elif defined (OPEN_MAX) || defined(FOPEN_MAX) 539 #if !defined(OPEN_MAX) 540 #define OPEN_MAX FOPEN_MAX 541 #endif 542 method = "OPEN_MAX"; 543 max_descs = OPEN_MAX; /* Uh oh.. rlimit didn't work, but we have 544 * OPEN_MAX */ 545 #elif defined (_SC_OPEN_MAX) 546 /* 547 * Okay, you don't have getrlimit() and you don't have OPEN_MAX. Time to 548 * try the POSIX sysconf() function. (See Stevens' _Advanced Programming 549 * in the UNIX Environment_). 550 */ 551 method = "POSIX sysconf"; 552 errno = 0; 553 if ((max_descs = sysconf(_SC_OPEN_MAX)) < 0) { 554 if (errno == 0) 555 max_descs = max_playing + NUM_RESERVED_DESCS; 556 else { 557 perror("SYSERR: Error calling sysconf"); 558 exit(1); 559 } 560 } 561 #else 562 /* if everything has failed, we'll just take a guess */ 563 method = "random guess"; 564 max_descs = max_playing + NUM_RESERVED_DESCS; 565 #endif 566 567 /* now calculate max _players_ based on max descs */ 568 max_descs = MIN(max_playing, max_descs - NUM_RESERVED_DESCS); 569 570 if (max_descs <= 0) { 571 log("SYSERR: Non-positive max player limit! (Set at %d using %s).", 572 max_descs, method); 573 exit(1); 574 } 575 log(" Setting player limit to %d using %s.", max_descs, method); 576 return (max_descs); 577 #endif /* CIRCLE_UNIX */ 578 } 579 580 581 582 /* 583 * game_loop contains the main loop which drives the entire MUD. It 584 * cycles once every 0.10 seconds and is responsible for accepting new 585 * new connections, polling existing connections for input, dequeueing 586 * output and sending it out to players, and calling "heartbeat" functions 587 * such as mobile_activity(). 588 */ 589 void game_loop(socket_t mother_desc) 590 { 591 fd_set input_set, output_set, exc_set, null_set; 592 struct timeval last_time, opt_time, process_time, temp_time; 593 struct timeval before_sleep, now, timeout; 594 char comm[MAX_INPUT_LENGTH]; 595 struct descriptor_data *d, *next_d; 596 int pulse = 0, missed_pulses, maxdesc, aliased; 597 598 /* initialize various time values */ 599 null_time.tv_sec = 0; 600 null_time.tv_usec = 0; 601 opt_time.tv_usec = OPT_USEC; 602 opt_time.tv_sec = 0; 603 FD_ZERO(&null_set); 604 605 gettimeofday(&last_time, (struct timezone *) 0); 606 607 /* The Main Loop. The Big Cheese. The Top Dog. The Head Honcho. The.. */ 608 while (!circle_shutdown) { 609 610 /* Sleep if we don't have any connections */ 611 if (descriptor_list == NULL) { 612 log("No connections. Going to sleep."); 613 FD_ZERO(&input_set); 614 FD_SET(mother_desc, &input_set); 615 if (select(mother_desc + 1, &input_set, (fd_set *) 0, (fd_set *) 0, NULL) < 0) { 616 if (errno == EINTR) 617 log("Waking up to process signal."); 618 else 619 perror("SYSERR: Select coma"); 620 } else 621 log("New connection. Waking up."); 622 gettimeofday(&last_time, (struct timezone *) 0); 623 } 624 /* Set up the input, output, and exception sets for select(). */ 625 FD_ZERO(&input_set); 626 FD_ZERO(&output_set); 627 FD_ZERO(&exc_set); 628 FD_SET(mother_desc, &input_set); 629 630 maxdesc = mother_desc; 631 for (d = descriptor_list; d; d = d->next) { 632 #ifndef CIRCLE_WINDOWS 633 if (d->descriptor > maxdesc) 634 maxdesc = d->descriptor; 635 #endif 636 FD_SET(d->descriptor, &input_set); 637 FD_SET(d->descriptor, &output_set); 638 FD_SET(d->descriptor, &exc_set); 639 } 640 641 /* 642 * At this point, we have completed all input, output and heartbeat 643 * activity from the previous iteration, so we have to put ourselves 644 * to sleep until the next 0.1 second tick. The first step is to 645 * calculate how long we took processing the previous iteration. 646 */ 647 648 gettimeofday(&before_sleep, (struct timezone *) 0); /* current time */ 649 timediff(&process_time, &before_sleep, &last_time); 650 651 /* 652 * If we were asleep for more than one pass, count missed pulses and sleep 653 * until we're resynchronized with the next upcoming pulse. 654 */ 655 if (process_time.tv_sec == 0 && process_time.tv_usec < OPT_USEC) { 656 missed_pulses = 0; 657 } else { 658 missed_pulses = process_time.tv_sec * PASSES_PER_SEC; 659 missed_pulses += process_time.tv_usec / OPT_USEC; 660 process_time.tv_sec = 0; 661 process_time.tv_usec = process_time.tv_usec % OPT_USEC; 662 } 663 664 /* Calculate the time we should wake up */ 665 timediff(&temp_time, &opt_time, &process_time); 666 timeadd(&last_time, &before_sleep, &temp_time); 667 668 /* Now keep sleeping until that time has come */ 669 gettimeofday(&now, (struct timezone *) 0); 670 timediff(&timeout, &last_time, &now); 671 672 /* Go to sleep */ 673 do { 674 circle_sleep(&timeout); 675 gettimeofday(&now, (struct timezone *) 0); 676 timediff(&timeout, &last_time, &now); 677 } while (timeout.tv_usec || timeout.tv_sec); 678 679 /* Poll (without blocking) for new input, output, and exceptions */ 680 if (select(maxdesc + 1, &input_set, &output_set, &exc_set, &null_time) < 0) { 681 perror("SYSERR: Select poll"); 682 return; 683 } 684 /* If there are new connections waiting, accept them. */ 685 if (FD_ISSET(mother_desc, &input_set)) 686 new_descriptor(mother_desc); 687 688 /* Kick out the freaky folks in the exception set and marked for close */ 689 for (d = descriptor_list; d; d = next_d) { 690 next_d = d->next; 691 if (FD_ISSET(d->descriptor, &exc_set)) { 692 FD_CLR(d->descriptor, &input_set); 693 FD_CLR(d->descriptor, &output_set); 694 close_socket(d); 695 } 696 } 697 698 /* Process descriptors with input pending */ 699 for (d = descriptor_list; d; d = next_d) { 700 next_d = d->next; 701 if (FD_ISSET(d->descriptor, &input_set)) 702 if (process_input(d) < 0) 703 close_socket(d); 704 } 705 706 /* Process commands we just read from process_input */ 707 for (d = descriptor_list; d; d = next_d) { 708 next_d = d->next; 709 710 /* 711 * Not combined to retain --(d->wait) behavior. -gg 2/20/98 712 * If no wait state, no subtraction. If there is a wait 713 * state then 1 is subtracted. Therefore we don't go less 714 * than 0 ever and don't require an 'if' bracket. -gg 2/27/99 715 */ 716 if (d->character) { 717 GET_WAIT_STATE(d->character) -= (GET_WAIT_STATE(d->character) > 0); 718 719 if (GET_WAIT_STATE(d->character)) 720 continue; 721 } 722 723 if (!get_from_q(&d->input, comm, &aliased)) 724 continue; 725 726 if (d->character) { 727 /* Reset the idle timer & pull char back from void if necessary */ 728 d->character->char_specials.timer = 0; 729 if (STATE(d) == CON_PLAYING && GET_WAS_IN(d->character) != NOWHERE) { 730 if (IN_ROOM(d->character) != NOWHERE) 731 char_from_room(d->character); 732 char_to_room(d->character, GET_WAS_IN(d->character)); 733 GET_WAS_IN(d->character) = NOWHERE; 734 act("$n has returned.", TRUE, d->character, 0, 0, TO_ROOM); 735 } 736 GET_WAIT_STATE(d->character) = 1; 737 } 738 d->has_prompt = FALSE; 739 740 if (d->str) /* Writing boards, mail, etc. */ 741 string_add(d, comm); 742 else if (d->showstr_count) /* Reading something w/ pager */ 743 show_string(d, comm); 744 else if (STATE(d) != CON_PLAYING) /* In menus, etc. */ 745 nanny(d, comm); 746 else { /* else: we're playing normally. */ 747 if (aliased) /* To prevent recursive aliases. */ 748 d->has_prompt = TRUE; /* To get newline before next cmd output. */ 749 else if (perform_alias(d, comm, sizeof(comm))) /* Run it through aliasing system */ 750 get_from_q(&d->input, comm, &aliased); 751 command_interpreter(d->character, comm); /* Send it to interpreter */ 752 } 753 } 754 755 /* Send queued output out to the operating system (ultimately to user). */ 756 for (d = descriptor_list; d; d = next_d) { 757 next_d = d->next; 758 if (*(d->output) && FD_ISSET(d->descriptor, &output_set)) { 759 /* Output for this player is ready. */ 760 761 process_output(d); 762 if (d->bufptr == 0) /* All output sent. */ 763 d->has_prompt = TRUE; 764 } 765 } 766 767 /* Print prompts for other descriptors who had no other output */ 768 for (d = descriptor_list; d; d = d->next) { 769 if (!d->has_prompt && d->bufptr == 0) { 770 write_to_descriptor(d->descriptor, make_prompt(d)); 771 d->has_prompt = TRUE; 772 } 773 } 774 775 /* Kick out folks in the CON_CLOSE or CON_DISCONNECT state */ 776 for (d = descriptor_list; d; d = next_d) { 777 next_d = d->next; 778 if (STATE(d) == CON_CLOSE || STATE(d) == CON_DISCONNECT) 779 close_socket(d); 780 } 781 782 /* 783 * Now, we execute as many pulses as necessary--just one if we haven't 784 * missed any pulses, or make up for lost time if we missed a few 785 * pulses by sleeping for too long. 786 */ 787 missed_pulses++; 788 789 if (missed_pulses <= 0) { 790 log("SYSERR: **BAD** MISSED_PULSES NONPOSITIVE (%d), TIME GOING BACKWARDS!!", missed_pulses); 791 missed_pulses = 1; 792 } 793 794 /* If we missed more than 30 seconds worth of pulses, just do 30 secs */ 795 if (missed_pulses > 30 RL_SEC) { 796 log("SYSERR: Missed %d seconds worth of pulses.", missed_pulses / PASSES_PER_SEC); 797 missed_pulses = 30 RL_SEC; 798 } 799 800 /* Now execute the heartbeat functions */ 801 while (missed_pulses--) 802 heartbeat(++pulse); 803 804 /* Check for any signals we may have received. */ 805 if (reread_wizlist) { 806 reread_wizlist = FALSE; 807 mudlog(CMP, LVL_IMMORT, TRUE, "Signal received - rereading wizlists."); 808 reboot_wizlists(); 809 } 810 if (emergency_unban) { 811 emergency_unban = FALSE; 812 mudlog(BRF, LVL_IMMORT, TRUE, "Received SIGUSR2 - completely unrestricting game (emergent)"); 813 ban_list = NULL; 814 circle_restrict = 0; 815 num_invalid = 0; 816 } 817 818 /* Roll pulse over after 10 hours */ 819 if (pulse >= (10 * 60 * 60 * PASSES_PER_SEC)) 820 pulse = 0; 821 822 #ifdef CIRCLE_UNIX 823 /* Update tics for deadlock protection (UNIX only) */ 824 tics++; 825 #endif 826 } 827 } 828 829 830 void heartbeat(int pulse) 831 { 832 static int mins_since_crashsave = 0; 833 834 if (!(pulse % PULSE_ZONE)) 835 zone_update(); 836 837 if (!(pulse % PULSE_IDLEPWD)) /* 15 seconds */ 838 check_idle_passwords(); 839 840 if (!(pulse % PULSE_MOBILE)) 841 mobile_activity(); 842 843 if (!(pulse % PULSE_VIOLENCE)) 844 perform_violence(); 845 846 if (!(pulse % (SECS_PER_MUD_HOUR * PASSES_PER_SEC))) { 847 weather_and_time(1); 848 affect_update(); 849 point_update(); 850 fflush(player_fl); 851 } 852 853 if (auto_save && !(pulse % PULSE_AUTOSAVE)) { /* 1 minute */ 854 if (++mins_since_crashsave >= autosave_time) { 855 mins_since_crashsave = 0; 856 Crash_save_all(); 857 House_save_all(); 858 } 859 } 860 861 if (!(pulse % PULSE_USAGE)) 862 record_usage(); 863 864 if (!(pulse % PULSE_TIMESAVE)) 865 save_mud_time(&time_info); 866 867 /* Every pulse! Don't want them to stink the place up... */ 868 extract_pending_chars(); 869 } 870 871 872 /* ****************************************************************** 873 * general utility stuff (for local use) * 874 ****************************************************************** */ 875 876 /* 877 * new code to calculate time differences, which works on systems 878 * for which tv_usec is unsigned (and thus comparisons for something 879 * being < 0 fail). Based on code submitted by ss@sirocco.cup.hp.com. 880 */ 881 882 /* 883 * code to return the time difference between a and b (a-b). 884 * always returns a nonnegative value (floors at 0). 885 */ 886 void timediff(struct timeval *rslt, struct timeval *a, struct timeval *b) 887 { 888 if (a->tv_sec < b->tv_sec) 889 *rslt = null_time; 890 else if (a->tv_sec == b->tv_sec) { 891 if (a->tv_usec < b->tv_usec) 892 *rslt = null_time; 893 else { 894 rslt->tv_sec = 0; 895 rslt->tv_usec = a->tv_usec - b->tv_usec; 896 } 897 } else { /* a->tv_sec > b->tv_sec */ 898 rslt->tv_sec = a->tv_sec - b->tv_sec; 899 if (a->tv_usec < b->tv_usec) { 900 rslt->tv_usec = a->tv_usec + 1000000 - b->tv_usec; 901 rslt->tv_sec--; 902 } else 903 rslt->tv_usec = a->tv_usec - b->tv_usec; 904 } 905 } 906 907 /* 908 * Add 2 time values. 909 * 910 * Patch sent by "d. hall" <dhall@OOI.NET> to fix 'static' usage. 911 */ 912 void timeadd(struct timeval *rslt, struct timeval *a, struct timeval *b) 913 { 914 rslt->tv_sec = a->tv_sec + b->tv_sec; 915 rslt->tv_usec = a->tv_usec + b->tv_usec; 916 917 while (rslt->tv_usec >= 1000000) { 918 rslt->tv_usec -= 1000000; 919 rslt->tv_sec++; 920 } 921 } 922 923 924 void record_usage(void) 925 { 926 int sockets_connected = 0, sockets_playing = 0; 927 struct descriptor_data *d; 928 929 for (d = descriptor_list; d; d = d->next) { 930 sockets_connected++; 931 if (STATE(d) == CON_PLAYING) 932 sockets_playing++; 933 } 934 935 log("nusage: %-3d sockets connected, %-3d sockets playing", 936 sockets_connected, sockets_playing); 937 938 #ifdef RUSAGE /* Not RUSAGE_SELF because it doesn't guarantee prototype. */ 939 { 940 struct rusage ru; 941 942 getrusage(RUSAGE_SELF, &ru); 943 log("rusage: user time: %ld sec, system time: %ld sec, max res size: %ld", 944 ru.ru_utime.tv_sec, ru.ru_stime.tv_sec, ru.ru_maxrss); 945 } 946 #endif 947 948 } 949 950 951 952 /* 953 * Turn off echoing (specific to telnet client) 954 */ 955 void echo_off(struct descriptor_data *d) 956 { 957 char off_string[] = 958 { 959 (char) IAC, 960 (char) WILL, 961 (char) TELOPT_ECHO, 962 (char) 0, 963 }; 964 965 write_to_output(d, "%s", off_string); 966 } 967 968 969 /* 970 * Turn on echoing (specific to telnet client) 971 */ 972 void echo_on(struct descriptor_data *d) 973 { 974 char on_string[] = 975 { 976 (char) IAC, 977 (char) WONT, 978 (char) TELOPT_ECHO, 979 (char) 0 980 }; 981 982 write_to_output(d, "%s", on_string); 983 } 984 985 986 char *make_prompt(struct descriptor_data *d) 987 { 988 static char prompt[MAX_PROMPT_LENGTH]; 989 990 /* Note, prompt is truncated at MAX_PROMPT_LENGTH chars (structs.h) */ 991 992 if (d->str) 993 strcpy(prompt, "] "); /* strcpy: OK (for 'MAX_PROMPT_LENGTH >= 3') */ 994 else if (d->showstr_count) { 995 snprintf(prompt, sizeof(prompt), 996 "\r\n[ Return to continue, (q)uit, (r)efresh, (b)ack, or page number (%d/%d) ]", 997 d->showstr_page, d->showstr_count); 998 } else if (STATE(d) == CON_PLAYING && !IS_NPC(d->character)) { 999 int count; 1000 size_t len = 0; 1001 1002 *prompt = '\0'; 1003 1004 if (GET_INVIS_LEV(d->character) && len < sizeof(prompt)) { 1005 count = snprintf(prompt + len, sizeof(prompt) - len, "i%d ", GET_INVIS_LEV(d->character)); 1006 if (count >= 0) 1007 len += count; 1008 } 1009 1010 if (PRF_FLAGGED(d->character, PRF_DISPHP) && len < sizeof(prompt)) { 1011 count = snprintf(prompt + len, sizeof(prompt) - len, "%dH ", GET_HIT(d->character)); 1012 if (count >= 0) 1013 len += count; 1014 } 1015 1016 if (PRF_FLAGGED(d->character, PRF_DISPMANA) && len < sizeof(prompt)) { 1017 count = snprintf(prompt + len, sizeof(prompt) - len, "%dM ", GET_MANA(d->character)); 1018 if (count >= 0) 1019 len += count; 1020 } 1021 1022 if (PRF_FLAGGED(d->character, PRF_DISPMOVE) && len < sizeof(prompt)) { 1023 count = snprintf(prompt + len, sizeof(prompt) - len, "%dV ", GET_MOVE(d->character)); 1024 if (count >= 0) 1025 len += count; 1026 } 1027 1028 if (len < sizeof(prompt)) 1029 strncat(prompt, "> ", sizeof(prompt) - len - 1); /* strncat: OK */ 1030 } else if (STATE(d) == CON_PLAYING && IS_NPC(d->character)) 1031 snprintf(prompt, sizeof(prompt), "%s> ", GET_NAME(d->character)); 1032 else 1033 *prompt = '\0'; 1034 1035 return (prompt); 1036 } 1037 1038 1039 /* 1040 * NOTE: 'txt' must be at most MAX_INPUT_LENGTH big. 1041 */ 1042 void write_to_q(const char *txt, struct txt_q *queue, int aliased) 1043 { 1044 struct txt_block *newt; 1045 1046 CREATE(newt, struct txt_block, 1); 1047 newt->text = strdup(txt); 1048 newt->aliased = aliased; 1049 1050 /* queue empty? */ 1051 if (!queue->head) { 1052 newt->next = NULL; 1053 queue->head = queue->tail = newt; 1054 } else { 1055 queue->tail->next = newt; 1056 queue->tail = newt; 1057 newt->next = NULL; 1058 } 1059 } 1060 1061 1062 /* 1063 * NOTE: 'dest' must be at least MAX_INPUT_LENGTH big. 1064 */ 1065 int get_from_q(struct txt_q *queue, char *dest, int *aliased) 1066 { 1067 struct txt_block *tmp; 1068 1069 /* queue empty? */ 1070 if (!queue->head) 1071 return (0); 1072 1073 strcpy(dest, queue->head->text); /* strcpy: OK (mutual MAX_INPUT_LENGTH) */ 1074 *aliased = queue->head->aliased; 1075 1076 tmp = queue->head; 1077 queue->head = queue->head->next; 1078 free(tmp->text); 1079 free(tmp); 1080 1081 return (1); 1082 } 1083 1084 1085 /* Empty the queues before closing connection */ 1086 void flush_queues(struct descriptor_data *d) 1087 { 1088 if (d->large_outbuf) { 1089 d->large_outbuf->next = bufpool; 1090 bufpool = d->large_outbuf; 1091 } 1092 while (d->input.head) { 1093 struct txt_block *tmp = d->input.head; 1094 d->input.head = d->input.head->next; 1095 free(tmp->text); 1096 free(tmp); 1097 } 1098 } 1099 1100 1101 /* Add a new string to a player's output queue. For outside use. */ 1102 size_t write_to_output(struct descriptor_data *t, const char *txt, ...) 1103 { 1104 va_list args; 1105 size_t left; 1106 1107 va_start(args, txt); 1108 left = vwrite_to_output(t, txt, args); 1109 va_end(args); 1110 1111 return left; 1112 } 1113 1114 1115 /* Add a new string to a player's output queue. */ 1116 size_t vwrite_to_output(struct descriptor_data *t, const char *format, va_list args) 1117 { 1118 static char txt[MAX_STRING_LENGTH]; 1119 size_t wantsize; 1120 int size; 1121 1122 /* if we're in the overflow state already, ignore this new output */ 1123 if (t->bufspace == 0) 1124 return (0); 1125 1126 wantsize = size = vsnprintf(txt, sizeof(txt), format, args); 1127 /* If exceeding the size of the buffer, truncate it for the overflow message */ 1128 if (size < 0 || wantsize >= sizeof(txt)) { 1129 size = sizeof(txt) - 1; 1130 strcpy(txt + size - strlen(text_overflow), text_overflow); /* strcpy: OK */ 1131 } 1132 1133 /* 1134 * If the text is too big to fit into even a large buffer, truncate 1135 * the new text to make it fit. (This will switch to the overflow 1136 * state automatically because t->bufspace will end up 0.) 1137 */ 1138 if (size + t->bufptr + 1 > LARGE_BUFSIZE) { 1139 size = LARGE_BUFSIZE - t->bufptr - 1; 1140 txt[size] = '\0'; 1141 buf_overflows++; 1142 } 1143 1144 /* 1145 * If we have enough space, just write to buffer and that's it! If the 1146 * text just barely fits, then it's switched to a large buffer instead. 1147 */ 1148 if (t->bufspace > size) { 1149 strcpy(t->output + t->bufptr, txt); /* strcpy: OK (size checked above) */ 1150 t->bufspace -= size; 1151 t->bufptr += size; 1152 return (t->bufspace); 1153 } 1154 1155 buf_switches++; 1156 1157 /* if the pool has a buffer in it, grab it */ 1158 if (bufpool != NULL) { 1159 t->large_outbuf = bufpool; 1160 bufpool = bufpool->next; 1161 } else { /* else create a new one */ 1162 CREATE(t->large_outbuf, struct txt_block, 1); 1163 CREATE(t->large_outbuf->text, char, LARGE_BUFSIZE); 1164 buf_largecount++; 1165 } 1166 1167 strcpy(t->large_outbuf->text, t->output); /* strcpy: OK (size checked previously) */ 1168 t->output = t->large_outbuf->text; /* make big buffer primary */ 1169 strcat(t->output, txt); /* strcat: OK (size checked) */ 1170 1171 /* set the pointer for the next write */ 1172 t->bufptr = strlen(t->output); 1173 1174 /* calculate how much space is left in the buffer */ 1175 t->bufspace = LARGE_BUFSIZE - 1 - t->bufptr; 1176 1177 return (t->bufspace); 1178 } 1179 1180 1181 1182 /* ****************************************************************** 1183 * socket handling * 1184 ****************************************************************** */ 1185 1186 1187 /* 1188 * get_bind_addr: Return a struct in_addr that should be used in our 1189 * call to bind(). If the user has specified a desired binding 1190 * address, we try to bind to it; otherwise, we bind to INADDR_ANY. 1191 * Note that inet_aton() is preferred over inet_addr() so we use it if 1192 * we can. If neither is available, we always bind to INADDR_ANY. 1193 */ 1194 1195 struct in_addr *get_bind_addr() 1196 { 1197 static struct in_addr bind_addr; 1198 1199 /* Clear the structure */ 1200 memset((char *) &bind_addr, 0, sizeof(bind_addr)); 1201 1202 /* If DLFT_IP is unspecified, use INADDR_ANY */ 1203 if (DFLT_IP == NULL) { 1204 bind_addr.s_addr = htonl(INADDR_ANY); 1205 } else { 1206 /* If the parsing fails, use INADDR_ANY */ 1207 if (!parse_ip(DFLT_IP, &bind_addr)) { 1208 log("SYSERR: DFLT_IP of %s appears to be an invalid IP address",DFLT_IP); 1209 bind_addr.s_addr = htonl(INADDR_ANY); 1210 } 1211 } 1212 1213 /* Put the address that we've finally decided on into the logs */ 1214 if (bind_addr.s_addr == htonl(INADDR_ANY)) 1215 log("Binding to all IP interfaces on this host."); 1216 else 1217 log("Binding only to IP address %s", inet_ntoa(bind_addr)); 1218 1219 return (&bind_addr); 1220 } 1221 1222 #ifdef HAVE_INET_ATON 1223 1224 /* 1225 * inet_aton's interface is the same as parse_ip's: 0 on failure, non-0 if 1226 * successful 1227 */ 1228 int parse_ip(const char *addr, struct in_addr *inaddr) 1229 { 1230 return (inet_aton(addr, inaddr)); 1231 } 1232 1233 #elif HAVE_INET_ADDR 1234 1235 /* inet_addr has a different interface, so we emulate inet_aton's */ 1236 int parse_ip(const char *addr, struct in_addr *inaddr) 1237 { 1238 long ip; 1239 1240 if ((ip = inet_addr(addr)) == -1) { 1241 return (0); 1242 } else { 1243 inaddr->s_addr = (unsigned long) ip; 1244 return (1); 1245 } 1246 } 1247 1248 #else 1249 1250 /* If you have neither function - sorry, you can't do specific binding. */ 1251 int parse_ip(const char *addr, struct in_addr *inaddr) 1252 { 1253 log("SYSERR: warning: you're trying to set DFLT_IP but your system has no " 1254 "functions to parse IP addresses (how bizarre!)"); 1255 return (0); 1256 } 1257 1258 #endif /* INET_ATON and INET_ADDR */ 1259 1260 1261 1262 /* Sets the kernel's send buffer size for the descriptor */ 1263 int set_sendbuf(socket_t s) 1264 { 1265 #if defined(SO_SNDBUF) && !defined(CIRCLE_MACINTOSH) 1266 int opt = MAX_SOCK_BUF; 1267 1268 if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *) &opt, sizeof(opt)) < 0) { 1269 perror("SYSERR: setsockopt SNDBUF"); 1270 return (-1); 1271 } 1272 1273 #if 0 1274 if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *) &opt, sizeof(opt)) < 0) { 1275 perror("SYSERR: setsockopt RCVBUF"); 1276 return (-1); 1277 } 1278 #endif 1279 1280 #endif 1281 1282 return (0); 1283 } 1284 1285 int new_descriptor(socket_t s) 1286 { 1287 socket_t desc; 1288 int sockets_connected = 0; 1289 socklen_t i; 1290 static int last_desc = 0; /* last descriptor number */ 1291 struct descriptor_data *newd; 1292 struct sockaddr_in peer; 1293 struct hostent *from; 1294 1295 /* accept the new connection */ 1296 i = sizeof(peer); 1297 if ((desc = accept(s, (struct sockaddr *) &peer, &i)) == INVALID_SOCKET) { 1298 perror("SYSERR: accept"); 1299 return (-1); 1300 } 1301 /* keep it from blocking */ 1302 nonblock(desc); 1303 1304 /* set the send buffer size */ 1305 if (set_sendbuf(desc) < 0) { 1306 CLOSE_SOCKET(desc); 1307 return (0); 1308 } 1309 1310 /* make sure we have room for it */ 1311 for (newd = descriptor_list; newd; newd = newd->next) 1312 sockets_connected++; 1313 1314 if (sockets_connected >= max_players) { 1315 write_to_descriptor(desc, "Sorry, CircleMUD is full right now... please try again later!\r\n"); 1316 CLOSE_SOCKET(desc); 1317 return (0); 1318 } 1319 /* create a new descriptor */ 1320 CREATE(newd, struct descriptor_data, 1); 1321 1322 /* find the sitename */ 1323 if (nameserver_is_slow || !(from = gethostbyaddr((char *) &peer.sin_addr, 1324 sizeof(peer.sin_addr), AF_INET))) { 1325 1326 /* resolution failed */ 1327 if (!nameserver_is_slow) 1328 perror("SYSERR: gethostbyaddr"); 1329 1330 /* find the numeric site address */ 1331 strncpy(newd->host, (char *)inet_ntoa(peer.sin_addr), HOST_LENGTH); /* strncpy: OK (n->host:HOST_LENGTH+1) */ 1332 *(newd->host + HOST_LENGTH) = '\0'; 1333 } else { 1334 strncpy(newd->host, from->h_name, HOST_LENGTH); /* strncpy: OK (n->host:HOST_LENGTH+1) */ 1335 *(newd->host + HOST_LENGTH) = '\0'; 1336 } 1337 1338 /* determine if the site is banned */ 1339 if (isbanned(newd->host) == BAN_ALL) { 1340 CLOSE_SOCKET(desc); 1341 mudlog(CMP, LVL_GOD, TRUE, "Connection attempt denied from [%s]", newd->host); 1342 free(newd); 1343 return (0); 1344 } 1345 #if 0 1346 /* 1347 * Log new connections - probably unnecessary, but you may want it. 1348 * Note that your immortals may wonder if they see a connection from 1349 * your site, but you are wizinvis upon login. 1350 */ 1351 mudlog(CMP, LVL_GOD, FALSE, "New connection from [%s]", newd->host); 1352 #endif 1353 1354 /* initialize descriptor data */ 1355 newd->descriptor = desc; 1356 newd->idle_tics = 0; 1357 newd->output = newd->small_outbuf; 1358 newd->bufspace = SMALL_BUFSIZE - 1; 1359 newd->login_time = time(0); 1360 *newd->output = '\0'; 1361 newd->bufptr = 0; 1362 newd->has_prompt = 1; /* prompt is part of greetings */ 1363 STATE(newd) = CON_GET_NAME; 1364 1365 /* 1366 * This isn't exactly optimal but allows us to make a design choice. 1367 * Do we embed the history in descriptor_data or keep it dynamically 1368 * allocated and allow a user defined history size? 1369 */ 1370 CREATE(newd->history, char *, HISTORY_SIZE); 1371 1372 if (++last_desc == 1000) 1373 last_desc = 1; 1374 newd->desc_num = last_desc; 1375 1376 /* prepend to list */ 1377 newd->next = descriptor_list; 1378 descriptor_list = newd; 1379 1380 write_to_output(newd, "%s", GREETINGS); 1381 1382 return (0); 1383 } 1384 1385 1386 /* 1387 * Send all of the output that we've accumulated for a player out to 1388 * the player's descriptor. 1389 * 1390 * 32 byte GARBAGE_SPACE in MAX_SOCK_BUF used for: 1391 * 2 bytes: prepended \r\n 1392 * 14 bytes: overflow message 1393 * 2 bytes: extra \r\n for non-comapct 1394 * 14 bytes: unused 1395 */ 1396 int process_output(struct descriptor_data *t) 1397 { 1398 char i[MAX_SOCK_BUF], *osb = i + 2; 1399 int result; 1400 1401 /* we may need this \r\n for later -- see below */ 1402 strcpy(i, "\r\n"); /* strcpy: OK (for 'MAX_SOCK_BUF >= 3') */ 1403 1404 /* now, append the 'real' output */ 1405 strcpy(osb, t->output); /* strcpy: OK (t->output:LARGE_BUFSIZE < osb:MAX_SOCK_BUF-2) */ 1406 1407 /* if we're in the overflow state, notify the user */ 1408 if (t->bufspace == 0) 1409 strcat(osb, "**OVERFLOW**\r\n"); /* strcpy: OK (osb:MAX_SOCK_BUF-2 reserves space) */ 1410 1411 /* add the extra CRLF if the person isn't in compact mode */ 1412 if (STATE(t) == CON_PLAYING && t->character && !IS_NPC(t->character) && !PRF_FLAGGED(t->character, PRF_COMPACT)) 1413 strcat(osb, "\r\n"); /* strcpy: OK (osb:MAX_SOCK_BUF-2 reserves space) */ 1414 1415 /* add a prompt */ 1416 strcat(i, make_prompt(t)); /* strcpy: OK (i:MAX_SOCK_BUF reserves space) */ 1417 1418 /* 1419 * now, send the output. If this is an 'interruption', use the prepended 1420 * CRLF, otherwise send the straight output sans CRLF. 1421 */ 1422 if (t->has_prompt) { 1423 t->has_prompt = FALSE; 1424 result = write_to_descriptor(t->descriptor, i); 1425 if (result >= 2) 1426 result -= 2; 1427 } else 1428 result = write_to_descriptor(t->descriptor, osb); 1429 1430 if (result < 0) { /* Oops, fatal error. Bye! */ 1431 close_socket(t); 1432 return (-1); 1433 } else if (result == 0) /* Socket buffer full. Try later. */ 1434 return (0); 1435 1436 /* Handle snooping: prepend "% " and send to snooper. */ 1437 if (t->snoop_by) 1438 write_to_output(t->snoop_by, "%% %*s%%%%", result, t->output); 1439 1440 /* The common case: all saved output was handed off to the kernel buffer. */ 1441 if (result >= t->bufptr) { 1442 /* 1443 * if we were using a large buffer, put the large buffer on the buffer pool 1444 * and switch back to the small one 1445 */ 1446 if (t->large_outbuf) { 1447 t->large_outbuf->next = bufpool; 1448 bufpool = t->large_outbuf; 1449 t->large_outbuf = NULL; 1450 t->output = t->small_outbuf; 1451 } 1452 /* reset total bufspace back to that of a small buffer */ 1453 t->bufspace = SMALL_BUFSIZE - 1; 1454 t->bufptr = 0; 1455 *(t->output) = '\0'; 1456 1457 /* 1458 * If the overflow message or prompt were partially written, try to save 1459 * them. There will be enough space for them if this is true. 'result' 1460 * is effectively unsigned here anyway. 1461 */ 1462 if ((unsigned int)result < strlen(osb)) { 1463 size_t savetextlen = strlen(osb + result); 1464 1465 strcat(t->output, osb + result); 1466 t->bufptr -= savetextlen; 1467 t->bufspace += savetextlen; 1468 } 1469 1470 } else { 1471 /* Not all data in buffer sent. result < output buffersize. */ 1472 1473 strcpy(t->output, t->output + result); /* strcpy: OK (overlap) */ 1474 t->bufptr -= result; 1475 t->bufspace += result; 1476 } 1477 1478 return (result); 1479 } 1480 1481 1482 /* 1483 * perform_socket_write: takes a descriptor, a pointer to text, and a 1484 * text length, and tries once to send that text to the OS. This is 1485 * where we stuff all the platform-dependent stuff that used to be 1486 * ugly #ifdef's in write_to_descriptor(). 1487 * 1488 * This function must return: 1489 * 1490 * -1 If a fatal error was encountered in writing to the descriptor. 1491 * 0 If a transient failure was encountered (e.g. socket buffer full). 1492 * >0 To indicate the number of bytes successfully written, possibly 1493 * fewer than the number the caller requested be written. 1494 * 1495 * Right now there are two versions of this function: one for Windows, 1496 * and one for all other platforms. 1497 */ 1498 1499 #if defined(CIRCLE_WINDOWS) 1500 1501 ssize_t perform_socket_write(socket_t desc, const char *txt, size_t length) 1502 { 1503 ssize_t result; 1504 1505 result = send(desc, txt, length, 0); 1506 1507 if (result > 0) { 1508 /* Write was sucessful */ 1509 return (result); 1510 } 1511 1512 if (result == 0) { 1513 /* This should never happen! */ 1514 log("SYSERR: Huh?? write() returned 0??? Please report this!"); 1515 return (-1); 1516 } 1517 1518 /* result < 0: An error was encountered. */ 1519 1520 /* Transient error? */ 1521 if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINTR) 1522 return (0); 1523 1524 /* Must be a fatal error. */ 1525 return (-1); 1526 } 1527 1528 #else 1529 1530 #if defined(CIRCLE_ACORN) 1531 #define write socketwrite 1532 #endif 1533 1534 /* perform_socket_write for all Non-Windows platforms */ 1535 ssize_t perform_socket_write(socket_t desc, const char *txt, size_t length) 1536 { 1537 ssize_t result; 1538 1539 result = write(desc, txt, length); 1540 1541 if (result > 0) { 1542 /* Write was successful. */ 1543 return (result); 1544 } 1545 1546 if (result == 0) { 1547 /* This should never happen! */ 1548 log("SYSERR: Huh?? write() returned 0??? Please report this!"); 1549 return (-1); 1550 } 1551 1552 /* 1553 * result < 0, so an error was encountered - is it transient? 1554 * Unfortunately, different systems use different constants to 1555 * indicate this. 1556 */ 1557 1558 #ifdef EAGAIN /* POSIX */ 1559 if (errno == EAGAIN) 1560 return (0); 1561 #endif 1562 1563 #ifdef EWOULDBLOCK /* BSD */ 1564 if (errno == EWOULDBLOCK) 1565 return (0); 1566 #endif 1567 1568 #ifdef EDEADLK /* Macintosh */ 1569 if (errno == EDEADLK) 1570 return (0); 1571 #endif 1572 1573 /* Looks like the error was fatal. Too bad. */ 1574 return (-1); 1575 } 1576 1577 #endif /* CIRCLE_WINDOWS */ 1578 1579 1580 /* 1581 * write_to_descriptor takes a descriptor, and text to write to the 1582 * descriptor. It keeps calling the system-level write() until all 1583 * the text has been delivered to the OS, or until an error is 1584 * encountered. 1585 * 1586 * Returns: 1587 * >=0 If all is well and good. 1588 * -1 If an error was encountered, so that the player should be cut off. 1589 */ 1590 int write_to_descriptor(socket_t desc, const char *txt) 1591 { 1592 ssize_t bytes_written; 1593 size_t total = strlen(txt), write_total = 0; 1594 1595 while (total > 0) { 1596 bytes_written = perform_socket_write(desc, txt, total); 1597 1598 if (bytes_written < 0) { 1599 /* Fatal error. Disconnect the player. */ 1600 perror("SYSERR: Write to socket"); 1601 return (-1); 1602 } else if (bytes_written == 0) { 1603 /* Temporary failure -- socket buffer full. */ 1604 return (write_total); 1605 } else { 1606 txt += bytes_written; 1607 total -= bytes_written; 1608 write_total += bytes_written; 1609 } 1610 } 1611 1612 return (write_total); 1613 } 1614 1615 1616 /* 1617 * Same information about perform_socket_write applies here. I like 1618 * standards, there are so many of them. -gg 6/30/98 1619 */ 1620 ssize_t perform_socket_read(socket_t desc, char *read_point, size_t space_left) 1621 { 1622 ssize_t ret; 1623 1624 #if defined(CIRCLE_ACORN) 1625 ret = recv(desc, read_point, space_left, MSG_DONTWAIT); 1626 #elif defined(CIRCLE_WINDOWS) 1627 ret = recv(desc, read_point, space_left, 0); 1628 #else 1629 ret = read(desc, read_point, space_left); 1630 #endif 1631 1632 /* Read was successful. */ 1633 if (ret > 0) 1634 return (ret); 1635 1636 /* read() returned 0, meaning we got an EOF. */ 1637 if (ret == 0) { 1638 log("WARNING: EOF on socket read (connection broken by peer)"); 1639 return (-1); 1640 } 1641 1642 /* 1643 * read returned a value < 0: there was an error 1644 */ 1645 1646 #if defined(CIRCLE_WINDOWS) /* Windows */ 1647 if (WSAGetLastError() == WSAEWOULDBLOCK || WSAGetLastError() == WSAEINTR) 1648 return (0); 1649 #else 1650 1651 #ifdef EINTR /* Interrupted system call - various platforms */ 1652 if (errno == EINTR) 1653 return (0); 1654 #endif 1655 1656 #ifdef EAGAIN /* POSIX */ 1657 if (errno == EAGAIN) 1658 return (0); 1659 #endif 1660 1661 #ifdef EWOULDBLOCK /* BSD */ 1662 if (errno == EWOULDBLOCK) 1663 return (0); 1664 #endif /* EWOULDBLOCK */ 1665 1666 #ifdef EDEADLK /* Macintosh */ 1667 if (errno == EDEADLK) 1668 return (0); 1669 #endif 1670 1671 #ifdef ECONNRESET 1672 if (errno == ECONNRESET) 1673 return (-1); 1674 #endif 1675 1676 #endif /* CIRCLE_WINDOWS */ 1677 1678 /* 1679 * We don't know what happened, cut them off. This qualifies for 1680 * a SYSERR because we have no idea what happened at this point. 1681 */ 1682 perror("SYSERR: perform_socket_read: about to lose connection"); 1683 return (-1); 1684 } 1685 1686 /* 1687 * ASSUMPTION: There will be no newlines in the raw input buffer when this 1688 * function is called. We must maintain that before returning. 1689 * 1690 * Ever wonder why 'tmp' had '+8' on it? The crusty old code could write 1691 * MAX_INPUT_LENGTH+1 bytes to 'tmp' if there was a '$' as the final 1692 * character in the input buffer. This would also cause 'space_left' to 1693 * drop to -1, which wasn't very happy in an unsigned variable. Argh. 1694 * So to fix the above, 'tmp' lost the '+8' since it doesn't need it 1695 * and the code has been changed to reserve space by accepting one less 1696 * character. (Do you really need 256 characters on a line?) 1697 * -gg 1/21/2000 1698 */ 1699 int process_input(struct descriptor_data *t) 1700 { 1701 int buf_length, failed_subst; 1702 ssize_t bytes_read; 1703 size_t space_left; 1704 char *ptr, *read_point, *write_point, *nl_pos = NULL; 1705 char tmp[MAX_INPUT_LENGTH]; 1706 1707 /* first, find the point where we left off reading data */ 1708 buf_length = strlen(t->inbuf); 1709 read_point = t->inbuf + buf_length; 1710 space_left = MAX_RAW_INPUT_LENGTH - buf_length - 1; 1711 1712 do { 1713 if (space_left <= 0) { 1714 log("WARNING: process_input: about to close connection: input overflow"); 1715 return (-1); 1716 } 1717 1718 bytes_read = perform_socket_read(t->descriptor, read_point, space_left); 1719 1720 if (bytes_read < 0) /* Error, disconnect them. */ 1721 return (-1); 1722 else if (bytes_read == 0) /* Just blocking, no problems. */ 1723 return (0); 1724 1725 /* at this point, we know we got some data from the read */ 1726 1727 *(read_point + bytes_read) = '\0'; /* terminate the string */ 1728 1729 /* search for a newline in the data we just read */ 1730 for (ptr = read_point; *ptr && !nl_pos; ptr++) 1731 if (ISNEWL(*ptr)) 1732 nl_pos = ptr; 1733 1734 read_point += bytes_read; 1735 space_left -= bytes_read; 1736 1737 /* 1738 * on some systems such as AIX, POSIX-standard nonblocking I/O is broken, 1739 * causing the MUD to hang when it encounters input not terminated by a 1740 * newline. This was causing hangs at the Password: prompt, for example. 1741 * I attempt to compensate by always returning after the _first_ read, instead 1742 * of looping forever until a read returns -1. This simulates non-blocking 1743 * I/O because the result is we never call read unless we know from select() 1744 * that data is ready (process_input is only called if select indicates that 1745 * this descriptor is in the read set). JE 2/23/95. 1746 */ 1747 #if !defined(POSIX_NONBLOCK_BROKEN) 1748 } while (nl_pos == NULL); 1749 #else 1750 } while (0); 1751 1752 if (nl_pos == NULL) 1753 return (0); 1754 #endif /* POSIX_NONBLOCK_BROKEN */ 1755 1756 /* 1757 * okay, at this point we have at least one newline in the string; now we 1758 * can copy the formatted data to a new array for further processing. 1759 */ 1760 1761 read_point = t->inbuf; 1762 1763 while (nl_pos != NULL) { 1764 write_point = tmp; 1765 space_left = MAX_INPUT_LENGTH - 1; 1766 1767 /* The '> 1' reserves room for a '$ => $$' expansion. */ 1768 for (ptr = read_point; (space_left > 1) && (ptr < nl_pos); ptr++) { 1769 if (*ptr == '\b' || *ptr == 127) { /* handle backspacing or delete key */ 1770 if (write_point > tmp) { 1771 if (*(--write_point) == '$') { 1772 write_point--; 1773 space_left += 2; 1774 } else 1775 space_left++; 1776 } 1777 } else if (isascii(*ptr) && isprint(*ptr)) { 1778 if ((*(write_point++) = *ptr) == '$') { /* copy one character */ 1779 *(write_point++) = '$'; /* if it's a $, double it */ 1780 space_left -= 2; 1781 } else 1782 space_left--; 1783 } 1784 } 1785 1786 *write_point = '\0'; 1787 1788 if ((space_left <= 0) && (ptr < nl_pos)) { 1789 char buffer[MAX_INPUT_LENGTH + 64]; 1790 1791 snprintf(buffer, sizeof(buffer), "Line too long. Truncated to:\r\n%s\r\n", tmp); 1792 if (write_to_descriptor(t->descriptor, buffer) < 0) 1793 return (-1); 1794 } 1795 if (t->snoop_by) 1796 write_to_output(t->snoop_by, "%% %s\r\n", tmp); 1797 failed_subst = 0; 1798 1799 if (*tmp == '!' && !(*(tmp + 1))) /* Redo last command. */ 1800 strcpy(tmp, t->last_input); /* strcpy: OK (by mutual MAX_INPUT_LENGTH) */ 1801 else if (*tmp == '!' && *(tmp + 1)) { 1802 char *commandln = (tmp + 1); 1803 int starting_pos = t->history_pos, 1804 cnt = (t->history_pos == 0 ? HISTORY_SIZE - 1 : t->history_pos - 1); 1805 1806 skip_spaces(&commandln); 1807 for (; cnt != starting_pos; cnt--) { 1808 if (t->history[cnt] && is_abbrev(commandln, t->history[cnt])) { 1809 strcpy(tmp, t->history[cnt]); /* strcpy: OK (by mutual MAX_INPUT_LENGTH) */ 1810 strcpy(t->last_input, tmp); /* strcpy: OK (by mutual MAX_INPUT_LENGTH) */ 1811 write_to_output(t, "%s\r\n", tmp); 1812 break; 1813 } 1814 if (cnt == 0) /* At top, loop to bottom. */ 1815 cnt = HISTORY_SIZE; 1816 } 1817 } else if (*tmp == '^') { 1818 if (!(failed_subst = perform_subst(t, t->last_input, tmp))) 1819 strcpy(t->last_input, tmp); /* strcpy: OK (by mutual MAX_INPUT_LENGTH) */ 1820 } else { 1821 strcpy(t->last_input, tmp); /* strcpy: OK (by mutual MAX_INPUT_LENGTH) */ 1822 if (t->history[t->history_pos]) 1823 free(t->history[t->history_pos]); /* Clear the old line. */ 1824 t->history[t->history_pos] = strdup(tmp); /* Save the new. */ 1825 if (++t->history_pos >= HISTORY_SIZE) /* Wrap to top. */ 1826 t->history_pos = 0; 1827 } 1828 1829 if (!failed_subst) 1830 write_to_q(tmp, &t->input, 0); 1831 1832 /* find the end of this line */ 1833 while (ISNEWL(*nl_pos)) 1834 nl_pos++; 1835 1836 /* see if there's another newline in the input buffer */ 1837 read_point = ptr = nl_pos; 1838 for (nl_pos = NULL; *ptr && !nl_pos; ptr++) 1839 if (ISNEWL(*ptr)) 1840 nl_pos = ptr; 1841 } 1842 1843 /* now move the rest of the buffer up to the beginning for the next pass */ 1844 write_point = t->inbuf; 1845 while (*read_point) 1846 *(write_point++) = *(read_point++); 1847 *write_point = '\0'; 1848 1849 return (1); 1850 } 1851 1852 1853 1854 /* perform substitution for the '^..^' csh-esque syntax orig is the 1855 * orig string, i.e. the one being modified. subst contains the 1856 * substition string, i.e. "^telm^tell" 1857 */ 1858 int perform_subst(struct descriptor_data *t, char *orig, char *subst) 1859 { 1860 char newsub[MAX_INPUT_LENGTH + 5]; 1861 1862 char *first, *second, *strpos; 1863 1864 /* 1865 * first is the position of the beginning of the first string (the one 1866 * to be replaced 1867 */ 1868 first = subst + 1; 1869 1870 /* now find the second '^' */ 1871 if (!(second = strchr(first, '^'))) { 1872 write_to_output(t, "Invalid substitution.\r\n"); 1873 return (1); 1874 } 1875 /* terminate "first" at the position of the '^' and make 'second' point 1876 * to the beginning of the second string */ 1877 *(second++) = '\0'; 1878 1879 /* now, see if the contents of the first string appear in the original */ 1880 if (!(strpos = strstr(orig, first))) { 1881 write_to_output(t, "Invalid substitution.\r\n"); 1882 return (1); 1883 } 1884 /* now, we construct the new string for output. */ 1885 1886 /* first, everything in the original, up to the string to be replaced */ 1887 strncpy(newsub, orig, strpos - orig); /* strncpy: OK (newsub:MAX_INPUT_LENGTH+5 > orig:MAX_INPUT_LENGTH) */ 1888 newsub[strpos - orig] = '\0'; 1889 1890 /* now, the replacement string */ 1891 strncat(newsub, second, MAX_INPUT_LENGTH - strlen(newsub) - 1); /* strncpy: OK */ 1892 1893 /* now, if there's anything left in the original after the string to 1894 * replaced, copy that too. */ 1895 if (((strpos - orig) + strlen(first)) < strlen(orig)) 1896 strncat(newsub, strpos + strlen(first), MAX_INPUT_LENGTH - strlen(newsub) - 1); /* strncpy: OK */ 1897 1898 /* terminate the string in case of an overflow from strncat */ 1899 newsub[MAX_INPUT_LENGTH - 1] = '\0'; 1900 strcpy(subst, newsub); /* strcpy: OK (by mutual MAX_INPUT_LENGTH) */ 1901 1902 return (0); 1903 } 1904 1905 1906 1907 void close_socket(struct descriptor_data *d) 1908 { 1909 struct descriptor_data *temp; 1910 1911 REMOVE_FROM_LIST(d, descriptor_list, next); 1912 CLOSE_SOCKET(d->descriptor); 1913 flush_queues(d); 1914 1915 /* Forget snooping */ 1916 if (d->snooping) 1917 d->snooping->snoop_by = NULL; 1918 1919 if (d->snoop_by) { 1920 write_to_output(d->snoop_by, "Your victim is no longer among us.\r\n"); 1921 d->snoop_by->snooping = NULL; 1922 } 1923 1924 if (d->character) { 1925 /* If we're switched, this resets the mobile taken. */ 1926 d->character->desc = NULL; 1927 1928 /* Plug memory leak, from Eric Green. */ 1929 if (!IS_NPC(d->character) && PLR_FLAGGED(d->character, PLR_MAILING) && d->str) { 1930 if (*(d->str)) 1931 free(*(d->str)); 1932 free(d->str); 1933 } 1934 1935 if (STATE(d) == CON_PLAYING || STATE(d) == CON_DISCONNECT) { 1936 struct char_data *link_challenged = d->original ? d->original : d->character; 1937 1938 /* We are guaranteed to have a person. */ 1939 act("$n has lost $s link.", TRUE, link_challenged, 0, 0, TO_ROOM); 1940 save_char(link_challenged); 1941 mudlog(NRM, MAX(LVL_IMMORT, GET_INVIS_LEV(link_challenged)), TRUE, "Closing link to: %s.", GET_NAME(link_challenged)); 1942 } else { 1943 mudlog(CMP, LVL_IMMORT, TRUE, "Losing player: %s.", GET_NAME(d->character) ? GET_NAME(d->character) : "<null>"); 1944 free_char(d->character); 1945 } 1946 } else 1947 mudlog(CMP, LVL_IMMORT, TRUE, "Losing descriptor without char."); 1948 1949 /* JE 2/22/95 -- part of my unending quest to make switch stable */ 1950 if (d->original && d->original->desc) 1951 d->original->desc = NULL; 1952 1953 /* Clear the command history. */ 1954 if (d->history) { 1955 int cnt; 1956 for (cnt = 0; cnt < HISTORY_SIZE; cnt++) 1957 if (d->history[cnt]) 1958 free(d->history[cnt]); 1959 free(d->history); 1960 } 1961 1962 if (d->showstr_head) 1963 free(d->showstr_head); 1964 if (d->showstr_count) 1965 free(d->showstr_vector); 1966 1967 free(d); 1968 } 1969 1970 1971 1972 void check_idle_passwords(void) 1973 { 1974 struct descriptor_data *d, *next_d; 1975 1976 for (d = descriptor_list; d; d = next_d) { 1977 next_d = d->next; 1978 if (STATE(d) != CON_PASSWORD && STATE(d) != CON_GET_NAME) 1979 continue; 1980 if (!d->idle_tics) { 1981 d->idle_tics++; 1982 continue; 1983 } else { 1984 echo_on(d); 1985 write_to_output(d, "\r\nTimed out... goodbye.\r\n"); 1986 STATE(d) = CON_CLOSE; 1987 } 1988 } 1989 } 1990 1991 1992 1993 /* 1994 * I tried to universally convert Circle over to POSIX compliance, but 1995 * alas, some systems are still straggling behind and don't have all the 1996 * appropriate defines. In particular, NeXT 2.x defines O_NDELAY but not 1997 * O_NONBLOCK. Krusty old NeXT machines! (Thanks to Michael Jones for 1998 * this and various other NeXT fixes.) 1999 */ 2000 2001 #if defined(CIRCLE_WINDOWS) 2002 2003 void nonblock(socket_t s) 2004 { 2005 unsigned long val = 1; 2006 ioctlsocket(s, FIONBIO, &val); 2007 } 2008 2009 #elif defined(CIRCLE_AMIGA) 2010 2011 void nonblock(socket_t s) 2012 { 2013 long val = 1; 2014 IoctlSocket(s, FIONBIO, &val); 2015 } 2016 2017 #elif defined(CIRCLE_ACORN) 2018 2019 void nonblock(socket_t s) 2020 { 2021 int val = 1; 2022 socket_ioctl(s, FIONBIO, &val); 2023 } 2024 2025 #elif defined(CIRCLE_VMS) 2026 2027 void nonblock(socket_t s) 2028 { 2029 int val = 1; 2030 2031 if (ioctl(s, FIONBIO, &val) < 0) { 2032 perror("SYSERR: Fatal error executing nonblock (comm.c)"); 2033 exit(1); 2034 } 2035 } 2036 2037 #elif defined(CIRCLE_UNIX) || defined(CIRCLE_OS2) || defined(CIRCLE_MACINTOSH) 2038 2039 #ifndef O_NONBLOCK 2040 #define O_NONBLOCK O_NDELAY 2041 #endif 2042 2043 void nonblock(socket_t s) 2044 { 2045 int flags; 2046 2047 flags = fcntl(s, F_GETFL, 0); 2048 flags |= O_NONBLOCK; 2049 if (fcntl(s, F_SETFL, flags) < 0) { 2050 perror("SYSERR: Fatal error executing nonblock (comm.c)"); 2051 exit(1); 2052 } 2053 } 2054 2055 #endif /* CIRCLE_UNIX || CIRCLE_OS2 || CIRCLE_MACINTOSH */ 2056 2057 2058 /* ****************************************************************** 2059 * signal-handling functions (formerly signals.c). UNIX only. * 2060 ****************************************************************** */ 2061 2062 #if defined(CIRCLE_UNIX) || defined(CIRCLE_MACINTOSH) 2063 2064 RETSIGTYPE reread_wizlists(int sig) 2065 { 2066 reread_wizlist = TRUE; 2067 } 2068 2069 2070 RETSIGTYPE unrestrict_game(int sig) 2071 { 2072 emergency_unban = TRUE; 2073 } 2074 2075 #ifdef CIRCLE_UNIX 2076 2077 /* clean up our zombie kids to avoid defunct processes */ 2078 RETSIGTYPE reap(int sig) 2079 { 2080 while (waitpid(-1, NULL, WNOHANG) > 0); 2081 2082 my_signal(SIGCHLD, reap); 2083 } 2084 2085 /* Dying anyway... */ 2086 RETSIGTYPE checkpointing(int sig) 2087 { 2088 if (!tics) { 2089 log("SYSERR: CHECKPOINT shutdown: tics not updated. (Infinite loop suspected)"); 2090 abort(); 2091 } else 2092 tics = 0; 2093 } 2094 2095 2096 /* Dying anyway... */ 2097 RETSIGTYPE hupsig(int sig) 2098 { 2099 log("SYSERR: Received SIGHUP, SIGINT, or SIGTERM. Shutting down..."); 2100 exit(1); /* perhaps something more elegant should 2101 * substituted */ 2102 } 2103 2104 #endif /* CIRCLE_UNIX */ 2105 2106 /* 2107 * This is an implementation of signal() using sigaction() for portability. 2108 * (sigaction() is POSIX; signal() is not.) Taken from Stevens' _Advanced 2109 * Programming in the UNIX Environment_. We are specifying that all system 2110 * calls _not_ be automatically restarted for uniformity, because BSD systems 2111 * do not restart select(), even if SA_RESTART is used. 2112 * 2113 * Note that NeXT 2.x is not POSIX and does not have sigaction; therefore, 2114 * I just define it to be the old signal. If your system doesn't have 2115 * sigaction either, you can use the same fix. 2116 * 2117 * SunOS Release 4.0.2 (sun386) needs this too, according to Tim Aldric. 2118 */ 2119 2120 #ifndef POSIX 2121 #define my_signal(signo, func) signal(signo, func) 2122 #else 2123 sigfunc *my_signal(int signo, sigfunc *func) 2124 { 2125 struct sigaction sact, oact; 2126 2127 sact.sa_handler = func; 2128 sigemptyset(&sact.sa_mask); 2129 sact.sa_flags = 0; 2130 #ifdef SA_INTERRUPT 2131 sact.sa_flags |= SA_INTERRUPT; /* SunOS */ 2132 #endif 2133 2134 if (sigaction(signo, &sact, &oact) < 0) 2135 return (SIG_ERR); 2136 2137 return (oact.sa_handler); 2138 } 2139 #endif /* POSIX */ 2140 2141 2142 void signal_setup(void) 2143 { 2144 #ifndef CIRCLE_MACINTOSH 2145 struct itimerval itime; 2146 struct timeval interval; 2147 2148 /* user signal 1: reread wizlists. Used by autowiz system. */ 2149 my_signal(SIGUSR1, reread_wizlists); 2150 2151 /* 2152 * user signal 2: unrestrict game. Used for emergencies if you lock 2153 * yourself out of the MUD somehow. (Duh...) 2154 */ 2155 my_signal(SIGUSR2, unrestrict_game); 2156 2157 /* 2158 * set up the deadlock-protection so that the MUD aborts itself if it gets 2159 * caught in an infinite loop for more than 3 minutes. 2160 */ 2161 interval.tv_sec = 180; 2162 interval.tv_usec = 0; 2163 itime.it_interval = interval; 2164 itime.it_value = interval; 2165 setitimer(ITIMER_VIRTUAL, &itime, NULL); 2166 my_signal(SIGVTALRM, checkpointing); 2167 2168 /* just to be on the safe side: */ 2169 my_signal(SIGHUP, hupsig); 2170 my_signal(SIGCHLD, reap); 2171 #endif /* CIRCLE_MACINTOSH */ 2172 my_signal(SIGINT, hupsig); 2173 my_signal(SIGTERM, hupsig); 2174 my_signal(SIGPIPE, SIG_IGN); 2175 my_signal(SIGALRM, SIG_IGN); 2176 } 2177 2178 #endif /* CIRCLE_UNIX || CIRCLE_MACINTOSH */ 2179 2180 /* **************************************************************** 2181 * Public routines for system-to-player-communication * 2182 **************************************************************** */ 2183 2184 size_t send_to_char(struct char_data *ch, const char *messg, ...) 2185 { 2186 if (ch->desc && messg && *messg) { 2187 size_t left; 2188 va_list args; 2189 2190 va_start(args, messg); 2191 left = vwrite_to_output(ch->desc, messg, args); 2192 va_end(args); 2193 return left; 2194 } 2195 return 0; 2196 } 2197 2198 2199 void send_to_all(const char *messg, ...) 2200 { 2201 struct descriptor_data *i; 2202 va_list args; 2203 2204 if (messg == NULL) 2205 return; 2206 2207 for (i = descriptor_list; i; i = i->next) { 2208 if (STATE(i) != CON_PLAYING) 2209 continue; 2210 2211 va_start(args, messg); 2212 vwrite_to_output(i, messg, args); 2213 va_end(args); 2214 } 2215 } 2216 2217 2218 void send_to_outdoor(const char *messg, ...) 2219 { 2220 struct descriptor_data *i; 2221 2222 if (!messg || !*messg) 2223 return; 2224 2225 for (i = descriptor_list; i; i = i->next) { 2226 va_list args; 2227 2228 if (STATE(i) != CON_PLAYING || i->character == NULL) 2229 continue; 2230 if (!AWAKE(i->character) || !OUTSIDE(i->character)) 2231 continue; 2232 2233 va_start(args, messg); 2234 vwrite_to_output(i, messg, args); 2235 va_end(args); 2236 } 2237 } 2238 2239 2240 2241 void send_to_room(room_rnum room, const char *messg, ...) 2242 { 2243 struct char_data *i; 2244 va_list args; 2245 2246 if (messg == NULL) 2247 return; 2248 2249 for (i = world[room].people; i; i = i->next_in_room) { 2250 if (!i->desc) 2251 continue; 2252 2253 va_start(args, messg); 2254 vwrite_to_output(i->desc, messg, args); 2255 va_end(args); 2256 } 2257 } 2258 2259 2260 2261 const char *ACTNULL = "<NULL>"; 2262 2263 #define CHECK_NULL(pointer, expression) \ 2264 if ((pointer) == NULL) i = ACTNULL; else i = (expression); 2265 2266 2267 /* higher-level communication: the act() function */ 2268 void perform_act(const char *orig, struct char_data *ch, struct obj_data *obj, 2269 const void *vict_obj, const struct char_data *to) 2270 { 2271 const char *i = NULL; 2272 char lbuf[MAX_STRING_LENGTH], *buf, *j; 2273 bool uppercasenext = FALSE; 2274 2275 buf = lbuf; 2276 2277 for (;;) { 2278 if (*orig == '$') { 2279 switch (*(++orig)) { 2280 case 'n': 2281 i = PERS(ch, to); 2282 break; 2283 case 'N': 2284 CHECK_NULL(vict_obj, PERS((const struct char_data *) vict_obj, to)); 2285 break; 2286 case 'm': 2287 i = HMHR(ch); 2288 break; 2289 case 'M': 2290 CHECK_NULL(vict_obj, HMHR((const struct char_data *) vict_obj)); 2291 break; 2292 case 's': 2293 i = HSHR(ch); 2294 break; 2295 case 'S': 2296 CHECK_NULL(vict_obj, HSHR((const struct char_data *) vict_obj)); 2297 break; 2298 case 'e': 2299 i = HSSH(ch); 2300 break; 2301 case 'E': 2302 CHECK_NULL(vict_obj, HSSH((const struct char_data *) vict_obj)); 2303 break; 2304 case 'o': 2305 CHECK_NULL(obj, OBJN(obj, to)); 2306 break; 2307 case 'O': 2308 CHECK_NULL(vict_obj, OBJN((const struct obj_data *) vict_obj, to)); 2309 break; 2310 case 'p': 2311 CHECK_NULL(obj, OBJS(obj, to)); 2312 break; 2313 case 'P': 2314 CHECK_NULL(vict_obj, OBJS((const struct obj_data *) vict_obj, to)); 2315 break; 2316 case 'a': 2317 CHECK_NULL(obj, SANA(obj)); 2318 break; 2319 case 'A': 2320 CHECK_NULL(vict_obj, SANA((const struct obj_data *) vict_obj)); 2321 break; 2322 case 'T': 2323 CHECK_NULL(vict_obj, (const char *) vict_obj); 2324 break; 2325 case 'F': 2326 CHECK_NULL(vict_obj, fname((const char *) vict_obj)); 2327 break; 2328 /* uppercase previous word */ 2329 case 'u': 2330 for (j=buf; j > lbuf && !isspace((int) *(j-1)); j--); 2331 if (j != buf) 2332 *j = UPPER(*j); 2333 i = ""; 2334 break; 2335 /* uppercase next word */ 2336 case 'U': 2337 uppercasenext = TRUE; 2338 i = ""; 2339 break; 2340 case '$': 2341 i = "$"; 2342 break; 2343 default: 2344 log("SYSERR: Illegal $-code to act(): %c", *orig); 2345 log("SYSERR: %s", orig); 2346 i = ""; 2347 break; 2348 } 2349 while ((*buf = *(i++))) 2350 { 2351 if (uppercasenext && !isspace((int) *buf)) 2352 { 2353 *buf = UPPER(*buf); 2354 uppercasenext = FALSE; 2355 } 2356 buf++; 2357 } 2358 orig++; 2359 } else if (!(*(buf++) = *(orig++))) { 2360 break; 2361 } else if (uppercasenext && !isspace((int) *(buf-1))) { 2362 *(buf-1) = UPPER(*(buf-1)); 2363 uppercasenext = FALSE; 2364 } 2365 } 2366 2367 *(--buf) = '\r'; 2368 *(++buf) = '\n'; 2369 *(++buf) = '\0'; 2370 2371 write_to_output(to->desc, "%s", CAP(lbuf)); 2372 } 2373 2374 2375 #define SENDOK(ch) ((ch)->desc && (to_sleeping || AWAKE(ch)) && \ 2376 (IS_NPC(ch) || !PLR_FLAGGED((ch), PLR_WRITING))) 2377 2378 void act(const char *str, int hide_invisible, struct char_data *ch, 2379 struct obj_data *obj, const void *vict_obj, int type) 2380 { 2381 const struct char_data *to; 2382 int to_sleeping; 2383 2384 if (!str || !*str) 2385 return; 2386 2387 /* 2388 * Warning: the following TO_SLEEP code is a hack. 2389 * 2390 * I wanted to be able to tell act to deliver a message regardless of sleep 2391 * without adding an additional argument. TO_SLEEP is 128 (a single bit 2392 * high up). It's ONLY legal to combine TO_SLEEP with one other TO_x 2393 * command. It's not legal to combine TO_x's with each other otherwise. 2394 * TO_SLEEP only works because its value "happens to be" a single bit; 2395 * do not change it to something else. In short, it is a hack. 2396 */ 2397 2398 /* check if TO_SLEEP is there, and remove it if it is. */ 2399 if ((to_sleeping = (type & TO_SLEEP))) 2400 type &= ~TO_SLEEP; 2401 2402 if (type == TO_CHAR) { 2403 if (ch && SENDOK(ch)) 2404 perform_act(str, ch, obj, vict_obj, ch); 2405 return; 2406 } 2407 2408 if (type == TO_VICT) { 2409 if ((to = (const struct char_data *) vict_obj) != NULL && SENDOK(to)) 2410 perform_act(str, ch, obj, vict_obj, to); 2411 return; 2412 } 2413 /* ASSUMPTION: at this point we know type must be TO_NOTVICT or TO_ROOM */ 2414 2415 if (ch && IN_ROOM(ch) != NOWHERE) 2416 to = world[IN_ROOM(ch)].people; 2417 else if (obj && IN_ROOM(obj) != NOWHERE) 2418 to = world[IN_ROOM(obj)].people; 2419 else { 2420 log("SYSERR: no valid target to act()!"); 2421 return; 2422 } 2423 2424 for (; to; to = to->next_in_room) { 2425 if (!SENDOK(to) || (to == ch)) 2426 continue; 2427 if (hide_invisible && ch && !CAN_SEE(to, ch)) 2428 continue; 2429 if (type != TO_ROOM && to == vict_obj) 2430 continue; 2431 perform_act(str, ch, obj, vict_obj, to); 2432 } 2433 } 2434 2435 2436 /* Prefer the file over the descriptor. */ 2437 void setup_log(const char *filename, int fd) 2438 { 2439 FILE *s_fp; 2440 2441 #if defined(__MWERKS__) || defined(__GNUC__) 2442 s_fp = stderr; 2443 #else 2444 if ((s_fp = fdopen(STDERR_FILENO, "w")) == NULL) { 2445 puts("SYSERR: Error opening stderr, trying stdout."); 2446 2447 if ((s_fp = fdopen(STDOUT_FILENO, "w")) == NULL) { 2448 puts("SYSERR: Error opening stdout, trying a file."); 2449 2450 /* If we don't have a file, try a default. */ 2451 if (filename == NULL || *filename == '\0') 2452 filename = "log/syslog"; 2453 } 2454 } 2455 #endif 2456 2457 if (filename == NULL || *filename == '\0') { 2458 /* No filename, set us up with the descriptor we just opened. */ 2459 logfile = s_fp; 2460 puts("Using file descriptor for logging."); 2461 return; 2462 } 2463 2464 /* We honor the default filename first. */ 2465 if (open_logfile(filename, s_fp)) 2466 return; 2467 2468 /* Well, that failed but we want it logged to a file so try a default. */ 2469 if (open_logfile("log/syslog", s_fp)) 2470 return; 2471 2472 /* Ok, one last shot at a file. */ 2473 if (open_logfile("syslog", s_fp)) 2474 return; 2475 2476 /* Erp, that didn't work either, just die. */ 2477 puts("SYSERR: Couldn't open anything to log to, giving up."); 2478 exit(1); 2479 } 2480 2481 int open_logfile(const char *filename, FILE *stderr_fp) 2482 { 2483 if (stderr_fp) /* freopen() the descriptor. */ 2484 logfile = freopen(filename, "w", stderr_fp); 2485 else 2486 logfile = fopen(filename, "w"); 2487 2488 if (logfile) { 2489 printf("Using log file '%s'%s.\n", 2490 filename, stderr_fp ? " with redirection" : ""); 2491 return (TRUE); 2492 } 2493 2494 printf("SYSERR: Error opening file '%s': %s\n", filename, strerror(errno)); 2495 return (FALSE); 2496 } 2497 2498 /* 2499 * This may not be pretty but it keeps game_loop() neater than if it was inline. 2500 */ 2501 #if defined(CIRCLE_WINDOWS) 2502 2503 void circle_sleep(struct timeval *timeout) 2504 { 2505 Sleep(timeout->tv_sec * 1000 + timeout->tv_usec / 1000); 2506 } 2507 2508 #else 2509 2510 void circle_sleep(struct timeval *timeout) 2511 { 2512 if (select(0, (fd_set *) 0, (fd_set *) 0, (fd_set *) 0, timeout) < 0) { 2513 if (errno != EINTR) { 2514 perror("SYSERR: Select sleep"); 2515 exit(1); 2516 } 2517 } 2518 } 2519 2520 #endif /* CIRCLE_WINDOWS */