2 * main() entry point for a stand-alone SBCL image
6 * This software is part of the SBCL system. See the README file for
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.
19 #include <sys/types.h>
24 #include <sys/param.h>
27 #include <sys/ptrace.h>
31 #if defined(SVR4) || defined(__linux__)
43 #include "interrupt.h"
54 #include "genesis/static-symbols.h"
55 #include "genesis/symbol.h"
63 /* SIGINT handler that invokes the monitor (for when Lisp isn't up to it) */
65 sigint_handler(int signal, siginfo_t *info, void *void_context)
67 lose("\nSIGINT hit at 0x%08lX\n",
68 (unsigned long) *os_context_pc_addr(void_context));
71 /* (This is not static, because we want to be able to call it from
76 SHOW("entering sigint_init()");
77 install_handler(SIGINT, sigint_handler);
78 SHOW("leaving sigint_init()");
82 * helper functions for dealing with command line args
86 successful_malloc(size_t size)
88 void* result = malloc(size);
90 lose("malloc failure");
94 return (void *) NULL; /* dummy value: return something ... */
98 copied_string(char *string)
100 return strcpy(successful_malloc(1+strlen(string)), string);
104 copied_existing_filename_or_null(char *filename)
106 struct stat filename_stat;
107 if (stat(filename, &filename_stat)) { /* if failure */
110 return copied_string(filename);
114 /* Convert a null-terminated array of null-terminated strings (e.g.
115 * argv or envp) into a Lisp list of Lisp strings. */
117 alloc_string_list(char *array_ptr[])
120 return alloc_cons(alloc_string(*array_ptr),
121 alloc_string_list(1 + array_ptr));
127 /* miscellaneous chattiness */
133 "SBCL is a Common Lisp programming environment. Ordinarily you shouldn't\n\
134 need command line options when you invoke it interactively: you can just\n\
135 start it and work with the customary Lisp READ-EVAL-PRINT loop.\n\
137 One option idiom which is sometimes useful interactively (e.g. when\n\
138 exercising a test case for a bug report) is\n\
139 sbcl --sysinit /dev/null --userinit /dev/null\n\
140 to keep SBCL from reading any initialization files at startup. And some\n\
141 people like to suppress the default startup message:\n\
144 Other options can be useful when you're running SBCL noninteractively,\n\
145 e.g. from a script, or if you have a strange system configuration, so\n\
146 that SBCL can't by default find one of the files it needs. For\n\
147 information on such options, see the sbcl(1) man page.\n\
149 More information on SBCL can be found on its man page, or at\n\
150 <http://sbcl.sf.net/>.\n");
156 printf("SBCL %s\n", SBCL_VERSION_STRING);
163 "This is SBCL %s, an implementation of ANSI Common Lisp.\n\
165 SBCL is derived from the CMU CL system created at Carnegie Mellon University.\n\
166 Besides software and documentation originally created at Carnegie Mellon\n\
167 University, SBCL contains some software originally from the Massachusetts\n\
168 Institute of Technology, Symbolics Incorporated, and Xerox Corporation, and\n\
169 material contributed by volunteers since the release of CMU CL into the\n\
170 public domain. See the CREDITS file in the distribution for more information.\n\
172 SBCL is a free software system, provided as is, with absolutely no warranty.\n\
173 It is mostly in the public domain, but also includes some software copyrighted\n\
174 Massachusetts Institute of Technology, 1986;\n\
175 Symbolics, Inc., 1989, 1990, 1991, 1992; and\n\
176 Xerox Corporation, 1985, 1986, 1987, 1988, 1989, 1990\n\
177 used under BSD-style licenses allowing copying only under certain conditions.\n\
178 See the COPYING file in the distribution for more information.\n\
180 More information about SBCL is available at <http://sbcl.sourceforge.net/>.\n\
181 ", SBCL_VERSION_STRING);
189 main(int argc, char *argv[], char *envp[])
191 /* the name of the core file we're to execute. Note that this is
192 * a malloc'ed string which should be freed eventually. */
195 /* other command line options */
196 boolean noinform = 0;
197 boolean end_runtime_options = 0;
199 lispobj initial_function;
201 /* KLUDGE: os_vm_page_size is set by os_init(), and on some
202 * systems (e.g. Alpha) arch_init() needs need os_vm_page_size, so
203 * it must follow os_init(). -- WHN 2000-01-26 */
209 /* Parse our part of the command line (aka "runtime options"),
210 * stripping out those options that we handle. */
213 while (argi < argc) {
214 char *arg = argv[argi];
215 if (0 == strcmp(arg, "--noinform")) {
218 } else if (0 == strcmp(arg, "--core")) {
220 lose("more than one core file specified");
224 lose("missing filename for --core argument");
226 core = copied_string(argv[argi]);
229 } else if (0 == strcmp(arg, "--help")) {
230 /* I think this is the (or a) usual convention: upon
231 * seeing "--help" we immediately print our help
232 * string and exit, ignoring everything else. */
235 } else if (0 == strcmp(arg, "--version")) {
236 /* As in "--help" case, I think this is expected. */
239 } else if (0 == strcmp(arg, "--end-runtime-options")) {
240 end_runtime_options = 1;
244 /* This option was unrecognized as a runtime option,
245 * so it must be a toplevel option or a user option,
246 * so we must be past the end of the runtime option
251 /* This is where we strip out those options that we handle. We
252 * also take this opportunity to make sure that we don't find
253 * an out-of-place "--end-runtime-options" option. */
255 char *argi0 = argv[argi];
257 while (argi < argc) {
258 char *arg = argv[argi++];
259 /* If we encounter --end-runtime-options for the first
260 * time after the point where we had to give up on
261 * runtime options, then the point where we had to
262 * give up on runtime options must've been a user
264 if (!end_runtime_options &&
265 0 == strcmp(arg, "--end-runtime-options")) {
266 lose("bad runtime option \"%s\"", argi0);
275 /* If no core file was specified, look for one. */
277 char *sbcl_home = getenv("SBCL_HOME");
280 char *stem = "/sbcl.core";
281 lookhere = (char *) calloc(strlen(sbcl_home) +
285 sprintf(lookhere, "%s%s", sbcl_home, stem);
286 core = copied_existing_filename_or_null(lookhere);
289 putenv("SBCL_HOME=/usr/local/lib/sbcl/");
290 core = copied_existing_filename_or_null("/usr/local/lib/sbcl/sbcl.core");
292 putenv("SBCL_HOME=/usr/lib/sbcl/");
294 copied_existing_filename_or_null("/usr/lib/sbcl/sbcl.core");
298 lose("can't find core file");
301 /* If a core was specified and SBCL_HOME is unset, set it */
302 char *sbcl_home = getenv("SBCL_HOME");
304 char *envstring, *copied_core, *dir;
305 char *stem = "SBCL_HOME=";
306 copied_core = copied_string(core);
307 dir = dirname(copied_core);
308 envstring = (char *) calloc(strlen(stem) +
312 sprintf(envstring, "%s%s", stem, dir);
326 #if defined(SVR4) || defined(__linux__)
330 define_var("nil", NIL, 1);
331 define_var("t", T, 1);
333 set_lossage_handler(monitor_or_something);
337 initial_function = load_core_file(core);
338 if (initial_function == NIL) {
339 lose("couldn't find initial function");
341 SHOW("freeing core");
344 gc_initialize_pointers();
347 arch_install_interrupt_handlers();
348 os_install_interrupt_handlers();
350 /* Convert remaining argv values to something that Lisp can grok. */
351 SHOW("setting POSIX-ARGV symbol value");
352 SetSymbolValue(POSIX_ARGV, alloc_string_list(argv),0);
354 /* Install a handler to pick off SIGINT until the Lisp system gets
355 * far enough along to install its own handler. */
358 FSHOW((stderr, "/funcalling initial_function=0x%lx\n", initial_function));
359 create_thread(initial_function);
360 /* in a unithread build, create_thread never returns */
361 #ifdef LISP_FEATURE_SB_THREAD
362 gc_thread_pid=getpid();
367 static void parent_sighandler(int signum,siginfo_t *info, void *void_context)
370 os_context_t *context = (os_context_t*)void_context;
372 "parent thread got signal %d from %d, maybe_gc_pending=%d\n",
373 signum, info->si_pid,
378 #ifdef LISP_FEATURE_SB_THREAD
379 static void parent_do_garbage_collect(void)
381 int waiting_threads=0;
385 for_each_thread(th) {
386 if(ptrace(PTRACE_ATTACH,th->pid,0,0)) {
387 perror("PTRACE_ATTACH");
389 else waiting_threads++;
394 /* not sure if we have to wait for PTRACE_ATTACH to finish
395 * before we can send PTRACE_CONT, so let's play it safe
397 while(waiting_threads>0) {
398 if((p=waitpid(-1,&status, WUNTRACED|__WALL))>0) {
399 if(WIFEXITED(status) || WIFSIGNALED(status))
400 destroy_thread(find_thread_by_pid(p));
403 fprintf(stderr, "wait returned pid %d signal %x\n",
406 if(WSTOPSIG(status)==SIGTRAP) {
407 if(ptrace(PTRACE_CONT,p,0,SIGTRAP))
408 perror("PTRACE_CONT");
410 else waiting_threads--;
414 for_each_thread(th) {
415 if(SymbolTlValue(PSEUDO_ATOMIC_ATOMIC,th)) {
416 /* restart the child, setting *p-a-i* which will cause it
417 * to go into interrupt_handle_pending as soon as it's
418 * finished being pseudo_atomic. once there it will
419 * signal itself SIGSTOP, which will give us another
420 * event to wait for */
421 fprintf(stderr, "%d was pseudo-atomic, letting it resume \n",
423 SetTlSymbolValue(PSEUDO_ATOMIC_INTERRUPTED,make_fixnum(1),th);
424 if(ptrace(PTRACE_CONT,th->pid,0,0))
425 perror("PTRACE_CONT");
429 } while (waiting_threads>0);
431 collect_garbage(maybe_gc_pending-1);
434 /* fprintf(stderr, "gc done\n"); */
436 if(ptrace(PTRACE_DETACH,th->pid,0,0))
437 perror("PTRACE_DETACH");
440 static void /* noreturn */ parent_loop(void)
446 sigemptyset(&sigset);
448 sigaddset(&sigset, SIGALRM);
449 sigaddset(&sigset, SIGCHLD);
450 sigprocmask(SIG_UNBLOCK,&sigset,0);
451 sa.sa_handler=parent_sighandler;
453 sa.sa_flags=SA_SIGINFO;
454 sigaction(SIGALRM, &sa, 0);
455 sigaction(SIGCHLD, &sa, 0);
457 sigemptyset(&sigset);
458 sa.sa_handler=SIG_IGN;
461 sigaction(SIGINT, &sa, 0);
463 while(!all_threads) {
469 while(pid=waitpid(-1,&status,__WALL|WUNTRACED)) {
473 if(maybe_gc_pending) parent_do_garbage_collect();
476 if(errno == ECHILD) break;
477 fprintf(stderr,"waitpid: %s\n",strerror(errno));
480 th=find_thread_by_pid(pid);
482 if(WIFEXITED(status) || WIFSIGNALED(status)) {
483 fprintf(stderr,"waitpid : child %d %x exited \n", pid,th);
485 /* FIXME arrange to call or fake (free-mutex *session-lock*)
487 if(!all_threads) break;
491 exit(WEXITSTATUS(status));