0f1f0ff023a01e837bf46361c78f2d76e300772c
[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 \n\
183 User options are not processed by SBCL. All runtime options must\n\
184 appear before toplevel options, and all toplevel options must\n\
185 appear before user options.\n\
186 \n\
187 For more information please refer to the SBCL User Manual, which\n\
188 should be installed along with SBCL, and is also available from the\n\
189 website <http://www.sbcl.org/>.\n");
190 }
191
192 void
193 print_version()
194 {
195     printf("SBCL %s\n", SBCL_VERSION_STRING);
196 }
197
198 void
199 print_banner()
200 {
201     printf(
202 "This is SBCL %s, an implementation of ANSI Common Lisp.\n\
203 More information about SBCL is available at <http://www.sbcl.org/>.\n\
204 \n\
205 SBCL is free software, provided as is, with absolutely no warranty.\n\
206 It is mostly in the public domain; some portions are provided under\n\
207 BSD-style licenses.  See the CREDITS and COPYING files in the\n\
208 distribution for more information.\n\
209 ", SBCL_VERSION_STRING);
210 }
211
212 /* Look for a core file to load, first in the directory named by the
213  * SBCL_HOME environment variable, then in a hardcoded default
214  * location.  Returns a malloced copy of the core filename. */
215 char *
216 search_for_core ()
217 {
218     char *sbcl_home = getenv("SBCL_HOME");
219     char *lookhere;
220     char *stem = "/sbcl.core";
221     char *core;
222
223     if (!(sbcl_home && *sbcl_home)) sbcl_home = SBCL_HOME;
224     lookhere = (char *) calloc(strlen(sbcl_home) +
225                                strlen(stem) +
226                                1,
227                                sizeof(char));
228     sprintf(lookhere, "%s%s", sbcl_home, stem);
229     core = copied_existing_filename_or_null(lookhere);
230
231     if (!core) {
232         lose("can't find core file at %s\n", lookhere);
233     }
234
235     free(lookhere);
236
237     return core;
238 }
239
240 /* Try to find the path to an executable from argv[0], this is only
241  * used when os_get_runtime_executable_path() returns NULL */
242 #ifdef LISP_FEATURE_WIN32
243 char *
244 search_for_executable(const char *argv0)
245 {
246     return NULL;
247 }
248 #else /* LISP_FEATURE_WIN32 */
249 char *
250 search_for_executable(const char *argv0)
251 {
252     char *search, *start, *end, *buf;
253
254     /* If argv[0] contains a slash then it's probably an absolute path
255      * or relative to the current directory, so check if it exists. */
256     if (strchr(argv0, '/') != NULL && access(argv0, F_OK) == 0)
257         return copied_realpath(argv0);
258
259     /* Bail on an absolute path which doesn't exist */
260     if (argv0[0] == '/')
261         return NULL;
262
263     /* Otherwise check if argv[0] exists relative to any directory in PATH */
264     search = getenv("PATH");
265     if (search == NULL)
266         return NULL;
267     search = copied_string(search);
268     buf = successful_malloc(PATH_MAX + 1);
269     for (start = search; (end = strchr(start, ':')) != NULL; start = end + 1) {
270         *end = '\0';
271         snprintf(buf, PATH_MAX + 1, "%s/%s", start, argv0);
272         if (access(buf, F_OK) == 0) {
273             free(search);
274             search = copied_realpath(buf);
275             free(buf);
276             return search;
277         }
278     }
279
280     free(search);
281     free(buf);
282     return NULL;
283 }
284 #endif /* LISP_FEATURE_WIN32 */
285
286 char **posix_argv;
287 char *core_string;
288
289 struct runtime_options *runtime_options;
290
291 char *saved_runtime_path = NULL;
292 \f
293 int
294 main(int argc, char *argv[], char *envp[])
295 {
296 #ifdef LISP_FEATURE_WIN32
297     /* Exception handling support structure. Evil Win32 hack. */
298     struct lisp_exception_frame exception_frame;
299 #endif
300
301     /* the name of the core file we're to execute. Note that this is
302      * a malloc'ed string which should be freed eventually. */
303     char *core = 0;
304     char **sbcl_argv = 0;
305     os_vm_offset_t embedded_core_offset = 0;
306     char *runtime_path = 0;
307
308     /* other command line options */
309     boolean noinform = 0;
310     boolean end_runtime_options = 0;
311     boolean disable_lossage_handler_p = 0;
312
313     lispobj initial_function;
314     const char *sbcl_home = getenv("SBCL_HOME");
315
316     interrupt_init();
317     block_blockable_signals(0, 0);
318
319     setlocale(LC_ALL, "");
320
321     runtime_options = NULL;
322
323     /* Save the argv[0] derived runtime path in case
324      * os_get_runtime_executable_path(1) isn't able to get an
325      * externally-usable path later on. */
326     saved_runtime_path = search_for_executable(argv[0]);
327
328     /* Check early to see if this executable has an embedded core,
329      * which also populates runtime_options if the core has runtime
330      * options */
331     runtime_path = os_get_runtime_executable_path(0);
332     if (runtime_path || saved_runtime_path) {
333         os_vm_offset_t offset = search_for_embedded_core(
334             runtime_path ? runtime_path : saved_runtime_path);
335         if (offset != -1) {
336             embedded_core_offset = offset;
337             core = (runtime_path ? runtime_path :
338                     copied_string(saved_runtime_path));
339         } else {
340             free(runtime_path);
341         }
342     }
343
344
345     /* Parse our part of the command line (aka "runtime options"),
346      * stripping out those options that we handle. */
347     if (runtime_options != NULL) {
348         dynamic_space_size = runtime_options->dynamic_space_size;
349         thread_control_stack_size = runtime_options->thread_control_stack_size;
350         sbcl_argv = argv;
351     } else {
352         int argi = 1;
353
354         runtime_options = successful_malloc(sizeof(struct runtime_options));
355
356         while (argi < argc) {
357             char *arg = argv[argi];
358             if (0 == strcmp(arg, "--script")) {
359                 /* This is both a runtime and a toplevel option. As a
360                  * runtime option, it is equivalent to --noinform.
361                  * This exits, and does not increment argi, so that
362                  * TOPLEVEL-INIT sees the option. */
363                 noinform = 1;
364                 end_runtime_options = 1;
365                 disable_lossage_handler_p = 1;
366                 lose_on_corruption_p = 1;
367                 break;
368             } else if (0 == strcmp(arg, "--noinform")) {
369                 noinform = 1;
370                 ++argi;
371             } else if (0 == strcmp(arg, "--core")) {
372                 if (core) {
373                     lose("more than one core file specified\n");
374                 } else {
375                     ++argi;
376                     if (argi >= argc) {
377                         lose("missing filename for --core argument\n");
378                     }
379                     core = copied_string(argv[argi]);
380                     ++argi;
381                 }
382             } else if (0 == strcmp(arg, "--help")) {
383                 /* I think this is the (or a) usual convention: upon
384                  * seeing "--help" we immediately print our help
385                  * string and exit, ignoring everything else. */
386                 print_help();
387                 exit(0);
388             } else if (0 == strcmp(arg, "--version")) {
389                 /* As in "--help" case, I think this is expected. */
390                 print_version();
391                 exit(0);
392             } else if (0 == strcmp(arg, "--dynamic-space-size")) {
393                 ++argi;
394                 if (argi >= argc)
395                     lose("missing argument for --dynamic-space-size");
396                 errno = 0;
397                 dynamic_space_size = strtol(argv[argi++], 0, 0) << 20;
398                 if (errno)
399                     lose("argument to --dynamic-space-size is not a number");
400 #               ifdef MAX_DYNAMIC_SPACE_END
401                 if (!((DYNAMIC_SPACE_START <
402                        DYNAMIC_SPACE_START+dynamic_space_size) &&
403                       (DYNAMIC_SPACE_START+dynamic_space_size <=
404                        MAX_DYNAMIC_SPACE_END)))
405                     lose("specified --dynamic-space-size too large");
406 #               endif
407             } else if (0 == strcmp(arg, "--control-stack-size")) {
408                 ++argi;
409                 if (argi >= argc)
410                     lose("missing argument for --control-stack-size");
411                 errno = 0;
412                 thread_control_stack_size = strtol(argv[argi++], 0, 0) << 20;
413                 if (errno)
414                     lose("argument to --control-stack-size is not a number");
415             } else if (0 == strcmp(arg, "--debug-environment")) {
416                 int n = 0;
417                 printf("; Commandline arguments:\n");
418                 while (n < argc) {
419                     printf(";  %2d: \"%s\"\n", n, argv[n]);
420                     ++n;
421                 }
422                 n = 0;
423                 printf(";\n; Environment:\n");
424                 while (ENVIRON[n]) {
425                     printf(";  %2d: \"%s\"\n", n, ENVIRON[n]);
426                     ++n;
427                 }
428                 ++argi;
429             } else if (0 == strcmp(arg, "--disable-ldb")) {
430                 disable_lossage_handler_p = 1;
431                 ++argi;
432             } else if (0 == strcmp(arg, "--lose-on-corruption")) {
433                 lose_on_corruption_p = 1;
434                 ++argi;
435             } else if (0 == strcmp(arg, "--end-runtime-options")) {
436                 end_runtime_options = 1;
437                 ++argi;
438                 break;
439             } else {
440                 /* This option was unrecognized as a runtime option,
441                  * so it must be a toplevel option or a user option,
442                  * so we must be past the end of the runtime option
443                  * section. */
444                 break;
445             }
446         }
447         /* This is where we strip out those options that we handle. We
448          * also take this opportunity to make sure that we don't find
449          * an out-of-place "--end-runtime-options" option. */
450         {
451             char *argi0 = argv[argi];
452             int argj = 1;
453             /* (argc - argi) for the arguments, one for the binary,
454                and one for the terminating NULL. */
455             sbcl_argv = successful_malloc((2 + argc - argi) * sizeof(char *));
456             sbcl_argv[0] = argv[0];
457             while (argi < argc) {
458                 char *arg = argv[argi++];
459                 /* If we encounter --end-runtime-options for the first
460                  * time after the point where we had to give up on
461                  * runtime options, then the point where we had to
462                  * give up on runtime options must've been a user
463                  * error. */
464                 if (!end_runtime_options &&
465                     0 == strcmp(arg, "--end-runtime-options")) {
466                     lose("bad runtime option \"%s\"\n", argi0);
467                 }
468                 sbcl_argv[argj++] = arg;
469             }
470             sbcl_argv[argj] = 0;
471         }
472     }
473
474     /* Align down to multiple of page_table page size, and to the appropriate
475      * stack alignment. */
476     dynamic_space_size &= ~(PAGE_BYTES-1);
477     thread_control_stack_size &= ~(CONTROL_STACK_ALIGNMENT_BYTES-1);
478
479     /* Preserve the runtime options for possible future core saving */
480     runtime_options->dynamic_space_size = dynamic_space_size;
481     runtime_options->thread_control_stack_size = thread_control_stack_size;
482
483     /* KLUDGE: os_vm_page_size is set by os_init(), and on some
484      * systems (e.g. Alpha) arch_init() needs need os_vm_page_size, so
485      * it must follow os_init(). -- WHN 2000-01-26 */
486     os_init(argv, envp);
487     arch_init();
488     gc_init();
489     validate();
490
491     /* If no core file was specified, look for one. */
492     if (!core) {
493         core = search_for_core();
494     }
495
496     /* Make sure that SBCL_HOME is set and not the empty string,
497        unless loading an embedded core. */
498     if (!(sbcl_home && *sbcl_home) && embedded_core_offset == 0) {
499         char *envstring, *copied_core, *dir;
500         char *stem = "SBCL_HOME=";
501         copied_core = copied_string(core);
502         dir = dirname(copied_core);
503         envstring = (char *) calloc(strlen(stem) +
504                                     strlen(dir) +
505                                     1,
506                                     sizeof(char));
507         sprintf(envstring, "%s%s", stem, dir);
508         putenv(envstring);
509         free(copied_core);
510     }
511
512     if (!noinform && embedded_core_offset == 0) {
513         print_banner();
514         fflush(stdout);
515     }
516
517 #if defined(SVR4) || defined(__linux__) || defined(__NetBSD__)
518     tzset();
519 #endif
520
521     define_var("nil", NIL, 1);
522     define_var("t", T, 1);
523
524     if (!disable_lossage_handler_p)
525         enable_lossage_handler();
526
527     globals_init();
528
529     initial_function = load_core_file(core, embedded_core_offset);
530     if (initial_function == NIL) {
531         lose("couldn't find initial function\n");
532     }
533 #ifdef LISP_FEATURE_HPUX
534     /* -1 = CLOSURE_FUN_OFFSET, 23 = SIMPLE_FUN_CODE_OFFSET, we are
535      * not in LANGUAGE_ASSEMBLY so we cant reach them. */
536     return_from_lisp_stub = (void *) ((char *)*((unsigned long *)
537                  ((char *)initial_function + -1)) + 23);
538 #endif
539
540     gc_initialize_pointers();
541
542     arch_install_interrupt_handlers();
543 #ifndef LISP_FEATURE_WIN32
544     os_install_interrupt_handlers();
545 #else
546 /*     wos_install_interrupt_handlers(handler); */
547     wos_install_interrupt_handlers(&exception_frame);
548 #endif
549
550     /* Pass core filename and the processed argv into Lisp. They'll
551      * need to be processed further there, to do locale conversion.
552      */
553     core_string = core;
554     posix_argv = sbcl_argv;
555
556     FSHOW((stderr, "/funcalling initial_function=0x%lx\n",
557           (unsigned long)initial_function));
558 #ifdef LISP_FEATURE_WIN32
559     fprintf(stderr, "\n\
560 This is experimental prerelease support for the Windows platform: use\n\
561 at your own risk.  \"Your Kitten of Death awaits!\"\n");
562     fflush(stdout);
563     fflush(stderr);
564 #endif
565     create_initial_thread(initial_function);
566     lose("CATS.  CATS ARE NICE.\n");
567     return 0;
568 }