1.0.47.1: fix longstanding bug in os_get_runtime_executable_path on darwin
[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             if (runtime_path)
341                 free(runtime_path);
342         }
343     }
344
345
346     /* Parse our part of the command line (aka "runtime options"),
347      * stripping out those options that we handle. */
348     if (runtime_options != NULL) {
349         dynamic_space_size = runtime_options->dynamic_space_size;
350         thread_control_stack_size = runtime_options->thread_control_stack_size;
351         sbcl_argv = argv;
352     } else {
353         int argi = 1;
354
355         runtime_options = successful_malloc(sizeof(struct runtime_options));
356
357         while (argi < argc) {
358             char *arg = argv[argi];
359             if (0 == strcmp(arg, "--script")) {
360                 /* This is both a runtime and a toplevel option. As a
361                  * runtime option, it is equivalent to --noinform.
362                  * This exits, and does not increment argi, so that
363                  * TOPLEVEL-INIT sees the option. */
364                 noinform = 1;
365                 end_runtime_options = 1;
366                 disable_lossage_handler_p = 1;
367                 lose_on_corruption_p = 1;
368                 break;
369             } else if (0 == strcmp(arg, "--noinform")) {
370                 noinform = 1;
371                 ++argi;
372             } else if (0 == strcmp(arg, "--core")) {
373                 if (core) {
374                     lose("more than one core file specified\n");
375                 } else {
376                     ++argi;
377                     if (argi >= argc) {
378                         lose("missing filename for --core argument\n");
379                     }
380                     core = copied_string(argv[argi]);
381                     ++argi;
382                 }
383             } else if (0 == strcmp(arg, "--help")) {
384                 /* I think this is the (or a) usual convention: upon
385                  * seeing "--help" we immediately print our help
386                  * string and exit, ignoring everything else. */
387                 print_help();
388                 exit(0);
389             } else if (0 == strcmp(arg, "--version")) {
390                 /* As in "--help" case, I think this is expected. */
391                 print_version();
392                 exit(0);
393             } else if (0 == strcmp(arg, "--dynamic-space-size")) {
394                 ++argi;
395                 if (argi >= argc)
396                     lose("missing argument for --dynamic-space-size");
397                 errno = 0;
398                 dynamic_space_size = strtol(argv[argi++], 0, 0) << 20;
399                 if (errno)
400                     lose("argument to --dynamic-space-size is not a number");
401 #               ifdef MAX_DYNAMIC_SPACE_END
402                 if (!((DYNAMIC_SPACE_START <
403                        DYNAMIC_SPACE_START+dynamic_space_size) &&
404                       (DYNAMIC_SPACE_START+dynamic_space_size <=
405                        MAX_DYNAMIC_SPACE_END)))
406                     lose("specified --dynamic-space-size too large");
407 #               endif
408             } else if (0 == strcmp(arg, "--control-stack-size")) {
409                 ++argi;
410                 if (argi >= argc)
411                     lose("missing argument for --control-stack-size");
412                 errno = 0;
413                 thread_control_stack_size = strtol(argv[argi++], 0, 0) << 20;
414                 if (errno)
415                     lose("argument to --control-stack-size is not a number");
416             } else if (0 == strcmp(arg, "--debug-environment")) {
417                 int n = 0;
418                 printf("; Commandline arguments:\n");
419                 while (n < argc) {
420                     printf(";  %2d: \"%s\"\n", n, argv[n]);
421                     ++n;
422                 }
423                 n = 0;
424                 printf(";\n; Environment:\n");
425                 while (ENVIRON[n]) {
426                     printf(";  %2d: \"%s\"\n", n, ENVIRON[n]);
427                     ++n;
428                 }
429                 ++argi;
430             } else if (0 == strcmp(arg, "--disable-ldb")) {
431                 disable_lossage_handler_p = 1;
432                 ++argi;
433             } else if (0 == strcmp(arg, "--lose-on-corruption")) {
434                 lose_on_corruption_p = 1;
435                 ++argi;
436             } else if (0 == strcmp(arg, "--end-runtime-options")) {
437                 end_runtime_options = 1;
438                 ++argi;
439                 break;
440             } else {
441                 /* This option was unrecognized as a runtime option,
442                  * so it must be a toplevel option or a user option,
443                  * so we must be past the end of the runtime option
444                  * section. */
445                 break;
446             }
447         }
448         /* This is where we strip out those options that we handle. We
449          * also take this opportunity to make sure that we don't find
450          * an out-of-place "--end-runtime-options" option. */
451         {
452             char *argi0 = argv[argi];
453             int argj = 1;
454             /* (argc - argi) for the arguments, one for the binary,
455                and one for the terminating NULL. */
456             sbcl_argv = successful_malloc((2 + argc - argi) * sizeof(char *));
457             sbcl_argv[0] = argv[0];
458             while (argi < argc) {
459                 char *arg = argv[argi++];
460                 /* If we encounter --end-runtime-options for the first
461                  * time after the point where we had to give up on
462                  * runtime options, then the point where we had to
463                  * give up on runtime options must've been a user
464                  * error. */
465                 if (!end_runtime_options &&
466                     0 == strcmp(arg, "--end-runtime-options")) {
467                     lose("bad runtime option \"%s\"\n", argi0);
468                 }
469                 sbcl_argv[argj++] = arg;
470             }
471             sbcl_argv[argj] = 0;
472         }
473     }
474
475     /* Align down to multiple of page_table page size, and to the appropriate
476      * stack alignment. */
477     dynamic_space_size &= ~(PAGE_BYTES-1);
478     thread_control_stack_size &= ~(CONTROL_STACK_ALIGNMENT_BYTES-1);
479
480     /* Preserve the runtime options for possible future core saving */
481     runtime_options->dynamic_space_size = dynamic_space_size;
482     runtime_options->thread_control_stack_size = thread_control_stack_size;
483
484     /* KLUDGE: os_vm_page_size is set by os_init(), and on some
485      * systems (e.g. Alpha) arch_init() needs need os_vm_page_size, so
486      * it must follow os_init(). -- WHN 2000-01-26 */
487     os_init(argv, envp);
488     arch_init();
489     gc_init();
490     validate();
491
492     /* If no core file was specified, look for one. */
493     if (!core) {
494         core = search_for_core();
495     }
496
497     /* Make sure that SBCL_HOME is set and not the empty string,
498        unless loading an embedded core. */
499     if (!(sbcl_home && *sbcl_home) && embedded_core_offset == 0) {
500         char *envstring, *copied_core, *dir;
501         char *stem = "SBCL_HOME=";
502         copied_core = copied_string(core);
503         dir = dirname(copied_core);
504         envstring = (char *) calloc(strlen(stem) +
505                                     strlen(dir) +
506                                     1,
507                                     sizeof(char));
508         sprintf(envstring, "%s%s", stem, dir);
509         putenv(envstring);
510         free(copied_core);
511     }
512
513     if (!noinform && embedded_core_offset == 0) {
514         print_banner();
515         fflush(stdout);
516     }
517
518 #if defined(SVR4) || defined(__linux__) || defined(__NetBSD__)
519     tzset();
520 #endif
521
522     define_var("nil", NIL, 1);
523     define_var("t", T, 1);
524
525     if (!disable_lossage_handler_p)
526         enable_lossage_handler();
527
528     globals_init();
529
530     initial_function = load_core_file(core, embedded_core_offset);
531     if (initial_function == NIL) {
532         lose("couldn't find initial function\n");
533     }
534 #ifdef LISP_FEATURE_HPUX
535     /* -1 = CLOSURE_FUN_OFFSET, 23 = SIMPLE_FUN_CODE_OFFSET, we are
536      * not in LANGUAGE_ASSEMBLY so we cant reach them. */
537     return_from_lisp_stub = (void *) ((char *)*((unsigned long *)
538                  ((char *)initial_function + -1)) + 23);
539 #endif
540
541     gc_initialize_pointers();
542
543     arch_install_interrupt_handlers();
544 #ifndef LISP_FEATURE_WIN32
545     os_install_interrupt_handlers();
546 #else
547 /*     wos_install_interrupt_handlers(handler); */
548     wos_install_interrupt_handlers(&exception_frame);
549 #endif
550
551     /* Pass core filename and the processed argv into Lisp. They'll
552      * need to be processed further there, to do locale conversion.
553      */
554     core_string = core;
555     posix_argv = sbcl_argv;
556
557     FSHOW((stderr, "/funcalling initial_function=0x%lx\n",
558           (unsigned long)initial_function));
559 #ifdef LISP_FEATURE_WIN32
560     fprintf(stderr, "\n\
561 This is experimental prerelease support for the Windows platform: use\n\
562 at your own risk.  \"Your Kitten of Death awaits!\"\n");
563     fflush(stdout);
564     fflush(stderr);
565 #endif
566     create_initial_thread(initial_function);
567     lose("CATS.  CATS ARE NICE.\n");
568     return 0;
569 }