| 1 | /** |
|---|
| 2 | * \file bsd.c |
|---|
| 3 | * |
|---|
| 4 | * \brief Network communication through BSD sockets for PennMUSH. |
|---|
| 5 | * |
|---|
| 6 | * While mysocket.c provides low-level functions for working with |
|---|
| 7 | * sockets, bsd.c focuses on player descriptors, a higher-level |
|---|
| 8 | * structure that tracks all information associated with a connection, |
|---|
| 9 | * and through which connection i/o is done. |
|---|
| 10 | * |
|---|
| 11 | * |
|---|
| 12 | */ |
|---|
| 13 | |
|---|
| 14 | #include "copyrite.h" |
|---|
| 15 | #include "config.h" |
|---|
| 16 | |
|---|
| 17 | #include <stdio.h> |
|---|
| 18 | #include <stdarg.h> |
|---|
| 19 | #ifdef I_SYS_TYPES |
|---|
| 20 | #include <sys/types.h> |
|---|
| 21 | #endif |
|---|
| 22 | #ifdef WIN32 |
|---|
| 23 | #define FD_SETSIZE 256 |
|---|
| 24 | #include <windows.h> |
|---|
| 25 | #include <winsock.h> |
|---|
| 26 | #include <io.h> |
|---|
| 27 | #include <process.h> |
|---|
| 28 | #define EINTR WSAEINTR |
|---|
| 29 | #define EWOULDBLOCK WSAEWOULDBLOCK |
|---|
| 30 | #define MAXHOSTNAMELEN 32 |
|---|
| 31 | #pragma warning( disable : 4761) /* disable warning re conversion */ |
|---|
| 32 | #else /* !WIN32 */ |
|---|
| 33 | #ifdef I_SYS_FILE |
|---|
| 34 | #include <sys/file.h> |
|---|
| 35 | #endif |
|---|
| 36 | #ifdef I_SYS_TIME |
|---|
| 37 | #include <sys/time.h> |
|---|
| 38 | #ifdef TIME_WITH_SYS_TIME |
|---|
| 39 | #include <time.h> |
|---|
| 40 | #endif |
|---|
| 41 | #else |
|---|
| 42 | #include <time.h> |
|---|
| 43 | #endif |
|---|
| 44 | #include <sys/ioctl.h> |
|---|
| 45 | #include <errno.h> |
|---|
| 46 | #ifdef I_SYS_SOCKET |
|---|
| 47 | #include <sys/socket.h> |
|---|
| 48 | #endif |
|---|
| 49 | #ifdef I_NETINET_IN |
|---|
| 50 | #include <netinet/in.h> |
|---|
| 51 | #endif |
|---|
| 52 | #ifdef I_NETDB |
|---|
| 53 | #include <netdb.h> |
|---|
| 54 | #endif |
|---|
| 55 | #ifdef I_SYS_PARAM |
|---|
| 56 | #include <sys/param.h> |
|---|
| 57 | #endif |
|---|
| 58 | #ifdef I_SYS_STAT |
|---|
| 59 | #include <sys/stat.h> |
|---|
| 60 | #endif |
|---|
| 61 | #endif /* !WIN32 */ |
|---|
| 62 | #include <fcntl.h> |
|---|
| 63 | #include <ctype.h> |
|---|
| 64 | #include <signal.h> |
|---|
| 65 | #include <string.h> |
|---|
| 66 | #include <stdlib.h> |
|---|
| 67 | #ifdef I_SYS_SELECT |
|---|
| 68 | #include <sys/select.h> |
|---|
| 69 | #endif |
|---|
| 70 | #ifdef HAVE_UNISTD_H |
|---|
| 71 | #include <unistd.h> |
|---|
| 72 | #endif |
|---|
| 73 | #ifdef HAVE_SYS_UIO_H |
|---|
| 74 | #include <sys/uio.h> |
|---|
| 75 | #endif |
|---|
| 76 | #include <limits.h> |
|---|
| 77 | #ifdef I_FLOATINGPOINT |
|---|
| 78 | #include <floatingpoint.h> |
|---|
| 79 | #endif |
|---|
| 80 | #ifdef HAVE_IEEEFP_H |
|---|
| 81 | #include <ieeefp.h> |
|---|
| 82 | #endif |
|---|
| 83 | #include <locale.h> |
|---|
| 84 | #include <setjmp.h> |
|---|
| 85 | |
|---|
| 86 | #include "conf.h" |
|---|
| 87 | |
|---|
| 88 | #include "externs.h" |
|---|
| 89 | #include "chunk.h" |
|---|
| 90 | #include "mushdb.h" |
|---|
| 91 | #include "dbdefs.h" |
|---|
| 92 | #include "flags.h" |
|---|
| 93 | #include "lock.h" |
|---|
| 94 | #include "help.h" |
|---|
| 95 | #include "match.h" |
|---|
| 96 | #include "ansi.h" |
|---|
| 97 | #include "pueblo.h" |
|---|
| 98 | #include "parse.h" |
|---|
| 99 | #include "access.h" |
|---|
| 100 | #include "command.h" |
|---|
| 101 | #include "version.h" |
|---|
| 102 | #include "patches.h" |
|---|
| 103 | #include "mysocket.h" |
|---|
| 104 | #include "ident.h" |
|---|
| 105 | |
|---|
| 106 | #ifndef WIN32 |
|---|
| 107 | #include "wait.h" |
|---|
| 108 | #ifdef INFO_SLAVE |
|---|
| 109 | #include "lookup.h" |
|---|
| 110 | #endif |
|---|
| 111 | #endif |
|---|
| 112 | |
|---|
| 113 | #include "strtree.h" |
|---|
| 114 | #include "log.h" |
|---|
| 115 | #include "mypcre.h" |
|---|
| 116 | #ifdef HAS_OPENSSL |
|---|
| 117 | #include "myssl.h" |
|---|
| 118 | #endif |
|---|
| 119 | #include "mymalloc.h" |
|---|
| 120 | #include "extmail.h" |
|---|
| 121 | #include "attrib.h" |
|---|
| 122 | #include "game.h" |
|---|
| 123 | #include "dbio.h" |
|---|
| 124 | #include "confmagic.h" |
|---|
| 125 | |
|---|
| 126 | #ifdef HAS_GETRLIMIT |
|---|
| 127 | void init_rlimit(void); |
|---|
| 128 | #endif |
|---|
| 129 | |
|---|
| 130 | |
|---|
| 131 | /* BSD 4.2 and maybe some others need these defined */ |
|---|
| 132 | #ifndef FD_ZERO |
|---|
| 133 | /** An fd_set is 4 bytes */ |
|---|
| 134 | #define fd_set int |
|---|
| 135 | /** Clear an fd_set */ |
|---|
| 136 | #define FD_ZERO(p) (*p = 0) |
|---|
| 137 | /** Set a bit in an fd_set */ |
|---|
| 138 | #define FD_SET(n,p) (*p |= (1<<(n))) |
|---|
| 139 | /** Clear a bit in an fd_set */ |
|---|
| 140 | #define FD_CLR(n,p) (*p &= ~(1<<(n))) |
|---|
| 141 | /** Check a bit in an fd_set */ |
|---|
| 142 | #define FD_ISSET(n,p) (*p & (1<<(n))) |
|---|
| 143 | #endif /* defines for BSD 4.2 */ |
|---|
| 144 | |
|---|
| 145 | #ifdef HAS_GETRUSAGE |
|---|
| 146 | void rusage_stats(void); |
|---|
| 147 | #endif |
|---|
| 148 | int que_next(void); /* from cque.c */ |
|---|
| 149 | |
|---|
| 150 | void dispatch(void); /* from timer.c */ |
|---|
| 151 | dbref email_register_player(const char *name, const char *email, const char *host, const char *ip); /* from player.c */ |
|---|
| 152 | |
|---|
| 153 | #ifdef SUN_OS |
|---|
| 154 | static int extrafd; |
|---|
| 155 | #endif |
|---|
| 156 | int shutdown_flag = 0; /**< Is it time to shut down? */ |
|---|
| 157 | void chat_player_announce(dbref player, char *msg, int ungag); |
|---|
| 158 | |
|---|
| 159 | static int login_number = 0; |
|---|
| 160 | static int under_limit = 1; |
|---|
| 161 | |
|---|
| 162 | char cf_motd_msg[BUFFER_LEN] = { '\0' }; /**< The message of the day */ |
|---|
| 163 | char cf_wizmotd_msg[BUFFER_LEN] = { '\0' }; /**< The wizard motd */ |
|---|
| 164 | char cf_downmotd_msg[BUFFER_LEN] = { '\0' }; /**< The down message */ |
|---|
| 165 | char cf_fullmotd_msg[BUFFER_LEN] = { '\0' }; /**< The 'mush full' message */ |
|---|
| 166 | static char poll_msg[DOING_LEN] = { '\0' }; |
|---|
| 167 | char confname[BUFFER_LEN] = { '\0' }; /**< Name of the config file */ |
|---|
| 168 | char errlog[BUFFER_LEN] = { '\0' }; /**< Name of the error log file */ |
|---|
| 169 | |
|---|
| 170 | /** Is this descriptor connected to a telnet-compatible terminal? */ |
|---|
| 171 | #define TELNET_ABLE(d) ((d)->conn_flags & (CONN_TELNET | CONN_TELNET_QUERY)) |
|---|
| 172 | |
|---|
| 173 | |
|---|
| 174 | /* When the mush gets a new connection, it tries sending a telnet |
|---|
| 175 | * option negotiation code for setting client-side line-editing mode |
|---|
| 176 | * to it. If it gets a reply, a flag in the descriptor struct is |
|---|
| 177 | * turned on indicated telnet-awareness. |
|---|
| 178 | * |
|---|
| 179 | * If the reply indicates that the client supports linemode, further |
|---|
| 180 | * instructions as to what linemode options are to be used is sent. |
|---|
| 181 | * Those options: Client-side line editing, and expanding literal |
|---|
| 182 | * client-side-entered tabs into spaces. |
|---|
| 183 | * |
|---|
| 184 | * Option negotation requests sent by the client are processed, |
|---|
| 185 | * with the only one we confirm rather than refuse outright being |
|---|
| 186 | * suppress-go-ahead, since a number of telnet clients try it. |
|---|
| 187 | * |
|---|
| 188 | * The character 255 is the telnet option escape character, so when it |
|---|
| 189 | * is sent to a telnet-aware client by itself (Since it's also often y-umlaut) |
|---|
| 190 | * it must be doubled to escape it for the client. This is done automatically, |
|---|
| 191 | * and is the original purpose of adding telnet option support. |
|---|
| 192 | */ |
|---|
| 193 | |
|---|
| 194 | /* Telnet codes */ |
|---|
| 195 | #define IAC 255 /**< interpret as command: */ |
|---|
| 196 | #define NOP 241 /**< no operation */ |
|---|
| 197 | #define AYT 246 /**< are you there? */ |
|---|
| 198 | #define DONT 254 /**< you are not to use option */ |
|---|
| 199 | #define DO 253 /**< please, you use option */ |
|---|
| 200 | #define WONT 252 /**< I won't use option */ |
|---|
| 201 | #define WILL 251 /**< I will use option */ |
|---|
| 202 | #define SB 250 /**< interpret as subnegotiation */ |
|---|
| 203 | #define SE 240 /**< end sub negotiation */ |
|---|
| 204 | #define TN_SGA 3 /**< Suppress go-ahead */ |
|---|
| 205 | #define TN_LINEMODE 34 /**< Line mode */ |
|---|
| 206 | #define TN_NAWS 31 /**< Negotiate About Window Size */ |
|---|
| 207 | #define TN_TTYPE 24 /**< Ask for termial type information */ |
|---|
| 208 | static void test_telnet(DESC *d); |
|---|
| 209 | static void setup_telnet(DESC *d); |
|---|
| 210 | static int handle_telnet(DESC *d, unsigned char **q, unsigned char *qend); |
|---|
| 211 | |
|---|
| 212 | /** Iterate through a list of descriptors, and do something with those |
|---|
| 213 | * that are connected. |
|---|
| 214 | */ |
|---|
| 215 | #define DESC_ITER_CONN(d) \ |
|---|
| 216 | for(d = descriptor_list;(d);d=(d)->next) \ |
|---|
| 217 | if((d)->connected) |
|---|
| 218 | |
|---|
| 219 | /** Is a descriptor hidden? */ |
|---|
| 220 | #define Hidden(d) ((d->hide == 1) && Can_Hide(d->player)) |
|---|
| 221 | |
|---|
| 222 | static const char *create_fail = |
|---|
| 223 | "Either there is already a player with that name, or that name is illegal."; |
|---|
| 224 | static const char *password_fail = "The password is invalid (or missing)."; |
|---|
| 225 | static const char *register_fail = |
|---|
| 226 | "Unable to register that player with that email address."; |
|---|
| 227 | static const char *register_success = |
|---|
| 228 | "Registration successful! You will receive your password by email."; |
|---|
| 229 | static const char *shutdown_message = "Going down - Bye"; |
|---|
| 230 | #ifdef HAS_OPENSSL |
|---|
| 231 | static const char *ssl_shutdown_message = |
|---|
| 232 | "GAME: SSL connections must be dropped, sorry."; |
|---|
| 233 | #endif |
|---|
| 234 | static const char *asterisk_line = |
|---|
| 235 | "**********************************************************************"; |
|---|
| 236 | /** Where we save the descriptor info across reboots. */ |
|---|
| 237 | #define REBOOTFILE "reboot.db" |
|---|
| 238 | |
|---|
| 239 | #if 0 |
|---|
| 240 | /* For translation */ |
|---|
| 241 | static void dummy_msgs(void); |
|---|
| 242 | static void |
|---|
| 243 | dummy_msgs() |
|---|
| 244 | { |
|---|
| 245 | char *temp; |
|---|
| 246 | temp = T("Either that player does not exist, or has a different password."); |
|---|
| 247 | temp = |
|---|
| 248 | T |
|---|
| 249 | ("Either there is already a player with that name, or that name is illegal."); |
|---|
| 250 | temp = T("The password is invalid (or missing)."); |
|---|
| 251 | temp = T("Unable to register that player with that email address."); |
|---|
| 252 | temp = T("Registration successful! You will receive your password by email."); |
|---|
| 253 | temp = T("Going down - Bye"); |
|---|
| 254 | temp = T("GAME: SSL connections must be dropped, sorry."); |
|---|
| 255 | } |
|---|
| 256 | |
|---|
| 257 | #endif |
|---|
| 258 | |
|---|
| 259 | DESC *descriptor_list = NULL; /**< The linked list of descriptors */ |
|---|
| 260 | |
|---|
| 261 | static int sock; |
|---|
| 262 | #ifdef HAS_OPENSSL |
|---|
| 263 | static int sslsock = 0; |
|---|
| 264 | SSL *ssl_master_socket = NULL; /**< Master SSL socket for ssl port */ |
|---|
| 265 | #endif |
|---|
| 266 | static int ndescriptors = 0; |
|---|
| 267 | #ifdef WIN32 |
|---|
| 268 | static WSADATA wsadata; |
|---|
| 269 | #endif |
|---|
| 270 | int restarting = 0; /**< Are we restarting the server after a reboot? */ |
|---|
| 271 | int maxd = 0; |
|---|
| 272 | |
|---|
| 273 | extern const unsigned char *tables; |
|---|
| 274 | |
|---|
| 275 | sig_atomic_t signal_shutdown_flag = 0; /**< Have we caught a shutdown signal? */ |
|---|
| 276 | sig_atomic_t signal_dump_flag = 0; /**< Have we caught a dump signal? */ |
|---|
| 277 | |
|---|
| 278 | #ifndef BOOLEXP_DEBUGGING |
|---|
| 279 | #ifdef WIN32SERVICES |
|---|
| 280 | void shutdown_checkpoint(void); |
|---|
| 281 | int mainthread(int argc, char **argv); |
|---|
| 282 | #else |
|---|
| 283 | int main(int argc, char **argv); |
|---|
| 284 | #endif |
|---|
| 285 | #endif |
|---|
| 286 | void set_signals(void); |
|---|
| 287 | static struct timeval *timeval_sub(struct timeval *now, struct timeval *then); |
|---|
| 288 | #ifdef WIN32 |
|---|
| 289 | /** Windows doesn't have gettimeofday(), so we implement it here */ |
|---|
| 290 | #define our_gettimeofday(now) win_gettimeofday((now)) |
|---|
| 291 | static void win_gettimeofday(struct timeval *now); |
|---|
| 292 | #else |
|---|
| 293 | /** A wrapper for gettimeofday() in case the system doesn't have it */ |
|---|
| 294 | #define our_gettimeofday(now) gettimeofday((now), (struct timezone *)NULL) |
|---|
| 295 | #endif |
|---|
| 296 | static long int msec_diff(struct timeval *now, struct timeval *then); |
|---|
| 297 | static struct timeval *msec_add(struct timeval *t, int x); |
|---|
| 298 | static void update_quotas(struct timeval *last, struct timeval *current); |
|---|
| 299 | |
|---|
| 300 | int how_many_fds(void); |
|---|
| 301 | static void shovechars(Port_t port, Port_t sslport); |
|---|
| 302 | static int test_connection(int newsock); |
|---|
| 303 | #ifndef INFO_SLAVE |
|---|
| 304 | static DESC *new_connection(int oldsock, int *result, int use_ssl); |
|---|
| 305 | #endif |
|---|
| 306 | |
|---|
| 307 | static void clearstrings(DESC *d); |
|---|
| 308 | |
|---|
| 309 | /** A block of cached text. */ |
|---|
| 310 | typedef struct fblock { |
|---|
| 311 | unsigned char *buff; /**< Pointer to the block as a string */ |
|---|
| 312 | size_t len; /**< Length of buff */ |
|---|
| 313 | } FBLOCK; |
|---|
| 314 | |
|---|
| 315 | /** The complete collection of cached text files. */ |
|---|
| 316 | struct fcache_entries { |
|---|
| 317 | FBLOCK connect_fcache[2]; /**< connect.txt and connect.html */ |
|---|
| 318 | FBLOCK motd_fcache[2]; /**< motd.txt and motd.html */ |
|---|
| 319 | FBLOCK wizmotd_fcache[2]; /**< wizmotd.txt and wizmotd.html */ |
|---|
| 320 | FBLOCK newuser_fcache[2]; /**< newuser.txt and newuser.html */ |
|---|
| 321 | FBLOCK register_fcache[2]; /**< register.txt and register.html */ |
|---|
| 322 | FBLOCK quit_fcache[2]; /**< quit.txt and quit.html */ |
|---|
| 323 | FBLOCK down_fcache[2]; /**< down.txt and down.html */ |
|---|
| 324 | FBLOCK full_fcache[2]; /**< full.txt and full.html */ |
|---|
| 325 | FBLOCK guest_fcache[2]; /**< guest.txt and guest.html */ |
|---|
| 326 | }; |
|---|
| 327 | |
|---|
| 328 | static struct fcache_entries fcache; |
|---|
| 329 | static void fcache_dump(DESC *d, FBLOCK fp[2], const unsigned char *prefix); |
|---|
| 330 | static int fcache_read(FBLOCK *cp, const char *filename); |
|---|
| 331 | static void logout_sock(DESC *d); |
|---|
| 332 | static void shutdownsock(DESC *d); |
|---|
| 333 | DESC *initializesock(int s, char *addr, char *ip, int use_ssl); |
|---|
| 334 | int process_output(DESC *d); |
|---|
| 335 | /* Notify.c */ |
|---|
| 336 | extern void free_text_block(struct text_block *t); |
|---|
| 337 | extern void add_to_queue(struct text_queue *q, const unsigned char *b, int n); |
|---|
| 338 | extern int queue_write(DESC *d, const unsigned char *b, int n); |
|---|
| 339 | extern int queue_eol(DESC *d); |
|---|
| 340 | extern int queue_newwrite(DESC *d, const unsigned char *b, int n); |
|---|
| 341 | extern int queue_string(DESC *d, const char *s); |
|---|
| 342 | extern int queue_string_eol(DESC *d, const char *s); |
|---|
| 343 | extern void freeqs(DESC *d); |
|---|
| 344 | static void welcome_user(DESC *d); |
|---|
| 345 | static void dump_info(DESC *call_by); |
|---|
| 346 | static void save_command(DESC *d, const unsigned char *command); |
|---|
| 347 | static int process_input(DESC *d, int output_ready); |
|---|
| 348 | static void process_input_helper(DESC *d, char *tbuf1, int got); |
|---|
| 349 | static void set_userstring(unsigned char **userstring, const char *command); |
|---|
| 350 | static void process_commands(void); |
|---|
| 351 | static int do_command(DESC *d, char *command); |
|---|
| 352 | static void parse_puebloclient(DESC *d, char *command); |
|---|
| 353 | static int dump_messages(DESC *d, dbref player, int new); |
|---|
| 354 | static int check_connect(DESC *d, const char *msg); |
|---|
| 355 | static void parse_connect(const char *msg, char *command, char *user, |
|---|
| 356 | char *pass); |
|---|
| 357 | static void close_sockets(void); |
|---|
| 358 | dbref find_player_by_desc(int port); |
|---|
| 359 | static DESC *lookup_desc(dbref executor, const char *name); |
|---|
| 360 | void NORETURN bailout(int sig); |
|---|
| 361 | void WIN32_CDECL signal_shutdown(int sig); |
|---|
| 362 | void WIN32_CDECL signal_dump(int sig); |
|---|
| 363 | void reaper(int sig); |
|---|
| 364 | #ifndef WIN32 |
|---|
| 365 | sig_atomic_t dump_error = 0; |
|---|
| 366 | WAIT_TYPE dump_status = 0; |
|---|
| 367 | #ifdef INFO_SLAVE |
|---|
| 368 | sig_atomic_t slave_error = 0; |
|---|
| 369 | #endif |
|---|
| 370 | #endif |
|---|
| 371 | extern pid_t forked_dump_pid; /**< Process id of forking dump process */ |
|---|
| 372 | static void dump_users(DESC *call_by, char *match, int doing); |
|---|
| 373 | static const char *time_format_1(time_t dt); |
|---|
| 374 | static const char *time_format_2(time_t dt); |
|---|
| 375 | static void announce_connect(dbref player, int isnew, int num); |
|---|
| 376 | static void announce_disconnect(DESC *saved); |
|---|
| 377 | void inactivity_check(void); |
|---|
| 378 | void reopen_logs(void); |
|---|
| 379 | void load_reboot_db(void); |
|---|
| 380 | |
|---|
| 381 | static bool in_suid_root_mode = 0; |
|---|
| 382 | static char *pidfile = NULL; |
|---|
| 383 | static char **saved_argv = NULL; |
|---|
| 384 | |
|---|
| 385 | #ifndef BOOLEXP_DEBUGGING |
|---|
| 386 | #ifdef WIN32SERVICES |
|---|
| 387 | /* Under WIN32, MUSH is a "service", so we just start a thread here. |
|---|
| 388 | * The real "main" is in win32/services.c |
|---|
| 389 | */ |
|---|
| 390 | int |
|---|
| 391 | mainthread(int argc, char **argv) |
|---|
| 392 | #else |
|---|
| 393 | /** The main function. |
|---|
| 394 | * \param argc number of arguments. |
|---|
| 395 | * \param argv vector of arguments. |
|---|
| 396 | * \return exit code. |
|---|
| 397 | */ |
|---|
| 398 | int |
|---|
| 399 | main(int argc, char **argv) |
|---|
| 400 | #endif /* WIN32SERVICES */ |
|---|
| 401 | { |
|---|
| 402 | FILE *newerr; |
|---|
| 403 | bool detach_session = 1; |
|---|
| 404 | |
|---|
| 405 | /* disallow running as root on unix. |
|---|
| 406 | * This is done as early as possible, before translation is initialized. |
|---|
| 407 | * Hence, no T()s around messages. |
|---|
| 408 | */ |
|---|
| 409 | #ifndef WIN32 |
|---|
| 410 | #ifdef HAVE_GETUID |
|---|
| 411 | if (getuid() == 0) { |
|---|
| 412 | fputs("Please run the server as another user.\n", stderr); |
|---|
| 413 | fputs("PennMUSH will not run as root as a security measure.\n", stderr); |
|---|
| 414 | return EXIT_FAILURE; |
|---|
| 415 | } |
|---|
| 416 | /* Add suid-root checks here. */ |
|---|
| 417 | #endif |
|---|
| 418 | #ifdef HAVE_GETEUID |
|---|
| 419 | if (geteuid() == 0) { |
|---|
| 420 | fprintf(stderr, "The %s binary is set suid and owned by root.\n", argv[0]); |
|---|
| 421 | #ifdef HAVE_SETEUID |
|---|
| 422 | fprintf(stderr, "Changing effective user to %d.\n", getuid()); |
|---|
| 423 | seteuid(getuid()); |
|---|
| 424 | in_suid_root_mode = 1; |
|---|
| 425 | #endif |
|---|
| 426 | } |
|---|
| 427 | #endif /* HAVE_GETEUID */ |
|---|
| 428 | #endif /* !WIN32 */ |
|---|
| 429 | |
|---|
| 430 | /* read the configuration file */ |
|---|
| 431 | if (argc < 2) { |
|---|
| 432 | fprintf(stderr, |
|---|
| 433 | "WARNING: Called without a config file argument. Assuming mush.cnf\n"); |
|---|
| 434 | strcpy(confname, "mush.cnf"); |
|---|
| 435 | } else { |
|---|
| 436 | int n; |
|---|
| 437 | for (n = 1; n < argc; n++) { |
|---|
| 438 | if (argv[n][0] == '-') { |
|---|
| 439 | if (strcmp(argv[n], "--no-session") == 0) |
|---|
| 440 | detach_session = 0; |
|---|
| 441 | else if (strncmp(argv[n], "--pid-file", 10) == 0) { |
|---|
| 442 | char *eq; |
|---|
| 443 | if ((eq = strchr(argv[n], '='))) |
|---|
| 444 | pidfile = eq + 1; |
|---|
| 445 | else { |
|---|
| 446 | if (n + 1 >= argc) { |
|---|
| 447 | fprintf(stderr, "%s: --pid-file needs a filename.\n", argv[0]); |
|---|
| 448 | return EXIT_FAILURE; |
|---|
| 449 | } |
|---|
| 450 | pidfile = argv[n + 1]; |
|---|
| 451 | n++; |
|---|
| 452 | } |
|---|
| 453 | } else |
|---|
| 454 | fprintf(stderr, "%s: unknown option \"%s\"\n", argv[0], argv[n]); |
|---|
| 455 | } else { |
|---|
| 456 | mush_strncpy(confname, argv[n], BUFFER_LEN); |
|---|
| 457 | break; |
|---|
| 458 | } |
|---|
| 459 | } |
|---|
| 460 | } |
|---|
| 461 | |
|---|
| 462 | |
|---|
| 463 | #ifdef HAVE_FORK |
|---|
| 464 | /* Fork off and detach from controlling terminal. */ |
|---|
| 465 | if (detach_session) { |
|---|
| 466 | pid_t child; |
|---|
| 467 | |
|---|
| 468 | child = fork(); |
|---|
| 469 | if (child < 0) { |
|---|
| 470 | /* Print a warning and continue */ |
|---|
| 471 | penn_perror("fork"); |
|---|
| 472 | } else if (child > 0) { |
|---|
| 473 | /* Parent process of a successful fork() */ |
|---|
| 474 | return EXIT_SUCCESS; |
|---|
| 475 | } else { |
|---|
| 476 | /* Child process */ |
|---|
| 477 | if (new_process_session() < 0) |
|---|
| 478 | penn_perror("Couldn't create a new process session"); |
|---|
| 479 | } |
|---|
| 480 | } |
|---|
| 481 | #endif |
|---|
| 482 | |
|---|
| 483 | #ifdef HAVE_GETPID |
|---|
| 484 | if (pidfile) { |
|---|
| 485 | FILE *f; |
|---|
| 486 | if (!(f = fopen(pidfile, "w"))) { |
|---|
| 487 | fprintf(stderr, "%s: Unable to write to pidfile '%s'\n", argv[0], |
|---|
| 488 | pidfile); |
|---|
| 489 | return EXIT_FAILURE; |
|---|
| 490 | } |
|---|
| 491 | fprintf(f, "%d\n", getpid()); |
|---|
| 492 | fclose(f); |
|---|
| 493 | } |
|---|
| 494 | #endif |
|---|
| 495 | |
|---|
| 496 | saved_argv = argv; |
|---|
| 497 | |
|---|
| 498 | #ifdef WIN32 |
|---|
| 499 | { |
|---|
| 500 | unsigned short wVersionRequested = MAKEWORD(1, 1); |
|---|
| 501 | int err; |
|---|
| 502 | |
|---|
| 503 | /* Need to include library: wsock32.lib for Windows Sockets */ |
|---|
| 504 | err = WSAStartup(wVersionRequested, &wsadata); |
|---|
| 505 | if (err) { |
|---|
| 506 | printf(T("Error %i on WSAStartup\n"), err); |
|---|
| 507 | exit(1); |
|---|
| 508 | } |
|---|
| 509 | } |
|---|
| 510 | #endif /* WIN32 */ |
|---|
| 511 | |
|---|
| 512 | #ifdef HAS_GETRLIMIT |
|---|
| 513 | init_rlimit(); /* unlimit file descriptors */ |
|---|
| 514 | #endif |
|---|
| 515 | |
|---|
| 516 | /* These are BSDisms to fix floating point exceptions */ |
|---|
| 517 | #ifdef HAVE_FPSETROUND |
|---|
| 518 | fpsetround(FP_RN); |
|---|
| 519 | #endif |
|---|
| 520 | #ifdef HAVE_FPSETMASK |
|---|
| 521 | fpsetmask(0L); |
|---|
| 522 | #endif |
|---|
| 523 | |
|---|
| 524 | time(&mudtime); |
|---|
| 525 | |
|---|
| 526 | options.mem_check = 1; |
|---|
| 527 | |
|---|
| 528 | /* If we have setlocale, call it to set locale info |
|---|
| 529 | * from environment variables |
|---|
| 530 | */ |
|---|
| 531 | #ifdef HAS_SETLOCALE |
|---|
| 532 | { |
|---|
| 533 | char *loc; |
|---|
| 534 | if ((loc = setlocale(LC_CTYPE, "")) == NULL) |
|---|
| 535 | do_rawlog(LT_ERR, "Failed to set ctype locale from environment."); |
|---|
| 536 | else |
|---|
| 537 | do_rawlog(LT_ERR, "Setting ctype locale to %s", loc); |
|---|
| 538 | if ((loc = setlocale(LC_TIME, "")) == NULL) |
|---|
| 539 | do_rawlog(LT_ERR, "Failed to set time locale from environment."); |
|---|
| 540 | else |
|---|
| 541 | do_rawlog(LT_ERR, "Setting time locale to %s", loc); |
|---|
| 542 | #ifdef LC_MESSAGES |
|---|
| 543 | if ((loc = setlocale(LC_MESSAGES, "")) == NULL) |
|---|
| 544 | do_rawlog(LT_ERR, "Failed to set messages locale from environment."); |
|---|
| 545 | else |
|---|
| 546 | do_rawlog(LT_ERR, "Setting messages locale to %s", loc); |
|---|
| 547 | #else |
|---|
| 548 | do_rawlog(LT_ERR, "No support for message locale."); |
|---|
| 549 | #endif |
|---|
| 550 | if ((loc = setlocale(LC_COLLATE, "")) == NULL) |
|---|
| 551 | do_rawlog(LT_ERR, "Failed to set collate locale from environment."); |
|---|
| 552 | else |
|---|
| 553 | do_rawlog(LT_ERR, "Setting collate locale to %s", loc); |
|---|
| 554 | } |
|---|
| 555 | #endif |
|---|
| 556 | #ifdef HAS_TEXTDOMAIN |
|---|
| 557 | textdomain("pennmush"); |
|---|
| 558 | #endif |
|---|
| 559 | #ifdef HAS_BINDTEXTDOMAIN |
|---|
| 560 | bindtextdomain("pennmush", "../po"); |
|---|
| 561 | #endif |
|---|
| 562 | |
|---|
| 563 | /* Build the locale-dependant tables used by PCRE */ |
|---|
| 564 | tables = pcre_maketables(); |
|---|
| 565 | |
|---|
| 566 | init_game_config(confname); |
|---|
| 567 | |
|---|
| 568 | /* save a file descriptor */ |
|---|
| 569 | reserve_fd(); |
|---|
| 570 | #ifdef SUN_OS |
|---|
| 571 | extrafd = open("/dev/null", O_RDWR); |
|---|
| 572 | #endif |
|---|
| 573 | |
|---|
| 574 | /* decide if we're in @shutdown/reboot */ |
|---|
| 575 | restarting = 0; |
|---|
| 576 | newerr = fopen(REBOOTFILE, "r"); |
|---|
| 577 | if (newerr) { |
|---|
| 578 | restarting = 1; |
|---|
| 579 | fclose(newerr); |
|---|
| 580 | } |
|---|
| 581 | |
|---|
| 582 | if (init_game_dbs() < 0) { |
|---|
| 583 | do_rawlog(LT_ERR, T("ERROR: Couldn't load databases! Exiting.")); |
|---|
| 584 | exit(2); |
|---|
| 585 | } |
|---|
| 586 | |
|---|
| 587 | init_game_postdb(confname); |
|---|
| 588 | |
|---|
| 589 | globals.database_loaded = 1; |
|---|
| 590 | |
|---|
| 591 | set_signals(); |
|---|
| 592 | |
|---|
| 593 | #ifdef INFO_SLAVE |
|---|
| 594 | init_info_slave(); |
|---|
| 595 | #endif |
|---|
| 596 | |
|---|
| 597 | /* go do it */ |
|---|
| 598 | #ifdef CSRI |
|---|
| 599 | #ifdef CSRI_DEBUG |
|---|
| 600 | mal_verify(1); |
|---|
| 601 | #endif |
|---|
| 602 | #ifdef CSRI_TRACE |
|---|
| 603 | mal_leaktrace(1); |
|---|
| 604 | #endif |
|---|
| 605 | #endif |
|---|
| 606 | load_reboot_db(); |
|---|
| 607 | |
|---|
| 608 | shovechars((Port_t) TINYPORT, (Port_t) SSLPORT); |
|---|
| 609 | #ifdef CSRI |
|---|
| 610 | #ifdef CSRI_DEBUG |
|---|
| 611 | mal_verify(1); |
|---|
| 612 | #endif |
|---|
| 613 | #endif |
|---|
| 614 | |
|---|
| 615 | /* someone has told us to shut down */ |
|---|
| 616 | #ifdef WIN32SERVICES |
|---|
| 617 | /* Keep service manager happy */ |
|---|
| 618 | shutdown_checkpoint(); |
|---|
| 619 | #endif |
|---|
| 620 | |
|---|
| 621 | shutdown_queues(); |
|---|
| 622 | |
|---|
| 623 | #ifdef WIN32SERVICES |
|---|
| 624 | /* Keep service manager happy */ |
|---|
| 625 | shutdown_checkpoint(); |
|---|
| 626 | #endif |
|---|
| 627 | |
|---|
| 628 | close_sockets(); |
|---|
| 629 | sql_shutdown(); |
|---|
| 630 | |
|---|
| 631 | #ifdef INFO_SLAVE |
|---|
| 632 | kill_info_slave(); |
|---|
| 633 | #endif |
|---|
| 634 | |
|---|
| 635 | #ifdef WIN32SERVICES |
|---|
| 636 | /* Keep service manager happy */ |
|---|
| 637 | shutdown_checkpoint(); |
|---|
| 638 | #endif |
|---|
| 639 | |
|---|
| 640 | dump_database(); |
|---|
| 641 | |
|---|
| 642 | local_shutdown(); |
|---|
| 643 | |
|---|
| 644 | end_all_logs(); |
|---|
| 645 | |
|---|
| 646 | if (pidfile) |
|---|
| 647 | remove(pidfile); |
|---|
| 648 | |
|---|
| 649 | #ifdef CSRI |
|---|
| 650 | #ifdef CSRI_PROFILESIZES |
|---|
| 651 | mal_statsdump(stderr); |
|---|
| 652 | #endif |
|---|
| 653 | #ifdef CSRI_TRACE |
|---|
| 654 | mal_dumpleaktrace(stderr); |
|---|
| 655 | #endif |
|---|
| 656 | fflush(stderr); |
|---|
| 657 | #endif |
|---|
| 658 | |
|---|
| 659 | #ifdef WIN32SERVICES |
|---|
| 660 | /* Keep service manager happy */ |
|---|
| 661 | shutdown_checkpoint(); |
|---|
| 662 | #endif |
|---|
| 663 | |
|---|
| 664 | #ifdef HAS_GETRUSAGE |
|---|
| 665 | rusage_stats(); |
|---|
| 666 | #endif /* HAS_RUSAGE */ |
|---|
| 667 | |
|---|
| 668 | do_rawlog(LT_ERR, T("MUSH shutdown completed.")); |
|---|
| 669 | |
|---|
| 670 | closesocket(sock); |
|---|
| 671 | #ifdef WIN32 |
|---|
| 672 | #ifdef WIN32SERVICES |
|---|
| 673 | shutdown_checkpoint(); |
|---|
| 674 | #endif |
|---|
| 675 | WSACleanup(); /* clean up */ |
|---|
| 676 | #else |
|---|
| 677 | exit(0); |
|---|
| 678 | #endif |
|---|
| 679 | } |
|---|
| 680 | #endif /* BOOLEXP_DEBUGGING */ |
|---|
| 681 | |
|---|
| 682 | /** Close and reopen the logfiles - called on SIGHUP */ |
|---|
| 683 | void |
|---|
| 684 | reopen_logs(void) |
|---|
| 685 | { |
|---|
| 686 | FILE *newerr; |
|---|
| 687 | /* close up the log files */ |
|---|
| 688 | end_all_logs(); |
|---|
| 689 | newerr = fopen(errlog, "a"); |
|---|
| 690 | if (!newerr) { |
|---|
| 691 | fprintf(stderr, |
|---|
| 692 | T("Unable to open %s. Error output continues to stderr.\n"), |
|---|
| 693 | errlog); |
|---|
| 694 | } else { |
|---|
| 695 | if (!freopen(errlog, "a", stderr)) { |
|---|
| 696 | printf(T("Ack! Failed reopening stderr!")); |
|---|
| 697 | exit(1); |
|---|
| 698 | } |
|---|
| 699 | setvbuf(stderr, NULL, _IOLBF, BUFSIZ); |
|---|
| 700 | fclose(newerr); |
|---|
| 701 | } |
|---|
| 702 | start_all_logs(); |
|---|
| 703 | } |
|---|
| 704 | |
|---|
| 705 | /** Install our default signal handlers. */ |
|---|
| 706 | void |
|---|
| 707 | set_signals(void) |
|---|
| 708 | { |
|---|
| 709 | |
|---|
| 710 | #ifndef WIN32 |
|---|
| 711 | /* we don't care about SIGPIPE, we notice it in select() and write() */ |
|---|
| 712 | ignore_signal(SIGPIPE); |
|---|
| 713 | install_sig_handler(SIGUSR2, signal_dump); |
|---|
| 714 | install_sig_handler(SIGINT, signal_shutdown); |
|---|
| 715 | install_sig_handler(SIGTERM, bailout); |
|---|
| 716 | install_sig_handler(SIGCHLD, reaper); |
|---|
| 717 | #else |
|---|
| 718 | /* Win32 stuff: |
|---|
| 719 | * No support for SIGUSR2 or SIGINT. |
|---|
| 720 | * SIGTERM is never generated on NT-based Windows (according to MSDN) |
|---|
| 721 | * MSVC++ will let you get away with installing a handler anyway, |
|---|
| 722 | * but VS.NET will not. So if it's MSVC++, we give it a try. |
|---|
| 723 | */ |
|---|
| 724 | #if _MSC_VER < 1200 |
|---|
| 725 | install_sig_handler(SIGTERM, bailout); |
|---|
| 726 | #endif |
|---|
| 727 | #endif |
|---|
| 728 | |
|---|
| 729 | } |
|---|
| 730 | |
|---|
| 731 | #ifdef WIN32 |
|---|
| 732 | /** Get the time using Windows function call. |
|---|
| 733 | * Looks weird, but it works. :-P |
|---|
| 734 | * \param now address to store timeval data. |
|---|
| 735 | */ |
|---|
| 736 | static void |
|---|
| 737 | win_gettimeofday(struct timeval *now) |
|---|
| 738 | { |
|---|
| 739 | |
|---|
| 740 | FILETIME win_time; |
|---|
| 741 | |
|---|
| 742 | GetSystemTimeAsFileTime(&win_time); |
|---|
| 743 | /* dwLow is in 100-s nanoseconds, not microseconds */ |
|---|
| 744 | now->tv_usec = win_time.dwLowDateTime % 10000000 / 10; |
|---|
| 745 | |
|---|
| 746 | /* dwLow contains at most 429 least significant seconds, since 32 bits maxint is 4294967294 */ |
|---|
| 747 | win_time.dwLowDateTime /= 10000000; |
|---|
| 748 | |
|---|
| 749 | /* Make room for the seconds of dwLow in dwHigh */ |
|---|
| 750 | /* 32 bits of 1 = 4294967295. 4294967295 / 429 = 10011578 */ |
|---|
| 751 | win_time.dwHighDateTime %= 10011578; |
|---|
| 752 | win_time.dwHighDateTime *= 429; |
|---|
| 753 | |
|---|
| 754 | /* And add them */ |
|---|
| 755 | now->tv_sec = win_time.dwHighDateTime + win_time.dwLowDateTime; |
|---|
| 756 | } |
|---|
| 757 | |
|---|
| 758 | #endif |
|---|
| 759 | |
|---|
| 760 | /** Return the difference between two timeval structs as a timeval struct. |
|---|
| 761 | * \param now pointer to the timeval to subtract from. |
|---|
| 762 | * \param then pointer to the timeval to subtract. |
|---|
| 763 | * \return pointer to a statically allocated timeval of the difference. |
|---|
| 764 | */ |
|---|
| 765 | static struct timeval * |
|---|
| 766 | timeval_sub(struct timeval *now, struct timeval *then) |
|---|
| 767 | { |
|---|
| 768 | static struct timeval mytime; |
|---|
| 769 | mytime.tv_sec = now->tv_sec; |
|---|
| 770 | mytime.tv_usec = now->tv_usec; |
|---|
| 771 | |
|---|
| 772 | mytime.tv_sec -= then->tv_sec; |
|---|
| 773 | mytime.tv_usec -= then->tv_usec; |
|---|
| 774 | if (mytime.tv_usec < 0) { |
|---|
| 775 | mytime.tv_usec += 1000000; |
|---|
| 776 | mytime.tv_sec--; |
|---|
| 777 | } |
|---|
| 778 | return &mytime; |
|---|
| 779 | } |
|---|
| 780 | |
|---|
| 781 | /** Return the difference between two timeval structs in milliseconds. |
|---|
| 782 | * \param now pointer to the timeval to subtract from. |
|---|
| 783 | * \param then pointer to the timeval to subtract. |
|---|
| 784 | * \return milliseconds of difference between them. |
|---|
| 785 | */ |
|---|
| 786 | static long int |
|---|
| 787 | msec_diff(struct timeval *now, struct timeval *then) |
|---|
| 788 | { |
|---|
| 789 | long int secs = now->tv_sec - then->tv_sec; |
|---|
| 790 | if (secs == 0) |
|---|
| 791 | return (now->tv_usec - then->tv_usec) / 1000; |
|---|
| 792 | else if (secs == 1) |
|---|
| 793 | return (now->tv_usec + (1000000 - then->tv_usec)) / 100; |
|---|
| 794 | else if (secs > 1) |
|---|
| 795 | return (secs * 1000) + ((now->tv_usec + (1000000 - then->tv_usec)) / 1000); |
|---|
| 796 | else |
|---|
| 797 | return 0; |
|---|
| 798 | } |
|---|
| 799 | |
|---|
| 800 | /** Add a given number of milliseconds to a timeval. |
|---|
| 801 | * \param t pointer to a timeval struct. |
|---|
| 802 | * \param x number of milliseconds to add to t. |
|---|
| 803 | * \return address of static timeval struct representing the sum. |
|---|
| 804 | */ |
|---|
| 805 | static struct timeval * |
|---|
| 806 | msec_add(struct timeval *t, int x) |
|---|
| 807 | { |
|---|
| 808 | static struct timeval mytime; |
|---|
| 809 | mytime.tv_sec = t->tv_sec; |
|---|
| 810 | mytime.tv_usec = t->tv_usec; |
|---|
| 811 | mytime.tv_sec += x / 1000; |
|---|
| 812 | mytime.tv_usec += (x % 1000) * 1000; |
|---|
| 813 | if (mytime.tv_usec >= 1000000) { |
|---|
| 814 | mytime.tv_sec += mytime.tv_usec / 1000000; |
|---|
| 815 | mytime.tv_usec = mytime.tv_usec % 1000000; |
|---|
| 816 | } |
|---|
| 817 | return &mytime; |
|---|
| 818 | } |
|---|
| 819 | |
|---|
| 820 | /** Update each descriptor's allowed rate of issuing commands. |
|---|
| 821 | * Players are rate-limited; they may only perform up to a certain |
|---|
| 822 | * number of commands per time slice. This function is run periodically |
|---|
| 823 | * to refresh each descriptor's available command quota based on how |
|---|
| 824 | * many slices have passed since it was last updated. |
|---|
| 825 | * \param last pointer to timeval struct of last time quota was updated. |
|---|
| 826 | * \param current pointer to timeval struct of current time. |
|---|
| 827 | */ |
|---|
| 828 | static void |
|---|
| 829 | update_quotas(struct timeval *last, struct timeval *current) |
|---|
| 830 | { |
|---|
| 831 | int nslices; |
|---|
| 832 | DESC *d; |
|---|
| 833 | nslices = (int) msec_diff(current, last) / COMMAND_TIME_MSEC; |
|---|
| 834 | |
|---|
| 835 | if (nslices > 0) { |
|---|
| 836 | for (d = descriptor_list; d; d = d->next) { |
|---|
| 837 | d->quota += COMMANDS_PER_TIME * nslices; |
|---|
| 838 | if (d->quota > COMMAND_BURST_SIZE) |
|---|
| 839 | d->quota = COMMAND_BURST_SIZE; |
|---|
| 840< |
|---|