/*--------------------------------------------------------------------------- * Driver main module. * *--------------------------------------------------------------------------- * Here is the main() function which parses the commandline arguments, * initializes everything and starts the backend loop. A documentation * of the available commandline arguments is in the file * doc/driver/invocation; the argument '--help' causes the driver to * print a short help to all available arguments. * * The commandline arguments are actually parsed twice, since some * arguments expect a functional master object (like the 'f' argument). * * The argument parser is an adaption of a generic parser. All the associated * code and comments is kept in the lower half of this source for better * readability. * * This file also contains several global functions and variables, some of * which should probably go into a dedicated 'global' module or somewhere else * appropriate. * TODO: Move out all those variables and functions which are illfitting here. * TODO: Put the argument parsing into a separate file? *--------------------------------------------------------------------------- */ #include "driver.h" #include "typedefs.h" #include "my-alloca.h" #include #include #ifdef HAVE_NETDB_H # include /* MAXHOSTNAMELEN on Solaris */ #endif #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_TIME_H #include #endif #include #include "main.h" #include "backend.h" #include "array.h" #include "comm.h" #include "filestat.h" #include "gcollect.h" #include "interpret.h" #include "lex.h" #include "mapping.h" #include "mempools.h" #include "mregex.h" #include "mstrings.h" #include "object.h" #include "otable.h" #include "patchlevel.h" #include "pkg-tls.h" #include "random.h" #include "simulate.h" #include "simul_efun.h" #include "stdstrings.h" #include "svalue.h" #include "swap.h" #include "wiz_list.h" #include "xalloc.h" #ifdef USE_MYSQL #include "pkg-mysql.h" #endif #ifdef USE_IKSEMEL #include "pkg-iksemel.h" #endif #include "i-eval_cost.h" #include "../mudlib/sys/regexp.h" /*-------------------------------------------------------------------------*/ #define PLAIN_MASTER "secure/master" #define COMPAT_MASTER "obj/master" /*-------------------------------------------------------------------------*/ /* -- Pure commandline arguments -- */ int d_flag = 0; /* Debuglevel */ /* TODO: Make this bitflags, one for 'trace refcounts' etc */ Bool t_flag = MY_FALSE; /* True: Disable heart beat and reset */ static int e_flag = MY_FALSE; /* Passed to preload(), usually disables it */ Bool comp_flag = MY_FALSE; /* Trace compilations */ #ifdef USE_PARANOIA Bool check_a_lot_ref_counts_flag = MY_FALSE; /* The name says it. */ #endif #ifdef DEBUG int check_state_level = 0; /* how of to check the state in the loop */ #endif #ifdef CHECK_OBJECT_STAT Bool check_object_stat = MY_FALSE; #endif Bool strict_euids = MY_FALSE; /* Enforce use of the euids */ Bool share_variables = MY_FALSE; /* Clones are initialized from their * blueprints. */ Bool allow_filename_spaces = MY_FALSE; /* Allow spaces in filenames */ static char * hostname = NULL; static char * hostaddr = NULL; /* Hostname and -addr given on the commandline. They are passed as * arguments to initialize_host_ip() and are then no longer used. */ /* -- Configuration options -- */ long time_to_reset = TIME_TO_RESET; long time_to_cleanup = TIME_TO_CLEAN_UP; /* A value <= 0 disables the reset/cleanup */ long time_to_swap = TIME_TO_SWAP; long time_to_swap_variables = TIME_TO_SWAP_VARIABLES; /* A value <= 0 disables the swapping. */ long alarm_time = ALARM_TIME; long heart_beat_interval = HEART_BEAT_INTERVAL; /* Minimum value is 1. */ #ifdef SYNCHRONOUS_HEART_BEAT Bool synch_heart_beats = MY_TRUE; #else Bool synch_heart_beats = MY_FALSE; #endif int port_numbers[MAXNUMPORTS] = { PORTNO }; /* The login port numbers. * Negative numbers are not ports, but the numbers of inherited * socket file descriptors. */ int numports = 0; /* Number of specified ports */ int udp_port = UDP_PORT; /* Port number for UDP. A negative number disables it. */ char *erq_file = NULL; /* Base- or pathname of the erq executable, * or NULL */ char **erq_args = NULL; /* Optional arguments of the erq executable, or NULL */ char *mud_lib; /* Path to the mudlib */ char master_name[100] = ""; /* Name of the master object */ string_t * master_name_str = NULL; /* master_name[] as tabled string */ static int new_mudlib = 0; /* True: mudlib directory was specified */ static int no_erq_demon = 0; /* True: don't start the erq */ #ifndef COMPAT_MODE Bool compat_mode = MY_FALSE; /* Plain mode */ #else Bool compat_mode = MY_TRUE; /* Compat mode */ #endif #ifdef USE_PCRE p_int regex_package = RE_PCRE; #else p_int regex_package = RE_TRADITIONAL; #endif /* The default regex package to use, expressed as one of the * acknowledged bitflags. */ /* -- Other Global Variables -- */ svalue_t const0, const1; /* The values 0 and 1 as svalues, mem-copied when needed */ double avg_consts[5]; /* Weight constants used to compute average figures */ #ifdef USE_DEBUG_LOG char *debug_file = NULL; /* Name of the debug log file. */ #endif object_t dummy_current_object_for_loads; /* Dummy object for functions, which need a current_object though * there is none. This is usually the case when (re)loading the * master object. */ int slow_shut_down_to_do = 0; /* If non-zero, the game should perform a graceful shutdown. * The value is the number of minutes to still last before shutting down. */ char input_escape = '!'; /* The input escape/input_to() bypass character. */ Bool reopen_debug_log = MY_FALSE; /* Set to TRUE by the USR2 handler to force the driver to reopen * the debug.log file. */ mp_int boot_time = 0; /* The time() the driver was started. */ long time_to_data_cleanup = 0; /* The time delay between two data cleans, derived from time_to_cleanup. */ int exit_code = 0; /* TODO: There are constants for this */ /* Exit code from the program, can be changed with the shutdown() * efun. */ /*-------------------------------------------------------------------------*/ /* Forward declarations */ static const char * drivertag (void); /* Forward declarations for the argument parser in the lower half */ static int getargs (int argc, char ** argv, int (*opt_eval)(int, const char *) ); static int eval_arg (int, const char *); /* Datastructures used to gather data during the argument scan which * needs to be evaluated later. * * struct FData: the values given to the '-f' options, allocated to size. */ typedef struct FData { struct FData * next; char txt[1]; /* The value, allocated to size */ } FData; static FData * f_head = NULL; static FData * f_tail = NULL; /*-------------------------------------------------------------------------*/ int main (int argc, char **argv) /* The main function. Nuff said. */ { int i; char *p; sigset_t set; volatile int rc; rc = 0; /* On some systems, SIGALRM is sometimes blocked. * The reasons are unknown, but this seems to be the cure. */ sigemptyset(&set); sigaddset(&set, SIGALRM); sigprocmask(SIG_UNBLOCK, &set, 0); /* Initialisations */ boot_time = (mp_int)time(NULL); setlocale(LC_CTYPE, ""); /* Use the locale defined in the LANG env var */ setlocale(LC_TIME, ""); get_stack_direction(); mb_init(); init_interpret(); rx_init(); put_number(&const0, 0); put_number(&const1, 1); current_time = get_current_time(); // Set prng_device_name to the default // prng_device_name = strdup(PRNG_DEFAULT_DEVICE); // init PRG by the default device (/dev/urandom) // if --random-seed or --randomdevice is given on the command-line it // will re-seeded later. seed_random(prng_device_name); do { dummy_current_object_for_loads = NULL_object; #ifdef DEBUG if (dummy_current_object_for_loads.user) { fprintf(stderr, "Assigning NULL_object does not clear the target.\n"); rc = 1; break; } #endif dummy_current_object_for_loads.ref = 1; dummy_current_object_for_loads.user = &default_wizlist_entry; #ifdef STRICT_EUIDS strict_euids = MY_TRUE; #endif #ifdef SHARE_VARIABLES share_variables = MY_TRUE; #endif #ifdef ALLOW_FILENAME_SPACES allow_filename_spaces = MY_TRUE; #endif #ifdef INPUT_ESCAPE /* Check the definition of the INPUT_ESCAPE character */ if (strlen(INPUT_ESCAPE) < 1) { fprintf(stderr, "Bad definition of INPUT_ESCAPE: string is empty.\n"); rc = 1; break; } if (strlen(INPUT_ESCAPE) > 1) { fprintf(stderr, "Bad definition of INPUT_ESCAPE: " "'%s' contains more than one character.\n" , INPUT_ESCAPE); rc = 1; break; } input_escape = INPUT_ESCAPE[0]; #endif /* * Check that the definition of EXTRACT_UCHAR() is correct. */ p = (char *)&i; *p = -10; if (EXTRACT_UCHAR(p) != 0x100 - 10) { fprintf(stderr, "Bad definition of EXTRACT_UCHAR().\n"); rc = 1; break; } init_driver_hooks(); init_rusage(); #ifdef HOST_DEPENDENT_INIT HOST_DEPENDENT_INIT #endif #ifdef WIZLIST_FILE /* Select a sensible default for the wizlist file. * This must be done before the parsing of the arguments so * that the name can be removed by commandline option. */ if ('\0' == wizlist_name[0]) { name_wizlist_file(WIZLIST_FILE); } #endif /* Scan of the arguments. */ if (getargs(argc, argv, eval_arg)) { rc = 1; break; } /* Print the driver tag line to stdout. The output to the debug.log * will follow when we opened it. */ printf("%s " PROGNAME " " DRIVER_VERSION LOCAL_LEVEL " (" PROJ_VERSION ")%s\n" , time_stamp(), drivertag() ); /* Setup comm::host_name, so that we can open the debug.log file * with the proper name. We do the complete setup later. */ initialize_host_name(hostname); /* Change to the mudlib dir early so that the debug.log file * is opened in the right place. * If a mudlib dir has been given by command option, we are already * in it. */ if (!new_mudlib && chdir(MUD_LIB) == -1) { printf("%s Bad library directory: %s\n", time_stamp(), MUD_LIB); rc = 1; break; } #ifdef USE_DEBUG_LOG /* If the name of the debug log file hasn't been set, use a sensible * default and make it available in the macro __DEBUG_LOG__. This * should happen before the first debug_message(). */ if (!debug_file) { char buf[MAXHOSTNAMELEN+40]; char * name; struct lpc_predef_s *tmp; if (compat_mode) strcpy(buf, "__DEBUG_LOG__=\""); else strcpy(buf, "__DEBUG_LOG__=\"/"); name = buf + strlen(buf); sprintf(name, "%s.debug.log", query_host_name()); debug_file = strdup(name); strcat(name, "\""); tmp = (struct lpc_predef_s *) xalloc(sizeof(struct lpc_predef_s)); tmp->flag = string_copy(buf); tmp->next = lpc_predefs; lpc_predefs = tmp; } /* Ugly how the same sprintf is already being executed for stdout */ debug_message("%s " PROGNAME " " DRIVER_VERSION LOCAL_LEVEL " (" PROJ_VERSION ")%s\n" , time_stamp(), drivertag() ); /* This also assures the existance of the fd for the debug log */ #endif time_to_data_cleanup = (time_to_cleanup > 0) ? time_to_cleanup : DEFAULT_CLEANUP_TIME; reserve_memory(); mstring_init(); /* Also initializes the standard strings, which may be required * early on should an error happen. */ #ifdef USE_TLS tls_global_init(); #endif if (numports < 1) /* then use the default port */ numports = 1; init_otable(); for (i = 0; i < (int)(sizeof avg_consts / sizeof avg_consts[0]); i++) avg_consts[i] = exp(- i / 900.0); #ifdef USE_MYSQL if (!pkg_mysql_init()) { rc = 1; break; } #endif #ifdef USE_IKSEMEL pkg_iksemel_init(); #endif /* If the master_name hasn't been set, select a sensible default */ if ('\0' == master_name[0]) { #ifdef MASTER_NAME strcpy(master_name, MASTER_NAME); #elif defined(COMPAT_MODE) strcpy(master_name, COMPAT_MASTER); #else strcpy(master_name, PLAIN_MASTER); #endif } /* Make sure the name of the master object is sensible. * This is important for modules like the lexer which * use it directly. * * We also need a copy of the master name as string_t (for * this the strings module has to be initialized). */ { const char *pName = make_name_sane(master_name, MY_FALSE); if (pName) strcpy(master_name, pName); master_name_str = new_tabled(master_name); if (!master_name_str) { printf("%s Out of memory for master object name (%lu bytes).\n" , time_stamp() , (unsigned long)strlen(master_name)); rc = 1; break; } } reset_machine(MY_TRUE); /* Cold reset the machine */ init_lexer(); /* The lexer needs the master_name, but also the VM * to throw errors. */ RESET_LIMITS; CLEAR_EVAL_COST; { char path[MAXPATHLEN+1]; #ifdef HAVE_GETCWD if (!getcwd(path, sizeof(path) )) #else if (!getwd(path)) #endif { perror("get(c)wd failed"); fatal("must be able to obtain current directory name\n"); } mud_lib = string_copy(path); } #ifdef ERQ_DEMON /* Make sure that erq_file contains a complete absolute pathname. */ if (!erq_file) { erq_file = malloc(strlen(BINDIR)+6); if (!erq_file) { fatal("Out of memory for erq pathname (%lu bytes).\n" , (unsigned long)strlen(BINDIR)+6); } strcpy(erq_file, BINDIR); #ifndef MSDOS_FS strcat(erq_file, "/erq"); #else strcat(erq_file, "\\erq"); #endif } else if (*erq_file != '/') { char * tmp; tmp = malloc(strlen(BINDIR)+1+strlen(erq_file)+1); if (!tmp) { fatal("Out of memory for erq pathname (%lu bytes).\n" , (unsigned long)(strlen(BINDIR)+2+strlen(erq_file))); } strcpy(tmp, BINDIR); #ifndef MSDOS_FS strcat(tmp, "/"); #else strcat(tmp, "\\"); #endif strcat(tmp, erq_file); free(erq_file); erq_file = tmp; } if (!no_erq_demon) start_erq_demon("", 0); #endif /* ERQ_DEMON */ initialize_host_ip_number(hostname, hostaddr); free(hostname); hostname = NULL; free(hostaddr); hostaddr = NULL; (void)signal(SIGFPE, SIG_IGN); current_object = &dummy_current_object_for_loads; if (setjmp(toplevel_context.con.text)) { clear_state(); add_message("Anomaly in the fabric of world space.\n"); } else { toplevel_context.rt.type = ERROR_RECOVERY_BACKEND; master_ob = get_object(master_name_str); } current_object = master_ob; toplevel_context.rt.type = ERROR_RECOVERY_NONE; if (master_ob == NULL) { printf("%s The file %s must be loadable.\n" , time_stamp(), master_name); rc = 1; break; } /* Make sure master_ob is never made a dangling pointer. * Look at apply_master_ob() for more details. */ ref_object(master_ob, "main"); initialize_master_uid(); push_number(inter_sp, 0); callback_master(STR_INAUGURATE, 1); setup_print_block_dispatcher(); /* Evaluate all the 'f' arguments we received, if any. */ while (f_head != NULL) { FData * fdata = f_head; f_head = f_head->next; push_c_string(inter_sp, fdata->txt); (void)callback_master(STR_FLAG, 1); free(fdata); if (game_is_being_shut_down) { fprintf(stderr, "%s Shutdown by master object.\n", time_stamp()); rc = 0; break; } } #ifdef USE_PARANOIA if (d_flag > 1 && time_to_swap_variables <= 0) check_a_lot_ref_counts_flag = MY_TRUE; #endif if (!assert_simul_efun_object()) { rc = 1; break; } if (game_is_being_shut_down) { rc = 1; break; } load_wiz_file(); preload_objects(e_flag); /* Start the backend loop. This won't return before * the game shuts down. */ backend(); /* Shutdown the game. */ rc = exit_code; printf("%s " PROGNAME " shutting down.\n", time_stamp()); callback_master(STR_SHUTDOWN, 0); ipc_remove(); remove_all_players(); handle_newly_destructed_objects(); #ifdef USE_SWAP /* Will perform the remove_interactive calls */ unlink_swap_file(); #endif #ifdef DEALLOCATE_MEMORY_AT_SHUTDOWN remove_all_objects(); remove_wiz_list(); #if defined(MALLOC_smalloc) dump_malloc_data(); #endif #endif } while(0); /* Mandatory cleanups - see also simulate::fatal() */ #ifdef USE_TLS tls_global_deinit(); #endif return rc; /* TODO: There are constants for this */ } /* main() */ /*-------------------------------------------------------------------------*/ void initialize_master_uid (void) /* After loading the master object, determine its (e)uid by calling the * lfun get_master_uid() in it. For details, better read the code. */ { svalue_t *ret; ret = apply_master(STR_GET_M_UID, 0); if (ret && ret->type == T_NUMBER && ret->u.number) { master_ob->user = &default_wizlist_entry; master_ob->eff_user = 0; } else if (ret == 0 || ret->type != T_STRING) { printf("%s %s: %s() in %s does not work\n" , time_stamp(), strict_euids ? "Fatal" : "Warning" , get_txt(STR_GET_M_UID), master_name); if (strict_euids) { exit(1); } } else { master_ob->user = add_name(ret->u.str); master_ob->eff_user = master_ob->user; } } /* initialize_master_uid() */ /*-------------------------------------------------------------------------*/ void vdebug_message(const char *fmt, va_list va) /* Print a message into the debug logfile, vprintf() style. */ { #ifdef USE_DEBUG_LOG static FILE *fp = NULL; if (fp == NULL || reopen_debug_log) { if (fp != NULL) { fclose(fp); fp = NULL; } reopen_debug_log = MY_FALSE; if (!debug_file) /* We can get called before it's been set */ return; fp = fopen(debug_file, "w"); if (fp == NULL) { perror(debug_file); abort(); } } (void)vfprintf(fp, fmt, va); (void)fflush(fp); #else (void)vfprintf(stdout, fmt, va); (void)fflush(stdout); #endif } /* vdebug_message() */ /*-------------------------------------------------------------------------*/ void debug_message(const char *a, ...) /* Print a message into the debug logfile, printf() style. */ { va_list va; va_start(va, a); vdebug_message(a, va); va_end(va); } /* debug_message() */ /*-------------------------------------------------------------------------*/ void write_x (int d, p_uint i) /* Memory safe function to write 4-byte hexvalue to fd . */ { int j; char c; for (j = 2 * sizeof i; --j >= 0; i <<= 4) { c = (char)((i >> (8 * sizeof i - 4) ) + '0'); if (c >= '9' + 1) c += (char)('a' - ('9' + 1)); write(d, &c, 1); } } /* write_x() */ /*-------------------------------------------------------------------------*/ void write_X (int d, unsigned char i) /* Memory safe function to write 1-byte hexvalue to fd . */ { int j; char c; for (j = 2 * sizeof i; --j >= 0; i <<= 4) { c = (char)((i >> (8 * sizeof i - 4) ) + '0'); if (c >= '9' + 1) c += (char)('a' - ('9' + 1)); write(d, &c, 1); } } /* write_X() */ /*-------------------------------------------------------------------------*/ void writed (int d, p_uint i) /* Memory safe function to write integer value to fd . */ { p_uint j; char c; for (j = 1000000000; j > i; j /= 10) NOOP; if (!j) j = 1; do { c = (char)((i / j) % 10 + '0'); write(d, &c, 1); j /= 10; } while (j > 0); } /* writed() */ /*-------------------------------------------------------------------------*/ void writes (int d, const char *s) /* Memory safe function to string to fd . */ { write(d, s, strlen(s)); } /*-------------------------------------------------------------------------*/ char * dprintf_first (int fd, char *s, p_int a) /* Write the string up to the next "%"-style argument to , the * write according to the %-formatter. Recognized are %s, %d, %c, * %x (4-Byte hex) and %X (a 1-Byte hex). * If no %-formatter is present, the whole string is written. * * Result is a pointer to the remaining string. */ { char *p; do { if ( !(p = strchr(s, '%')) ) { write(fd, s, strlen(s)); return ""; } write(fd, s, p - s); switch(p[1]) { case '%': write(fd, p+1, 1); continue; case 's': write(fd, (char *)a, strlen((char*)a)); break; case 'c': { char c = (char)a; write(fd, (char *)&c, 1); break; } case 'd': writed(fd, a); break; case 'x': write_x(fd, a); break; case 'X': write_X(fd, (unsigned char)a); break; } return p+2; } while(1); } /* dprintf_first() */ /*-------------------------------------------------------------------------*/ void dprintf1 (int fd, char *s, p_int a) /* Write a message to . may contain one %-style formatter. * for the argument . */ { s = dprintf_first(fd, s, a); write(fd, s, strlen(s)); } /* dprintf1() */ /*-------------------------------------------------------------------------*/ void dprintf2 (int fd, char *s, p_int a, p_int b) /* Write a message to . may contain two %-style formatter. * for the arguments and . */ { s = dprintf_first(fd, s, a); dprintf1(fd, s, b); } /* dprintf2() */ /*-------------------------------------------------------------------------*/ void dprintf3 (int fd, char *s, p_int a, p_int b, p_int c) /* Write a message to . may contain three %-style formatter. * for the arguments , and . */ { s = dprintf_first(fd, s, a); dprintf2(fd, s, b, c); } /* dprintf3() */ /*-------------------------------------------------------------------------*/ void dprintf4 (int fd, char *s, p_int a, p_int b, p_int c, p_int d) /* Write a message to . may contain three %-style formatter. * for the arguments , , and . */ { s = dprintf_first(fd, s, a); dprintf3(fd, s, b, c, d); } /* dprintf4() */ /*=========================================================================*/ /* The argument parser */ /*=========================================================================*/ /* This code parses the arguments passed to the program in the count * and the array of strings . The parser distinguishes options, which * start with a '-', from normal arguments; options are further distinguished * by their name and may take an additional value. The parser neither * imposes nor expects any order of options and arguments. * * Options are recognized in two forms. In the short form the option must * be given as a single '-' followed by a single letter. In the long form, * options start with '--' followed by a string of arbitrary length. * Short options are case sensitive, long options aren't. * Examples are: '-r' and '--recursive'. * * If an option takes a value, it must follow the option immediately after * a separating space or '='. Additionally, the value for a short option * may follow the option without separator. Examples are: '-fMakefile', * '-f Makefile', '--file=Makefile' and '--file Makefile'. * * Short options may be collated into one argument, e.g. '-rtl', but * of these only the last may take a value. * * The option '--' marks the end of options. All following command arguments * are considered proper arguments even if they start with a '-' or '--'. * * The arguments are usually taken from the commandline; but the parser * is also able to read them from a textfiles, which can be nested. The * content of the textfiles is broken down into words delimited by whitespace, * which are then treated as given on the commandline at the place where * the instruction to read the textfile stood. * * The file parser recognizes simple double-quoted strings, which must be * contained on a single line. Additionally, the '#' character given by * itself is a comment marker - everthing after the '#' until the end * of the current line is ignored. *------------------------------------------------------------------------- * Internally every option recognized by the program is associated with * an id number, defined as the enum OptNumber. The parser itself uses the * two id numbers 'cUnknown' for unrecognized options, and 'cArgument' for * proper command arguments. * * Id numbers are associated with their option strings/letters by the * statically initialized arrays aOptions. Every element * in this array is a structure defining the option's name (string * or letter), the associated id number, whether or not the option * takes a value, and the short and long help text. The order of the * elements does not matter, except for the help text output. * * The parsing is done by calling the function * * int getargs(int argc, char ** argv, int (*)handler(int, const char *)) * * The function is passed the argument count and vector as * they were received from the main() function, and a callback function * . getargs() returns 0 if the parsing completed successfully, * and non-zero else. * * The handler function is called for every successfully recognized option * and argument. Its prototype is * * int handler(int eOption, const char *pValue) * * Parameter denotes the recognized option, and pValue points * to the beginning of the value string if the option takes a value. * Proper arguments are parsed with eOption==cArgument and pValue * pointing to the argument string. The handler has to return one of the * following values: * hrSuccess: if the option/argument was processed correctly. * hrError: if the option/argument couldn't be processed. * hrArgFile: if the given value is the name of an arguments file * to include. *------------------------------------------------------------------------- */ /* Handler return values */ typedef enum HandlerResult { hrSuccess = 0 /* Argument parsed */ , hrError /* Error parsing argument */ , hrArgFile /* Value of this argument is the filename of an argument * file. */ } HandlerResult; /* Desription of short ('-') options */ typedef struct ShortOpt { char cOption; /* The option character */ int eNumber; /* The associated option number */ short bValue; /* True: takes a value */ } ShortOpt; /* Desription of long ('--') options */ typedef struct LongOpt { char * pOption; /* The option string */ int eNumber; /* The associated option number */ short bValue; /* True: takes a value */ } LongOpt; /* Description of an option */ typedef struct Option { char cOption; /* The short option char, or \0 if none */ char * pOption; /* The long option string, or NULL if none */ int eNumber; /* The associated option number */ short bValue; /* True: takes a value */ char * pSHelp; /* Short help string, or NULL */ char * pLHelp; /* Long help string, or NULL */ } Option; /* Every recognized option has a ordinal number */ typedef enum OptNumber { cUnknown = 0 /* unknown option */ , cArgument /* normal argument (for us: portnumber) */ , cArgFile /* --args */ , cInherited /* --inherit */ , cUdpPort /* --udp */ , cTrace /* --list-compiles */ , cAlarmTime /* --alarm-time */ , cCleanupTime /* --cleanup-time */ , cCompat /* --compat */ , cNoCompat /* --no-compat */ , cDebug /* --debug */ , cDefine /* --define */ , cErq /* --erq */ , cEvalcost /* --eval-cost */ , cFilenameSpaces /* --filename-spaces */ , cNoFilenameSpaces /* --no-filename-spaces */ , cFuncall /* --funcall */ , cMaster /* --master */ , cMudlib /* --mudlib */ #if 1 /* def USE_DEBUG_LOG */ , cDebugFile /* --debug-file */ #endif , cHBInterval /* --heart-interval */ , cHostname /* --hostname */ , cHostaddr /* --hostaddr */ , cMaxMalloc /* --max-malloc */ , cMaxArray /* --max-array */ , cMaxBytes /* --max-bytes */ , cMaxCallouts /* --max-callouts */ , cMaxFile /* --max-file */ , cMaxMapping /* --max-mapping */ , cMaxMappingKeys /* --max-mapping-keys */ , cMaxThreadPend /* --max-thread-pending */ , cMinMalloc /* --min-malloc */ , cMinSmallMalloc /* --min-small-malloc */ , cNoERQ /* --no-erq */ , cNoHeart /* --no-heart */ , cNoPreload /* --no-preload */ , cPidFile /* --pidfile */ , cRandomdevice /* --randomdevice */ , cRandomSeed /* --random-seed */ , cRegexp /* --regexp */ , cResetTime /* --reset-time */ , cReserved /* -r */ , cReserveUser /* --reserve-user */ , cReserveMaster /* --reserve-master */ , cReserveSystem /* --reserve-system */ , cStrictEuids /* --strict-euids */ , cNoStrictEuids /* --no-strict-euids */ , cShareVariables /* --share-variables */ , cNoShareVariables /* --init-variables */ , cSwap /* -s */ , cSwapTime /* --swap-time */ , cSwapVars /* --swap-variables */ , cSwapFile /* --swap-file */ , cSwapCompact /* --swap-compact */ , cSyncHB /* --sync-heart */ , cASyncHB /* --async-heart */ , cWizlistFile /* --wizlist-file */ , cNoWizlistFile /* --no-wizlist-file */ #ifdef GC_SUPPORT , cGcollectFD /* --gcollect-outfd */ #endif #ifdef USE_TLS , cTLSkey /* --tls-key */ , cTLScert /* --tls-cert */ , cTLStrustdir /* --tls-trustdirectory */ , cTLStrustfile /* --tls-trustfile */ , cTLScrlfile /* --tls-crlfile */ , cTLScrldir /* --tls-crldirectory */ #endif #ifdef USE_PARANOIA , cCheckRefs /* --check-refcounts */ , cCheckState /* --check-state */ #endif #ifdef DEBUG , cGobbleFDs /* --gobble-descriptors */ #endif #ifdef CHECK_OBJECT_STAT , cCheckObjectStat /* --check-object-stat */ #endif #ifdef YYDEBUG , cYYDebug /* --yydebug */ #endif , cOptions /* --options */ , cVersion /* --version */ , cLongHelp /* --longhelp */ , cHelp /* --help */ } OptNumber; /* Comprehensive lists of recognized options */ static Option aOptions[] = { { 0, "args", cArgFile, MY_TRUE , " --args \n" , " --args \n" " Read the options from as if they were given on the\n" " commandline.\n" } , { 'P', "inherit", cInherited, MY_TRUE , " -P|--inherit \n" , " -P|--inherit \n" " Inherit filedescriptor from the parent process\n" " as socket to listen for connections.\n" } , { 'u', "udp", cUdpPort, MY_TRUE , " -u|--udp \n" , " -u|--udp \n" " Specify the for the UDP port, overriding the compiled-in\n" " default.\n" } , { 'D', "define", cDefine, MY_TRUE , " -D|--define [=]\n" , " -D|--define [=]\n" " Add (optionally to be expanded to ) to the list of\n" " predefined macros known by the LPC compiler.\n" } , { 'E', "eval-cost", cEvalcost, MY_TRUE , " -E|--eval-cost \n" , " -E|--eval-cost \n" " Set the number of available for one evaluation thread.\n" } , { 'M', "master", cMaster, MY_TRUE , " -M|--master \n" , " -M|--master \n" " Use for the master object.\n" } , { 'm', "mudlib", cMudlib, MY_TRUE , " -m|--mudlib \n" , " -m|--mudlib \n" " Use as the top directory of the library.\n" } /* This option _should_ go completely, but in order to not break existing * scripts immediately, we just pretend it is still there and ignore what * it says. */ #if 1 /* def USE_DEBUG_LOG */ , { 0, "debug-file", cDebugFile, MY_TRUE , " --debug-file \n" , " --debug-file \n" #ifdef USE_DEBUG_LOG " Log all debug output in instead of .debug.log .\n" #else " This function has been disabled at compile time.\n" " Please remove it from your scripts.\n" #endif } #endif , { 0, "hostname", cHostname, MY_TRUE , " --hostname \n" , " --hostname \n" " Use as hostname, instead of what the system says.\n" } , { 0, "hostaddr", cHostaddr, MY_TRUE , " --hostaddr \n" , " --hostaddr \n" " Use as address of this machine, instead of what the\n" " system says. In particular this address will be used to open\n" " the driver ports.\n" } , { 0, "compat", cCompat, MY_FALSE , " --compat\n" , " --compat\n" " --no-compat\n" " Select the mode (compat or plain) of the driver.\n" " Note that this choice does not affect the default name of the master\n" " object.\n" } , { 0, "no-compat", cNoCompat, MY_FALSE , " --no-compat\n" , NULL } , { 'd', "debug", cDebug, MY_FALSE , " -d|--debug\n" , " -d|--debug\n" " Generate debug output; repeat the argument for even more output.\n" } , { 'c', "list-compiles", cTrace, MY_FALSE , " -c|--list-compiles\n" , " -c|--list-compiles\n" " List the name of every compiled file on stderr.\n" } , { 'e', "no-preload", cNoPreload, MY_FALSE , " -e|--no-preload\n" , " -e|--no-preload\n" " Pass a non-zero argument (the number of occurences of this option)\n" " to master->preload(), which usually inhibits all preloads of castles\n" " and other objects.\n" } , { 0, "erq", cErq, MY_TRUE , " --erq | --erq \" \"\n" , " --erq \n" " --erq \" \"\n" " Use instead of 'erq' as the name of the ERQ executable.\n" " If the name starts with a '/', it is take to be an absolute pathname,\n" " otherwise it is interpreted relative to " BINDIR ".\n" " If not specified, 'erq' is used as executable name.\n" " With the proper use of quotes it is legal to pass arbitrary arguments\n" " to the erq, however, these may not contain spaces themselves.\n" } , { 'N', "no-erq", cNoERQ, MY_FALSE , " -N|--no-erq\n" , " -N|--no-erq\n" " Don't start the erq demon (if it would be started at all).\n" } , { 0, "alarm-time", cAlarmTime, MY_TRUE , " --alarm-time