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