Document further common toplevel options
[sbcl.git] / src / runtime / runtime.c
1 /*
2  * main() entry point for a stand-alone SBCL image
3  */
4
5 /*
6  * This software is part of the SBCL system. See the README file for
7  * more information.
8  *
9  * This software is derived from the CMU CL system, which was
10  * written at Carnegie Mellon University and released into the
11  * public domain. The software is in the public domain and is
12  * provided with absolutely no warranty. See the COPYING and CREDITS
13  * files for more information.
14  */
15
16 #include "sbcl.h"
17
18 #include <stdio.h>
19 #include <string.h>
20 #ifndef LISP_FEATURE_WIN32
21 #include <libgen.h>
22 #endif
23 #include <sys/types.h>
24 #ifndef LISP_FEATURE_WIN32
25 #include <sys/wait.h>
26 #endif
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <sys/file.h>
30 #include <sys/param.h>
31 #include <sys/stat.h>
32 #include <signal.h>
33 #ifndef LISP_FEATURE_WIN32
34 #include <sched.h>
35 #endif
36 #include <errno.h>
37 #include <locale.h>
38 #include <limits.h>
39
40 #if defined(SVR4) || defined(__linux__)
41 #include <time.h>
42 #endif
43
44 #include "signal.h"
45
46 #include "runtime.h"
47 #include "vars.h"
48 #include "globals.h"
49 #include "os.h"
50 #include "interr.h"
51 #include "alloc.h"
52 #include "interrupt.h"
53 #include "arch.h"
54 #include "gc.h"
55 #include "validate.h"
56 #include "core.h"
57 #include "save.h"
58 #include "lispregs.h"
59 #include "thread.h"
60
61 #include "genesis/static-symbols.h"
62 #include "genesis/symbol.h"
63
64
65 #ifdef irix
66 #include <string.h>
67 #include "interr.h"
68 #endif
69
70 #ifndef SBCL_HOME
71 #define SBCL_HOME SBCL_PREFIX"/lib/sbcl/"
72 #endif
73
74 #ifdef LISP_FEATURE_HPUX
75 extern void *return_from_lisp_stub;
76 #include "genesis/closure.h"
77 #include "genesis/simple-fun.h"
78 #endif
79
80 \f
81 /* SIGINT handler that invokes the monitor (for when Lisp isn't up to it) */
82 static void
83 sigint_handler(int signal, siginfo_t *info, os_context_t *context)
84 {
85     lose("\nSIGINT hit at 0x%08lX\n",
86          (unsigned long) *os_context_pc_addr(context));
87 }
88
89 /* (This is not static, because we want to be able to call it from
90  * Lisp land.) */
91 void
92 sigint_init(void)
93 {
94     SHOW("entering sigint_init()");
95     install_handler(SIGINT, sigint_handler);
96     SHOW("leaving sigint_init()");
97 }
98 \f
99 /*
100  * helper functions for dealing with command line args
101  */
102
103 void *
104 successful_malloc(size_t size)
105 {
106     void* result = malloc(size);
107     if (0 == result) {
108         lose("malloc failure\n");
109     } else {
110         return result;
111     }
112     return (void *) NULL; /* dummy value: return something ... */
113 }
114
115 char *
116 copied_string(char *string)
117 {
118     return strcpy(successful_malloc(1+strlen(string)), string);
119 }
120
121 char *
122 copied_existing_filename_or_null(char *filename)
123 {
124     struct stat filename_stat;
125     if (stat(filename, &filename_stat)) { /* if failure */
126         return 0;
127     } else {
128         return copied_string(filename);
129     }
130 }
131
132 #ifndef LISP_FEATURE_WIN32
133 char *
134 copied_realpath(const char *pathname)
135 {
136     char *messy, *tidy;
137     size_t len;
138
139     /* realpath() supposedly can't be counted on to always return
140      * an absolute path, so we prepend the cwd to relative paths */
141     messy = NULL;
142     if (pathname[0] != '/') {
143         messy = successful_malloc(PATH_MAX + 1);
144         if (getcwd(messy, PATH_MAX + 1) == NULL) {
145             free(messy);
146             return NULL;
147         }
148         len = strlen(messy);
149         snprintf(messy + len, PATH_MAX + 1 - len, "/%s", pathname);
150     }
151
152     tidy = successful_malloc(PATH_MAX + 1);
153     if (realpath((messy ? messy : pathname), tidy) == NULL) {
154         free(messy);
155         free(tidy);
156         return NULL;
157     }
158
159     return tidy;
160 }
161 #endif /* LISP_FEATURE_WIN32 */
162 \f
163 /* miscellaneous chattiness */
164
165 void
166 print_help()
167 {
168     puts(
169 "Usage: sbcl [runtime-options] [toplevel-options] [user-options]\n\
170 Common runtime options:\n\
171   --help                     Print this message and exit.\n\
172   --version                  Print version information and exit.\n\
173   --core <filename>          Use the specified core file instead of the default.\n\
174   --dynamic-space-size <MiB> Size of reserved dynamic space in megabytes.\n\
175   --control-stack-size <MiB> Size of reserved control stack in megabytes.\n\
176 \n\
177 Common toplevel options:\n\
178   --sysinit <filename>       System-wide init-file to use instead of default.\n\
179   --userinit <filename>      Per-user init-file to use instead of default.\n\
180   --no-sysinit               Inhibit processing of any system-wide init-file.\n\
181   --no-userinit              Inhibit processing of any per-user init-file.\n\
182   --disable-debugger         Invoke sb-ext:disable-debugger.\n\
183   --noprint                  Run a Read-Eval Loop without printing results.\n\
184   --script [<filename>]      Skip #! line, disable debugger, avoid verbosity.\n\
185 Common toplevel options that are processed in order:\n\
186   --eval <form>              Form to eval when processing this option.\n\
187   --load <filename>          File to load when processing this option.\n\
188 \n\
189 User options are not processed by SBCL. All runtime options must\n\
190 appear before toplevel options, and all toplevel options must\n\
191 appear before user options.\n\
192 \n\
193 For more information please refer to the SBCL User Manual, which\n\
194 should be installed along with SBCL, and is also available from the\n\
195 website <http://www.sbcl.org/>.\n");
196 }
197
198 void
199 print_version()
200 {
201     printf("SBCL %s\n", SBCL_VERSION_STRING);
202 }
203
204 void
205 print_banner()
206 {
207     printf(
208 "This is SBCL %s, an implementation of ANSI Common Lisp.\n\
209 More information about SBCL is available at <http://www.sbcl.org/>.\n\
210 \n\
211 SBCL is free software, provided as is, with absolutely no warranty.\n\
212 It is mostly in the public domain; some portions are provided under\n\
213 BSD-style licenses.  See the CREDITS and COPYING files in the\n\
214 distribution for more information.\n\
215 ", SBCL_VERSION_STRING);
216 }
217
218 /* Look for a core file to load, first in the directory named by the
219  * SBCL_HOME environment variable, then in a hardcoded default
220  * location.  Returns a malloced copy of the core filename. */
221 char *
222 search_for_core ()
223 {
224     char *sbcl_home = getenv("SBCL_HOME");
225     char *lookhere;
226     char *stem = "/sbcl.core";
227     char *core;
228
229     if (!(sbcl_home && *sbcl_home)) sbcl_home = SBCL_HOME;
230     lookhere = (char *) calloc(strlen(sbcl_home) +
231                                strlen(stem) +
232                                1,
233                                sizeof(char));
234     sprintf(lookhere, "%s%s", sbcl_home, stem);
235     core = copied_existing_filename_or_null(lookhere);
236
237     if (!core) {
238         lose("can't find core file at %s\n", lookhere);
239     }
240
241     free(lookhere);
242
243     return core;
244 }
245
246 /* Try to find the path to an executable from argv[0], this is only
247  * used when os_get_runtime_executable_path() returns NULL */
248 #ifdef LISP_FEATURE_WIN32
249 char *
250 search_for_executable(const char *argv0)
251 {
252     return NULL;
253 }
254 #else /* LISP_FEATURE_WIN32 */
255 char *
256 search_for_executable(const char *argv0)
257 {
258     char *search, *start, *end, *buf;
259
260     /* If argv[0] contains a slash then it's probably an absolute path
261      * or relative to the current directory, so check if it exists. */
262     if (strchr(argv0, '/') != NULL && access(argv0, F_OK) == 0)
263         return copied_realpath(argv0);
264
265     /* Bail on an absolute path which doesn't exist */
266     if (argv0[0] == '/')
267         return NULL;
268
269     /* Otherwise check if argv[0] exists relative to any directory in PATH */
270     search = getenv("PATH");
271     if (search == NULL)
272         return NULL;
273     search = copied_string(search);
274     buf = successful_malloc(PATH_MAX + 1);
275     for (start = search; (end = strchr(start, ':')) != NULL; start = end + 1) {
276         *end = '\0';
277         snprintf(buf, PATH_MAX + 1, "%s/%s", start, argv0);
278         if (access(buf, F_OK) == 0) {
279             free(search);
280             search = copied_realpath(buf);
281             free(buf);
282             return search;
283         }
284     }
285
286     free(search);
287     free(buf);
288     return NULL;
289 }
290 #endif /* LISP_FEATURE_WIN32 */
291
292 char **posix_argv;
293 char *core_string;
294
295 struct runtime_options *runtime_options;
296
297 char *saved_runtime_path = NULL;
298 \f
299 int
300 main(int argc, char *argv[], char *envp[])
301 {
302 #ifdef LISP_FEATURE_WIN32
303     /* Exception handling support structure. Evil Win32 hack. */
304     struct lisp_exception_frame exception_frame;
305 #endif
306
307     /* the name of the core file we're to execute. Note that this is
308      * a malloc'ed string which should be freed eventually. */
309     char *core = 0;
310     char **sbcl_argv = 0;
311     os_vm_offset_t embedded_core_offset = 0;
312     char *runtime_path = 0;
313
314     /* other command line options */
315     boolean noinform = 0;
316     boolean end_runtime_options = 0;
317     boolean disable_lossage_handler_p = 0;
318
319     lispobj initial_function;
320     const char *sbcl_home = getenv("SBCL_HOME");
321
322     interrupt_init();
323     block_blockable_signals(0, 0);
324
325     setlocale(LC_ALL, "");
326
327     runtime_options = NULL;
328
329     /* Save the argv[0] derived runtime path in case
330      * os_get_runtime_executable_path(1) isn't able to get an
331      * externally-usable path later on. */
332     saved_runtime_path = search_for_executable(argv[0]);
333
334     /* Check early to see if this executable has an embedded core,
335      * which also populates runtime_options if the core has runtime
336      * options */
337     runtime_path = os_get_runtime_executable_path(0);
338     if (runtime_path || saved_runtime_path) {
339         os_vm_offset_t offset = search_for_embedded_core(
340             runtime_path ? runtime_path : saved_runtime_path);
341         if (offset != -1) {
342             embedded_core_offset = offset;
343             core = (runtime_path ? runtime_path :
344                     copied_string(saved_runtime_path));
345         } else {
346             if (runtime_path)
347                 free(runtime_path);
348         }
349     }
350
351
352     /* Parse our part of the command line (aka "runtime options"),
353      * stripping out those options that we handle. */
354     if (runtime_options != NULL) {
355         dynamic_space_size = runtime_options->dynamic_space_size;
356         thread_control_stack_size = runtime_options->thread_control_stack_size;
357         sbcl_argv = argv;
358     } else {
359         int argi = 1;
360
361         runtime_options = successful_malloc(sizeof(struct runtime_options));
362
363         while (argi < argc) {
364             char *arg = argv[argi];
365             if (0 == strcmp(arg, "--script")) {
366                 /* This is both a runtime and a toplevel option. As a
367                  * runtime option, it is equivalent to --noinform.
368                  * This exits, and does not increment argi, so that
369                  * TOPLEVEL-INIT sees the option. */
370                 noinform = 1;
371                 end_runtime_options = 1;
372                 disable_lossage_handler_p = 1;
373                 lose_on_corruption_p = 1;
374                 break;
375             } else if (0 == strcmp(arg, "--noinform")) {
376                 noinform = 1;
377                 ++argi;
378             } else if (0 == strcmp(arg, "--core")) {
379                 if (core) {
380                     lose("more than one core file specified\n");
381                 } else {
382                     ++argi;
383                     if (argi >= argc) {
384                         lose("missing filename for --core argument\n");
385                     }
386                     core = copied_string(argv[argi]);
387                     ++argi;
388                 }
389             } else if (0 == strcmp(arg, "--help")) {
390                 /* I think this is the (or a) usual convention: upon
391                  * seeing "--help" we immediately print our help
392                  * string and exit, ignoring everything else. */
393                 print_help();
394                 exit(0);
395             } else if (0 == strcmp(arg, "--version")) {
396                 /* As in "--help" case, I think this is expected. */
397                 print_version();
398                 exit(0);
399             } else if (0 == strcmp(arg, "--dynamic-space-size")) {
400                 ++argi;
401                 if (argi >= argc)
402                     lose("missing argument for --dynamic-space-size");
403                 {
404                   char *tail;
405                   long tmp = strtol(argv[argi++], &tail, 0);
406                   if (tail[0])
407                     lose("--dynamic-space-size argument is not a number");
408                   if ((tmp <= 0) ||
409                       (tmp >= (LONG_MAX >> 20))) {
410                     lose("--dynamic-space-size argument is out of range");
411                   }
412                   dynamic_space_size = tmp << 20;
413                 }
414 #               ifdef MAX_DYNAMIC_SPACE_END
415                 if (!((DYNAMIC_SPACE_START <
416                        DYNAMIC_SPACE_START+dynamic_space_size) &&
417                       (DYNAMIC_SPACE_START+dynamic_space_size <=
418                        MAX_DYNAMIC_SPACE_END)))
419                     lose("--dynamic-space-size argument is too large");
420 #               endif
421             } else if (0 == strcmp(arg, "--control-stack-size")) {
422                 ++argi;
423                 if (argi >= argc)
424                     lose("missing argument for --control-stack-size");
425                 errno = 0;
426                 thread_control_stack_size = strtol(argv[argi++], 0, 0) << 20;
427                 if (errno)
428                     lose("argument to --control-stack-size is not a number");
429             } else if (0 == strcmp(arg, "--debug-environment")) {
430                 int n = 0;
431                 printf("; Commandline arguments:\n");
432                 while (n < argc) {
433                     printf(";  %2d: \"%s\"\n", n, argv[n]);
434                     ++n;
435                 }
436                 n = 0;
437                 printf(";\n; Environment:\n");
438                 while (ENVIRON[n]) {
439                     printf(";  %2d: \"%s\"\n", n, ENVIRON[n]);
440                     ++n;
441                 }
442                 ++argi;
443             } else if (0 == strcmp(arg, "--disable-ldb")) {
444                 disable_lossage_handler_p = 1;
445                 ++argi;
446             } else if (0 == strcmp(arg, "--lose-on-corruption")) {
447                 lose_on_corruption_p = 1;
448                 ++argi;
449             } else if (0 == strcmp(arg, "--end-runtime-options")) {
450                 end_runtime_options = 1;
451                 ++argi;
452                 break;
453             } else {
454                 /* This option was unrecognized as a runtime option,
455                  * so it must be a toplevel option or a user option,
456                  * so we must be past the end of the runtime option
457                  * section. */
458                 break;
459             }
460         }
461         /* This is where we strip out those options that we handle. We
462          * also take this opportunity to make sure that we don't find
463          * an out-of-place "--end-runtime-options" option. */
464         {
465             char *argi0 = argv[argi];
466             int argj = 1;
467             /* (argc - argi) for the arguments, one for the binary,
468                and one for the terminating NULL. */
469             sbcl_argv = successful_malloc((2 + argc - argi) * sizeof(char *));
470             sbcl_argv[0] = argv[0];
471             while (argi < argc) {
472                 char *arg = argv[argi++];
473                 /* If we encounter --end-runtime-options for the first
474                  * time after the point where we had to give up on
475                  * runtime options, then the point where we had to
476                  * give up on runtime options must've been a user
477                  * error. */
478                 if (!end_runtime_options &&
479                     0 == strcmp(arg, "--end-runtime-options")) {
480                     lose("bad runtime option \"%s\"\n", argi0);
481                 }
482                 sbcl_argv[argj++] = arg;
483             }
484             sbcl_argv[argj] = 0;
485         }
486     }
487
488     /* Align down to multiple of page_table page size, and to the appropriate
489      * stack alignment. */
490     dynamic_space_size &= ~(PAGE_BYTES-1);
491 #ifdef LISP_FEATURE_GENCGC
492     dynamic_space_size &= ~(GENCGC_CARD_BYTES-1);
493 #endif
494     thread_control_stack_size &= ~(CONTROL_STACK_ALIGNMENT_BYTES-1);
495
496     /* Preserve the runtime options for possible future core saving */
497     runtime_options->dynamic_space_size = dynamic_space_size;
498     runtime_options->thread_control_stack_size = thread_control_stack_size;
499
500     /* KLUDGE: os_vm_page_size is set by os_init(), and on some
501      * systems (e.g. Alpha) arch_init() needs need os_vm_page_size, so
502      * it must follow os_init(). -- WHN 2000-01-26 */
503     os_init(argv, envp);
504     arch_init();
505     gc_init();
506     validate();
507
508     /* If no core file was specified, look for one. */
509     if (!core) {
510         core = search_for_core();
511     }
512
513     /* Make sure that SBCL_HOME is set and not the empty string,
514        unless loading an embedded core. */
515     if (!(sbcl_home && *sbcl_home) && embedded_core_offset == 0) {
516         char *envstring, *copied_core, *dir;
517         char *stem = "SBCL_HOME=";
518         copied_core = copied_string(core);
519         dir = dirname(copied_core);
520         envstring = (char *) calloc(strlen(stem) +
521                                     strlen(dir) +
522                                     1,
523                                     sizeof(char));
524         sprintf(envstring, "%s%s", stem, dir);
525         putenv(envstring);
526         free(copied_core);
527     }
528
529     if (!noinform && embedded_core_offset == 0) {
530         print_banner();
531         fflush(stdout);
532     }
533
534 #if defined(SVR4) || defined(__linux__) || defined(__NetBSD__)
535     tzset();
536 #endif
537
538     define_var("nil", NIL, 1);
539     define_var("t", T, 1);
540
541     if (!disable_lossage_handler_p)
542         enable_lossage_handler();
543
544     globals_init();
545
546     initial_function = load_core_file(core, embedded_core_offset);
547     if (initial_function == NIL) {
548         lose("couldn't find initial function\n");
549     }
550 #ifdef LISP_FEATURE_HPUX
551     /* -1 = CLOSURE_FUN_OFFSET, 23 = SIMPLE_FUN_CODE_OFFSET, we are
552      * not in LANGUAGE_ASSEMBLY so we cant reach them. */
553     return_from_lisp_stub = (void *) ((char *)*((unsigned long *)
554                  ((char *)initial_function + -1)) + 23);
555 #endif
556
557     gc_initialize_pointers();
558
559     arch_install_interrupt_handlers();
560 #ifndef LISP_FEATURE_WIN32
561     os_install_interrupt_handlers();
562 #else
563 /*     wos_install_interrupt_handlers(handler); */
564     wos_install_interrupt_handlers(&exception_frame);
565 #endif
566
567     /* Pass core filename and the processed argv into Lisp. They'll
568      * need to be processed further there, to do locale conversion.
569      */
570     core_string = core;
571     posix_argv = sbcl_argv;
572
573     FSHOW((stderr, "/funcalling initial_function=0x%lx\n",
574           (unsigned long)initial_function));
575 #ifdef LISP_FEATURE_WIN32
576     fprintf(stderr, "\n\
577 This is experimental prerelease support for the Windows platform: use\n\
578 at your own risk.  \"Your Kitten of Death awaits!\"\n");
579     fflush(stdout);
580     fflush(stderr);
581 #endif
582     create_initial_thread(initial_function);
583     lose("CATS.  CATS ARE NICE.\n");
584     return 0;
585 }