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 #ifdef LISP_FEATURE_SB_THREAD
28 #include <sys/ptrace.h>
33 #if defined(SVR4) || defined(__linux__)
45 #include "interrupt.h"
56 #include "genesis/static-symbols.h"
57 #include "genesis/symbol.h"
65 /* SIGINT handler that invokes the monitor (for when Lisp isn't up to it) */
67 sigint_handler(int signal, siginfo_t *info, void *void_context)
69 lose("\nSIGINT hit at 0x%08lX\n",
70 (unsigned long) *os_context_pc_addr(void_context));
73 /* (This is not static, because we want to be able to call it from
78 SHOW("entering sigint_init()");
79 install_handler(SIGINT, sigint_handler);
80 SHOW("leaving sigint_init()");
84 * helper functions for dealing with command line args
88 successful_malloc(size_t size)
90 void* result = malloc(size);
92 lose("malloc failure");
96 return (void *) NULL; /* dummy value: return something ... */
100 copied_string(char *string)
102 return strcpy(successful_malloc(1+strlen(string)), string);
106 copied_existing_filename_or_null(char *filename)
108 struct stat filename_stat;
109 if (stat(filename, &filename_stat)) { /* if failure */
112 return copied_string(filename);
116 /* Convert a null-terminated array of null-terminated strings (e.g.
117 * argv or envp) into a Lisp list of Lisp base-strings. */
119 alloc_base_string_list(char *array_ptr[])
122 return alloc_cons(alloc_base_string(*array_ptr),
123 alloc_base_string_list(1 + array_ptr));
129 /* miscellaneous chattiness */
135 "SBCL is a Common Lisp programming environment. Ordinarily you shouldn't\n\
136 need command line options when you invoke it interactively: you can just\n\
137 start it and work with the customary Lisp READ-EVAL-PRINT loop.\n\
139 One option idiom which is sometimes useful interactively (e.g. when\n\
140 exercising a test case for a bug report) is\n\
141 sbcl --sysinit /dev/null --userinit /dev/null\n\
142 to keep SBCL from reading any initialization files at startup. And some\n\
143 people like to suppress the default startup message:\n\
146 Other options can be useful when you're running SBCL noninteractively,\n\
147 e.g. from a script, or if you have a strange system configuration, so\n\
148 that SBCL can't by default find one of the files it needs. For\n\
149 information on such options, see the sbcl(1) man page.\n\
151 More information on SBCL can be found on its man page, or at\n\
152 <http://sbcl.sf.net/>.\n");
158 printf("SBCL %s\n", SBCL_VERSION_STRING);
165 "This is SBCL %s, an implementation of ANSI Common Lisp.\n\
167 SBCL is derived from the CMU CL system created at Carnegie Mellon University.\n\
168 Besides software and documentation originally created at Carnegie Mellon\n\
169 University, SBCL contains some software originally from the Massachusetts\n\
170 Institute of Technology, Symbolics Incorporated, and Xerox Corporation, and\n\
171 material contributed by volunteers since the release of CMU CL into the\n\
172 public domain. See the CREDITS file in the distribution for more information.\n\
174 SBCL is a free software system, provided as is, with absolutely no warranty.\n\
175 It is mostly in the public domain, but also includes some software copyrighted\n\
176 Massachusetts Institute of Technology, 1986;\n\
177 Symbolics, Inc., 1989, 1990, 1991, 1992; and\n\
178 Xerox Corporation, 1985, 1986, 1987, 1988, 1989, 1990\n\
179 used under BSD-style licenses allowing copying only under certain conditions.\n\
180 See the COPYING file in the distribution for more information.\n\
182 More information about SBCL is available at <http://sbcl.sourceforge.net/>.\n\
183 ", SBCL_VERSION_STRING);
191 main(int argc, char *argv[], char *envp[])
193 /* the name of the core file we're to execute. Note that this is
194 * a malloc'ed string which should be freed eventually. */
197 /* other command line options */
198 boolean noinform = 0;
199 boolean end_runtime_options = 0;
201 lispobj initial_function;
203 /* KLUDGE: os_vm_page_size is set by os_init(), and on some
204 * systems (e.g. Alpha) arch_init() needs need os_vm_page_size, so
205 * it must follow os_init(). -- WHN 2000-01-26 */
211 /* Parse our part of the command line (aka "runtime options"),
212 * stripping out those options that we handle. */
215 while (argi < argc) {
216 char *arg = argv[argi];
217 if (0 == strcmp(arg, "--noinform")) {
220 } else if (0 == strcmp(arg, "--core")) {
222 lose("more than one core file specified");
226 lose("missing filename for --core argument");
228 core = copied_string(argv[argi]);
231 } else if (0 == strcmp(arg, "--help")) {
232 /* I think this is the (or a) usual convention: upon
233 * seeing "--help" we immediately print our help
234 * string and exit, ignoring everything else. */
237 } else if (0 == strcmp(arg, "--version")) {
238 /* As in "--help" case, I think this is expected. */
241 } else if (0 == strcmp(arg, "--end-runtime-options")) {
242 end_runtime_options = 1;
246 /* This option was unrecognized as a runtime option,
247 * so it must be a toplevel option or a user option,
248 * so we must be past the end of the runtime option
253 /* This is where we strip out those options that we handle. We
254 * also take this opportunity to make sure that we don't find
255 * an out-of-place "--end-runtime-options" option. */
257 char *argi0 = argv[argi];
259 while (argi < argc) {
260 char *arg = argv[argi++];
261 /* If we encounter --end-runtime-options for the first
262 * time after the point where we had to give up on
263 * runtime options, then the point where we had to
264 * give up on runtime options must've been a user
266 if (!end_runtime_options &&
267 0 == strcmp(arg, "--end-runtime-options")) {
268 lose("bad runtime option \"%s\"", argi0);
277 /* If no core file was specified, look for one. */
279 char *sbcl_home = getenv("SBCL_HOME");
282 char *stem = "/sbcl.core";
283 lookhere = (char *) calloc(strlen(sbcl_home) +
287 sprintf(lookhere, "%s%s", sbcl_home, stem);
288 core = copied_existing_filename_or_null(lookhere);
291 putenv("SBCL_HOME=/usr/local/lib/sbcl/");
292 core = copied_existing_filename_or_null("/usr/local/lib/sbcl/sbcl.core");
294 putenv("SBCL_HOME=/usr/lib/sbcl/");
296 copied_existing_filename_or_null("/usr/lib/sbcl/sbcl.core");
300 lose("can't find core file");
303 /* If a core was specified and SBCL_HOME is unset, set it */
304 char *sbcl_home = getenv("SBCL_HOME");
306 char *envstring, *copied_core, *dir;
307 char *stem = "SBCL_HOME=";
308 copied_core = copied_string(core);
309 dir = dirname(copied_core);
310 envstring = (char *) calloc(strlen(stem) +
314 sprintf(envstring, "%s%s", stem, dir);
328 #if defined(SVR4) || defined(__linux__)
332 define_var("nil", NIL, 1);
333 define_var("t", T, 1);
335 set_lossage_handler(monitor_or_something);
339 initial_function = load_core_file(core);
340 if (initial_function == NIL) {
341 lose("couldn't find initial function");
343 SHOW("freeing core");
346 gc_initialize_pointers();
349 arch_install_interrupt_handlers();
350 os_install_interrupt_handlers();
352 /* Convert remaining argv values to something that Lisp can grok. */
353 SHOW("setting POSIX-ARGV symbol value");
354 SetSymbolValue(POSIX_ARGV, alloc_base_string_list(argv),0);
356 /* Install a handler to pick off SIGINT until the Lisp system gets
357 * far enough along to install its own handler. */
360 FSHOW((stderr, "/funcalling initial_function=0x%lx\n", initial_function));
361 create_thread(initial_function);
362 /* in a unithread build, create_thread never returns */
363 #ifdef LISP_FEATURE_SB_THREAD
364 gc_thread_pid=getpid();
369 static void parent_sighandler(int signum,siginfo_t *info, void *void_context)
372 os_context_t *context = (os_context_t*)void_context;
374 "parent thread got signal %d from %d, maybe_gc_pending=%d\n",
375 signum, info->si_pid,
380 #ifdef LISP_FEATURE_SB_THREAD
381 static void parent_do_garbage_collect(void)
383 int waiting_threads=0;
387 for_each_thread(th) {
388 if(ptrace(PTRACE_ATTACH,th->pid,0,0)) {
389 perror("PTRACE_ATTACH");
391 else waiting_threads++;
396 /* not sure if we have to wait for PTRACE_ATTACH to finish
397 * before we can send PTRACE_CONT, so let's play it safe
399 while(waiting_threads>0) {
400 if((p=waitpid(-1,&status, WUNTRACED|__WALL))>0) {
401 if(WIFEXITED(status) || WIFSIGNALED(status))
402 destroy_thread(find_thread_by_pid(p));
405 fprintf(stderr, "wait returned pid %d signal %x\n",
408 if(WSTOPSIG(status)==SIGTRAP) {
409 if(ptrace(PTRACE_CONT,p,0,SIGTRAP))
410 perror("PTRACE_CONT");
412 else waiting_threads--;
416 for_each_thread(th) {
417 if(SymbolTlValue(PSEUDO_ATOMIC_ATOMIC,th)) {
418 /* restart the child, setting *p-a-i* which will cause it
419 * to go into interrupt_handle_pending as soon as it's
420 * finished being pseudo_atomic. once there it will
421 * signal itself SIGSTOP, which will give us another
422 * event to wait for */
424 fprintf(stderr, "%d was pseudo-atomic, letting it resume \n",
427 SetTlSymbolValue(PSEUDO_ATOMIC_INTERRUPTED,make_fixnum(1),th);
428 if(ptrace(PTRACE_CONT,th->pid,0,0))
429 perror("PTRACE_CONT");
433 } while (waiting_threads>0);
435 collect_garbage(maybe_gc_pending-1);
439 if(ptrace(PTRACE_DETACH,th->pid,0,0))
440 perror("PTRACE_DETACH");
443 static void /* noreturn */ parent_loop(void)
450 sigemptyset(&sigset);
452 sigaddset(&sigset, SIGALRM);
453 sigaddset(&sigset, SIGCHLD);
454 sigprocmask(SIG_UNBLOCK,&sigset,0);
455 sa.sa_handler=parent_sighandler;
457 sa.sa_flags=SA_SIGINFO;
458 sigaction(SIGALRM, &sa, 0);
459 sigaction(SIGCHLD, &sa, 0);
461 sigemptyset(&sigset);
462 sa.sa_handler=SIG_IGN;
465 sigaction(SIGINT, &sa, 0);
467 while(!all_threads) {
471 while(all_threads && (pid=waitpid(-1,&status,__WALL|WUNTRACED))) {
473 int real_errno=errno;
474 while(maybe_gc_pending) parent_do_garbage_collect();
476 if(real_errno == EINTR) {
479 if(real_errno == ECHILD) break;
480 fprintf(stderr,"waitpid: %s\n",strerror(real_errno));
483 th=find_thread_by_pid(pid);
485 if(WIFEXITED(status) || WIFSIGNALED(status)) {
486 fprintf(stderr,"waitpid : child %d %x exited \n", pid,th);
488 /* FIXME arrange to call or fake (free-mutex *session-lock*)
490 if(!all_threads) break;
493 exit(WEXITSTATUS(status));