1 /*---------------------------------------------------------------------------
2  * LPMud Driver main module.
3  *
4  *---------------------------------------------------------------------------
5  * Here is the main() function which parses the commandline arguments,
6  * initializes everything and starts the backend loop. A documentation
7  * of the available commandline arguments is in the file
8  * doc/driver/invocation; the argument '--help' causes the driver to
9  * print a short help to all available arguments.
10  *
11  * The commandline arguments are actually parsed twice, since some
12  * arguments expect a functional master object (like the 'f' argument).
13  *
14  * The argument parser is an adaption of a generic parser. All the associated
15  * code and comments is kept in the lower half of this source for better
16  * readability.
17  *
18  * This file also contains several global functions and variables, some of
19  * which should probably go into a dedicated 'global' module or somewhere else
20  * appropriate.
21  * TODO: Move out all those variables and functions which are illfitting here.
22  * TODO: Put the argument parsing into a separate file?
23  *---------------------------------------------------------------------------
24  */
25 
26 #include "driver.h"
27 #include "typedefs.h"
28 
29 #include "my-alloca.h"
30 #include <ctype.h>
31 #include <fcntl.h>
32 #ifdef HAVE_NETDB_H
33 #    include <netdb.h>
34      /* MAXHOSTNAMELEN on Solaris */
35 #endif
36 #include <locale.h>
37 #include <math.h>
38 #include <setjmp.h>
39 #include <signal.h>
40 #include <stdarg.h>
41 #include <stdio.h>
42 #include <sys/stat.h>
43 #include <sys/types.h>
44 #ifdef HAVE_SYS_TIME_H
45 #include <sys/time.h>
46 #endif
47 #include <time.h>
48 
49 #include "main.h"
50 
51 #include "backend.h"
52 #include "array.h"
53 #include "access_check.h"
54 #include "comm.h"
55 #include "filestat.h"
56 #include "gcollect.h"
57 #include "interpret.h"
58 #include "lex.h"
59 #include "mapping.h"
60 #include "mempools.h"
61 #include "mregex.h"
62 #include "mstrings.h"
63 #include "object.h"
64 #include "otable.h"
65 #include "patchlevel.h"
66 #include "pkg-tls.h"
67 #include "random.h"
68 #include "simulate.h"
69 #include "simul_efun.h"
70 #include "stdstrings.h"
71 #include "svalue.h"
72 #include "swap.h"
73 #include "wiz_list.h"
74 #include "xalloc.h"
75 
76 #ifdef USE_MYSQL
77 #include "pkg-mysql.h"
78 #endif
79 
80 #ifdef USE_XML
81 #    if defined(HAS_XML2) && defined(HAS_IKSEMEL)
82 #        error Both, libxml2 and iksemel enabled.
83 #    endif
84 #    ifdef HAS_XML2
85 #        include "pkg-xml2.h"
86 #    endif
87 #    ifdef HAS_IKSEMEL
88 #        include "pkg-iksemel.h"
89 #    endif
90 #endif
91 
92 #ifdef USE_GCRYPT
93 #include "pkg-gcrypt.h"
94 #endif
95 
96 #include "i-eval_cost.h"
97 
98 #include "../mudlib/sys/regexp.h"
99 
100 /*-------------------------------------------------------------------------*/
101 
102 #define PLAIN_MASTER "secure/master"
103 #define COMPAT_MASTER "obj/master"
104 
105 /*-------------------------------------------------------------------------*/
106 
107 /* -- Pure commandline arguments -- */
108 
109 int d_flag    = 0;  /* Debuglevel */
110 /* TODO: Make this bitflags, one for 'trace refcounts' etc */
111 Bool t_flag    = MY_FALSE;  /* True: Disable heart beat and reset */
112 static int e_flag = MY_FALSE;  /* Passed to preload(), usually disables it */
113 Bool comp_flag = MY_FALSE;  /* Trace compilations */
114 #ifdef DEBUG
115 Bool check_a_lot_ref_counts_flag = MY_FALSE;  /* The name says it. */
116 int check_state_level = 0;     /* how of to check the state in the loop */
117 #endif
118 
119 #ifdef CHECK_OBJECT_STAT
120 Bool check_object_stat = MY_FALSE;
121 #endif
122 
123 Bool strict_euids = MY_FALSE;     /* Enforce use of the euids */
124 Bool share_variables = MY_FALSE;  /* Clones are initialized from their
125                                    * blueprints.
126                                    */
127 
128 Bool allow_filename_spaces = MY_FALSE; /* Allow spaces in filenames */
129 
130 static char * hostname = NULL;
131 static char * hostaddr = NULL;
132   /* Hostname and -addr given on the commandline. They are passed as
133    * arguments to initialize_host_ip() and are then no longer used.
134    */
135 
136 
137 /* -- Configuration options -- */
138 
139 long time_to_reset          = TIME_TO_RESET;
140 long time_to_cleanup        = TIME_TO_CLEAN_UP;
141   /* A value <= 0 disables the reset/cleanup */
142 
143 long time_to_swap           = TIME_TO_SWAP;
144 long time_to_swap_variables = TIME_TO_SWAP_VARIABLES;
145   /* A value <= 0 disables the swapping. */
146 
147 long alarm_time             = ALARM_TIME;
148 long heart_beat_interval    = HEART_BEAT_INTERVAL;
149   /* Minimum value is 1. */
150 
151 #ifdef SYNCHRONOUS_HEART_BEAT
152 Bool synch_heart_beats      = MY_TRUE;
153 #else
154 Bool synch_heart_beats      = MY_FALSE;
155 #endif
156 
157 int port_numbers[MAXNUMPORTS] = { PORTNO };
158   /* The login port numbers.
159    * Negative numbers are not ports, but the numbers of inherited
160    * socket file descriptors.
161    */
162 int numports = 0;  /* Number of specified ports */
163 
164 int udp_port = UDP_PORT;
165   /* Port number for UDP. A negative number disables it. */
166 
167 char *erq_file = NULL;        /* Base- or pathname of the erq executable,
168                                * or NULL */
169 char **erq_args = NULL;       /* Optional arguments of the erq executable,
170                                  or NULL */
171 
172 char *mud_lib;                /* Path to the mudlib */
173 char master_name[100] = "";   /* Name of the master object */
174 string_t * master_name_str = NULL;  /* master_name[] as tabled string */
175 
176 static int new_mudlib = 0;    /* True: mudlib directory was specified */
177 static int no_erq_demon = 0;  /* True: don't start the erq */
178 
179 #ifndef COMPAT_MODE
180 Bool compat_mode = MY_FALSE; /* Plain mode */
181 #else
182 Bool compat_mode = MY_TRUE;  /* Compat mode */
183 #endif
184 
185 #ifdef USE_PCRE
186 p_int regex_package = RE_PCRE;
187 #else
188 p_int regex_package = RE_TRADITIONAL;
189 #endif
190   /* The default regex package to use, expressed as one of the
191    * acknowledged bitflags.
192    */
193 
194 /* -- Other Global Variables -- */
195 svalue_t const0, const1;
196   /* The values 0 and 1 as svalues, mem-copied when needed */
197 
198 double avg_consts[5];
199   /* Weight constants used to compute average figures */
200 
201 char *debug_file = NULL;  /* Name of the debug log file. */
202 
203 object_t dummy_current_object_for_loads;
204   /* Dummy object for functions, which need a current_object though
205    * there is none. This is usually the case when (re)loading the
206    * master object.
207    */
208 
209 int slow_shut_down_to_do = 0;
210   /* If non-zero, the game should perform a graceful shutdown.
211    * The value is the number of minutes to still last before shutting down.
212    */
213 
214 char input_escape = '!';
215   /* The input escape/input_to() bypass character.
216    */
217 
218 Bool reopen_debug_log = MY_FALSE;
219   /* Set to TRUE by the USR2 handler to force the driver to reopen
220    * the debug.log file.
221    */
222 
223 mp_int boot_time = 0;
224   /* The time() the driver was started.
225    */
226 
227 long time_to_data_cleanup = 0;
228   /* The time delay between two data cleans, derived from time_to_cleanup. */
229 
230 int exit_code = 0; /* TODO: There are constants for this */
231   /* Exit code from the program, can be changed with the shutdown()
232    * efun.
233    */
234 
235 /*-------------------------------------------------------------------------*/
236 
237 /* Forward declarations */
238 
239 static const char * drivertag (void);
240 
241 /* Forward declarations for the argument parser in the lower half */
242 
243 static int getargs (int argc, char ** argv, int (*opt_eval)(int, const char *) );
244 static int eval_arg (int, const char *);
245 
246 /* Datastructures used to gather data during the argument scan which
247  * needs to be evaluated later.
248  *
249  * struct FData: the values given to the '-f' options, allocated to size.
250  */
251 
252 typedef struct FData {
253     struct FData * next;
254     char           txt[1];  /* The value, allocated to size */
255 } FData;
256 
257 static FData * f_head = NULL;
258 static FData * f_tail = NULL;
259 
260 /*-------------------------------------------------------------------------*/
261 int
main(int argc,char ** argv)262 main (int argc, char **argv)
263 
264 /* The main function. Nuff said. */
265 
266 {
267     int i;
268     char *p;
269     sigset_t set;
270     volatile int rc;
271 
272     rc = 0;
273 
274     /* On some systems, SIGALRM is sometimes blocked.
275      * The reasons are unknown, but this seems to be the cure.
276      */
277     sigemptyset(&set);
278     sigaddset(&set, SIGALRM);
279     sigprocmask(SIG_UNBLOCK, &set, 0);
280 
281     /* Initialisations */
282 
283     boot_time = (mp_int)time(NULL);
284     setlocale(LC_CTYPE, ""); /* Use the locale defined in the LANG env var */
285     setlocale(LC_TIME, "");
286     get_stack_direction();
287     mb_init();
288     init_interpret();
289     rx_init();
290 
291     put_number(&const0, 0);
292     put_number(&const1, 1);
293 
294     current_time = get_current_time();
295 
296 
297     // Set prng_device_name to the default //
298     prng_device_name = strdup(PRNG_DEFAULT_DEVICE);
299     // init PRG by the default device (/dev/urandom)
300     // if --random-seed or --randomdevice is given on the command-line it
301     // will re-seeded later.
302     seed_random(prng_device_name);
303 
304 #ifdef ACCESS_FILE
305     access_file = strdup(ACCESS_FILE);
306 #endif
307 #ifdef ACCESS_LOG
308     access_log = strdup(ACCESS_LOG);
309 #endif
310 #ifdef USE_TLS
311 #  ifdef TLS_DEFAULT_KEYFILE
312     tls_keyfile = strdup(TLS_DEFAULT_KEYFILE);
313 #  endif
314 #  ifdef TLS_DEFAULT_CERTFILE
315     tls_certfile = strdup(TLS_DEFAULT_CERTFILE);
316 #  endif
317 #  ifdef TLS_DEFAULT_TRUSTFILE
318     tls_trustfile = strdup(TLS_DEFAULT_TRUSTFILE);
319 #  endif
320 #  ifdef TLS_DEFAULT_TRUSTDIRECTORY
321     tls_trustdirectory = strdup(TLS_DEFAULT_TRUSTDIRECTORY);
322 #  endif
323 #  ifdef TLS_DEFAULT_CRLFILE
324     tls_crlfile = strdup(TLS_DEFAULT_CRLFILE);
325 #  endif
326 #  ifdef TLS_DEFAULT_CRLDIRECTORY
327     tls_crldirectory = strdup(TLS_DEFAULT_CRLDIRECTORY);
328 #  endif
329 #endif
330 
331     do {
332         dummy_current_object_for_loads = NULL_object;
333 #ifdef DEBUG
334         if (dummy_current_object_for_loads.user)
335         {
336             fprintf(stderr, "Assigning NULL_object does not clear the target.\n");
337             rc = 1;
338             break;
339         }
340 #endif
341         dummy_current_object_for_loads.ref = 1;
342         dummy_current_object_for_loads.user = &default_wizlist_entry;
343 
344 #ifdef STRICT_EUIDS
345         strict_euids = MY_TRUE;
346 #endif
347 #ifdef SHARE_VARIABLES
348         share_variables = MY_TRUE;
349 #endif
350 #ifdef ALLOW_FILENAME_SPACES
351         allow_filename_spaces = MY_TRUE;
352 #endif
353 
354 #ifdef INPUT_ESCAPE
355         /* Check the definition of the INPUT_ESCAPE character */
356         if (strlen(INPUT_ESCAPE) < 1)
357         {
358             fprintf(stderr, "Bad definition of INPUT_ESCAPE: string is empty.\n");
359             rc = 1;
360             break;
361         }
362         if (strlen(INPUT_ESCAPE) > 1)
363         {
364             fprintf(stderr, "Bad definition of INPUT_ESCAPE: "
365                             "'%s' contains more than one character.\n"
366                           , INPUT_ESCAPE);
367             rc = 1;
368             break;
369         }
370         input_escape = INPUT_ESCAPE[0];
371 #endif
372 
373         /*
374          * Check that the definition of EXTRACT_UCHAR() is correct.
375          */
376         p = (char *)&i;
377         *p = -10;
378         if (EXTRACT_UCHAR(p) != 0x100 - 10) {
379             fprintf(stderr, "Bad definition of EXTRACT_UCHAR().\n");
380             rc = 1;
381             break;
382         }
383 
384         init_driver_hooks();
385         init_rusage();
386 #ifdef HOST_DEPENDENT_INIT
387         HOST_DEPENDENT_INIT
388 #endif
389 
390 #ifdef WIZLIST_FILE
391         /* Select a sensible default for the wizlist file.
392          * This must be done before the parsing of the arguments so
393          * that the name can be removed by commandline option.
394          */
395         if ('\0' == wizlist_name[0])
396         {
397             name_wizlist_file(WIZLIST_FILE);
398         }
399 #endif
400 
401         /* Scan of the arguments.
402          */
403         if (getargs(argc, argv, eval_arg))
404         {
405             rc = 1;
406             break;
407         }
408 
409         /* Print the driver tag line to stdout. The output to the debug.log
410          * will follow when we opened it.
411          */
412         printf("%s LDMud " DRIVER_VERSION LOCAL_LEVEL
413                " (" PROJ_VERSION ")%s\n"
414               , time_stamp(), drivertag()
415               );
416 
417         /* Setup comm::host_name, so that we can open the debug.log file
418          * with the proper name. We do the complete setup later.
419          */
420         initialize_host_name(hostname);
421 
422         /* Change to the mudlib dir early so that the debug.log file
423          * is opened in the right place.
424          * If a mudlib dir has been given by command option, we are already
425          * in it.
426          */
427         if (!new_mudlib && chdir(MUD_LIB) == -1) {
428             printf("%s Bad mudlib directory: %s\n", time_stamp(), MUD_LIB);
429             rc = 1;
430             break;
431         }
432 
433         /* If the name of the debug log file hasn't been set, use a sensible
434          * default and make it available in the macro __DEBUG_LOG__. This
435          * should happen before the first debug_message().
436          */
437 
438         if  (!debug_file)
439         {
440             char buf[MAXHOSTNAMELEN+40];
441             char * name;
442             struct lpc_predef_s *tmp;
443 
444             if (compat_mode)
445                 strcpy(buf, "__DEBUG_LOG__=\"");
446             else
447                 strcpy(buf, "__DEBUG_LOG__=\"/");
448             name = buf + strlen(buf);
449             sprintf(name, "%s.debug.log", query_host_name());
450             debug_file = strdup(name);
451             strcat(name, "\"");
452 
453             tmp = (struct lpc_predef_s *) xalloc(sizeof(struct lpc_predef_s));
454             tmp->flag = string_copy(buf);
455             tmp->next = lpc_predefs;
456             lpc_predefs = tmp;
457         }
458 
459         debug_message("%s LDMud " DRIVER_VERSION LOCAL_LEVEL
460                       " (" PROJ_VERSION ")%s\n"
461                      , time_stamp(), drivertag()
462                      );
463           /* This also assures the existance of the fd for the debug log */
464 
465         time_to_data_cleanup = (time_to_cleanup > 0) ? time_to_cleanup
466                                                      : DEFAULT_CLEANUP_TIME;
467         reserve_memory();
468         mstring_init();
469           /* Also initializes the standard strings, which may be required
470            * early on should an error happen.
471            */
472 
473 #ifdef USE_TLS
474         tls_global_init();
475 #endif
476 
477 #ifdef USE_GCRYPT
478         if (!pkg_gcrypt_init())
479         {
480             rc = 1;
481             break;
482         }
483 #endif
484 
485         if (numports < 1) /* then use the default port */
486             numports = 1;
487 
488         init_otable();
489         for (i = 0; i < (int)(sizeof avg_consts / sizeof avg_consts[0]); i++)
490             avg_consts[i] = exp(- i / 900.0);
491 
492 #ifdef USE_MYSQL
493         if (!pkg_mysql_init())
494         {
495             rc = 1;
496             break;
497         }
498 #endif
499 
500 #ifdef USE_XML
501 #ifdef HAS_IKSEMEL
502         pkg_iksemel_init();
503 #endif
504 
505 #ifdef HAS_XML2
506         pkg_xml2_init();
507 #endif
508 #endif
509 
510         /* If the master_name hasn't been set, select a sensible default */
511         if ('\0' == master_name[0])
512         {
513 #ifdef MASTER_NAME
514             strcpy(master_name, MASTER_NAME);
515 #elif defined(COMPAT_MODE)
516             strcpy(master_name, COMPAT_MASTER);
517 #else
518             strcpy(master_name, PLAIN_MASTER);
519 #endif
520         }
521 
522         /* Make sure the name of the master object is sensible.
523          * This is important for modules like the lexer which
524          * use it directly.
525          *
526          * We also need a copy of the master name as string_t (for
527          * this the strings module has to be initialized).
528          */
529         {
530             const char *pName = make_name_sane(master_name, MY_FALSE);
531             if (pName)
532                 strcpy(master_name, pName);
533             master_name_str = new_tabled(master_name);
534             if (!master_name_str)
535             {
536                 printf("%s Out of memory for master object name (%lu bytes).\n"
537                       , time_stamp()
538                       , (unsigned long)strlen(master_name));
539                 rc = 1;
540                 break;
541             }
542         }
543 
544         reset_machine(MY_TRUE); /* Cold reset the machine */
545         init_lexer();
546           /* The lexer needs the master_name, but also the VM
547            * to throw errors.
548            */
549 
550         RESET_LIMITS;
551         CLEAR_EVAL_COST;
552         {
553             char path[MAXPATHLEN+1];
554 #ifdef HAVE_GETCWD
555             if (!getcwd(path, sizeof(path) ))
556 #else
557             if (!getwd(path))
558 #endif
559             {
560                 perror("get(c)wd failed");
561                 fatal("must be able to obtain current directory name\n");
562             }
563             mud_lib = string_copy(path);
564         }
565 
566 #ifdef ERQ_DEMON
567         /* Make sure that erq_file contains a complete absolute pathname. */
568 
569         if (!erq_file)
570         {
571             erq_file = malloc(strlen(BINDIR)+6);
572             if (!erq_file)
573             {
574                 fatal("Out of memory for erq pathname (%lu bytes).\n"
575                      , (unsigned long)strlen(BINDIR)+6);
576             }
577             strcpy(erq_file, BINDIR);
578             strcat(erq_file, "/erq");
579         }
580         else if (*erq_file != '/')
581         {
582             char * tmp;
583             tmp = malloc(strlen(BINDIR)+1+strlen(erq_file)+1);
584             if (!tmp)
585             {
586                 fatal("Out of memory for erq pathname (%lu bytes).\n"
587                      , (unsigned long)(strlen(BINDIR)+2+strlen(erq_file)));
588             }
589             strcpy(tmp, BINDIR);
590             strcat(tmp, "/");
591             strcat(tmp, erq_file);
592             free(erq_file);
593             erq_file = tmp;
594         }
595 
596         if (!no_erq_demon)
597             start_erq_demon("", 0);
598 #endif /* ERQ_DEMON */
599         initialize_host_ip_number(hostname, hostaddr);
600         free(hostname); hostname = NULL;
601         free(hostaddr); hostaddr = NULL;
602 
603         initialize_host_access();
604 
605         install_signal_handlers();
606 
607         (void)signal(SIGFPE, SIG_IGN);
608         current_object = &dummy_current_object_for_loads;
609         if (setjmp(toplevel_context.con.text)) {
610             clear_state();
611             add_message("Anomaly in the fabric of world space.\n");
612         }
613         else
614         {
615             toplevel_context.rt.type = ERROR_RECOVERY_BACKEND;
616             master_ob = get_object(master_name_str);
617         }
618         current_object = master_ob;
619         toplevel_context.rt.type = ERROR_RECOVERY_NONE;
620         if (master_ob == NULL) {
621             printf("%s The file %s must be loadable.\n"
622                   , time_stamp(), master_name);
623             rc = 1;
624             break;
625         }
626 
627         /* Make sure master_ob is never made a dangling pointer.
628          * Look at apply_master_ob() for more details.
629          */
630         ref_object(master_ob, "main");
631         initialize_master_uid();
632         push_number(inter_sp, 0);
633         callback_master(STR_INAUGURATE, 1);
634         setup_print_block_dispatcher();
635 
636         /* Evaluate all the 'f' arguments we received, if any. */
637         while (f_head != NULL)
638         {
639             FData * fdata = f_head;
640 
641             f_head = f_head->next;
642             push_c_string(inter_sp, fdata->txt);
643             (void)callback_master(STR_FLAG, 1);
644             free(fdata);
645             if (game_is_being_shut_down) {
646                 fprintf(stderr, "%s Shutdown by master object.\n", time_stamp());
647                 rc = 0;
648                 break;
649             }
650         }
651 
652 #ifdef DEBUG
653         if (d_flag > 1 && time_to_swap_variables <= 0)
654             check_a_lot_ref_counts_flag = MY_TRUE;
655 #endif
656 
657         if (!assert_simul_efun_object())
658         {
659             rc = 1;
660             break;
661         }
662 
663         if (game_is_being_shut_down)
664         {
665             rc = 1;
666             break;
667         }
668 
669         load_wiz_file();
670         preload_objects(e_flag);
671 
672         /* Start the backend loop. This won't return before
673          * the game shuts down.
674          */
675         backend();
676 
677         /* Shutdown the game.
678          */
679 
680         rc = exit_code;
681         printf("%s LDMud shutting down.\n", time_stamp());
682 
683         callback_master(STR_SHUTDOWN, 0);
684         ipc_remove();
685         remove_all_players();
686         handle_newly_destructed_objects();
687           /* Will perform the remove_interactive calls */
688         unlink_swap_file();
689 #ifdef DEALLOCATE_MEMORY_AT_SHUTDOWN
690         remove_all_objects();
691         remove_wiz_list();
692 #if defined(MALLOC_smalloc)
693         dump_malloc_data();
694 #endif
695 #endif
696     } while(0);
697 
698     /* Mandatory cleanups - see also simulate::fatal() */
699 #ifdef USE_TLS
700     tls_global_deinit();
701 #endif
702 
703     return rc; /* TODO: There are constants for this */
704 } /* main() */
705 
706 
707 /*-------------------------------------------------------------------------*/
initialize_master_uid(void)708 void initialize_master_uid (void)
709 
710 /* After loading the master object, determine its (e)uid by calling the
711  * lfun get_master_uid() in it. For details, better read the code.
712  */
713 
714 {
715     svalue_t *ret;
716 
717     ret = apply_master(STR_GET_M_UID, 0);
718     if (ret && ret->type == T_NUMBER && ret->u.number)
719     {
720         master_ob->user = &default_wizlist_entry;
721         master_ob->eff_user = 0;
722     }
723     else if (ret == 0 || ret->type != T_STRING)
724     {
725         printf("%s %s: %s() in %s does not work\n"
726               , time_stamp(), strict_euids ? "Fatal" : "Warning"
727               , get_txt(STR_GET_M_UID), master_name);
728         if (strict_euids)
729         {
730             exit(1);
731         }
732     }
733     else
734     {
735         master_ob->user = add_name(ret->u.str);
736         master_ob->eff_user = master_ob->user;
737     }
738 } /* initialize_master_uid() */
739 
740 
741 /*-------------------------------------------------------------------------*/
742 void
vdebug_message(const char * fmt,va_list va)743 vdebug_message(const char *fmt, va_list va)
744 
745 /* Print a message into the debug logfile, vprintf() style.
746  */
747 
748 {
749     static FILE *fp = NULL;
750 
751     if (fp == NULL || reopen_debug_log) {
752         if (fp != NULL)
753         {
754             fclose(fp);
755             fp = NULL;
756         }
757         reopen_debug_log = MY_FALSE;
758 
759         if (!debug_file) /* We can get called before it's been set */
760             return;
761 
762         fp = fopen(debug_file, "w");
763         if (fp == NULL) {
764             perror(debug_file);
765             abort();
766         }
767         else
768             set_cloexec_flag(fileno(fp));
769     }
770     (void)vfprintf(fp, fmt, va);
771     (void)fflush(fp);
772 } /* vdebug_message() */
773 
774 /*-------------------------------------------------------------------------*/
775 void
debug_message(const char * a,...)776 debug_message(const char *a, ...)
777 
778 /* Print a message into the debug logfile, printf() style.
779  */
780 
781 {
782     va_list va;
783 
784     va_start(va, a);
785     vdebug_message(a, va);
786     va_end(va);
787 } /* debug_message() */
788 
789 /*-------------------------------------------------------------------------*/
790 void
write_x(int d,p_uint i)791 write_x (int d, p_uint i)
792 
793 /* Memory safe function to write 4-byte hexvalue <i> to fd <d>. */
794 
795 {
796     int j;
797     char c;
798 
799     for (j = 2 * sizeof i; --j >= 0; i <<= 4) {
800         c = (char)((i >> (8 * sizeof i - 4) ) + '0');
801         if (c >= '9' + 1)
802             c += (char)('a' - ('9' + 1));
803         write(d, &c, 1);
804     }
805 } /* write_x() */
806 
807 /*-------------------------------------------------------------------------*/
808 void
write_X(int d,unsigned char i)809 write_X (int d, unsigned char i)
810 
811 /* Memory safe function to write 1-byte hexvalue <i> to fd <d>. */
812 
813 {
814     int j;
815     char c;
816 
817     for (j = 2 * sizeof i; --j >= 0; i <<= 4) {
818         c = (char)((i >> (8 * sizeof i - 4) ) + '0');
819         if (c >= '9' + 1)
820             c += (char)('a' - ('9' + 1));
821         write(d, &c, 1);
822     }
823 } /* write_X() */
824 
825 /*-------------------------------------------------------------------------*/
826 void
writed(int d,p_uint i)827 writed (int d, p_uint i)
828 
829 /* Memory safe function to write integer value <i> to fd <d>. */
830 
831 {
832     p_uint j;
833     char c;
834 
835     for (j = 1000000000; j > i; j /= 10) NOOP;
836     if (!j) j = 1;
837     do {
838         c = (char)((i / j) % 10 + '0');
839         write(d, &c, 1);
840         j /= 10;
841     } while (j > 0);
842 } /* writed() */
843 
844 /*-------------------------------------------------------------------------*/
845 void
writes(int d,const char * s)846 writes (int d, const char *s)
847 
848 /* Memory safe function to string <s> to fd <d>. */
849 
850 {
851     write(d, s, strlen(s));
852 }
853 
854 /*-------------------------------------------------------------------------*/
855 char *
dprintf_first(int fd,char * s,p_int a)856 dprintf_first (int fd, char *s, p_int a)
857 
858 /* Write the string <s> up to the next "%"-style argument to <fd>, the
859  * write <a> according to the %-formatter. Recognized are %s, %d, %c,
860  * %x (4-Byte hex) and %X (a 1-Byte hex).
861  * If no %-formatter is present, the whole string is written.
862  *
863  * Result is a pointer to the remaining string.
864  */
865 
866 {
867     char *p;
868 
869     do {
870         if ( !(p = strchr(s, '%')) )
871         {
872             write(fd, s, strlen(s));
873             return "";
874         }
875 
876         write(fd, s, p - s);
877         switch(p[1])
878         {
879         case '%':
880             write(fd, p+1, 1);
881             continue;
882         case 's':
883             write(fd, (char *)a, strlen((char*)a));
884             break;
885         case 'c':
886           {
887             char c = (char)a;
888             write(fd, (char *)&c, 1);
889             break;
890           }
891         case 'd':
892             writed(fd, a);
893             break;
894         case 'x':
895             write_x(fd, a);
896             break;
897         case 'X':
898             write_X(fd, (unsigned char)a);
899             break;
900         }
901         return p+2;
902     } while(1);
903 } /* dprintf_first() */
904 
905 /*-------------------------------------------------------------------------*/
906 void
dprintf1(int fd,char * s,p_int a)907 dprintf1 (int fd, char *s, p_int a)
908 
909 /* Write a message <s> to <fd>. <s> may contain one %-style formatter.
910  * for the argument <a>.
911  */
912 
913 {
914     s = dprintf_first(fd, s, a);
915     write(fd, s, strlen(s));
916 } /* dprintf1() */
917 
918 /*-------------------------------------------------------------------------*/
919 void
dprintf2(int fd,char * s,p_int a,p_int b)920 dprintf2 (int fd, char *s, p_int a, p_int b)
921 
922 /* Write a message <s> to <fd>. <s> may contain two %-style formatter.
923  * for the arguments <a> and <b>.
924  */
925 
926 {
927     s = dprintf_first(fd, s, a);
928     dprintf1(fd, s, b);
929 } /* dprintf2() */
930 
931 /*-------------------------------------------------------------------------*/
932 void
dprintf3(int fd,char * s,p_int a,p_int b,p_int c)933 dprintf3 (int fd, char *s, p_int a, p_int b, p_int c)
934 
935 /* Write a message <s> to <fd>. <s> may contain three %-style formatter.
936  * for the arguments <a>, <b> and <c>.
937  */
938 
939 {
940     s = dprintf_first(fd, s, a);
941     dprintf2(fd, s, b, c);
942 } /* dprintf3() */
943 
944 /*-------------------------------------------------------------------------*/
945 void
dprintf4(int fd,char * s,p_int a,p_int b,p_int c,p_int d)946 dprintf4 (int fd, char *s, p_int a, p_int b, p_int c, p_int d)
947 
948 /* Write a message <s> to <fd>. <s> may contain three %-style formatter.
949  * for the arguments <a>, <b>, <c> and <d>.
950  */
951 
952 {
953     s = dprintf_first(fd, s, a);
954     dprintf3(fd, s, b, c, d);
955 } /* dprintf4() */
956 
957 /*-------------------------------------------------------------------------*/
958 void
set_cloexec_flag(int fd)959 set_cloexec_flag (int fd)
960 
961 /* Sets the FD_CLOEXEC flag, so that the file is closed on exec()
962  * and the erq doesn't inherit it.
963  */
964 {
965 #if defined(HAVE_FCNTL) && defined(FD_CLOEXEC)
966     int flags = fcntl(fd, F_GETFD);
967 
968     if (flags != -1)
969         fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
970 #endif
971 }
972 
973 /*=========================================================================*/
974 /*                        The argument parser                              */
975 /*=========================================================================*/
976 /* This code parses the arguments passed to the program in the count <argc>
977  * and the array of strings <argv>. The parser distinguishes options, which
978  * start with a '-', from normal arguments; options are further distinguished
979  * by their name and may take an additional value. The parser neither
980  * imposes nor expects any order of options and arguments.
981  *
982  * Options are recognized in two forms. In the short form the option must
983  * be given as a single '-' followed by a single letter. In the long form,
984  * options start with '--' followed by a string of arbitrary length.
985  * Short options are case sensitive, long options aren't.
986  * Examples are: '-r' and '--recursive'.
987  *
988  * If an option takes a value, it must follow the option immediately after
989  * a separating space or '='. Additionally, the value for a short option
990  * may follow the option without separator. Examples are: '-fMakefile',
991  * '-f Makefile', '--file=Makefile' and '--file Makefile'.
992  *
993  * Short options may be collated into one argument, e.g. '-rtl', but
994  * of these only the last may take a value.
995  *
996  * The option '--' marks the end of options. All following command arguments
997  * are considered proper arguments even if they start with a '-' or '--'.
998  *
999  * The arguments are usually taken from the commandline; but the parser
1000  * is also able to read them from a textfiles, which can be nested. The
1001  * content of the textfiles is broken down into words delimited by whitespace,
1002  * which are then treated as given on the commandline at the place where
1003  * the instruction to read the textfile stood.
1004  *
1005  * The file parser recognizes simple double-quoted strings, which must be
1006  * contained on a single line. Additionally, the '#' character given by
1007  * itself is a comment marker - everthing after the '#' until the end
1008  * of the current line is ignored.
1009  *-------------------------------------------------------------------------
1010  * Internally every option recognized by the program is associated with
1011  * an id number, defined as the enum OptNumber. The parser itself uses the
1012  * two id numbers 'cUnknown' for unrecognized options, and 'cArgument' for
1013  * proper command arguments.
1014  *
1015  * Id numbers are associated with their option strings/letters by the
1016  * statically initialized arrays aOptions. Every element
1017  * in this array is a structure defining the option's name (string
1018  * or letter), the associated id number, whether or not the option
1019  * takes a value, and the short and long help text. The order of the
1020  * elements does not matter, except for the help text output.
1021  *
1022  * The parsing is done by calling the function
1023  *
1024  *   int getargs(int argc, char ** argv, int (*)handler(int, const char *))
1025  *
1026  * The function is passed the argument count <argc> and vector <argv> as
1027  * they were received from the main() function, and a callback function
1028  * <handler>. getargs() returns 0 if the parsing completed successfully,
1029  * and non-zero else.
1030  *
1031  * The handler function is called for every successfully recognized option
1032  * and argument. Its prototype is
1033  *
1034  *   int handler(int eOption, const char *pValue)
1035  *
1036  * Parameter <eOption> denotes the recognized option, and pValue points
1037  * to the beginning of the value string if the option takes a value.
1038  * Proper arguments are parsed with eOption==cArgument and pValue
1039  * pointing to the argument string. The handler has to return one of the
1040  * following values:
1041  *    hrSuccess: if the option/argument was processed correctly.
1042  *    hrError:   if the option/argument couldn't be processed.
1043  *    hrArgFile: if the given value is the name of an arguments file
1044  *               to include.
1045  *-------------------------------------------------------------------------
1046  */
1047 
1048 /* Handler return values */
1049 
1050 typedef enum HandlerResult {
1051     hrSuccess = 0  /* Argument parsed */
1052   , hrError        /* Error parsing argument */
1053   , hrArgFile      /* Value of this argument is the filename of an argument
1054                     * file.
1055                     */
1056 } HandlerResult;
1057 
1058 /* Desription of short ('-') options */
1059 
1060 typedef struct ShortOpt {
1061   char      cOption;  /* The option character */
1062   int       eNumber;  /* The associated option number */
1063   short     bValue;   /* True: takes a value */
1064 } ShortOpt;
1065 
1066 /* Desription of long ('--') options */
1067 
1068 typedef struct LongOpt {
1069   char      * pOption;  /* The option string */
1070   int         eNumber;  /* The associated option number */
1071   short       bValue;   /* True: takes a value */
1072 } LongOpt;
1073 
1074 /* Description of an option */
1075 
1076 typedef struct Option {
1077   char        cOption;  /* The short option char, or \0 if none */
1078   char      * pOption;  /* The long option string, or NULL if none */
1079   int         eNumber;  /* The associated option number */
1080   short       bValue;   /* True: takes a value */
1081   char      * pSHelp;   /* Short help string, or NULL */
1082   char      * pLHelp;   /* Long help string, or NULL */
1083 } Option;
1084 
1085 /* Every recognized option has a ordinal number */
1086 
1087 typedef enum OptNumber {
1088    cUnknown = 0     /* unknown option                     */
1089  , cArgument        /* normal argument (for us: portnumber) */
1090  , cArgFile         /* --args               */
1091  , cInherited       /* --inherit            */
1092  , cUdpPort         /* --udp                */
1093  , cTrace           /* --list-compiles      */
1094  , cAlarmTime       /* --alarm-time       */
1095  , cCleanupTime     /* --cleanup-time       */
1096  , cCompat          /* --compat             */
1097  , cNoCompat        /* --no-compat          */
1098  , cDebug           /* --debug              */
1099  , cDefine          /* --define             */
1100  , cErq             /* --erq                */
1101  , cEvalcost        /* --eval-cost          */
1102  , cFilenameSpaces   /* --filename-spaces    */
1103  , cNoFilenameSpaces /* --no-filename-spaces */
1104  , cFuncall         /* --funcall            */
1105  , cMaster          /* --master             */
1106  , cMudlib          /* --mudlib             */
1107  , cDebugFile       /* --debug-file         */
1108  , cHBInterval      /* --heart-interval     */
1109  , cHostname        /* --hostname           */
1110  , cHostaddr        /* --hostaddr           */
1111  , cAccessFile      /* --access-file        */
1112  , cAccessLogFile   /* --access-log         */
1113  , cMaxMalloc       /* --hard-malloc-limit  */
1114  , cSoftMallocLimit /* --soft-malloc-limit  */
1115  , cMaxArray        /* --max-array          */
1116  , cMaxBytes        /* --max-bytes          */
1117  , cMaxCallouts     /* --max-callouts       */
1118  , cMaxFile         /* --max-file           */
1119  , cMaxMapping      /* --max-mapping        */
1120  , cMaxMappingKeys  /* --max-mapping-keys   */
1121  , cMaxWriteBuffer  /* --max-write-buffer   */
1122  , cMinMalloc       /* --min-malloc         */
1123  , cMinSmallMalloc  /* --min-small-malloc   */
1124  , cNoERQ           /* --no-erq             */
1125  , cNoHeart         /* --no-heart           */
1126  , cNoPreload       /* --no-preload         */
1127  , cPidFile         /* --pidfile            */
1128  , cRandomdevice    /* --randomdevice       */
1129  , cRandomSeed      /* --random-seed        */
1130  , cRegexp          /* --regexp             */
1131  , cResetTime       /* --reset-time         */
1132  , cReserved        /* -r                   */
1133  , cReserveUser     /* --reserve-user       */
1134  , cReserveMaster   /* --reserve-master     */
1135  , cReserveSystem   /* --reserve-system     */
1136  , cStrictEuids     /* --strict-euids       */
1137  , cNoStrictEuids   /* --no-strict-euids    */
1138  , cShareVariables    /* --share-variables    */
1139  , cNoShareVariables  /* --init-variables     */
1140  , cSwap            /* -s                   */
1141  , cSwapTime        /* --swap-time          */
1142  , cSwapVars        /* --swap-variables     */
1143  , cSwapFile        /* --swap-file          */
1144  , cSwapCompact     /* --swap-compact       */
1145  , cSyncHB          /* --sync-heart         */
1146  , cASyncHB         /* --async-heart        */
1147  , cWizlistFile     /* --wizlist-file       */
1148  , cNoWizlistFile   /* --no-wizlist-file    */
1149 #ifdef GC_SUPPORT
1150  , cGcollectFD      /* --gcollect-outfd     */
1151 #endif
1152 #ifdef USE_TLS
1153  , cTLSkey          /* --tls-key            */
1154  , cTLScert         /* --tls-cert           */
1155  , cTLStrustdir     /* --tls-trustdirectory */
1156  , cTLStrustfile    /* --tls-trustfile      */
1157  , cTLScrlfile	    /* --tls-crlfile        */
1158  , cTLScrldir       /* --tls-crldirectory   */
1159 #endif
1160 #ifdef DEBUG
1161  , cCheckRefs       /* --check-refcounts    */
1162  , cCheckState      /* --check-state        */
1163  , cGobbleFDs       /* --gobble-descriptors */
1164 #endif
1165 #ifdef CHECK_OBJECT_STAT
1166  , cCheckObjectStat /* --check-object-stat  */
1167 #endif
1168 #ifdef YYDEBUG
1169  , cYYDebug         /* --yydebug            */
1170 #endif
1171  , cOptions         /* --options            */
1172  , cVersion         /* --version            */
1173  , cLongHelp        /* --longhelp           */
1174  , cHelp            /* --help               */
1175 } OptNumber;
1176 
1177 /* Comprehensive lists of recognized options */
1178 
1179 static Option aOptions[]
1180   = { { 0,   "args",               cArgFile,        MY_TRUE
1181       , "  --args <filename>\n"
1182       , "  --args <filename>\n"
1183         "    Read the options from <filename> as if they were given on the\n"
1184         "    commandline.\n"
1185       }
1186 
1187     , { 'P', "inherit",            cInherited,      MY_TRUE
1188       , "  -P|--inherit <fd-number>\n"
1189       , "  -P|--inherit <fd-number>\n"
1190         "    Inherit filedescriptor <fd-number> from the parent process\n"
1191         "    as socket to listen for connections.\n"
1192       }
1193 
1194     , { 'u', "udp",                cUdpPort,        MY_TRUE
1195       , "  -u|--udp <portnumber>\n"
1196       , "  -u|--udp <portnumber>\n"
1197         "    Specify the <portnumber> for the UDP port, overriding the compiled-in\n"
1198         "    default.\n"
1199       }
1200 
1201     , { 'D', "define",             cDefine,         MY_TRUE
1202       , "  -D|--define <macro>[=<text>]\n"
1203       , "  -D|--define <macro>[=<text>]\n"
1204         "    Add <macro> (optionally to be expanded to <text>) to the list of\n"
1205         "    predefined macros known by the LPC compiler.\n"
1206       }
1207 
1208     , { 'E', "eval-cost",          cEvalcost,       MY_TRUE
1209       , "  -E|--eval-cost <ticks>\n"
1210       , "  -E|--eval-cost <ticks>\n"
1211         "    Set the number of <ticks> available for one evaluation thread.\n"
1212       }
1213 
1214     , { 'M', "master",             cMaster,         MY_TRUE
1215       , "  -M|--master <filename>\n"
1216       , "  -M|--master <filename>\n"
1217         "    Use <filename> for the master object.\n"
1218       }
1219 
1220     , { 'm', "mudlib",             cMudlib,         MY_TRUE
1221       , "  -m|--mudlib <pathname>\n"
1222       , "  -m|--mudlib <pathname>\n"
1223         "    Use <pathname> as the top directory of the mudlib.\n"
1224       }
1225 
1226     , { 0,   "debug-file",         cDebugFile,      MY_TRUE
1227       , "  --debug-file <filename>\n"
1228       , "  --debug-file <filename>\n"
1229         "    Log all debug output in <filename> instead of <host>.debug.log .\n"
1230       }
1231 
1232     , { 0,   "access-file",        cAccessFile,     MY_TRUE
1233       , "  --access-file <filename>|none\n"
1234       , "  --access-file <filename>|none\n"
1235         "    Activate access control with these access permissions data.\n"
1236         "    If 'none' is given access control is deactivated.\n"
1237       }
1238 
1239     , { 0,   "access-log",        cAccessLogFile, MY_TRUE
1240       , "  --access-log <filename>|none\n"
1241       , "  --access-log <filename>|none\n"
1242         "    Log valid and rejected connections into this file.\n"
1243         "    If 'none' is given no log is written.\n"
1244       }
1245 
1246     , { 0,   "hostname",           cHostname,       MY_TRUE
1247       , "  --hostname <name>\n"
1248       , "  --hostname <name>\n"
1249         "    Use <name> as hostname, instead of what the system says.\n"
1250       }
1251 
1252     , { 0,   "hostaddr",           cHostaddr,       MY_TRUE
1253       , "  --hostaddr <addr>\n"
1254       , "  --hostaddr <addr>\n"
1255         "    Use <addr> as address of this machine, instead of what the\n"
1256         "    system says. In particular this address will be used to open\n"
1257         "    the driver ports.\n"
1258       }
1259 
1260     , { 0,   "compat",             cCompat,         MY_FALSE
1261       , "  --compat\n"
1262       , "  --compat\n"
1263         "  --no-compat\n"
1264         "    Select the mode (compat or plain) of the driver.\n"
1265         "    Note that this choice does not affect the default name of the master\n"
1266         "    object.\n"
1267       }
1268 
1269     , { 0,   "no-compat",          cNoCompat,       MY_FALSE
1270       , "  --no-compat\n"
1271       , NULL
1272       }
1273 
1274     , { 'd', "debug",              cDebug,          MY_FALSE
1275       , "  -d|--debug\n"
1276       , "  -d|--debug\n"
1277         "    Generate debug output; repeat the argument for even more output.\n"
1278       }
1279 
1280     , { 'c', "list-compiles",      cTrace,          MY_FALSE
1281       , "  -c|--list-compiles\n"
1282       , "  -c|--list-compiles\n"
1283         "    List the name of every compiled file on stderr.\n"
1284       }
1285 
1286     , { 'e', "no-preload",         cNoPreload,      MY_FALSE
1287       , "  -e|--no-preload\n"
1288       , "  -e|--no-preload\n"
1289         "    Pass a non-zero argument (the number of occurences of this option)\n"
1290         "    to master->preload(), which usually inhibits all preloads of castles\n"
1291         "    and other objects.\n"
1292       }
1293 
1294     , { 0,   "erq",                cErq,            MY_TRUE
1295       , "  --erq <filename> | --erq \"<filename> <erq args>\"\n"
1296       , "  --erq <filename>\n"
1297         "  --erq \"<filename> <erq arguments>\"\n"
1298         "    Use <filename> instead of 'erq' as the name of the ERQ executable.\n"
1299         "    If the name starts with a '/', it is take to be an absolute pathname,\n"
1300         "    otherwise it is interpreted relative to " BINDIR ".\n"
1301         "    If not specified, 'erq' is used as executable name.\n"
1302         "    With the proper use of quotes it is legal to pass arbitrary arguments\n"
1303         "    to the erq, however, these may not contain spaces themselves.\n"
1304       }
1305 
1306     , { 'N', "no-erq",             cNoERQ,          MY_FALSE
1307       , "  -N|--no-erq\n"
1308       , "  -N|--no-erq\n"
1309         "    Don't start the erq demon (if it would be started at all).\n"
1310       }
1311 
1312     , { 0,   "alarm-time",         cAlarmTime,    MY_TRUE
1313       , "  --alarm-time <time>\n"
1314       , "  --alarm-time <time>\n"
1315         "    The granularity of call_out()s and heartbeats (minimum: 1).\n"
1316       }
1317 
1318     , { 0,   "heart-interval",     cHBInterval,   MY_TRUE
1319       , "  --heart-interval <time>\n"
1320       , "  --heart-interval <time>\n"
1321         "    The time to elapse between two heartbeats (minimum: 1).\n"
1322       }
1323 
1324     , { 0,   "sync-heart",         cSyncHB,       MY_FALSE
1325       , "  --sync-heart\n"
1326       , "  --sync-heart\n"
1327         "    All heartbeats are executed at the same time (modulo granularity).\n"
1328       }
1329 
1330     , { 0,   "async-heart",        cASyncHB,       MY_FALSE
1331       , "  --async-heart\n"
1332       , "  --async-heart\n"
1333         "    Heartbeats are executed immediately when they are due (modulo granularity).\n"
1334       }
1335 
1336     , { 't', "no-heart",           cNoHeart,        MY_FALSE
1337       , "  -t|--no-heart\n"
1338       , "  -t|--no-heart\n"
1339         "    Disable heartbeats and call_outs.\n"
1340       }
1341 
1342     , { 'f', "funcall",            cFuncall,        MY_TRUE
1343       , "  -f|--funcall <word>\n"
1344       , "  -f|--funcall <word>\n"
1345         "    The lfun master->flag() is called with <word> as argument before the\n"
1346         "    gamedriver accepts network connections.\n"
1347       }
1348 
1349     , { 0,   "cleanup-time",       cCleanupTime,    MY_TRUE
1350       , "  --cleanup-time <time>\n"
1351       , "  --cleanup-time <time>\n"
1352         "    The idle time in seconds for an object before the driver tries to\n"
1353         "    clean it up. This time should be substantially higher than the\n"
1354         "    reset time. A time <= 0 disables the cleanup mechanism.\n"
1355       }
1356 
1357     , { 0,   "reset-time",         cResetTime,      MY_TRUE
1358       , "  --reset-time <time>\n"
1359       , "  --reset-time <time>\n"
1360         "    The time in seconds for an object before it is reset.\n"
1361         "    A time <= 0 disables the reset mechanism.\n"
1362       }
1363 
1364     , { 0,   "regexp",             cRegexp,         MY_TRUE
1365       , "  --regexp pcre|traditional\n"
1366       , "  --regexp pcre|traditional\n"
1367         "    Select the default regexp package.\n"
1368       }
1369 
1370     , { 0,   "max-array",          cMaxArray,       MY_TRUE
1371       , "  --max-array <size>\n"
1372       , "  --max-array <size>\n"
1373         "    The maximum number of elements an array can hold.\n"
1374         "    Set to 0, arrays of any size are allowed.\n"
1375       }
1376 
1377     , { 0,   "max-callouts",       cMaxCallouts,    MY_TRUE
1378       , "  --max-callouts <number>\n"
1379       , "  --max-callouts <number>\n"
1380         "    The maximum number of callouts at one time.\n"
1381         "    Set to 0, any number of callouts is allowed.\n"
1382       }
1383 
1384     , { 0,   "max-mapping",        cMaxMapping,     MY_TRUE
1385       , "  --max-mapping <size>\n"
1386       , "  --max-mapping <size>\n"
1387         "    The maximum number of elements (keys+values) a mapping can hold.\n"
1388         "    Set to 0, mappings of any size are allowed.\n"
1389       }
1390 
1391     , { 0,   "max-mapping-keys",   cMaxMappingKeys, MY_TRUE
1392       , "  --max-mapping-keys <size>\n"
1393       , "  --max-mapping-keys <size>\n"
1394         "    The maximum number of entries (keys) a mapping can hold.\n"
1395         "    Set to 0, mappings of any size are allowed.\n"
1396       }
1397 
1398     , { 0,   "max-bytes",          cMaxBytes,       MY_TRUE
1399       , "  --max-bytes <size>\n"
1400       , "  --max-bytes <size>\n"
1401         "    The maximum number of bytes one read_bytes()/write_bytes() call\n"
1402         "    can handle.\n"
1403         "    Set to 0, reads and writes of any size are allowed.\n"
1404       }
1405 
1406     , { 0,   "max-file",           cMaxFile,        MY_TRUE
1407       , "  --max-file <size>\n"
1408       , "  --max-file <size>\n"
1409         "    The maximum number of bytes one read_file()/write_file() call\n"
1410         "    can handle.\n"
1411         "    Set to 0, reads and writes of any size are allowed.\n"
1412       }
1413 
1414     , { 0,   "max-write-buffer", cMaxWriteBuffer,  MY_TRUE
1415       , "  --max-write-buffer<size>\n"
1416       , "  --max-write-buffer <size>\n"
1417         "    The maximum number of bytes to be kept pending for each socket\n"
1418         "    to write.\n"
1419         "    Set to 0, an unlimited amount of data can be kept pending.\n"
1420       }
1421 
1422     , { 's', NULL,                 cSwap,           MY_TRUE
1423       , NULL
1424       , "  -s <time>  | --swap-time <time>\n"
1425         "  -s v<time> | --swap-variables <time>\n"
1426         "    Time in seconds before an object (or its variables) are swapped out.\n"
1427         "    A time less or equal 0 disables swapping.\n"
1428       }
1429 
1430     , { 0,   "swap-time",          cSwapTime,       MY_TRUE
1431       , "  -s <time>  | --swap-time <time>\n"
1432       , NULL
1433       }
1434 
1435     , { 0,   "swap-variables",     cSwapVars,       MY_TRUE
1436       , "  -s v<time> | --swap-variables <time>\n"
1437       , NULL
1438       }
1439 
1440     , { 0,   "swap-file",          cSwapFile,       MY_TRUE
1441       , "  -s f<name> | --swap-file <name>\n"
1442       , "  -s f<name> | --swap-file <name>\n"
1443         "    Swap into file <name> instead of " SWAP_FILE ".<host> .\n"
1444       }
1445 
1446     , { 0,   "swap-compact",       cSwapCompact,    MY_FALSE
1447       , "  -s c       | --swap-compact\n"
1448       , "  -s c | --swap-compact\n"
1449         "    Reuse free space in the swap file immediately.\n"
1450       }
1451 
1452     , { 0,   "hard-malloc-limit",  cMaxMalloc,      MY_TRUE
1453       , "  --hard-malloc-lmit <size>\n"
1454       , "  --hard-malloc-limit <size>\n"
1455         "    Restrict total memory allocation to <size> bytes. A <size> of 0\n"
1456         "    or 'unlimited' removes any restriction.\n"
1457       }
1458 
1459     , { 0,   "soft-malloc-limit",  cSoftMallocLimit,  MY_TRUE
1460       , "  --soft-malloc-limit <size>\n"
1461       , "  --soft-malloc-limit <size>\n"
1462         "    If total memory allocation exceeds <size> bytes, inform the mudlib\n"
1463         "    master about a developing low memory situation. A <size> of 0\n"
1464         "    or 'unlimited' removes the threshold. <size> must be smaller than\n"
1465         "    --hard-malloc-limit.\n"
1466       }
1467 
1468     , { 0,   "min-malloc",         cMinMalloc,      MY_TRUE
1469       , "  --min-malloc <size>\n"
1470       , "  --min-malloc <size>\n"
1471         "  --min-small-malloc <size>\n"
1472         "    Determine the sizes for the explicite initial large resp. small chunk\n"
1473         "    allocation. A size of 0 disables the explicite initial allocations.\n"
1474       }
1475 
1476     , { 0,   "min-small-malloc",   cMinSmallMalloc, MY_TRUE
1477       , "  --min-small-malloc <size>\n"
1478       , NULL
1479       }
1480 
1481     , { 'r', NULL,                 cReserved,       MY_TRUE
1482       , NULL
1483       , "  -r u<size> | --reserve-user <size>\n"
1484         "  -r m<size> | --reserve-master <size>\n"
1485         "  -r s<size> | --reserve-system <size>\n"
1486         "    Reserve <size> amount of memory for user/master/system allocations to\n"
1487         "    be held until main memory runs out.\n"
1488       }
1489 
1490     , { 0,   "reserve-user",       cReserveUser,    MY_TRUE
1491       , "  -r u<size> | --reserve-user <size>\n"
1492       , NULL
1493       }
1494 
1495     , { 0,   "reserve-master",     cReserveMaster,  MY_TRUE
1496       , "  -r m<size> | --reserve-master <size>\n"
1497       , NULL
1498       }
1499 
1500     , { 0,   "reserve-system",     cReserveSystem,  MY_TRUE
1501       , "  -r s<size> | --reserve-system <size>\n"
1502       , NULL
1503       }
1504 
1505     , { 0,   "filename-spaces",    cFilenameSpaces,  MY_FALSE
1506       , "  --filename-spaces\n"
1507       , "  --filename-spaces\n"
1508         "  --no-filename-spaces\n"
1509         "    Allow/disallow the use of spaces in filenames.\n"
1510       }
1511 
1512     , { 0,   "no-filename-spaces",    cNoFilenameSpaces,  MY_FALSE
1513       , "  --no-filename-spaces\n"
1514       , NULL
1515       }
1516 
1517     , { 0,   "strict-euids",       cStrictEuids,    MY_FALSE
1518       , "  --strict-euids\n"
1519       , "  --strict-euids\n"
1520         "  --no-strict-euids\n"
1521         "    Enforce/don't enforce the proper use of euids.\n"
1522       }
1523 
1524     , { 0,   "no-strict-euids",     cNoStrictEuids,    MY_FALSE
1525       , "  --no-strict-euids\n"
1526       , NULL
1527       }
1528 
1529     , { 0,   "share-variables",    cShareVariables,  MY_FALSE
1530       , "  --share-variables\n"
1531       , "  --share-variables\n"
1532         "  --init-variables\n"
1533         "    Select how clones initialize their variables:\n"
1534         "      - by sharing the current values of their blueprint\n"
1535         "      - by initializing them afresh (using __INIT()).\n"
1536       }
1537 
1538     , { 0,   "init-variables",    cNoStrictEuids,  MY_FALSE
1539       , "  --init-variables\n"
1540       , NULL
1541       }
1542 
1543 #ifdef USE_TLS
1544     , { 0,   "tls-key",               cTLSkey,            MY_TRUE
1545       , "  --tls-key <pathname>|none\n"
1546       , "  --tls-key <pathname>|none\n"
1547 #  ifdef TLS_DEFAULT_KEYFILE
1548         "    Use <pathname> as the x509 keyfile, default is '" TLS_DEFAULT_KEYFILE "'.\n"
1549 #  else
1550         "    Use <pathname> as the x509 keyfile, default is 'none'.\n"
1551 #  endif
1552         "    If relative, <pathname> is interpreted relative to <mudlib>.\n"
1553         "    If 'none' is given TLS is deactivated.\n"
1554       }
1555 
1556     , { 0,   "tls-cert",              cTLScert,           MY_TRUE
1557       , "  --tls-cert <pathname>\n"
1558       , "  --tls-cert <pathname>\n"
1559 #  ifdef TLS_DEFAULT_CERTFILE
1560         "    Use <pathname> as the x509 certfile, default is '" TLS_DEFAULT_CERTFILE "'.\n"
1561 #  else
1562         "    Use <pathname> as the x509 certfile, default is 'none'.\n"
1563 #  endif
1564         "    If relative, <pathname> is interpreted relative to <mudlib>.\n"
1565       }
1566     , { 0,      "tls-trustfile",       cTLStrustfile,     MY_TRUE
1567       , "  --tls-trustfile <pathname>|none\n"
1568       , "  --tls-trustfile <pathname>|none\n"
1569         "    Use <pathname> as the filename holding your trusted PEM certificates,\n"
1570 #  ifdef TLS_DEFAULT_TRUSTFILE
1571        "     default is '" TLS_DEFAULT_TRUSTFILE "'.\n"
1572 #  else
1573        "     default is 'none'.\n"
1574 #  endif
1575         "    If relative, <pathname> is interpreted relative to <mudlib>.\n"
1576       }
1577     , { 0,      "tls-trustdirectory",  cTLStrustdir,      MY_TRUE
1578       , "  --tls-trustdirectory <pathname>|none\n"
1579       , "  --tls-trustdirectory <pathname>|none\n"
1580         "    Use <pathname> as the directory where your trusted PEM certificates reside,\n"
1581 #  ifdef TLS_DEFAULT_TRUSTDIRECTORY
1582        "     default is '" TLS_DEFAULT_TRUSTDIRECTORY "'.\n"
1583 #  else
1584        "     default is 'none'.\n"
1585 #  endif
1586         "    If relative, <pathname> is interpreted relative to <mudlib>.\n"
1587       }
1588     , { 0,      "tls-crlfile",       cTLScrlfile,     MY_TRUE
1589       , "  --tls-crlfile <pathname>|none\n"
1590       , "  --tls-crlfile <pathname>|none\n"
1591         "    Use <pathname> as the filename holding your certificate revocation lists,\n"
1592 #  ifdef TLS_DEFAULT_CRLFILE
1593        "     default is '" TLS_DEFAULT_CRLFILE "'.\n"
1594 #  else
1595        "     default is 'none'.\n"
1596 #  endif
1597         "    If relative, <pathname> is interpreted relative to <mudlib>.\n"
1598       }
1599     , { 0,      "tls-crldirectory",  cTLScrldir,      MY_TRUE
1600       , "  --tls-crldirectory <pathname>|none\n"
1601       , "  --tls-crldirectory <pathname>|none\n"
1602         "    Use <pathname> as the directory where your certificate revocation lists reside,\n"
1603 #  ifdef TLS_DEFAULT_CRLDIRECTORY
1604        "     default is '" TLS_DEFAULT_CRLDIRECTORY "'.\n"
1605 #  else
1606        "     default is 'none'.\n"
1607 #  endif
1608         "    If relative, <pathname> is interpreted relative to <mudlib>.\n"
1609       }
1610 #endif /* USE_TLS */
1611 
1612     , { 0,   "wizlist-file",       cWizlistFile,    MY_TRUE
1613       , "  --wizlist-file <filename>\n"
1614       , "  --wizlist-file <filename>\n"
1615         "  --no-wizlist-file\n"
1616         "    Read and save the wizlist in the named file (always interpreted\n"
1617         "    relative the mudlib); resp. don't read or save the wizlist.\n"
1618       }
1619 
1620     , { 0,   "no-wizlist-file",    cNoWizlistFile,  MY_FALSE
1621       , "  --no-wizlist-file\n"
1622       ,
1623       }
1624 
1625     , { 0,   "pidfile",            cPidFile,        MY_TRUE
1626       , "  --pidfile <filename>\n"
1627       , "  --pidfile <filename>\n"
1628         "    Write the pid of the driver process into <filename>.\n"
1629       }
1630 
1631     , { 0,   "randomdevice",       cRandomdevice,   MY_TRUE
1632       , "  --randomdevice <filename>\n"
1633       , "  --randomdevice <filename>\n"
1634         "    Determines the source of the seed for the random number generator.\n"
1635         "    (tries /dev/urandom by default and uses system clock as fallback)\n"
1636       }
1637 
1638     , { 0,   "random-seed",        cRandomSeed,     MY_TRUE
1639       , "  --random-seed <num>\n"
1640       , "  --random-seed <num>\n"
1641         "    Seed value for the random number generator. If not given, the\n"
1642         "    driver chooses a seed value on its own.\n"
1643       }
1644 
1645 #ifdef GC_SUPPORT
1646     , { 0,   "gcollect-outfd",     cGcollectFD,     MY_TRUE
1647       , "  --gcollect-outfd <filename>|<num>\n"
1648       , "  --gcollect-outfd <filename>|<num>\n"
1649         "    Garbage collector output (like a log of all reclaimed memory blocks)\n"
1650         "    is sent to <filename> (or inherited fd <num>) instead of stderr.\n"
1651       }
1652 #endif
1653 
1654 #ifdef DEBUG
1655     , { 0,   "check-refcounts",    cCheckRefs,      MY_FALSE
1656       , "  --check-refcounts\n"
1657       , "  --check-refcounts\n"
1658         "    Every backend cycle, all refcounts in the system are checked.\n"
1659         "    SLOW!\n"
1660       }
1661 
1662     , { 0,   "check-state",        cCheckState,     MY_TRUE
1663       , "  --check-state <lvl>\n"
1664       , "  --check-state <lvl>\n"
1665         "    Perform a regular simplistic check of the virtual machine according\n"
1666         "    to <lvl>:\n"
1667         "      = 0: no check\n"
1668         "      = 1: once per backend loop\n"
1669         "      = 2: at various points in the backend loop\n"
1670       }
1671 
1672     , { 0,   "gobble-descriptors", cGobbleFDs,      MY_TRUE
1673       , "  --gobble-descriptors <num>\n"
1674       , "  --gobble-descriptors <num>\n"
1675         "    <num> (more) filedescriptors are used up. You'll know when you need it.\n"
1676       }
1677 #endif
1678 
1679 #ifdef CHECK_OBJECT_STAT
1680     , { 0,   "check-object-stat",  cCheckObjectStat, MY_FALSE
1681       , "  --check-object-stat\n"
1682       , "  --check-object-stat\n"
1683         "    Activate tracing of the object size statistic - available in order\n"
1684         "    to find the bug in the statistics.\n"
1685       }
1686 #endif
1687 
1688 #ifdef YYDEBUG
1689     , { 'y', "yydebug",            cYYDebug,        MY_FALSE
1690       , "  -y|--yydebug\n"
1691       , "  -y|--yydebug\n"
1692         "    Enable debugging of the LPC compiler.\n"
1693       }
1694 #endif
1695 
1696     , { 0,   "options",            cOptions,        MY_FALSE
1697       , "  --options\n"
1698       , "  --options\n"
1699         "    Print the version and compilation options of the driver, then exit.\n"
1700       }
1701 
1702     , { 'V', "version",            cVersion,        MY_FALSE
1703       , "  -V|--version\n"
1704       , "  -V|--version\n"
1705         "    Print the version of the driver, then exit.\n"
1706       }
1707 
1708     , { 0,   "longhelp",           cLongHelp,       MY_FALSE
1709       , "  --longhelp\n"
1710       , "  --longhelp\n"
1711         "    Display this help and exit.\n"
1712       }
1713 
1714     , { 'h', "help",               cHelp,           MY_FALSE
1715       , "  -h|-?|--help\n"
1716       , "  -h|-?|--help\n"
1717         "    Display the short help text and exit.\n"
1718       }
1719 
1720     , { '?', NULL,                 cHelp,           MY_FALSE
1721       , NULL
1722       , NULL
1723       }
1724 
1725     };
1726 
1727 /*-------------------------------------------------------------------------*/
1728 
1729 /* Internal management structure to handle an (argc,argv) input source.
1730  * It is allocated to the proper length.
1731  */
1732 
1733 typedef struct InputSource {
1734     struct InputSource * next;  /* Link pointer */
1735     int     arg;   /* Number of next argument to evaluate */
1736     int     argc;  /* Total number of arguments */
1737     char ** argv;  /* Allocated array of argument pointers */
1738     char  * name;  /* Filename from where the arguments have been read,
1739                     * NULL for commandline */
1740     char    data[1];
1741       /* Block holding the filename and the text read from
1742        * the file.
1743        *
1744        * The data read from the file has been broken up into separate
1745        * strings which are pointed to from the argv array.
1746        */
1747 } InputSource;
1748 
1749 /*-------------------------------------------------------------------------*/
1750 static const char *
drivertag(void)1751 drivertag (void)
1752 
1753 /* Return the driver's type tag string.
1754  */
1755 
1756 {
1757     if (strcmp(RELEASE_LONGTYPE, ""))
1758         return " (" RELEASE_LONGTYPE ")";
1759 
1760     return "";
1761 } /* drivertag() */
1762 
1763 /*-------------------------------------------------------------------------*/
1764 static void
version(void)1765 version (void)
1766 
1767 /* Print the version of the gamedriver.
1768  */
1769 
1770 {
1771   fputs("LDMud ", stdout);
1772 
1773   fputs(DRIVER_VERSION, stdout);
1774 
1775   fputs(LOCAL_LEVEL " - a LPMud Game Driver.\n"
1776         "\nRelease:  " PROJ_VERSION
1777        , stdout);
1778 
1779   fputs(drivertag(), stdout);
1780 
1781   fputs("; " RELEASE_DATE
1782         "\nCompiled: " __DATE__
1783 #ifdef __TIME__
1784         " " __TIME__
1785 #endif
1786         "\n"
1787        , stdout);
1788 } /* version() */
1789 
1790 /*-------------------------------------------------------------------------*/
1791 static void
options(void)1792 options (void)
1793 
1794 /* Print the version of the gamedriver and the compile time options.
1795  */
1796 
1797 {
1798   version();
1799   fputs("\n           Mode: "
1800 #ifdef COMPAT_MODE
1801         "Compat"
1802 #else
1803         "Plain (aka cross-compat)"
1804 #endif
1805 #ifdef STRICT_EUIDS
1806         " with strict euids.\n"
1807 #else
1808         "\n"
1809 #endif
1810        , stdout);
1811 
1812   fputs("    Mudlib path: " MUD_LIB "\n"
1813         "    Binary path: " BINDIR "\n"
1814 #ifdef MASTER_NAME
1815         "  Master object: <mudlib>/" MASTER_NAME "\n"
1816 #elif defined(COMPAT_MODE)
1817         "  Master object: <mudlib>/" COMPAT_MASTER "\n"
1818 #else
1819         "  Master object: <mudlib>/" PLAIN_MASTER "\n"
1820 #endif
1821        , stdout);
1822 
1823   printf(" Multiple ports: %d ports max, default is %d.\n", MAXNUMPORTS, PORTNO);
1824 
1825   printf("            UDP: default port is %d.\n", UDP_PORT);
1826 
1827 #ifdef ERQ_DEMON
1828   printf("            ERQ: max data length: send %d / recv %d bytes.\n"
1829          "                 directory: %s.\n"
1830         , ERQ_MAX_SEND, ERQ_MAX_REPLY, ERQ_DIR);
1831 #else
1832   fputs("            ERQ: disabled.\n", stdout);
1833 #endif
1834 
1835 #ifndef INPUT_ESCAPE
1836   fputs("   Input Escape: '!'\n", stdout);
1837 #else
1838   fputs("   Input Escape: '" INPUT_ESCAPE "'\n", stdout);
1839 #endif
1840 
1841 #ifdef ACCESS_FILE
1842   fputs(" Access control: using <mudlib>/" ACCESS_FILE
1843 #    ifdef ACCESS_LOG
1844         ", logs into <mudlib>/" ACCESS_LOG "\n"
1845 #    else
1846         ", no logs.\n"
1847 #    endif
1848         , stdout);
1849 #else
1850   fputs(" Access control: disabled.\n", stdout);
1851 #endif
1852 
1853 #ifdef WIZLIST_FILE
1854   fputs("        Wizlist: saved in <mudlib>/" WIZLIST_FILE "\n", stdout);
1855 #else
1856   fputs("        Wizlist: not saved\n", stdout);
1857 #endif
1858 
1859     /* Print the language options, nicely indented. */
1860     {
1861         char * optstrings[] = { "" /* have at least one string in here */
1862 #ifdef USE_ARRAY_CALLS
1863                               , "call_other() on (object*) enabled\n"
1864 #endif
1865 #ifdef USE_PARSE_COMMAND
1866                               , "parse_command() enabled\n"
1867 #endif
1868 #ifdef USE_PROCESS_STRING
1869                               , "process_string() enabled\n"
1870 #endif
1871 #ifdef USE_SET_LIGHT
1872                               , "set_light() enabled\n"
1873 #endif
1874 #ifdef USE_SET_IS_WIZARD
1875                               , "set_is_wizard() enabled\n"
1876 #endif
1877 #ifdef SHARE_VARIABLES
1878                               , "clones initialized from blueprint\n"
1879 #else
1880                               , "clones initialized by __INIT()\n"
1881 #endif
1882 #ifdef USE_DEPRECATED
1883                               , "obsolete and deprecated efuns enabled\n"
1884 #endif
1885 #ifdef NO_NEGATIVE_RANGES
1886                               , "assignments to negative ranges disabled\n"
1887 #endif
1888 #ifdef USE_STRUCTS
1889                               , "structs enabled\n"
1890 #endif
1891 #ifdef USE_NEW_INLINES
1892                               , "new inline closures enabled\n"
1893 #endif
1894 #ifdef HAS_ICONV
1895                               , "convert_charset() via iconv available\n"
1896 #endif
1897 #ifdef ALLOW_FILENAME_SPACES
1898                               , "filenames may contain space characters\n"
1899 #else
1900                               , "filenames may not contain space characters\n"
1901 #endif
1902                               };
1903         size_t nStrings = sizeof(optstrings) / sizeof(optstrings[0]);
1904         size_t i;
1905 
1906         for (i = 1; i < nStrings; i++)
1907         {
1908             if (1 == i)
1909                 fputs("       Language: ", stdout);
1910             else
1911                 fputs("                 ", stdout);
1912             fputs(optstrings[i], stdout);
1913         }
1914     } /* print language options */
1915     fputs("                 default regexps: "
1916 #ifdef USE_PCRE
1917                                             "PCRE\n"
1918 #else
1919                                             "traditional\n"
1920 #endif
1921          , stdout);
1922 
1923     /* Print the package options, nicely indented. */
1924     {
1925         char * optstrings[] = { "" /* have at least one string in here */
1926 #ifdef HAS_IDN
1927                               , "idna supported\n"
1928 #endif
1929 #ifdef USE_XML
1930                               , "XML supported ("
1931 #  if defined(HAS_XML2)
1932                                 "libxml2"
1933 #  elif defined(HAS_IKSEMEL)
1934                                 "iksemel"
1935 #  else
1936                                 "<unknown>"
1937 #  endif
1938                                 ")\n"
1939 #endif
1940 #ifdef USE_IPV6
1941                               , "IPv6 supported\n"
1942 #endif
1943 #ifdef USE_MCCP
1944                               , "MCCP supported\n"
1945 #endif
1946 #ifdef USE_MYSQL
1947                               , "mySQL supported\n"
1948 #endif
1949 #ifdef USE_PGSQL
1950                               , "PostgreSQL supported\n"
1951 #endif
1952 #ifdef USE_SQLITE
1953                               , "SQLite3 supported\n"
1954 #endif
1955 #ifdef USE_ALISTS
1956                               , "Alists supported\n"
1957 #endif
1958 #ifdef USE_TLS
1959                               , "TLS supported ("
1960 #  if defined(HAS_OPENSSL)
1961                                                "OpenSSL"
1962 #  elif defined(HAS_GNUTLS)
1963                                                "GnuTLS"
1964 #  else
1965                                                "<unknown>"
1966 #  endif
1967                                                ", x509 key: '"
1968 #  ifdef TLS_DEFAULT_KEYFILE
1969                                   TLS_DEFAULT_KEYFILE
1970 #  else
1971                                                "none"
1972 #  endif
1973                                                "', cert: '"
1974 #  ifdef TLS_DEFAULT_CERTFILE
1975                                   TLS_DEFAULT_CERTFILE
1976 #  else
1977                                                "none"
1978 #  endif
1979                                                "')\n"
1980 #endif
1981                               };
1982         size_t nStrings = sizeof(optstrings) / sizeof(optstrings[0]);
1983         size_t i;
1984 
1985         fputs("       Packages: PCRE ", stdout);
1986         fputs(rx_pcre_version(), stdout);
1987         fputs("\n", stdout);
1988         for (i = 1; i < nStrings; i++)
1989         {
1990             fputs("                 ", stdout);
1991             fputs(optstrings[i], stdout);
1992         }
1993     } /* print package options */
1994 
1995   printf(" Runtime limits: max read file size:     %7d\n"
1996          "                 max byte read/write:    %7d\n"
1997          "                 max socket buf size:    %7d\n"
1998          "                 max write buf size:     %7d\n"
1999          "                 max eval cost:        %9d %s\n"
2000          "                 catch eval cost:        %7d\n"
2001          "                 master eval cost:       %7d\n"
2002          "                 eval stack:             %7d\n"
2003          "                 user call depth:        %7d\n"
2004          "                 max call depth:         %7d\n"
2005          "                 max bitfield length:    %7d\n"
2006          "                 max array size:         %7d\n"
2007          "                 max mapping size:       %7d\n"
2008          "                 max mapping keys:       %7d\n"
2009          "                 max number callouts:    %7d\n"
2010          "                 max number players:     %7d\n"
2011          "                 ed cmd/cmd ratio:       %7d:1\n"
2012 #if defined(TRACE_CODE)
2013          "                 max trace length:       %7d\n"
2014 #endif
2015         , READ_FILE_MAX_SIZE, MAX_BYTE_TRANSFER
2016         , SET_BUFFER_SIZE_MAX
2017         , WRITE_BUFFER_MAX_SIZE
2018         , MAX_COST
2019 #if defined(DYNAMIC_COSTS)
2020         , "(dynamic)"
2021 #else
2022         , ""
2023 #endif
2024         , CATCH_RESERVED_COST, MASTER_RESERVED_COST
2025         , EVALUATOR_STACK_SIZE
2026         , MAX_USER_TRACE, MAX_TRACE
2027         , MAX_BITS, MAX_ARRAY_SIZE
2028         , MAX_MAPPING_SIZE, MAX_MAPPING_KEYS
2029         , MAX_CALLOUTS, MAX_PLAYERS
2030         , ALLOWED_ED_CMDS
2031 #ifdef TRACE_CODE
2032         , TOTAL_TRACE_LENGTH
2033 #endif
2034         );
2035 
2036   printf("         Timing: reset:                  %7d s\n"
2037          "                 clean up:               %7d s\n"
2038          "                 alarm interval:         %7d s\n"
2039          "                 heartbeat interval:     %7d s %s\n"
2040         , TIME_TO_RESET, TIME_TO_CLEAN_UP
2041         , ALARM_TIME, HEART_BEAT_INTERVAL
2042 #ifdef SYNCHRONOUS_HEART_BEAT
2043         , "(synchronous)"
2044 #else
2045         , ""
2046 #endif
2047         );
2048 
2049   printf("       Swapping: objects              ");
2050   if (TIME_TO_SWAP > 0)
2051       printf("after %4d s\n", TIME_TO_SWAP);
2052   else
2053       printf("     never\n");
2054   printf("                 variables            ");
2055   if (TIME_TO_SWAP_VARIABLES > 0)
2056       printf("after %4d s\n", TIME_TO_SWAP_VARIABLES);
2057   else
2058       printf("     never\n");
2059   if (SWAP_FILE[0] == '/')
2060       printf("                 file: %s.<host>\n"
2061             , SWAP_FILE
2062             );
2063   else
2064       printf("                 file: <mudlib>/%s.<host>\n"
2065             , SWAP_FILE
2066             );
2067 
2068   printf("       Compiler: max stack size:          %6d\n"
2069          "                 max local variables:     %6d\n"
2070          "                 max define length:       %6d\n"
2071         , COMPILER_STACK_SIZE
2072         , MAX_LOCAL
2073         , DEFMAX
2074         );
2075 
2076   printf("         Memory: using %s\n"
2077          "                 reserved user size:    %8d\n"
2078          "                 reserved master size:  %8d\n"
2079          "                 reserved system size:  %8d\n"
2080          "                 initial allocation:   %9d\n"
2081          "                 initial small alloc:   %8d\n"
2082 #ifdef MALLOC_sysmalloc
2083         , "system malloc"
2084 #elif defined(MALLOC_slaballoc)
2085         , "slaballoc"
2086 #elif defined(MALLOC_smalloc)
2087         , "smalloc"
2088 #elif defined(MALLOC_ptmalloc)
2089         , "ptmalloc"
2090 #else
2091         , "unknown malloc"
2092 #endif
2093 #if defined(MALLOC_CHECK) || defined(MALLOC_TRACE) || defined(MALLOC_LPC_TRACE) || defined(MALLOC_SBRK_TRACE)
2094               " ("
2095 #    if defined(MALLOC_CHECK)
2096                 " MALLOC_CHECK"
2097 #    endif
2098 #    if defined(MALLOC_TRACE)
2099                 " MALLOC_TRACE"
2100 #    endif
2101 #    if defined(MALLOC_LPC_TRACE)
2102                 " MALLOC_LPC_TRACE"
2103 #    endif
2104 #    if defined(MALLOC_SBRK_TRACE)
2105                 " MALLOC_SBRK_TRACE"
2106 #    endif
2107               " )"
2108 #endif
2109         , RESERVED_USER_SIZE
2110         , RESERVED_MASTER_SIZE
2111         , RESERVED_SYSTEM_SIZE
2112         , MIN_MALLOCED
2113         , MIN_SMALL_MALLOCED
2114         );
2115 
2116   printf("                 hard memory allocation limit: ");
2117   if (HARD_MALLOC_LIMIT_DEFAULT > 0)
2118       printf("%9d\n", HARD_MALLOC_LIMIT_DEFAULT);
2119   else
2120       printf("unlimited\n");
2121 
2122   printf("                 soft memory allocation limit: ");
2123     if (SOFT_MALLOC_LIMIT_DEFAULT > 0)
2124         printf("%9d\n", SOFT_MALLOC_LIMIT_DEFAULT);
2125     else
2126         printf("unlimited\n");
2127 
2128   printf("Internal tables: shared string hash:      %6d entries\n"
2129          "                 object hash:             %6d entries\n"
2130          "                 reserved name hash:      %6d entries\n"
2131          "                 apply cache:             %6d entries\n"
2132 #ifdef RXCACHE_TABLE
2133          "                 regexp cache:            %6d entries\n"
2134 #endif
2135         , HTABLE_SIZE
2136         , OTABLE_SIZE
2137         , ITABLE_SIZE
2138         , 1<<APPLY_CACHE_BITS
2139 #ifdef RXCACHE_TABLE
2140         , RXCACHE_TABLE
2141 #endif
2142         );
2143 
2144 #ifdef DEBUG
2145   printf("  Debug options: check state: %d ("
2146         , check_state_level
2147         );
2148   switch (check_state_level)
2149   {
2150   case 0: fputs("never", stdout); break;
2151   case 1: fputs("once per loop", stdout); break;
2152   case 2: fputs("several times per loop", stdout); break;
2153   default: fputs("???", stdout); break;
2154   }
2155   fputs(")\n", stdout);
2156 
2157   if (check_a_lot_ref_counts_flag)
2158       fputs("                 check refcounts\n", stdout);
2159   else
2160       fputs("                 don't check refcounts\n", stdout);
2161 #endif
2162 
2163     /* Print the other options, nicely formatted. */
2164     {
2165         char * optstrings[] = { "  Other options: "
2166 #       if defined(DEBUG)
2167                               , "DEBUG"
2168 #       endif
2169 #       if defined(CHECK_OBJECT_STAT)
2170                               , "CHECK_OBJECT_STAT"
2171 #       endif
2172 #       if defined(DEBUG_TELNET)
2173                               , "DEBUG_TELNET"
2174 #       endif
2175 #       if defined(DEBUG_MALLOC_ALLOCS)
2176                               , "DEBUG_MALLOC_ALLOCS"
2177 #       endif
2178 #       if defined(YYDEBUG)
2179                               , "YYDEBUG"
2180 #       endif
2181 #       if defined(NO_INLINES)
2182                               , "NO_INLINES"
2183 #       endif
2184 #       if defined(TRACECODE)
2185                               , "TRACECODE"
2186 #       endif
2187 #       if defined(COMM_STAT)
2188                               , "COMM_STAT"
2189 #       endif
2190 #       if defined(APPLY_CACHE_STAT)
2191                               , "APPLY_CACHE_STAT"
2192 #       endif
2193 #       if defined(OPCPROF)
2194                               , "OPCPROF"
2195 #           if defined(OPCPROF_VERBOSE)
2196                               , "OPCPROF_VERBOSE"
2197 #           endif
2198 #       endif
2199 #       if defined(CHECK_MAPPINGS)
2200                               , "CHECK_MAPPINGS"
2201 #       endif
2202 #       if defined(CHECK_MAPPING_TOTAL)
2203                               , "CHECK_MAPPING_TOTAL"
2204 #       endif
2205 #       if defined(CHECK_OBJECT_REF)
2206                               , "CHECK_OBJECT_REF"
2207 #       endif
2208 #       if defined(CHECK_OBJECT_GC_REF)
2209                               , "CHECK_OBJECT_GC_REF"
2210 #       endif
2211 #       if defined(DUMP_GC_REFS)
2212                               , "DUMP_GC_REFS"
2213 #       endif
2214 #       if defined(NEW_CLEANUP)
2215                               , "NEW_CLEANUP"
2216 #       endif
2217 #       if defined(LOG_NEW_CLEANUP)
2218                               , "LOG_NEW_CLEANUP"
2219 #       endif
2220 #       if defined(USE_AVL_FREELIST)
2221                               , "USE_AVL_FREELIST"
2222 #       endif
2223 #       if defined(SLABALLOC_DYNAMIC_SLABS)
2224                               , "SLABALLOC_DYNAMIC_SLABS"
2225 #       endif
2226 #       if defined(MALLOC_ORDER_LARGE_FREELISTS)
2227                               , "MALLOC_ORDER_LARGE_FREELISTS"
2228 #       endif
2229 #       if defined(MALLOC_ORDER_SLAB_FREELISTS)
2230                               , "MALLOC_ORDER_SLAB_FREELISTS"
2231 #       endif
2232 #       if defined(MALLOC_EXT_STATISTICS)
2233                               , "MALLOC_EXT_STATISTICS"
2234 #       endif
2235 #       if defined(EXT_STRING_STATS)
2236                               , "EXT_STRING_STATS"
2237 #       endif
2238                               };
2239         size_t nStrings = sizeof(optstrings) / sizeof(optstrings[0]);
2240         size_t iInitial = strlen(optstrings[0]);
2241         size_t curlen = 0;
2242         size_t i;
2243 
2244         if (nStrings > 1)
2245         {
2246             fputs(optstrings[0], stdout);
2247             curlen = iInitial;
2248 
2249             for (i = 1; i < nStrings; i++)
2250             {
2251                 curlen += strlen(optstrings[i]) + 2;
2252                 if (curlen > 78)
2253                 {
2254                     printf("\n%*s", (int)iInitial, " ");
2255                     curlen = iInitial + strlen(optstrings[i]) + 2;
2256                 }
2257                 fputs(optstrings[i], stdout);
2258                 if (i < nStrings-1)
2259                     fputs(", ", stdout);
2260             }
2261             fputs(".\n", stdout);
2262         }
2263     }
2264 } /* options() */
2265 
2266 /*-------------------------------------------------------------------------*/
2267 static void
shortusage(void)2268 shortusage (void)
2269 
2270 /* Print the short help information to stdout. */
2271 
2272 {
2273   int i;
2274 
2275   version();
2276   fputs("\n"
2277 "Usage: ldmud [options] [<portnumber>...]\n"
2278 "\nOptions are:\n"
2279 "\n"
2280        , stdout);
2281 
2282   for (i = 0; (size_t)i < sizeof(aOptions) / sizeof(aOptions[0]); i++)
2283       if (aOptions[i].pSHelp != NULL)
2284           fputs(aOptions[i].pSHelp, stdout);
2285 } /* shortusage() */
2286 
2287 /*-------------------------------------------------------------------------*/
2288 static void
usage(void)2289 usage (void)
2290 
2291 /* Print the help information to stdout. */
2292 
2293 {
2294   int i;
2295 
2296   version();
2297   fputs("\n"
2298 "Usage: ldmud [options] [<portnumber>...]\n"
2299 "\nOptions are:\n"
2300        , stdout);
2301 
2302   for (i = 0; (size_t)i < sizeof(aOptions) / sizeof(aOptions[0]); i++)
2303   {
2304       if (aOptions[i].pLHelp != NULL)
2305       {
2306           fputs("\n", stdout);
2307           fputs(aOptions[i].pLHelp, stdout);
2308       }
2309   }
2310 } /* usage() */
2311 
2312 /*-------------------------------------------------------------------------*/
2313 static int
eval_arg(int eOption,const char * pValue)2314 eval_arg (int eOption, const char * pValue)
2315 
2316 /* Callback from getargs() for the first scan of the commandline
2317  * arguments. <eOption> is the option recognized, <pValue> a value
2318  * or NULL.
2319  * Return hrSuccess on success, hrError on a failure.
2320  * Return hrArgFile if the given value is the name of an arguments file.
2321  */
2322 
2323 {
2324     switch (eOption)
2325     {
2326     case cArgument:
2327         if (numports >= MAXNUMPORTS)
2328             fprintf(stderr, "Portnumber '%s' ignored.\n", pValue);
2329         else if (atoi(pValue))
2330               port_numbers[numports++] = atoi(pValue);
2331         else
2332             fprintf(stderr, "Illegal portnumber '%s' ignored.\n", pValue);
2333         break;
2334 
2335     case cArgFile:
2336         return hrArgFile;
2337 
2338     case cInherited:
2339         if (numports >= MAXNUMPORTS)
2340             fprintf(stderr, "fd '%s' ignored.\n", pValue);
2341         else if (atoi(pValue))
2342               port_numbers[numports++] = -atoi(pValue);
2343         else
2344             fprintf(stderr, "Illegal fd '%s' ignored.\n", pValue);
2345         break;
2346 
2347     case cUdpPort:
2348         if (atoi(pValue))
2349             udp_port = atoi(pValue);
2350         else
2351             fprintf(stderr, "Illegal portnumber '%s' ignored.\n", pValue);
2352         break;
2353 
2354     case cDefine:
2355         {
2356             struct lpc_predef_s *tmp;
2357 
2358             tmp = (struct lpc_predef_s *) xalloc(sizeof(struct lpc_predef_s));
2359             tmp->flag = string_copy(pValue);
2360             tmp->next = lpc_predefs;
2361             lpc_predefs = tmp;
2362         }
2363         break;
2364 
2365     case cEvalcost:
2366       {
2367         long val;
2368 
2369         val = atoi(pValue);
2370         if (val >= 0)
2371             def_eval_cost = val;
2372         else
2373             fprintf(stderr, "Illegal eval-cost '%s' ignored.\n", pValue);
2374         break;
2375       }
2376 
2377     case cCompat:
2378         compat_mode = MY_TRUE;
2379         break;
2380 
2381     case cNoCompat:
2382         compat_mode = MY_FALSE;
2383         break;
2384 
2385     case cNoPreload:
2386         e_flag++;
2387         break;
2388 
2389     case cErq:
2390       {
2391         char * begin_arg;
2392 
2393         if (erq_file != NULL)
2394             free(erq_file);
2395         if (erq_args != NULL)
2396         {
2397             free(erq_args);
2398             erq_args = NULL;
2399         }
2400         erq_file = strdup(pValue);
2401         begin_arg = strchr(erq_file, ' ');
2402         if (begin_arg)
2403         {
2404             /* Split the string into command and arguments */
2405 
2406             int num_args;
2407             char * cp;
2408 
2409             /* Skip leading spaces */
2410             *begin_arg++ = '\0';
2411             while (*begin_arg == ' ')
2412                 begin_arg++;
2413 
2414             /* Count the arguments */
2415             for (num_args = 0, cp = begin_arg; *cp != '\0'; )
2416             {
2417                 /* Found an argument: skip it */
2418                 num_args++;
2419                 while (*cp != ' ' && *cp != '\0')
2420                     cp++;
2421 
2422                 /* Skip trailing spaces */
2423                 while (*cp == ' ')
2424                     cp++;
2425             }
2426 
2427             if (num_args != 0)
2428             {
2429                 /* There are arguments!
2430                  * Put them into the argument array.
2431                  */
2432                 erq_args = malloc(sizeof(*erq_args) * (num_args+3));
2433                 erq_args[0] = "erq";
2434                 erq_args[1] = "--forked";
2435                 erq_args[num_args+2] = NULL;
2436 
2437                 for (num_args = 2, cp = begin_arg; *cp != '\0'; )
2438                 {
2439                     /* Found an argument: store and skip it */
2440                     erq_args[num_args++] = cp;
2441                     while (*cp != ' ' && *cp != '\0')
2442                         cp++;
2443 
2444                     /* Skip trailing spaces, replacing them by \0 to
2445                      * ensure proper string termination.
2446                      */
2447                     while (*cp == ' ')
2448                         *cp++ = '\0';
2449                 }
2450             }
2451         }
2452         break;
2453       }
2454 
2455     case cNoERQ:
2456         no_erq_demon++;
2457         break;
2458 
2459     case cDebug:
2460         d_flag++;
2461         break;
2462 
2463     case cTrace:
2464         comp_flag = MY_TRUE;
2465         break;
2466 
2467     case cAlarmTime:
2468       {
2469         long t = atoi(pValue);
2470 
2471         if (t >= 1)
2472         {
2473             alarm_time = t;
2474         }
2475         else
2476             fprintf(stderr, "Illegal alarm-time '%s' ignored.\n", pValue);
2477         break;
2478       }
2479 
2480     case cHBInterval:
2481       {
2482         long t = atoi(pValue);
2483 
2484         if (t >= 1)
2485         {
2486             heart_beat_interval = t;
2487         }
2488         else
2489             fprintf(stderr, "Illegal heart-interval '%s' ignored.\n", pValue);
2490         break;
2491       }
2492 
2493     case cSyncHB:
2494         synch_heart_beats = MY_TRUE;
2495         break;
2496 
2497     case cASyncHB:
2498         synch_heart_beats = MY_FALSE;
2499         break;
2500 
2501     case cNoHeart:
2502         t_flag = MY_TRUE;
2503         break;
2504 
2505     case cCleanupTime:
2506         if (atoi(pValue))
2507         {
2508             time_to_cleanup = atoi(pValue);
2509             if (time_to_cleanup < 0)
2510                 time_to_cleanup = 0;
2511         }
2512         else
2513             fprintf(stderr, "Illegal cleanup-time '%s' ignored.\n", pValue);
2514         break;
2515 
2516     case cResetTime:
2517         if (atoi(pValue))
2518         {
2519             time_to_reset = atoi(pValue);
2520             if (time_to_reset < 0)
2521                 time_to_reset = 0;
2522         }
2523         else
2524             fprintf(stderr, "Illegal cleanup-time '%s' ignored.\n", pValue);
2525         break;
2526 
2527     case cRegexp:
2528         if (!strcasecmp(pValue, "pcre"))
2529             regex_package = RE_PCRE;
2530         else if (!strcasecmp(pValue, "traditional"))
2531             regex_package = RE_TRADITIONAL;
2532         else
2533             fprintf(stderr, "Unknown regexp package '%s' ignored.\n", pValue);
2534         break;
2535 
2536     case cMaxArray:
2537     case cMaxBytes:
2538     case cMaxCallouts:
2539     case cMaxFile:
2540     case cMaxMapping:
2541     case cMaxMappingKeys:
2542       {
2543         long val = atoi(pValue);
2544 
2545         if (val >= 0)
2546         {
2547             switch(eOption)
2548             {
2549             case cMaxArray:        def_array_size = (size_t)val;   break;
2550             case cMaxBytes:        def_byte_xfer = val;            break;
2551             case cMaxCallouts:     def_callouts = val;             break;
2552             case cMaxFile:         def_file_xfer = val;            break;
2553             case cMaxMapping:      def_mapping_size = (size_t)val; break;
2554             case cMaxMappingKeys:  def_mapping_keys = (size_t)val; break;
2555             }
2556         }
2557         else
2558             fprintf(stderr, "Illegal value for limit '%s' ignored.\n", pValue);
2559         break;
2560       }
2561 
2562     case cMaxWriteBuffer:
2563       {
2564         long val = atoi(pValue);
2565 
2566         if (val >= 0)
2567             write_buffer_max_size = val;
2568         else
2569             fprintf(stderr, "Illegal value for limit '%s' ignored.\n", pValue);
2570         break;
2571       }
2572 
2573     case cSwap:
2574         /* Compatibility vs. one-char-only options *sigh* */
2575         switch (*pValue) {
2576         case 'c': eOption = cSwapCompact; break;
2577         case 'v': eOption = cSwapVars;    pValue++; break;
2578         case 'f': eOption = cSwapFile;    pValue++; break;
2579         default:  eOption = cSwapTime;    break;
2580         }
2581         /* FALLTHROUGH */
2582 
2583     case cSwapVars:
2584     case cSwapFile:
2585     case cSwapTime:
2586     case cSwapCompact:
2587         if (cSwapTime == eOption)
2588         {
2589             time_to_swap = atoi(pValue);
2590             if (time_to_swap < 0)
2591                 time_to_swap = 0;
2592         }
2593         else if (cSwapVars == eOption)
2594         {
2595             time_to_swap_variables = atoi(pValue);
2596             if (time_to_swap_variables < 0)
2597                 time_to_swap_variables = 0;
2598         }
2599         else if (cSwapFile == eOption)
2600             name_swap_file(pValue);
2601         else /* cSwapCompact */
2602             swap_compact_mode = MY_TRUE;
2603         break;
2604 
2605     case cWizlistFile:
2606     case cNoWizlistFile:
2607         if (cWizlistFile == eOption)
2608             name_wizlist_file(pValue);
2609         else /* cNoWizlistFile */
2610             name_wizlist_file("");
2611         break;
2612 
2613 #ifdef YYDEBUG
2614     case cYYDebug:
2615       {
2616         extern int yydebug;
2617         yydebug = MY_TRUE;
2618         break;
2619       }
2620 #endif
2621 
2622 
2623     case cHostname:
2624         if (hostname != NULL)
2625             free(hostname);
2626         hostname = strdup(pValue);
2627         break;
2628 
2629     case cHostaddr:
2630         if (hostaddr != NULL)
2631             free(hostaddr);
2632         hostaddr = strdup(pValue);
2633         break;
2634 
2635     case cAccessFile:
2636         if (access_file != NULL)
2637             free(access_file);
2638         if (!strcmp(pValue, "none"))
2639             access_file = NULL;
2640         else
2641             access_file = strdup(pValue);
2642         break;
2643 
2644     case cAccessLogFile:
2645         if (access_log != NULL)
2646             free(access_log);
2647         if (!strcmp(pValue, "none"))
2648             access_log = NULL;
2649         else
2650             access_log = strdup(pValue);
2651         break;
2652 
2653     case cMaster:
2654         if (strlen(pValue) >= sizeof(master_name)) {
2655             fprintf(stderr, "Too long master name '%s'\n", pValue);
2656             return hrError;
2657         }
2658         strcpy(master_name, pValue);
2659         break;
2660 
2661     case cMinMalloc:
2662         min_malloced = strtol(pValue, (char **)0, 0);
2663         if (min_malloced < 0)
2664         {
2665             fprintf(stderr, "Illegal value '%s' for --min-malloc\n", pValue);
2666             return hrError;
2667         }
2668         break;
2669 
2670     case cMinSmallMalloc:
2671         min_small_malloced = strtol(pValue, (char **)0, 0);
2672         if (min_small_malloced < 0)
2673         {
2674             fprintf(stderr, "Illegal value '%s' for --min-small-malloc\n", pValue);
2675             return hrError;
2676         }
2677         break;
2678 
2679     case cMaxMalloc:
2680         if (!strcasecmp(pValue, "unlimited"))
2681         {
2682             set_memory_limit(MALLOC_HARD_LIMIT, 0);
2683         }
2684         else
2685         {
2686             if (!set_memory_limit(MALLOC_HARD_LIMIT, strtol(pValue, (char **)0, 0)))
2687             {
2688                 fprintf(stderr, "Illegal value '%s' for --hard-malloc-limit\n", pValue);
2689                 return hrError;
2690             }
2691         }
2692         break;
2693 
2694     case cSoftMallocLimit:
2695             if (!strcasecmp(pValue, "unlimited"))
2696             {
2697                 set_memory_limit(MALLOC_SOFT_LIMIT, 0);
2698             }
2699             else
2700             {
2701                 if (!set_memory_limit(MALLOC_SOFT_LIMIT, strtol(pValue, (char **)0, 0)))
2702                 {
2703                     fprintf(stderr, "Illegal value '%s' for --soft-malloc-limit\n", pValue);
2704                     return hrError;
2705                 }
2706             }
2707             break;
2708 
2709     case cMudlib:
2710         if (chdir(pValue) == -1) {
2711             fprintf(stderr, "Bad mudlib directory: %s\n", pValue);
2712             return hrError;
2713         }
2714         new_mudlib = 1;
2715         break;
2716 
2717     case cDebugFile:
2718         if (debug_file != NULL)
2719             free(debug_file);
2720         debug_file = strdup(pValue);
2721         break;
2722 
2723     case cRandomdevice:
2724         // sets prng_device_name to some file/device and re-seeds the PRNG
2725         // from it.
2726         if (prng_device_name != NULL)
2727             free(prng_device_name);
2728         prng_device_name = strdup(pValue);
2729         seed_random(prng_device_name);
2730         break;
2731 
2732     case cRandomSeed:
2733     	// seeds PRG with given value
2734 #ifdef HAVE_STRTOUL
2735         seed_random_from_int(strtoul(pValue, (char **)0, 0));
2736 #else
2737         seed_random_from_int((uint32)strtol(pValue, (char **)0, 0));
2738 #endif
2739         break;
2740 
2741     case cReserved:
2742     case cReserveUser:
2743     case cReserveMaster:
2744     case cReserveSystem:
2745         {
2746             mp_int *sizep = &reserved_user_size;
2747 
2748             if (cReserved == eOption)
2749             {
2750                 /* This is a rather nasty compromise between being compatible
2751                  * to original Amylaar and the one-char-only short options.
2752                  */
2753 
2754                 switch(*pValue++)
2755                 {
2756                 default:  pValue--; /* FALLTHROUGH */
2757                 case 'u': sizep = &reserved_user_size; break;
2758                 case 'm': sizep = &reserved_master_size; break;
2759                 case 's': sizep = &reserved_system_size; break;
2760                 }
2761             }
2762             else
2763             switch (eOption)
2764             {
2765             case cReserveUser:   sizep = &reserved_user_size; break;
2766             case cReserveMaster: sizep = &reserved_master_size; break;
2767             case cReserveSystem: sizep = &reserved_system_size; break;
2768             }
2769 
2770             *sizep = strtol(pValue, (char**)0, 0);
2771             break;
2772         }
2773 
2774     case cStrictEuids:
2775         strict_euids = MY_TRUE;
2776         break;
2777 
2778     case cNoStrictEuids:
2779         strict_euids = MY_FALSE;
2780         break;
2781 
2782     case cShareVariables:
2783         share_variables = MY_TRUE;
2784         break;
2785 
2786     case cNoShareVariables:
2787         share_variables = MY_FALSE;
2788         break;
2789 
2790     case cFilenameSpaces:
2791         allow_filename_spaces = MY_TRUE;
2792         break;
2793 
2794     case cNoFilenameSpaces:
2795         allow_filename_spaces = MY_FALSE;
2796         break;
2797 
2798 #ifdef USE_TLS
2799     case cTLSkey:
2800         if (tls_keyfile != NULL)
2801             free(tls_keyfile);
2802         if (!strcmp(pValue, "none"))
2803             tls_keyfile = NULL;
2804         else
2805             tls_keyfile = strdup(pValue);
2806         break;
2807 
2808     case cTLScert:
2809         if (tls_certfile != NULL)
2810             free(tls_certfile);
2811         if (!strcmp(pValue, "none"))
2812             tls_certfile = NULL;
2813         else
2814             tls_certfile = strdup(pValue);
2815         break;
2816 
2817     case cTLStrustdir:
2818         if (tls_trustdirectory != NULL)
2819             free(tls_trustdirectory);
2820         if (!strcmp(pValue, "none"))
2821             tls_trustdirectory = NULL;
2822         else
2823             tls_trustdirectory = strdup(pValue);
2824         break;
2825 
2826     case cTLStrustfile:
2827         if (tls_trustfile != NULL)
2828             free(tls_trustfile);
2829         if (!strcmp(pValue, "none"))
2830             tls_trustfile = NULL;
2831         else
2832             tls_trustfile = strdup(pValue);
2833         break;
2834     case cTLScrlfile:
2835         if (tls_crlfile != NULL)
2836            free(tls_crlfile);
2837         if (!strcmp(pValue, "none"))
2838             tls_crlfile = NULL;
2839         else
2840             tls_crlfile = strdup(pValue);
2841         break;
2842     case cTLScrldir:
2843         if (tls_crldirectory != NULL)
2844             free(tls_crldirectory);
2845         if (!strcmp(pValue, "none"))
2846             tls_crldirectory = NULL;
2847         else
2848             tls_crldirectory = strdup(pValue);
2849         break;
2850 #endif
2851 
2852 #ifdef GC_SUPPORT
2853     case cGcollectFD:
2854         if (isdigit((unsigned char)*pValue)) {
2855             default_gcollect_outfd = strtol(pValue, (char **)0, 0);
2856         } else {
2857             default_gcollect_outfd = ixopen3(pValue, O_CREAT|O_TRUNC|O_WRONLY, 0640);
2858         }
2859         set_cloexec_flag(default_gcollect_outfd);
2860         gcollect_outfd = default_gcollect_outfd;
2861         break;
2862 #endif
2863 
2864     case cOptions:
2865         options();
2866         exit(0);
2867         break;
2868 
2869     case cVersion:
2870         version();
2871         exit(0);
2872         break;
2873 
2874     case cHelp:
2875         shortusage();
2876         return hrError;
2877 
2878     case cLongHelp:
2879         usage();
2880         return hrError;
2881 
2882     case cPidFile:
2883         {
2884             FILE * pidfile;
2885 
2886             pidfile = fopen(pValue, "w");
2887             if (!pidfile)
2888             {
2889                 fprintf(stderr, "Can't open pidfile '%s': %s.\n"
2890                        , pValue, strerror(errno));
2891                 return hrError;
2892             }
2893             fprintf(pidfile, "%ld\n", (long)getpid());
2894             fclose(pidfile);
2895             break;
2896         }
2897 
2898 #ifdef DEBUG
2899     case cCheckRefs:
2900         check_a_lot_ref_counts_flag = MY_TRUE;
2901         break;
2902 
2903     case cCheckState:
2904         {
2905             int n;
2906             char * end;
2907 
2908             n = strtol(pValue, &end, 0);
2909             if (n < 0 || n > 2 || end == NULL || *end != '\0')
2910             {
2911                 fprintf(stderr, "Bad check-state level: %s\n", pValue);
2912                 return hrError;
2913             }
2914             check_state_level = n;
2915             break;
2916         }
2917 
2918     case cGobbleFDs:
2919         {
2920             int n;
2921 
2922             n = strtol(pValue, (char **)0, 0);
2923             while(--n >= 0) {
2924                 (void)dup(2);
2925             }
2926             break;
2927         }
2928 #endif
2929 
2930 #ifdef CHECK_OBJECT_STAT
2931     case cCheckObjectStat:
2932         check_object_stat = MY_TRUE;
2933         break;
2934 #endif
2935 
2936     case cFuncall:
2937         /* Store the value in a list for later evaluation */
2938         {
2939             FData * fdata;
2940 
2941             fdata = malloc(sizeof(*fdata) + strlen(pValue));
2942             if (!fdata)
2943             {
2944                 fprintf(stderr, "Out of memory for '-f %s'.\n", pValue);
2945                 return hrError;
2946             }
2947 
2948             fdata->next = NULL;
2949             strcpy(fdata->txt, pValue);
2950 
2951             if (f_tail)
2952                 f_tail->next = fdata;
2953             f_tail = fdata;
2954             if (!f_head)
2955                 f_head = fdata;
2956         }
2957         break;
2958 
2959     default:
2960         /* This shouldn't happen. */
2961         fprintf(stderr, "%s ldmud: (eval_arg) Internal error, eOption is %d\n"
2962                       , time_stamp(), eOption);
2963         return hrError;
2964     } /* switch */
2965 
2966   return hrSuccess;
2967 } /* eval_arg() */
2968 
2969 /*-------------------------------------------------------------------------*/
2970 static Bool
open_arg_file(InputSource ** ppInput,const char * pName)2971 open_arg_file (InputSource ** ppInput, const char * pName)
2972 
2973 /* Try to open the file <pName> as new input source.
2974  * If successful, add the source to the list starting at *ppInput and
2975  * return MY_TRUE.
2976  * On failure, print an error message and return MY_FALSE.
2977  */
2978 
2979 {
2980     InputSource * pSrc;
2981     size_t        size, left;
2982     FILE        * f;
2983     struct stat   st;
2984     char        * pData;
2985 
2986     /* Auxiliary structure to store the found argument words. */
2987     typedef struct Marker {
2988         struct Marker * next;
2989         char          * pArg;
2990     } Marker;
2991 
2992     Marker * mHead = NULL;
2993     Marker * mTail = NULL;
2994 
2995     /* If this is a nested include, make sure that we don't get caught
2996      * in a recursion.
2997      */
2998     for (pSrc = *ppInput; pSrc && pSrc->name != NULL; pSrc = pSrc->next)
2999     {
3000         if (!strcmp(pSrc->name, pName))
3001         {
3002             fprintf(stderr
3003                    , "ldmud: Recursion in nested argument files: %s\n"
3004                    , pName);
3005             for (pSrc = *ppInput; pSrc && pSrc->name != NULL; pSrc = pSrc->next)
3006                 fprintf(stderr, "          included by %s\n", pSrc->name);
3007             return MY_FALSE;
3008         }
3009     }
3010 
3011     /* If the file would be opened in text mode, the size from fstat would
3012      * not match the number of characters that we can read.
3013      */
3014     f = fopen(pName, "rb");
3015     if (f == NULL)
3016     {
3017         int err = errno;
3018         fprintf( stderr
3019                , "ldmud: Can't open argument file '%s' for reading: (%d) %s\n"
3020                , pName, err, strerror(err)
3021                );
3022         return MY_FALSE;
3023     }
3024 
3025     /* Check if the file is small enough to be read. */
3026 
3027     if (fstat(fileno(f), &st) == -1)
3028     {
3029         int err = errno;
3030         fprintf( stderr
3031                , "ldmud: Can't stat argument file '%s': (%d) %s\n"
3032                , pName, err, strerror(err)
3033                );
3034         fclose(f);
3035         return MY_FALSE;
3036     }
3037 
3038     size = (size_t)st.st_size;
3039 
3040     /* Get a new input source structure and read in the file. */
3041     {
3042         size_t len = strlen(pName);
3043 
3044         pSrc = malloc(sizeof(*pSrc) + len + 1 + size);
3045         if (pSrc == NULL)
3046         {
3047             fprintf( stderr
3048                    , "ldmud: Out of memory reading argument file '%s'\n"
3049                    , pName
3050                    );
3051             fclose(f);
3052             return MY_FALSE;
3053         }
3054 
3055         pSrc->arg = 0;
3056         pSrc->argc = 0;
3057         pSrc->argv = NULL;
3058         pSrc->next = NULL;
3059 
3060         strcpy(pSrc->data, pName);
3061         pSrc->data[len] = '\0';
3062         pSrc->name = pSrc->data;
3063 
3064         pData = &(pSrc->data[len+1]);
3065 
3066         if (1 != fread(pData, size, 1, f))
3067         {
3068             int err = errno;
3069             fprintf( stderr
3070                    , "ldmud: Error reading argument file '%s': (%d) %s\n"
3071                    , pName, err, strerror(err)
3072                    );
3073             free(pSrc);
3074             fclose(f);
3075             return MY_FALSE;
3076         }
3077 
3078         /* Ensure a terminating 0 */
3079         pData[size] = '\0';
3080     }
3081 
3082     fclose(f);
3083 
3084     /* Now scan the read data and search for words.
3085      * Store the found words in the Marker list, and insert the
3086      * \0 terminators.
3087      */
3088 
3089     for (left = 0; left < size && *pData != '\0'; left++, pData++)
3090     {
3091         Marker *pMarker;
3092         Bool    endFound, quoted;
3093 
3094         unsigned char c = (unsigned char)*pData;
3095 
3096         if (isascii(c) && (isspace(c) || c == '\r' || c == '\n'))
3097             continue;
3098 
3099         /* Found a non-space. If it is a '#', it is a comment - skip it. */
3100         if (c == '#')
3101         {
3102             for ( left++, pData++
3103                 ; left < size && *pData != '\0'
3104                 ; left++, pData++)
3105                 if (*pData == '\r' || *pData == '\n')
3106                     break;
3107             /* pData now points to the lineend character, or to the end
3108              * of the file. Either way, continuing the outer loop will
3109              * do the right thing.
3110              */
3111             continue;
3112         }
3113 
3114         /* It is a true new word. Store it's starting position in
3115          * a new marker. Oh, and count it.
3116          */
3117         pSrc->argc++;
3118 
3119         pMarker = alloca(sizeof(*pMarker));
3120         if (pMarker == NULL)
3121         {
3122             fprintf( stderr
3123                    , "ldmud: Out of memory reading argument file '%s'\n"
3124                    , pName
3125                    );
3126             free(pSrc);
3127             return MY_FALSE;
3128         }
3129 
3130         pMarker->pArg = pData;
3131         pMarker->next = NULL;
3132 
3133         if (mTail)
3134             mTail->next = pMarker;
3135         mTail = pMarker;
3136         if (!mHead)
3137             mHead = pMarker;
3138 
3139         /* Now search for the end of the word.
3140          * Look at the first character again in case it's a quote.
3141          */
3142         for ( endFound = MY_FALSE, quoted = MY_FALSE
3143             ; left < size && *pData != '\0'
3144             ; left++, pData++
3145             )
3146         {
3147             c = (unsigned char)*pData;
3148 
3149             /* Line end always terminates the search */
3150             if (c == '\r' || c == '\n')
3151             {
3152                 if (quoted)
3153                 {
3154                     fprintf( stderr
3155                            , "ldmud: Error in argument file '%s': "
3156                              "Quoted string spans more than one line.\n"
3157                            , pName
3158                            );
3159                     free(pSrc);
3160                     return MY_FALSE;
3161                 }
3162                 endFound = MY_TRUE;
3163                 break;
3164             }
3165 
3166             /* Space outside of a quoted string also terminates */
3167             if (!quoted && isascii(c) && isspace(c))
3168             {
3169                 endFound = MY_TRUE;
3170                 break;
3171             }
3172 
3173             /* If it's a '"', toggle the quoted flag. */
3174             if (c == '"')
3175                 quoted = !quoted;
3176 
3177             /* Anyway, it's not a space here, so continue the search */
3178         }
3179 
3180         /* One possible error: end of file in a quoted string */
3181         if (!endFound && quoted)
3182         {
3183             fprintf( stderr
3184                    , "ldmud: Error in argument file '%s': "
3185                      "Unexpected end of file in quoted string.\n"
3186                    , pName
3187                    );
3188             free(pSrc);
3189             return MY_FALSE;
3190         }
3191 
3192         /* pData now points to the first character after the found word
3193          * It is a space (or the terminating '\0' at the end of the file),
3194          * so we can put the '\0' for the argument word in it.
3195          */
3196         *pData = '\0';
3197     }
3198 
3199     /* We found all the arguments. Now setup an argv array. */
3200 
3201     if (pSrc->argc == 0)
3202     {
3203         fprintf( stderr
3204                , "ldmud: Warning: Argument file '%s' contained no data.\n"
3205                , pName);
3206         /* We will setup an empty InputSource - no need to abort argument
3207          * parsing because of this.
3208          */
3209     }
3210 
3211     {
3212         int i;
3213 
3214         pSrc->argv = malloc(sizeof(pSrc->argv[0]) * (pSrc->argc+1));
3215         if (pSrc->argv == NULL)
3216         {
3217             fprintf( stderr
3218                    , "ldmud: Out of memory reading argument file '%s'\n"
3219                    , pName
3220                    );
3221             free(pSrc);
3222             return MY_FALSE;
3223         }
3224 
3225         for (i = 0; i < pSrc->argc; i++, mHead = mHead->next)
3226         {
3227             pSrc->argv[i] = mHead->pArg;
3228         }
3229 
3230         pSrc->argv[pSrc->argc] = NULL; /* Just in case */
3231 
3232     }
3233 
3234     /* Link the now complete InputSource structure into the list */
3235     pSrc->next = *ppInput;
3236     *ppInput = pSrc;
3237 
3238     return MY_TRUE;
3239 } /* open_arg_file() */
3240 
3241 /*-------------------------------------------------------------------------*/
3242 static INLINE void
free_sources(InputSource * pInput)3243 free_sources (InputSource * pInput)
3244 
3245 /* <pInput> points at the head of a list of input sources.
3246  * Deallocate all of them.
3247  */
3248 
3249 {
3250     while (pInput != NULL)
3251     {
3252         InputSource * pSrc = pInput;
3253 
3254         pInput = pInput->next;
3255 
3256         if (pSrc->name != NULL && pSrc->argv)
3257             /* Not the commandline */
3258             free(pSrc->argv);
3259 
3260         free(pSrc);
3261     }
3262 } /* free_sources() */
3263 
3264 /*-------------------------------------------------------------------------*/
3265 static int
getargs(int argc,char ** argv,int (* opt_eval)(int,const char *))3266 getargs (int argc, char ** argv, int (*opt_eval)(int, const char *) )
3267 
3268 /* Get the arguments from the commandline and pass them
3269  * as (number, optional value) to the opt_eval callback.
3270  * If opt_eval() returns hrError, argument scanning is terminated.
3271  * In that case, or if getargs() detects an error itself, getargs() returns
3272  * non-zero.
3273  * A zero return means 'success' in both cases.
3274  */
3275 
3276 {
3277   int           i;        /* all purpose */
3278   int           iArg;     /* Number of argument under inspection */
3279   OptNumber     eOption;  /* The current recognized option */
3280   int           iOption;  /* The index of the recognized option */
3281   short         bCont;    /* True: find another option in the same argument */
3282   short         bDone;    /* True: all options parsed, only args left */
3283   short         bShort;   /* True: current argument is a short option */
3284   char        * pArg;     /* Next argument character to consider */
3285   InputSource * pInput;   /* Head of list of input sources */
3286 
3287   /* Make the compiler happy */
3288   bShort = MY_FALSE;
3289   pArg = NULL;
3290 
3291   /* Set up the basic input source with the commandline arguments */
3292   pInput = malloc(sizeof(*pInput));
3293   if (!pInput)
3294   {
3295       fputs("ldmud: Out of memory.\n", stderr);
3296       return 1;
3297   }
3298   pInput->argc = argc-1;
3299   pInput->argv = argv+1;
3300   pInput->arg = 0;
3301   pInput->name = NULL;
3302   pInput->next = NULL;
3303 
3304   /* Scan arguments from all input sources.
3305    */
3306   while (pInput != NULL)
3307   {
3308       /* Scan all arguments */
3309       bCont = MY_FALSE;
3310       bDone = MY_FALSE;
3311       for (iArg = pInput->arg; iArg < pInput->argc; !bCont ? iArg++ : iArg)
3312       {
3313         size_t   iArglen;      /* Length of remaining argument */
3314         char   * pValue;       /* First character of an option value, or NULL */
3315         int      bTakesValue;  /* This option takes a value */
3316 
3317         /* Make the compiler happy */
3318         iArglen = 0;
3319         pValue = NULL;
3320         bTakesValue = MY_FALSE;
3321 
3322         if (bDone)
3323           eOption = cArgument;
3324         else
3325         /* If this is not a continuation, reinitialise the inspection vars.
3326          * For --opt=val arguments, pValue is set to the first character of val.
3327          */
3328         if (!bCont)
3329         {
3330           pArg = pInput->argv[iArg];
3331           if ('-' == pArg[0] && '-' == pArg[1]) /* Long option? */
3332           {
3333             eOption = cUnknown;
3334             bShort = MY_FALSE;
3335             pArg += 2;
3336             /* Special case: if the argument is just '--', it marks the
3337              * end of all options.
3338              * We set a flag and continue with the next argument.
3339              */
3340             if ('\0' == *pArg)
3341             {
3342               bDone = MY_TRUE;
3343               continue;
3344             }
3345             pValue = strchr(pArg, '=');
3346             if (pValue != NULL)
3347             {
3348               iArglen = (size_t)(pValue - pArg);
3349               pValue++;
3350             }
3351             else
3352               iArglen = strlen(pArg);
3353           }
3354           else if ('-' == pArg[0]) /* Short option? */
3355           {
3356             eOption = cUnknown;
3357             bShort = MY_TRUE;
3358             pArg++;
3359             iArglen = strlen(pArg);
3360             pValue = NULL;
3361           }
3362           else /* No option */
3363           {
3364             eOption = cArgument;
3365             pValue = pArg;
3366             iArglen = 0;
3367           }
3368         }
3369         else
3370           eOption = cUnknown;
3371 
3372         /* If the option is not determined yet, do it.
3373          * Set pValue to the first character of the value if any.
3374          */
3375         if (cUnknown == eOption)
3376         {
3377           if (bShort) /* search the short option */
3378           {
3379             for (iOption = 0; (size_t)iOption < sizeof(aOptions) / sizeof(aOptions[0]); iOption++)
3380             {
3381               if (*pArg == aOptions[iOption].cOption)
3382               {
3383                 eOption = (OptNumber)aOptions[iOption].eNumber;
3384                 bTakesValue = aOptions[iOption].bValue;
3385                 pArg++; iArglen--;  /* Consume this character */
3386                 break;
3387               }
3388             }
3389             /* Consume a following '=' if appropriate */
3390             if (cUnknown != eOption && bTakesValue && iArglen > 0 && '=' == *pArg)
3391             {
3392               pArg++; iArglen--;
3393             }
3394 
3395             /* If there is a value following in the same argument, set pValue to
3396              * it and mark the remaining characters as 'consumed'
3397              */
3398             if (cUnknown != eOption && bTakesValue && iArglen > 0)
3399             {
3400               pValue = pArg;
3401               pArg += iArglen;
3402               iArglen = 0;
3403             }
3404           }
3405           else  /* search the long option */
3406           {
3407             for (iOption = 0; (size_t)iOption < sizeof(aOptions) / sizeof(aOptions[0]); iOption++)
3408               if (aOptions[iOption].pOption != NULL
3409                && iArglen == strlen(aOptions[iOption].pOption)
3410                && !strncasecmp(pArg, aOptions[iOption].pOption, iArglen))
3411               {
3412                 eOption = (OptNumber)aOptions[iOption].eNumber;
3413                 bTakesValue = aOptions[iOption].bValue;
3414                 break;
3415               }
3416           }
3417 
3418           if (cUnknown == eOption)
3419           {
3420             if (pInput->name != NULL)
3421                 fprintf(stderr, "ldmud: (%s) Unknown option '", pInput->name);
3422             else
3423                 fputs("ldmud: Unknown option '", stderr);
3424             if (bShort)
3425               fprintf(stderr, "-%c", *pArg);
3426             else
3427               fprintf(stderr, "--%*.*s", (int)iArglen, (int)iArglen, pArg);
3428             fputs("'.\n", stderr);
3429             free_sources(pInput);
3430             return 1;
3431           }
3432 
3433           /* If at this point bTakesValue is true, but pValue is still NULL,
3434            * then the value is in the next argument. Get it if it's there.
3435            */
3436           if (bTakesValue && pValue == NULL && iArg + 1 < pInput->argc)
3437           {
3438             iArg++;
3439             pValue = pInput->argv[iArg];
3440           }
3441 
3442           /* Signal an error if pValue is still NULL or if it's empty. */
3443           if (bTakesValue && (pValue == NULL || !strlen(pValue)))
3444           {
3445             fputs("ldmud: Option '", stderr);
3446             if (bShort)
3447               putc((unsigned char)(aOptions[iOption].cOption), stderr);
3448             else
3449               fputs(aOptions[iOption].pOption, stderr);
3450             fputs("' expects a value.\n", stderr);
3451             free_sources(pInput);
3452             return 1;
3453           }
3454 
3455         } /* if (unknown option) */
3456 
3457         /* Before evaluation of the parsed option, determine 'bCont' */
3458         bCont = bShort && (iArglen > 0) && !bTakesValue;
3459 
3460         /* --- The option evaluation --- */
3461 
3462         i = (*opt_eval)(eOption, pValue);
3463         if (i == hrError)
3464         {
3465           free_sources(pInput);
3466           return 1;
3467         }
3468 
3469         if (i == hrArgFile)
3470         {
3471             pInput->arg = iArg+1;
3472             if (!open_arg_file(&pInput, pValue))
3473                 return 1;
3474 
3475             /* We have a new input source here, so reset the loop
3476              * parameters.
3477              */
3478             iArg = -1;
3479             bCont = MY_FALSE;
3480             bDone = MY_FALSE;
3481         }
3482 
3483       } /* for (iArg) */
3484 
3485       /* We are done with this input source - free it */
3486       {
3487           InputSource * pSrc = pInput;
3488 
3489           pInput = pInput->next;
3490           pSrc->next = NULL;
3491           free_sources(pSrc);
3492       }
3493   } /* while(pInput) */
3494 
3495   return 0;
3496 } /* getargs() */
3497 
3498 /***************************************************************************/
3499