signal.scm 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  2. ; Authors: Richard Kelsey, Jonathan Rees, Mike Sperber, Marcus Crestani,
  3. ; Roderic Morris, Will Noble
  4. ; 3.3 Signals
  5. (import-dynamic-externals "=scheme48external/posix")
  6. ; int kill(pid_t pid, int sig)
  7. ;
  8. ; Also signal sets, sigaction(), blocked and waiting signals,
  9. ; alarm(), pause(), sleep().
  10. ;
  11. ; There are two signal record types, one for signals whose names we know and
  12. ; one for those which are anonymous. The anonymous signals cannot be dumped
  13. ; in images, because we have no way of knowing if they will have the same
  14. ; meaning on startup.
  15. ;
  16. ; Only the C code can determine which names correspond to signals in the current
  17. ; OS, and then only at compile time by doing
  18. ; #ifdef SIGSTKFLT /* or whatever the name is */
  19. ; ...
  20. ; #endif
  21. ; So what we do is construct a C file that checks for the presence of all of the
  22. ; signals we have ever heard of. Any others have to be anonymous and cannot be
  23. ; saved in images.
  24. (define-record-type unnamed-signal :unnamed-signal
  25. (make-unnamed-signal resume-value os-number queues)
  26. unnamed-signal?
  27. (resume-value unnamed-signal-resume-value)
  28. (os-number unnamed-signal-os-number)
  29. (queues unnamed-signal-queues set-unnamed-signal-queues!))
  30. (define-record-discloser :unnamed-signal
  31. (lambda (u-s)
  32. (list 'signal (unnamed-signal-os-number u-s))))
  33. ; These are not meaningful after a dump (because the value may not have the
  34. ; same meaning on the OS on which we are resumed).
  35. (define-record-resumer :unnamed-signal #f)
  36. (define *unnamed-signals* #f)
  37. (define-finite-type signal :named-signal
  38. (queues)
  39. named-signal?
  40. named-signals
  41. named-signal-name
  42. named-signal-index
  43. (os-number named-signal-os-number set-named-signal-os-number!)
  44. (queues named-signal-queues set-named-signal-queues!)
  45. (; POSIX
  46. (abrt '()) ; abort - abnormal termination (as by abort())
  47. (alrm '()) ; alarm - timeout signal (as by alarm())
  48. (fpe '()) ; floating point exception
  49. (hup '()) ; hangup - hangup on controlling terminal or death of
  50. ; controlling process
  51. (ill '()) ; illegal instruction
  52. (int '()) ; interrupt - interaction attention
  53. (kill '()) ; kill - termination signal, cannot be caught or ignored
  54. (pipe '()) ; pipe - write on a pipe with no readers
  55. (quit '()) ; quit - interaction termination
  56. (segv '()) ; segmentation violation - invalid memory reference
  57. (term '()) ; termination - termination signal
  58. (usr1 '()) ; user1 - for use by applications
  59. (usr2 '()) ; user2 - for use by applications
  60. (chld '()) ; child - child process stopped or terminated
  61. (cont '()) ; continue - continue if stopped
  62. (stop '()) ; stop - cannot be caught or ignored
  63. (tstp '()) ; interactive stop
  64. (ttin '()) ; read from control terminal attempted by background process
  65. (ttou '()) ; write to control terminal attempted by background process
  66. (bus '()) ; bus error - access to undefined portion of memory
  67. ; Additional signals from (RedHat) Linux
  68. (trap '()) ; trace or breakpoint trap
  69. (iot '()) ; IOT trap - a synonym for ABRT
  70. (emt '()) ; ambulance?
  71. (sys '()) ; bad argument to routine (SVID)
  72. (stkflt '()) ; stack fault on coprocessor
  73. (urg '()) ; urgent condition on socket (4.2 BSD)
  74. (io '()) ; I/O now possible (4.2 BSD)
  75. (poll '()) ; A synonym for SIGIO (System V)
  76. (cld '()) ; A synonym for SIGCHLD
  77. (xcpu '()) ; CPU time limit exceeded (4.2 BSD)
  78. (xfsz '()) ; File size limit exceeded (4.2 BSD)
  79. (vtalrm '()) ; Virtual alarm clock (4.2 BSD)
  80. (prof '()) ; Profile alarm clock
  81. (pwr '()) ; Power failure (System V)
  82. (info '()) ; A synonym for SIGPWR
  83. (lost '()) ; File lock lost
  84. (winch '()) ; Window resize signal (4.3 BSD, Sun)
  85. (unused '()) ; Unused signal
  86. ))
  87. (define-record-discloser :named-signal
  88. (lambda (n-s)
  89. (list 'signal (named-signal-name n-s))))
  90. ; Find the signal called `name'.
  91. (define (name->signal name)
  92. (if (not (symbol? name))
  93. (assertion-violation 'name->signal "argument not a symbol" name)
  94. (let loop ((i 0))
  95. (cond ((= i (vector-length named-signals))
  96. #f)
  97. ((eq? name
  98. (named-signal-name
  99. (vector-ref named-signals i)))
  100. (vector-ref named-signals i))
  101. (else
  102. (loop (+ i 1)))))))
  103. (define (get-unnamed-signal signum)
  104. (call-with-current-continuation
  105. (lambda (return)
  106. (walk-population
  107. (lambda (sig)
  108. (if (= signum (unnamed-signal-os-number sig)) (return sig)))
  109. *unnamed-signals*)
  110. (let ((sig (make-unnamed-signal 'nonportable-signal signum '())))
  111. (add-to-population! sig *unnamed-signals*)
  112. sig))))
  113. (define (integer->signal signum)
  114. (let loop ((i 0))
  115. (if (= i (vector-length named-signals))
  116. (get-unnamed-signal signum)
  117. (let ((s (vector-ref named-signals i)))
  118. (if (= signum (named-signal-os-number s))
  119. s
  120. (loop (+ i 1)))))))
  121. ; Write the contents of the C array mapping canonical signal numbers
  122. ; to os signal numbers.
  123. (define (write-c-signal-include-file filename)
  124. (call-with-output-file filename
  125. (lambda (out)
  126. (do ((i 0 (+ i 1)))
  127. ((= i (vector-length named-signals)))
  128. (let ((name (symbol->string
  129. (named-signal-name
  130. (vector-ref named-signals i)))))
  131. (display (string-append
  132. "#ifdef SIG" (string-upcase name) newline-string
  133. " SIG" (string-upcase name) "," newline-string
  134. "#else" newline-string
  135. " -1," newline-string
  136. "#endif" newline-string)
  137. out))))))
  138. (define newline-string (list->string '(#\newline)))
  139. (define (string-map proc)
  140. (lambda (list)
  141. (list->string (map proc (string->list list)))))
  142. (define string-upcase (string-map char-upcase))
  143. (define string-downcase (string-map char-downcase))
  144. ;----------------
  145. ; Dispatching on the two kinds of signals.
  146. (define (signal? x)
  147. (or (named-signal? x)
  148. (unnamed-signal? x)))
  149. (define (signal-name x)
  150. (cond ((named-signal? x)
  151. (named-signal-name x))
  152. ((unnamed-signal? x)
  153. #f)
  154. (else
  155. (assertion-violation 'signal-name "argument not a signal" x))))
  156. (define (signal-os-number x)
  157. (cond ((named-signal? x)
  158. (named-signal-os-number x))
  159. ((unnamed-signal? x)
  160. (unnamed-signal-os-number x))
  161. (else
  162. (assertion-violation 'signal-os-number "argument not a signal" x))))
  163. (define (signal-queues x)
  164. (cond ((named-signal? x)
  165. (named-signal-queues x))
  166. ((unnamed-signal? x)
  167. (unnamed-signal-queues x))
  168. (else
  169. (assertion-violation 'signal-queues "argument not a signal" x))))
  170. (define (set-signal-queues! x qs)
  171. (cond ((named-signal? x)
  172. (set-named-signal-queues! x qs))
  173. ((unnamed-signal? x)
  174. (set-unnamed-signal-queues! x qs))
  175. (else
  176. (assertion-violation 'set-signal-queues! "argument not a signal" x qs))))
  177. (define (clean-signal-queues x)
  178. (let* ((old (signal-queues x))
  179. (new (clean-weaks old)))
  180. (if (not (eq? new old))
  181. (set-signal-queues! x new))
  182. new))
  183. ; Two signals are the same if they are exactly the same or if they are
  184. ; both named signals and have the same (non-#F) os number.
  185. (define (signal=? s1 s2)
  186. (or (eq? s1 s2)
  187. (and (named-signal? s1)
  188. (named-signal? s2)
  189. (named-signal-os-number s1)
  190. (eq? (named-signal-os-number s1)
  191. (named-signal-os-number s2)))))
  192. ;----------------
  193. ; What we contribute to and receive from the C layer.
  194. (define-exported-binding "posix-signals-vector" named-signals)
  195. (import-lambda-definition-2 initialize-named-signals ()
  196. "posix_initialize_named_signals")
  197. (import-lambda-definition-2 request-interrupts! (os-number)
  198. "posix_request_interrupts")
  199. (import-lambda-definition-2 cancel-interrupt-request! (os-number)
  200. "posix_cancel_interrupt_request")
  201. ;----------------
  202. ; A vector mapping os-signal numbers to signals and add to it any signals
  203. ; that have existing signal queues.
  204. (define os-signal-map (make-session-data-slot! #f))
  205. ; Initializing the above vector.
  206. (define (initialize-signals)
  207. (set! *unnamed-signals* (make-population))
  208. (let ((ints (set-enabled-interrupts! no-interrupts)))
  209. (initialize-named-signals)
  210. (let* ((named (vector->list named-signals))
  211. (size (+ 1 (apply max
  212. (map (lambda (signal)
  213. (or (signal-os-number signal)
  214. -1))
  215. named))))
  216. (mapper (make-vector size '())))
  217. (for-each (lambda (signal)
  218. (if (and (signal-os-number signal)
  219. (not (null? (clean-signal-queues signal))))
  220. (let* ((number (signal-os-number signal))
  221. (old (vector-ref mapper number)))
  222. (if (null? old)
  223. (request-interrupts! number))
  224. (vector-set! mapper number (cons signal old)))))
  225. named)
  226. (session-data-set! os-signal-map mapper)
  227. (maybe-request-os-signal! (signal chld))
  228. (set-enabled-interrupts! ints)))
  229. (set-interrupt-handler! (enum interrupt os-signal) os-signal-handler))
  230. ; Add SIGNAL to the list of those waiting for that signal number from the OS.
  231. ; If this is the first such we tell the OS we want the signal.
  232. ;
  233. ; Called with interrupts disabled.
  234. (define (maybe-request-os-signal! signal)
  235. (let* ((os-number (signal-os-number signal))
  236. (mapper (session-data-ref os-signal-map))
  237. (mapper (if (< os-number (vector-length mapper))
  238. mapper
  239. (let ((new (make-vector (+ os-number 1) '())))
  240. (do ((i 0 (+ i 1)))
  241. ((= i (vector-length mapper)))
  242. (vector-set! new i (vector-ref mapper i)))
  243. (session-data-set! os-signal-map new)
  244. new)))
  245. (old (vector-ref mapper os-number)))
  246. (if (not (memq signal old))
  247. (begin
  248. (vector-set! mapper os-number (cons signal old))
  249. (if (null? old)
  250. (request-interrupts! os-number))))))
  251. ;----------------
  252. ; Sending a signal to a process.
  253. (import-lambda-definition-2 posix-kill (pid signal) "posix_kill")
  254. (define (signal-process pid signal)
  255. (posix-kill (process-id->integer pid) (signal-os-number signal)))
  256. ;----------------
  257. ; Handling signals sent to the current process. Runs with interrupts disabled.
  258. ;
  259. ; SIGCHLD has meaning for the POSIX layer, nothing else does. The proc code
  260. ; could create a queue to receive SIGCHLD signals, but that would require a
  261. ; separate thread. That would be too much mechanism.
  262. ;
  263. ; Find the list of signals for OS-NUMBER and then deliver the signal to each.
  264. ; If no one really wants it we tell the OS layer to stop delivering it to us.
  265. (define (os-signal-handler os-number enabled-interrupts)
  266. (if (= os-number (signal-os-number (signal chld)))
  267. (process-terminated-children))
  268. (let ((mapper (session-data-ref os-signal-map)))
  269. (if (<= (vector-length mapper)
  270. os-number)
  271. (cancel-interrupt-request! os-number)
  272. (let ((signals (vector-ref mapper os-number)))
  273. (let loop ((signals signals)
  274. (okay '()))
  275. (cond ((null? signals)
  276. (if (null? okay)
  277. (cancel-interrupt-request! os-number))
  278. (vector-set! mapper os-number okay))
  279. (else
  280. (loop (cdr signals)
  281. (if (or (deliver-signal (car signals))
  282. ;; Never cancel interrupts for SIGCHLD.
  283. (signal=? (car signals) (signal chld)))
  284. (cons (car signals) okay)
  285. okay)))))))))
  286. ; Send SIGNAL to each of its queues.
  287. (define (deliver-signal signal)
  288. (let loop ((queues (signal-queues signal))
  289. (okay '()))
  290. (cond ((null? queues)
  291. (set-signal-queues! signal okay)
  292. (not (null? okay)))
  293. (else
  294. (loop (cdr queues)
  295. (cond ((weak-pointer-ref (car queues))
  296. => (lambda (queue)
  297. (if (memq signal
  298. (signal-queue-signals queue))
  299. (begin
  300. (pipe-push! (signal-queue-pipe queue)
  301. signal)
  302. (cons (car queues)
  303. okay))
  304. okay)))
  305. (else
  306. okay)))))))
  307. ; Adding and removing QUEUE to the list of queues receiving SIGNAL.
  308. ;
  309. ; 1. check to see if it is already doing so.
  310. ; 2. add the signal to the queue and the (weak) queue to the signal
  311. ; 3. check that the os-signal has been requested
  312. (define (add-signal-queue-signal! queue signal)
  313. (let ((ints (set-enabled-interrupts! no-interrupts)))
  314. (if (not (memq signal (signal-queue-signals queue)))
  315. (begin
  316. (set-signal-queues! signal
  317. (cons (signal-queue-weak queue)
  318. (signal-queues signal)))
  319. (set-signal-queue-signals! queue
  320. (cons signal
  321. (signal-queue-signals queue)))
  322. (maybe-request-os-signal! signal)))
  323. (set-enabled-interrupts! ints)))
  324. ; Undo the above actions. The signal will be un-requested the next time it
  325. ; is delivered.
  326. (define (remove-signal-queue-signal! queue signal)
  327. (let ((ints (set-enabled-interrupts! no-interrupts)))
  328. (if (memq signal (signal-queue-signals queue))
  329. (begin
  330. (set-signal-queues! signal (delq (signal-queue-weak queue)
  331. (signal-queues signal)))
  332. (set-signal-queue-signals! queue
  333. (delq signal
  334. (signal-queue-signals queue)))))
  335. (set-enabled-interrupts! ints)))
  336. (define (delq signal signals)
  337. (let recur ((signals signals))
  338. (cond ((null? signals)
  339. '())
  340. ((eq? signal (car signals))
  341. (cdr signals))
  342. (else
  343. (cons (car signals)
  344. (recur (cdr signals)))))))
  345. ; A signal queue has:
  346. ; - list of the the signals of interest
  347. ; - pipe for received signals
  348. ; - stashed weak pointer for adding to signals' queue lists
  349. (define-record-type signal-queue :signal-queue
  350. (really-make-signal-queue signals pipe)
  351. signal-queue?
  352. (signals signal-queue-signals set-signal-queue-signals!)
  353. (pipe signal-queue-pipe)
  354. (weak signal-queue-weak set-signal-queue-weak!))
  355. ; Exported version that copies the list.
  356. (define (signal-queue-monitored-signals queue)
  357. (apply list (signal-queue-signals queue)))
  358. (define (make-signal-queue signals)
  359. (let ((queue (really-make-signal-queue '() (make-pipe))))
  360. (set-signal-queue-weak! queue (make-weak-pointer queue))
  361. (for-each (lambda (signal)
  362. (add-signal-queue-signal! queue signal))
  363. signals)
  364. queue))
  365. ; (read-signal <signal-queue>) -> <signal> is analogous to
  366. ; (read-char <input-port>) -> <char>
  367. ; MAYBE-READ-SIGNAL returns #f if there are no signals currently on the queue.
  368. (define (dequeue-signal! queue)
  369. (find-next-signal queue pipe-read!))
  370. (define (maybe-dequeue-signal! queue)
  371. (find-next-signal queue pipe-maybe-read!))
  372. (define (find-next-signal queue pipe-reader)
  373. (let ((pipe (signal-queue-pipe queue)))
  374. (let loop ()
  375. (let ((signal (pipe-reader pipe)))
  376. (cond ((not signal)
  377. #f)
  378. ((memq signal (signal-queue-signals queue))
  379. signal)
  380. (else
  381. (loop)))))))
  382. ;----------------
  383. ; Returns the weak-pointers in WEAKS that still have values. No copying is done
  384. ; if all have values.
  385. (define (clean-weaks weaks)
  386. (let recur ((weaks weaks) (top? #t))
  387. (cond ((null? weaks)
  388. (if top? '() #f))
  389. ((weak-pointer-ref (car weaks))
  390. (let ((rest (recur (cdr weaks) #f)))
  391. (if rest
  392. (cons (car weaks)
  393. rest)
  394. weaks)))
  395. (else
  396. (recur (cdr weaks) #t)))))
  397. ;----------------
  398. ; Initialize signals now ...
  399. (initialize-signals)
  400. ; ... and on later startups.
  401. (define-reinitializer signal-reinitializer initialize-signals)