123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722 |
- /* Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees.
- See file COPYING. */
- /*
- * Scheme 48/POSIX process environment interface
- */
- #include <stdio.h>
- #include <errno.h>
- #include <string.h>
- #include <signal.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include "c-mods.h"
- #include "scheme48.h"
- #include "scheme48vm.h"
- #include "event.h"
- #include "posix.h"
- #include "unix.h"
- extern void s48_init_posix_proc(void),
- s48_uninit_posix_proc(void);
- static s48_ref_t posix_fork(s48_call_t call),
- posix_exec(s48_call_t call, s48_ref_t program, s48_ref_t lookup_p,
- s48_ref_t env, s48_ref_t args),
- posix_enter_pid(s48_call_t call, s48_ref_t pid),
- posix_waitpid(s48_call_t call),
- posix_integer_to_signal(s48_call_t call, s48_ref_t sig_int),
- posix_initialize_named_signals(s48_call_t call),
- posix_request_interrupts(s48_call_t call, s48_ref_t int_number),
- posix_cancel_interrupt_request(s48_call_t call, s48_ref_t sch_signal),
- posix_kill(s48_call_t call, s48_ref_t sch_pid, s48_ref_t sch_signal);
- static s48_ref_t enter_signal(s48_call_t call, int signal);
- static int extract_signal(s48_call_t call, s48_ref_t sch_signal);
- static void signal_map_init(void);
- static void signal_map_uninit(void);
- static void cancel_interrupt_requests(void);
- static char **enter_byte_vector_array(s48_call_t call, s48_ref_t strings),
- *add_dot_slash(char *name);
- /*
- * Two lists, one with all the child process ids and the other with all the
- * unnamed signals. Each CAR is a weak pointer to the actual object.
- *
- * We also have a handy procedure for lookup up values in the lists.
- *
- * These are in C instead of Scheme to prevent them from being written out in
- * images.
- */
- static s48_ref_t child_pids;
- static s48_ref_t unnamed_signals;
- s48_ref_t s48_lookup_record(s48_call_t call, s48_ref_t *list_loc, int offset, s48_ref_t key);
- /*
- * Record types imported from Scheme.
- */
- static s48_ref_t posix_process_id_type_binding;
- static s48_ref_t posix_named_signal_type_binding;
- static s48_ref_t posix_unnamed_signal_type_binding;
- /*
- * Vector of Scheme signal objects imported from Scheme, and a marker that
- * is put in unnamed signals.
- */
- static s48_ref_t posix_signals_vector_binding;
- static s48_ref_t posix_unnamed_signal_marker_binding;
- /*
- * Queue of received interrupts that need to be passed on to Scheme.
- * Kept in a finite array to avoid consing.
- */
- /*
- * Install all exported functions in Scheme48.
- */
- void
- s48_init_posix_proc(void)
- {
- S48_EXPORT_FUNCTION(posix_fork);
- S48_EXPORT_FUNCTION(posix_exec);
- S48_EXPORT_FUNCTION(posix_enter_pid);
- S48_EXPORT_FUNCTION(posix_waitpid);
- S48_EXPORT_FUNCTION(posix_integer_to_signal);
- S48_EXPORT_FUNCTION(posix_initialize_named_signals);
- S48_EXPORT_FUNCTION(posix_request_interrupts);
- S48_EXPORT_FUNCTION(posix_cancel_interrupt_request);
- S48_EXPORT_FUNCTION(posix_kill);
- posix_process_id_type_binding =
- s48_get_imported_binding_2("posix-process-id-type");
- posix_named_signal_type_binding =
- s48_get_imported_binding_2("posix-named-signal-type");
- posix_unnamed_signal_type_binding =
- s48_get_imported_binding_2("posix-unnamed-signal-type");
- posix_signals_vector_binding =
- s48_get_imported_binding_2("posix-signals-vector");
- posix_unnamed_signal_marker_binding =
- s48_get_imported_binding_2("posix-unnamed-signal-marker");
- child_pids = s48_make_global_ref(_s48_value_null);
- unnamed_signals = s48_make_global_ref(_s48_value_null);
- signal_map_init();
- }
- void
- s48_uninit_posix_proc(void)
- {
- /* this will lose our signal handlers without reinstalling them; too bad */
- cancel_interrupt_requests();
- signal_map_uninit();
- }
- /*
- * Box a process id in a Scheme record.
- */
- static s48_ref_t
- make_pid(s48_call_t call, pid_t c_pid)
- {
- s48_ref_t weak, temp;
- s48_ref_t sch_pid = s48_make_record_2(call, posix_process_id_type_binding);
- s48_unsafe_record_set_2(call, sch_pid, 0, s48_enter_long_2(call, c_pid));
- s48_unsafe_record_set_2(call, sch_pid, 1, s48_false_2(call)); /* return status */
- s48_unsafe_record_set_2(call, sch_pid, 2, s48_false_2(call)); /* terminating signal */
- s48_unsafe_record_set_2(call, sch_pid, 3, s48_false_2(call)); /* placeholder for waiting threads */
- weak = s48_make_weak_pointer_2(call, sch_pid);
- temp = child_pids;
- child_pids = s48_local_to_global_ref(s48_cons_2(call, weak, child_pids));
- s48_free_global_ref(temp);
- return sch_pid;
- }
- /*
- * Lookup a pid in the list of same. We clear out any dropped weak pointers
- * on the way.
- */
- static s48_ref_t
- lookup_pid(s48_call_t call, pid_t c_pid)
- {
- return s48_lookup_record(call, &child_pids, 0, s48_enter_long_2(call, c_pid));
- }
- /*
- * Lookup a record on a list of weak pointers to same. We get a value and
- * the record offset at which to look for the value. Any dropped pointers
- * are cleared out along the way. If any have been seen we walk the entire
- * list to clear them all out.
- *
- * This is too much C code! It should all be done in Scheme.
- */
- s48_ref_t
- s48_lookup_record(s48_call_t call, s48_ref_t *the_list_loc, int offset, s48_ref_t key)
- {
- int cleanup_p = 0;
- s48_ref_t the_list = *the_list_loc;
- /* Clear out initial dropped weaks */
- while (!s48_null_p_2(call, the_list) &&
- s48_false_p_2(call,
- s48_unsafe_weak_pointer_ref_2(call, s48_unsafe_car_2(call, the_list))))
- the_list = s48_unsafe_cdr_2(call, the_list);
- if (the_list != *the_list_loc) {
- s48_free_global_ref(*the_list_loc);
- the_list = s48_local_to_global_ref(the_list);
- *the_list_loc = the_list;
- cleanup_p = 1; }
- if (s48_null_p_2(call, the_list))
- return s48_false_2(call); /* Nothing */
- {
- s48_ref_t first = s48_unsafe_weak_pointer_ref_2(call, s48_unsafe_car_2(call, the_list));
- if (s48_eq_p_2(call, key, s48_unsafe_record_ref_2(call, first, offset)))
- /* Found it first thing. We skip the cleanup, but so what. */
- return first;
- {
- /* Loop down. */
- s48_ref_t found = s48_false_2(call);
- s48_ref_t prev = the_list;
- s48_ref_t next = s48_unsafe_cdr_2(call, prev);
- for(; !s48_null_p_2(call, next) && s48_false_p_2(call, found);
- next = s48_unsafe_cdr_2(call, prev)) {
- s48_ref_t first = s48_unsafe_weak_pointer_ref_2(call, s48_unsafe_car_2(call, next));
- if (s48_false_p_2(call, first)) {
- s48_unsafe_set_cdr_2(call, prev, s48_unsafe_cdr_2(call, next));
- cleanup_p = 1; }
- else if (s48_eq_p_2(call, key, s48_unsafe_record_ref_2(call, first, offset)))
- found = first;
- else
- prev = next; }
-
- /* If we found any empty weaks we check the entire list for them. */
-
- if (cleanup_p) {
-
- for(; !s48_null_p_2(call, next); next = s48_unsafe_cdr_2(call, next)) {
- s48_ref_t first = s48_unsafe_weak_pointer_ref_2(call, s48_unsafe_car_2(call, next));
- if (s48_false_p_2(call, first))
- s48_unsafe_set_cdr_2(call, prev, s48_unsafe_cdr_2(call, next)); } }
-
- return found; } }
- }
- /*
- * If we already have this process, return it, else make a new one.
- */
- s48_ref_t
- s48_enter_pid(s48_call_t call, pid_t c_pid)
- {
- s48_ref_t sch_pid = lookup_pid(call, c_pid);
- return s48_false_p_2(call, sch_pid) ? make_pid(call, c_pid) : sch_pid;
- }
- /*
- * Version of above for calling from Scheme.
- */
- static s48_ref_t
- posix_enter_pid(s48_call_t call, s48_ref_t sch_pid)
- {
- return s48_enter_pid(call, s48_extract_long_2(call, sch_pid));
- }
- /*
- * Waiting for children. We get finished pid's until we reach one for which
- * there is a Scheme pid record. The exit status or terminating signal is
- * saved in the record which is then returned.
- *
- * This does not looked for stopped children, only terminated ones.
- */
- static s48_ref_t
- posix_waitpid(s48_call_t call)
- {
- while(1==1) {
- int stat;
- pid_t c_pid = waitpid(-1, &stat, WNOHANG);
- if (c_pid == -1) {
- if (errno == ECHILD) /* no one left to wait for */
- return s48_false_2(call);
- else if (errno != EINTR)
- s48_os_error_2(call, "posix_waitpid", errno, 0);
- }
- else {
- s48_ref_t sch_pid = lookup_pid(call, c_pid);
- s48_ref_t temp = s48_unspecific_2(call);
- if (!s48_false_p_2(call, sch_pid)) {
- if (WIFEXITED(stat))
- s48_unsafe_record_set_2(call, sch_pid, 1, s48_enter_long_2(call, WEXITSTATUS(stat)));
- else {
- temp = enter_signal(call, WTERMSIG(stat));
- s48_unsafe_record_set_2(call, sch_pid, 2, temp);
- }
- return sch_pid;
- }
- }
- }
- }
- /*
- * Fork and exec.
- */
- static s48_ref_t
- posix_fork(s48_call_t call)
- {
- pid_t child_pid = fork();
- if (child_pid < 0)
- s48_os_error_2(call, "posix_fork", errno, 0);
- if (child_pid == 0)
- return s48_false_2(call);
- else
- return make_pid(call, child_pid);
- }
- /*
- * The environment is an array of strings of the form "name=value", where
- * `name' cannot contain `='.
- *
- * It is a nuisance that given three binary choices (arguments explicit or
- * in a vector, path lookup or not, explicit or implicit environment) Posix
- * only gives six functions. The two calls that have an explict environment
- * both do path lookup. We work around this by adding `./' to the beginning
- * of the program, if it does not already contain a `/'.
- */
- static s48_ref_t
- posix_exec(s48_call_t call, s48_ref_t program, s48_ref_t lookup_p,
- s48_ref_t env, s48_ref_t args)
- {
- char **c_args = enter_byte_vector_array(call, args);
- char *c_program, *real_c_program;
- int status;
- c_program = s48_extract_byte_vector_readonly_2(call, program);
- s48_stop_alarm_interrupts();
- if (s48_false_p_2(call, env))
- if (s48_false_p_2(call, lookup_p))
- status = execv(c_program, c_args);
- else {
- status = execvp(c_program, c_args);
- }
- else {
- char **c_env = enter_byte_vector_array(call, env);
-
- if (NULL == strchr(c_program, '/'))
- real_c_program = add_dot_slash(c_program);
- else
- real_c_program = c_program;
- status = execve(c_program, c_args, c_env);
- free(c_env);
- if (real_c_program != c_program)
- free(real_c_program); }
- /* If we get here, then something has gone wrong. */
- free(c_args);
- s48_start_alarm_interrupts();
- s48_os_error_2(call, "posix_exec", errno, 0);
- /* appease gcc -Wall */
- return s48_false_2(call);
- }
- /*
- * Convert a list of byte vectors into an array of char pointers.
- */
- static char **
- enter_byte_vector_array(s48_call_t call, s48_ref_t vectors)
- {
- int length = s48_unsafe_extract_long_2(call, s48_length_2(call, vectors));
- char **result = (char **)malloc((length + 1) * sizeof(char *));
- int i;
- if (result == NULL)
- s48_out_of_memory_error();
-
- for(i = 0; i < length; i++, vectors = s48_unsafe_cdr_2(call, vectors)) {
- s48_ref_t vector = s48_unsafe_car_2(call, vectors);
- if (! s48_byte_vector_p_2(call, vector)) {
- free(result);
- s48_assertion_violation_2(call, NULL, "not a byte vector", 1, vector); }
- result[i] = s48_extract_byte_vector_readonly_2(call, vector); }
- result[length] = NULL;
- return result;
- }
-
- /*
- * Add `./' to the beginning of `name'.
- */
- static char *
- add_dot_slash(char *name)
- {
- int len = strlen(name);
- char *new_name = (char *)malloc((len + 1) * sizeof(char));
-
- if (new_name == NULL)
- s48_out_of_memory_error();
-
- new_name[0] = '.';
- new_name[1] = '/';
- strcpy(new_name + 2, name);
- return new_name;
- }
- /*
- * Signals
- */
- /*
- * Simple front for kill(). We have to retry if interrupted.
- */
- s48_ref_t
- posix_kill(s48_call_t call, s48_ref_t sch_pid, s48_ref_t sch_signal)
- {
- int status;
- s48_check_record_type_2(call, sch_pid, posix_process_id_type_binding);
- RETRY_OR_RAISE_NEG(status,
- kill(s48_extract_long_2(call, s48_unsafe_record_ref_2(call, sch_pid, 0)),
- extract_signal(call, sch_signal)));
- return s48_unspecific_2(call);
- }
- /*
- * This is an array that maps our `canonical' signal numbers to the local
- * OS's numbers. The initialization is done via an include file written
- * by a Scheme program. The include file first calls signal_count_is()
- * with the number of named signals and then adds the named signals supported
- * by the current os to `signal_map'.
- */
- static int *signal_map, signal_map_size;
- static void
- signal_count_is(int count)
- {
- int i;
- signal_map_size = count;
- signal_map = (int *) malloc(count * sizeof(int));
- if (signal_map == NULL) {
- fprintf(stderr, "ran out of memory during initialization\n");
- exit(1); }
- for (i = 0; i < count; i++)
- signal_map[i] = -1;
- }
-
- static void
- signal_map_init()
- {
- #include "s48_signals.h"
- }
- static void
- signal_map_uninit(void)
- {
- free(signal_map);
- }
- /*
- * Converts from an OS signal to a canonical signal number.
- * We return -1 if there is no matching named signal.
- */
- static int
- lookup_signal(int c_signal) {
- int i = 0;
- for (i = 0; i < signal_map_size; i++)
- if (signal_map[i] == c_signal)
- return i;
- return -1;
- }
- /*
- * Use the signal map to set the os-number slot in each named signal to
- * its value in the current OS.
- */
- static s48_ref_t
- posix_initialize_named_signals(s48_call_t call)
- {
- int i, length;
- s48_ref_t named_signals;
- s48_shared_binding_check_2(call, posix_signals_vector_binding);
- named_signals = s48_shared_binding_ref_2(call, posix_signals_vector_binding);
- if(! s48_vector_p_2(call, named_signals))
- s48_assertion_violation_2(call,
- "posix_initialize_named_signals", "not a vector", 1,
- named_signals);
-
- length = s48_unsafe_vector_length_2(call, named_signals);
- for(i = 0; i < length; i++) {
- s48_ref_t signal = s48_unsafe_vector_ref_2(call, named_signals, i);
- int canonical = s48_extract_long_2(call, s48_unsafe_record_ref_2(call, signal, 1));
- int c_signal = signal_map[canonical];
- s48_ref_t scm_signal = (c_signal == -1) ?
- s48_false_2(call) :
- s48_enter_long_2(call, c_signal);
-
- s48_unsafe_record_set_2(call, signal, 2, scm_signal); }
- return s48_unspecific_2(call);
- }
- /*
- * Make a new unnamed signal containing `fx_signal' and add it to the weak
- * list of unnamed signals.
- */
- static s48_ref_t
- make_unnamed_signal(s48_call_t call, s48_ref_t fx_signal)
- {
- s48_ref_t weak, temp;
- s48_ref_t unnamed = s48_make_record_2(call, posix_unnamed_signal_type_binding);
- s48_unsafe_record_set_2(call,
- unnamed,
- 0,
- s48_unsafe_shared_binding_ref_2(call,
- posix_unnamed_signal_marker_binding));
- s48_unsafe_record_set_2(call, unnamed, 1, fx_signal);
- s48_unsafe_record_set_2(call, unnamed, 2, s48_null_2(call)); /* No queues */
- weak = s48_make_weak_pointer_2(call, unnamed);
- temp = unnamed_signals;
- unnamed_signals = s48_local_to_global_ref(s48_cons_2(call, weak, unnamed_signals));
- s48_free_global_ref(temp);
- return unnamed;
- }
- /*
- * Returns a signal record for `signal'. Unnamed signals are looked up in
- * the weak list of same; if none is found we make one. Scheme records for
- * named signals are retrieved from a vector sent down by the Scheme code.
- */
- static s48_ref_t
- enter_signal(s48_call_t call, int c_signal)
- {
- int canonical = lookup_signal(c_signal);
- if (canonical == -1) {
- s48_ref_t fx_signal = s48_enter_long_2(call, c_signal);
- s48_ref_t unnamed = s48_lookup_record(call, &unnamed_signals, 1, fx_signal);
-
- if (!s48_false_p_2(call, unnamed))
- return unnamed;
- else
- return make_unnamed_signal(call, fx_signal); }
- else
- return s48_vector_ref_2(call,
- s48_shared_binding_ref_2(call, posix_signals_vector_binding),
- canonical);
- }
- /*
- * Wrapper for enter_signal() for calling from Scheme.
- */
- static s48_ref_t
- posix_integer_to_signal(s48_call_t call, s48_ref_t signal_int)
- {
- if (s48_fixnum_p_2(call, signal_int))
- return enter_signal(call, s48_extract_long_2(call, signal_int));
- else
- /* really should do an integer? test here */
- return s48_false_2(call);
- }
- /*
- * Go from a signal back to the local integer. For named signals we extract
- * the canonical signal to use as an index into the signal map. Unnamed signals
- * contain the local signal already.
- */
- static int
- extract_signal(s48_call_t call, s48_ref_t sch_signal)
- {
- s48_ref_t type;
- if (! s48_record_p_2(call, sch_signal))
- s48_assertion_violation_2(call, NULL, "not a record", 1, sch_signal);
- type = s48_unsafe_record_type_2(call, sch_signal);
- if (s48_eq_p_2(call, type, s48_unsafe_shared_binding_ref_2(call, posix_named_signal_type_binding))) {
- int canonical = s48_extract_long_2(call, s48_unsafe_record_ref_2(call, sch_signal, 1));
- if ((0 <= canonical) && (canonical < signal_map_size)
- && signal_map[canonical] != -1)
- return signal_map[canonical];
- else
- s48_assertion_violation_2(call, NULL, "not a valid signal index", 1, sch_signal); }
- else if (s48_eq_p_2(call, type,
- s48_unsafe_shared_binding_ref_2(call, posix_unnamed_signal_type_binding)))
- return s48_extract_long_2(call, s48_unsafe_record_ref_2(call, sch_signal, 1));
- else
- s48_assertion_violation_2(call, NULL, "not a signal", 1, sch_signal);
- }
- /*
- * Queue the interrupt. For SIGINT and SIGALRM we call the event-system's
- * handler as well.
- */
- static void
- generic_interrupt_catcher(int signum)
- {
- extern void s48_add_os_signal(long);
- s48_add_os_signal(signum);
- switch (signum) {
- case SIGINT: {
- s48_when_keyboard_interrupt(0);
- break; }
- case SIGALRM: {
- s48_when_alarm_interrupt(0);
- break; }
- case SIG_EXTERNAL_EVENT: {
- s48_when_external_event_interrupt(0);
- break; }
- default:
- NOTE_EVENT; }
-
- return;
- }
- /*
- * Array of actions to be restored when we no longer listen for a signal.
- */
- #define MAX_SIGNAL 1023 /* Just a guess. */
- struct sigaction *saved_actions[MAX_SIGNAL + 1] = {NULL};
- /*
- * If there is a saved action then our handler is already in place and
- * we need do nothing. Otherwise we save the current action and install
- * our own.
- */
- s48_ref_t
- posix_request_interrupts(s48_call_t call, s48_ref_t sch_signum)
- {
- int signum = s48_extract_long_2(call, sch_signum);
- struct sigaction sa;
- if (saved_actions[signum] == NULL) {
- struct sigaction * old = (struct sigaction *)
- malloc(sizeof(struct sigaction));
-
- if (old == NULL)
- s48_out_of_memory_error();
- sa.sa_handler = generic_interrupt_catcher;
- sigfillset(&sa.sa_mask);
- sa.sa_flags = 0;
- if (sigaction(signum, &sa, old) != 0) {
- free(old);
- s48_os_error_2(call, "posix_request_interrupts", errno, 1, sch_signum); }
- saved_actions[signum] = old; }
-
- return s48_unspecific_2(call);
- }
- /*
- * The reverse of the above. If there is a saved action then we install it
- * and remove it from the saved_action array.
- */
- static void
- cancel_interrupt_request(int signum)
- {
- struct sigaction * old = saved_actions[signum];
- if (old != NULL)
- {
-
- if (sigaction(signum, old, (struct sigaction *) NULL) != 0)
- /* THIS IS STILL OLD FFI! FIX THIS! */
- s48_os_error_2(NULL, NULL, errno, 1, s48_enter_fixnum(signum));
-
- free(old);
- saved_actions[signum] = NULL;
- }
- }
- s48_ref_t
- posix_cancel_interrupt_request(s48_call_t call, s48_ref_t sch_signum)
- {
- cancel_interrupt_request(s48_extract_long_2(call, sch_signum));
- return s48_unspecific_2(call);
- }
- static void
- cancel_interrupt_requests(void)
- {
- int signum = 0;
- while (signum <= MAX_SIGNAL)
- {
- cancel_interrupt_request(signum);
- ++signum;
- }
- }
|