proc.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. /*
  2. * Part of Scheme 48 1.9. See file COPYING for notices and license.
  3. *
  4. * Authors: Richard Kelsey, Jonathan Rees, Mike Sperber, Marcus Crestani,
  5. * Robert Ransom, Will Noble, Roderic Morris
  6. */
  7. /*
  8. * Scheme 48/POSIX process environment interface
  9. */
  10. #define _GNU_SOURCE
  11. #include <stdio.h>
  12. #include <errno.h>
  13. #include <string.h>
  14. #include <signal.h>
  15. #include <unistd.h>
  16. #include <stdlib.h>
  17. #include <sys/types.h>
  18. #include <sys/wait.h>
  19. #include "c-mods.h"
  20. #include "scheme48.h"
  21. #include "scheme48vm.h"
  22. #include "event.h"
  23. #include "posix.h"
  24. #include "unix.h"
  25. #include "sysdep.h"
  26. /*
  27. * Mapping from our `canonical' signal numbers to the local OS's
  28. * numbers. To avoid having to manually keep the values here in sync
  29. * with the NAMED-SIGNALS finite record type, we generate the values
  30. * using a Scheme program.
  31. */
  32. static int signal_map[] = {
  33. #include "s48_signals.h"
  34. };
  35. extern void s48_init_posix_proc(void),
  36. s48_uninit_posix_proc(void);
  37. static s48_ref_t posix_fork(s48_call_t call),
  38. posix_exec(s48_call_t call, s48_ref_t program, s48_ref_t lookup_p,
  39. s48_ref_t env, s48_ref_t args),
  40. posix_waitpid(s48_call_t call),
  41. posix_initialize_named_signals(s48_call_t call),
  42. posix_request_interrupts(s48_call_t call, s48_ref_t int_number),
  43. posix_cancel_interrupt_request(s48_call_t call, s48_ref_t sch_signal),
  44. posix_kill(s48_call_t call, s48_ref_t sch_pid, s48_ref_t sch_signal);
  45. static void cancel_interrupt_requests(void);
  46. static char **enter_byte_vector_array(s48_call_t call, s48_ref_t strings);
  47. /*
  48. * Vector of Scheme signal objects imported from Scheme, and a marker that
  49. * is put in unnamed signals.
  50. */
  51. static s48_ref_t posix_signals_vector_binding;
  52. /*
  53. * Queue of received interrupts that need to be passed on to Scheme.
  54. * Kept in a finite array to avoid consing.
  55. */
  56. /*
  57. * Install all exported functions in Scheme48.
  58. */
  59. void
  60. s48_init_posix_proc(void)
  61. {
  62. S48_EXPORT_FUNCTION(posix_fork);
  63. S48_EXPORT_FUNCTION(posix_exec);
  64. S48_EXPORT_FUNCTION(posix_waitpid);
  65. S48_EXPORT_FUNCTION(posix_initialize_named_signals);
  66. S48_EXPORT_FUNCTION(posix_request_interrupts);
  67. S48_EXPORT_FUNCTION(posix_cancel_interrupt_request);
  68. S48_EXPORT_FUNCTION(posix_kill);
  69. posix_signals_vector_binding =
  70. s48_get_imported_binding_2("posix-signals-vector");
  71. }
  72. void
  73. s48_uninit_posix_proc(void)
  74. {
  75. /* this will lose our signal handlers without reinstalling them; too bad */
  76. cancel_interrupt_requests();
  77. }
  78. /*
  79. * Waiting for children. We get finished pid's until we reach one for which
  80. * there is a Scheme pid record. The exit status or terminating signal is
  81. * saved in the record which is then returned.
  82. *
  83. * This does not looked for stopped children, only terminated ones.
  84. */
  85. static s48_ref_t
  86. posix_waitpid(s48_call_t call)
  87. {
  88. int status;
  89. pid_t pid;
  90. s48_ref_t result;
  91. retry:
  92. pid = waitpid(-1, &status, WNOHANG);
  93. if (pid < 0) {
  94. if (errno == EINTR) goto retry;
  95. else if (errno == ECHILD) return s48_false_2(call);
  96. else s48_os_error_2(call, "posix_waitpid", errno, 0);
  97. } else if (pid == 0) {
  98. return s48_false_2(call); /* no statuses available now */
  99. }
  100. result = s48_make_vector_2(call, 3, s48_false_2(call));
  101. s48_unsafe_vector_set_2(call, result, 0, s48_enter_long_2(call, pid));
  102. if (WIFEXITED(status))
  103. s48_unsafe_vector_set_2(call, result, 1,
  104. s48_enter_long_2(call, WEXITSTATUS(status)));
  105. else
  106. s48_unsafe_vector_set_2(call, result, 2,
  107. s48_enter_long_2(call, WTERMSIG(status)));
  108. return result;
  109. }
  110. /*
  111. * Fork and exec.
  112. */
  113. static s48_ref_t
  114. posix_fork(s48_call_t call)
  115. {
  116. pid_t pid = fork();
  117. if (pid < 0)
  118. s48_os_error_2(call, "posix_fork", errno, 0);
  119. return s48_enter_long_2(call, pid);
  120. }
  121. #ifndef HAVE_EXECVPE
  122. static int execvpe(const char *file, char **argv, char **env) {
  123. char *path, *buf;
  124. int path_len, file_len;
  125. path = getenv("PATH");
  126. if (path == NULL) path = "/bin:/usr/bin";
  127. else if (*path == '\0') path = ".";
  128. path_len = strlen(path);
  129. file_len = strlen(file);
  130. buf = malloc(path_len + file_len + 2);
  131. if (buf == NULL)
  132. s48_out_of_memory_error();
  133. while (*path) {
  134. int len;
  135. char *colon = strchr(path, ':');
  136. if (path == colon) {
  137. path++;
  138. path_len--;
  139. continue;
  140. }
  141. len = (colon == NULL) ? path_len : (colon - path);
  142. memcpy(buf, path, len);
  143. buf[len] = '/';
  144. memcpy(buf + len + 1, file, file_len);
  145. buf[len + file_len + 1] = '\0';
  146. execve(buf, argv, env);
  147. if (errno == EACCES || errno == ENOENT || errno == ENOTDIR) {
  148. path_len -= len;
  149. path += len;
  150. } else {
  151. /* File accessible but failed to execute */
  152. break;
  153. }
  154. }
  155. free(buf);
  156. return -1;
  157. }
  158. #endif /* HAVE_EXECVPE */
  159. /*
  160. * The environment is an array of strings of the form "name=value", where
  161. * `name' cannot contain `='.
  162. */
  163. static s48_ref_t
  164. posix_exec(s48_call_t call, s48_ref_t program, s48_ref_t lookup_p,
  165. s48_ref_t env, s48_ref_t args)
  166. {
  167. char **c_args = enter_byte_vector_array(call, args);
  168. char *c_program;
  169. int status;
  170. c_program = s48_extract_byte_vector_readonly_2(call, program);
  171. s48_stop_alarm_interrupts();
  172. if (s48_false_p_2(call, env)) {
  173. if (s48_false_p_2(call, lookup_p))
  174. status = execv(c_program, c_args);
  175. else
  176. status = execvp(c_program, c_args);
  177. }
  178. else {
  179. char **c_env = enter_byte_vector_array(call, env);
  180. if (s48_false_p_2(call, lookup_p) || strchr(c_program, '/'))
  181. status = execve(c_program, c_args, c_env);
  182. else
  183. status = execvpe(c_program, c_args, c_env);
  184. free(c_env);
  185. }
  186. /* If we get here, then something has gone wrong. */
  187. free(c_args);
  188. s48_start_alarm_interrupts();
  189. s48_os_error_2(call, "posix_exec", errno, 0);
  190. /* appease gcc -Wall */
  191. return s48_false_2(call);
  192. }
  193. /*
  194. * Convert a list of byte vectors into an array of char pointers.
  195. */
  196. static char **
  197. enter_byte_vector_array(s48_call_t call, s48_ref_t vectors)
  198. {
  199. int length = s48_unsafe_extract_long_2(call, s48_length_2(call, vectors));
  200. char **result = (char **)malloc((length + 1) * sizeof(char *));
  201. int i;
  202. if (result == NULL)
  203. s48_out_of_memory_error();
  204. for(i = 0; i < length; i++, vectors = s48_unsafe_cdr_2(call, vectors)) {
  205. s48_ref_t vector = s48_unsafe_car_2(call, vectors);
  206. if (! s48_byte_vector_p_2(call, vector)) {
  207. free(result);
  208. s48_assertion_violation_2(call, NULL, "not a byte vector", 1, vector); }
  209. result[i] = s48_extract_byte_vector_readonly_2(call, vector); }
  210. result[length] = NULL;
  211. return result;
  212. }
  213. /*
  214. * Signals
  215. */
  216. /*
  217. * Simple front for kill(). We have to retry if interrupted.
  218. */
  219. s48_ref_t
  220. posix_kill(s48_call_t call, s48_ref_t pid, s48_ref_t signal)
  221. {
  222. int status;
  223. RETRY_OR_RAISE_NEG(status,
  224. kill(s48_extract_long_2(call, pid),
  225. s48_extract_long_2(call, signal)));
  226. return s48_unspecific_2(call);
  227. }
  228. /*
  229. * Use the signal map to set the os-number slot in each named signal to
  230. * its value in the current OS.
  231. */
  232. static s48_ref_t
  233. posix_initialize_named_signals(s48_call_t call)
  234. {
  235. int i, length;
  236. s48_ref_t named_signals;
  237. s48_shared_binding_check_2(call, posix_signals_vector_binding);
  238. named_signals = s48_shared_binding_ref_2(call, posix_signals_vector_binding);
  239. if(! s48_vector_p_2(call, named_signals))
  240. s48_assertion_violation_2(call,
  241. "posix_initialize_named_signals", "not a vector", 1,
  242. named_signals);
  243. length = s48_unsafe_vector_length_2(call, named_signals);
  244. for(i = 0; i < length; i++) {
  245. s48_ref_t signal = s48_unsafe_vector_ref_2(call, named_signals, i);
  246. int canonical = s48_extract_long_2(call, s48_unsafe_record_ref_2(call, signal, 1));
  247. int c_signal = signal_map[canonical];
  248. s48_ref_t scm_signal = (c_signal == -1) ?
  249. s48_false_2(call) :
  250. s48_enter_long_2(call, c_signal);
  251. s48_unsafe_record_set_2(call, signal, 2, scm_signal); }
  252. return s48_unspecific_2(call);
  253. }
  254. /*
  255. * Queue the interrupt. For SIGINT and SIGALRM we call the event-system's
  256. * handler as well.
  257. */
  258. static void
  259. generic_interrupt_catcher(int signum)
  260. {
  261. extern void s48_add_os_signal(long);
  262. s48_add_os_signal(signum);
  263. switch (signum) {
  264. case SIGINT: {
  265. s48_when_keyboard_interrupt(0);
  266. break; }
  267. case SIGALRM: {
  268. s48_when_alarm_interrupt(0);
  269. break; }
  270. case SIG_EXTERNAL_EVENT: {
  271. s48_when_external_event_interrupt(0);
  272. break; }
  273. default:
  274. NOTE_EVENT; }
  275. return;
  276. }
  277. /*
  278. * Array of actions to be restored when we no longer listen for a signal.
  279. */
  280. #define MAX_SIGNAL 1023 /* Just a guess. */
  281. struct sigaction *saved_actions[MAX_SIGNAL + 1] = {NULL};
  282. /*
  283. * If there is a saved action then our handler is already in place and
  284. * we need do nothing. Otherwise we save the current action and install
  285. * our own.
  286. */
  287. s48_ref_t
  288. posix_request_interrupts(s48_call_t call, s48_ref_t sch_signum)
  289. {
  290. int signum = s48_extract_long_2(call, sch_signum);
  291. struct sigaction sa;
  292. if (saved_actions[signum] == NULL) {
  293. struct sigaction * old = (struct sigaction *)
  294. malloc(sizeof(struct sigaction));
  295. if (old == NULL)
  296. s48_out_of_memory_error();
  297. sa.sa_handler = generic_interrupt_catcher;
  298. sigfillset(&sa.sa_mask);
  299. sa.sa_flags = 0;
  300. if (sigaction(signum, &sa, old) != 0) {
  301. free(old);
  302. s48_os_error_2(call, "posix_request_interrupts", errno, 1, sch_signum); }
  303. saved_actions[signum] = old; }
  304. return s48_unspecific_2(call);
  305. }
  306. /*
  307. * The reverse of the above. If there is a saved action then we install it
  308. * and remove it from the saved_action array.
  309. */
  310. static void
  311. cancel_interrupt_request(int signum)
  312. {
  313. struct sigaction * old = saved_actions[signum];
  314. if (old != NULL)
  315. {
  316. if (sigaction(signum, old, (struct sigaction *) NULL) != 0)
  317. /* THIS IS STILL OLD FFI! FIX THIS! */
  318. s48_os_error_2(NULL, NULL, errno, 1, s48_enter_fixnum(signum));
  319. free(old);
  320. saved_actions[signum] = NULL;
  321. }
  322. }
  323. s48_ref_t
  324. posix_cancel_interrupt_request(s48_call_t call, s48_ref_t sch_signum)
  325. {
  326. cancel_interrupt_request(s48_extract_long_2(call, sch_signum));
  327. return s48_unspecific_2(call);
  328. }
  329. static void
  330. cancel_interrupt_requests(void)
  331. {
  332. int signum = 0;
  333. while (signum <= MAX_SIGNAL)
  334. {
  335. cancel_interrupt_request(signum);
  336. ++signum;
  337. }
  338. }