Initial commit
This commit is contained in:
commit
c9cf16740a
|
@ -0,0 +1,2 @@
|
||||||
|
compiled/
|
||||||
|
htmldocs/
|
|
@ -0,0 +1,44 @@
|
||||||
|
PACKAGENAME=unix-signals
|
||||||
|
COLLECTS=unix-signals
|
||||||
|
|
||||||
|
all: setup
|
||||||
|
|
||||||
|
clean:
|
||||||
|
find . -name compiled -type d | xargs rm -rf
|
||||||
|
rm -rf unix-signals/doc
|
||||||
|
rm -rf htmldocs
|
||||||
|
|
||||||
|
setup:
|
||||||
|
raco setup $(COLLECTS)
|
||||||
|
|
||||||
|
link:
|
||||||
|
raco pkg install --link -n $(PACKAGENAME) $$(pwd)
|
||||||
|
|
||||||
|
unlink:
|
||||||
|
raco pkg remove $(PACKAGENAME)
|
||||||
|
|
||||||
|
htmldocs:
|
||||||
|
raco scribble \
|
||||||
|
--html \
|
||||||
|
--dest htmldocs \
|
||||||
|
--dest-name index \
|
||||||
|
++main-xref-in \
|
||||||
|
--redirect-main http://docs.racket-lang.org/ \
|
||||||
|
\
|
||||||
|
unix-signals/unix-signals.scrbl
|
||||||
|
|
||||||
|
pages:
|
||||||
|
@(git branch -v | grep -q gh-pages || (echo local gh-pages branch missing; false))
|
||||||
|
@echo
|
||||||
|
@git branch -av | grep gh-pages
|
||||||
|
@echo
|
||||||
|
@(echo 'Is the branch up to date? Press enter to continue.'; read dummy)
|
||||||
|
git clone -b gh-pages . pages
|
||||||
|
|
||||||
|
publish: htmldocs pages
|
||||||
|
rm -rf pages/*
|
||||||
|
cp -r htmldocs/. pages/.
|
||||||
|
(cd pages; git add -A)
|
||||||
|
-(cd pages; git commit -m "Update $$(date +%Y%m%d%H%M%S)")
|
||||||
|
(cd pages; git push)
|
||||||
|
rm -rf pages
|
|
@ -0,0 +1,5 @@
|
||||||
|
#lang setup/infotab
|
||||||
|
(define collection 'multi)
|
||||||
|
(define deps '("base" "rackunit-lib"))
|
||||||
|
(define build-deps '("racket-doc" "scribble-lib"))
|
||||||
|
(define homepage "https://github.com/tonyg/racket-unix-signals")
|
|
@ -0,0 +1,4 @@
|
||||||
|
#lang setup/infotab
|
||||||
|
(define scribblings '(("unix-signals.scrbl" ())))
|
||||||
|
(define pre-install-collection "private/install.rkt")
|
||||||
|
(define compile-omit-files '("private/install.rkt"))
|
|
@ -0,0 +1,48 @@
|
||||||
|
#lang racket/base
|
||||||
|
|
||||||
|
(provide next-signal-evt
|
||||||
|
read-signal
|
||||||
|
lookup-signal-number
|
||||||
|
lookup-signal-name
|
||||||
|
capture-signal!
|
||||||
|
ignore-signal!
|
||||||
|
release-signal!
|
||||||
|
getpid
|
||||||
|
send-signal!)
|
||||||
|
|
||||||
|
(require (only-in racket/os getpid))
|
||||||
|
(require "private/unix-signals-extension.rkt")
|
||||||
|
|
||||||
|
(define signal-fd (get-signal-fd))
|
||||||
|
|
||||||
|
(define next-signal-evt
|
||||||
|
(handle-evt signal-fd (lambda (_) (read-signal))))
|
||||||
|
|
||||||
|
(define (read-signal) (read-byte signal-fd))
|
||||||
|
|
||||||
|
(define signals-by-name (get-signal-names))
|
||||||
|
(define signals-by-number
|
||||||
|
(for/hash [((name number) (in-hash signals-by-name))] (values number name)))
|
||||||
|
|
||||||
|
(define (lookup-signal-number sym) (hash-ref signals-by-name sym #f))
|
||||||
|
(define (lookup-signal-name num) (hash-ref signals-by-number num #f))
|
||||||
|
|
||||||
|
(define (name->signum who n)
|
||||||
|
(cond
|
||||||
|
[(symbol? n) (or (lookup-signal-number n)
|
||||||
|
(error who "Unknown signal name ~a" n))]
|
||||||
|
[(fixnum? n) n]
|
||||||
|
[else (error who "Expects signal name symbol or signal number; got ~v" n)]))
|
||||||
|
|
||||||
|
(define (capture-signal! sig)
|
||||||
|
(set-signal-handler! (name->signum 'capture-signal! sig) 0))
|
||||||
|
|
||||||
|
(define (ignore-signal! sig)
|
||||||
|
(set-signal-handler! (name->signum 'capture-signal! sig) 1))
|
||||||
|
|
||||||
|
(define (release-signal! sig)
|
||||||
|
(set-signal-handler! (name->signum 'capture-signal! sig) 2))
|
||||||
|
|
||||||
|
(define (send-signal! pid sig)
|
||||||
|
(when (not (fixnum? pid)) (error 'send-signal! "Expected fixnum pid; got ~v" pid))
|
||||||
|
(lowlevel-send-signal! pid (name->signum 'send-signal! sig)))
|
|
@ -0,0 +1,19 @@
|
||||||
|
#lang racket/base
|
||||||
|
|
||||||
|
(require make/setup-extension)
|
||||||
|
|
||||||
|
(provide pre-installer)
|
||||||
|
|
||||||
|
(define (pre-installer collections-top-path our-path)
|
||||||
|
(pre-install our-path
|
||||||
|
(build-path our-path "private")
|
||||||
|
"unix-signals-extension.c"
|
||||||
|
"."
|
||||||
|
'()
|
||||||
|
'()
|
||||||
|
'()
|
||||||
|
'()
|
||||||
|
'()
|
||||||
|
'()
|
||||||
|
(lambda (thunk) (thunk))
|
||||||
|
#t))
|
|
@ -0,0 +1,204 @@
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "escheme.h"
|
||||||
|
|
||||||
|
/* This implementation uses djb's "self-pipe trick".
|
||||||
|
* See http://cr.yp.to/docs/selfpipe.html. */
|
||||||
|
|
||||||
|
static int self_pipe_initialized = 0;
|
||||||
|
static int self_pipe_read_end = -1;
|
||||||
|
static int self_pipe_write_end = -1;
|
||||||
|
|
||||||
|
static int setup_self_pipe(void) {
|
||||||
|
{
|
||||||
|
int pipefd[2];
|
||||||
|
if (pipe(pipefd) == -1) {
|
||||||
|
perror("unix-signals-extension pipe(2)");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
self_pipe_read_end = pipefd[0];
|
||||||
|
self_pipe_write_end = pipefd[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
int flags = fcntl(self_pipe_write_end, F_GETFL, 0);
|
||||||
|
if (flags == -1) {
|
||||||
|
perror("unix-signals-extension F_GETFL");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (fcntl(self_pipe_write_end, F_SETFL, flags | O_NONBLOCK) == -1) {
|
||||||
|
perror("unix-signals-extension F_SETFL");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (self_pipe_write_end != -1) {
|
||||||
|
int tmp = self_pipe_write_end;
|
||||||
|
self_pipe_write_end = -1;
|
||||||
|
close(tmp);
|
||||||
|
}
|
||||||
|
if (self_pipe_read_end != -1) {
|
||||||
|
int tmp = self_pipe_read_end;
|
||||||
|
self_pipe_read_end = -1;
|
||||||
|
close(tmp);
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void signal_handler_fn(int signum) {
|
||||||
|
if (self_pipe_write_end == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
uint8_t b;
|
||||||
|
b = (uint8_t) (signum & 0xff);
|
||||||
|
if (write(self_pipe_write_end, &b, 1) == -1) {
|
||||||
|
perror("unix-signals-extension write");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scheme_Object *prim_get_signal_fd(int argc, Scheme_Object **argv) {
|
||||||
|
if (self_pipe_read_end == -1) {
|
||||||
|
return scheme_false;
|
||||||
|
} else {
|
||||||
|
return scheme_make_fd_input_port(self_pipe_read_end, scheme_intern_symbol("signal-fd"), 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Scheme_Object *prim_get_signal_names(int argc, Scheme_Object **argv) {
|
||||||
|
Scheme_Hash_Table *ht;
|
||||||
|
|
||||||
|
ht = scheme_make_hash_table(SCHEME_hash_ptr);
|
||||||
|
|
||||||
|
#define ADD_SIGNAL_NAME(n) scheme_hash_set(ht, scheme_intern_symbol(#n), scheme_make_integer(n))
|
||||||
|
|
||||||
|
/* POSIX.1-1990 */
|
||||||
|
ADD_SIGNAL_NAME(SIGHUP);
|
||||||
|
ADD_SIGNAL_NAME(SIGINT);
|
||||||
|
ADD_SIGNAL_NAME(SIGQUIT);
|
||||||
|
ADD_SIGNAL_NAME(SIGILL);
|
||||||
|
ADD_SIGNAL_NAME(SIGABRT);
|
||||||
|
ADD_SIGNAL_NAME(SIGFPE);
|
||||||
|
ADD_SIGNAL_NAME(SIGKILL);
|
||||||
|
ADD_SIGNAL_NAME(SIGSEGV);
|
||||||
|
ADD_SIGNAL_NAME(SIGPIPE);
|
||||||
|
ADD_SIGNAL_NAME(SIGALRM);
|
||||||
|
ADD_SIGNAL_NAME(SIGTERM);
|
||||||
|
ADD_SIGNAL_NAME(SIGUSR1);
|
||||||
|
ADD_SIGNAL_NAME(SIGUSR2);
|
||||||
|
ADD_SIGNAL_NAME(SIGCHLD);
|
||||||
|
ADD_SIGNAL_NAME(SIGCONT);
|
||||||
|
ADD_SIGNAL_NAME(SIGSTOP);
|
||||||
|
ADD_SIGNAL_NAME(SIGTSTP);
|
||||||
|
ADD_SIGNAL_NAME(SIGTTIN);
|
||||||
|
ADD_SIGNAL_NAME(SIGTTOU);
|
||||||
|
|
||||||
|
/* Not POSIX.1-1990, but SUSv2 and POSIX.1-2001 */
|
||||||
|
ADD_SIGNAL_NAME(SIGBUS);
|
||||||
|
ADD_SIGNAL_NAME(SIGPOLL);
|
||||||
|
ADD_SIGNAL_NAME(SIGPROF);
|
||||||
|
ADD_SIGNAL_NAME(SIGSYS);
|
||||||
|
ADD_SIGNAL_NAME(SIGTRAP);
|
||||||
|
ADD_SIGNAL_NAME(SIGURG);
|
||||||
|
ADD_SIGNAL_NAME(SIGVTALRM);
|
||||||
|
ADD_SIGNAL_NAME(SIGXCPU);
|
||||||
|
ADD_SIGNAL_NAME(SIGXFSZ);
|
||||||
|
|
||||||
|
/* Misc, that we hope are widely-supported enough not to have to
|
||||||
|
bother with a feature test. */
|
||||||
|
ADD_SIGNAL_NAME(SIGIO);
|
||||||
|
ADD_SIGNAL_NAME(SIGWINCH);
|
||||||
|
|
||||||
|
#undef ADD_SIGNAL_NAME
|
||||||
|
|
||||||
|
return (Scheme_Object *) ht;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scheme_Object *prim_capture_signal(int argc, Scheme_Object **argv) {
|
||||||
|
int signum = SCHEME_INT_VAL(argv[0]);
|
||||||
|
int code = SCHEME_INT_VAL(argv[1]);
|
||||||
|
switch (code) {
|
||||||
|
case 0:
|
||||||
|
if (signal(signum, signal_handler_fn) == SIG_ERR) {
|
||||||
|
perror("unix-signals-extension signal(2) install");
|
||||||
|
return scheme_false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (signal(signum, SIG_IGN) == SIG_ERR) {
|
||||||
|
perror("unix-signals-extension signal(2) ignore");
|
||||||
|
return scheme_false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (signal(signum, SIG_DFL) == SIG_ERR) {
|
||||||
|
perror("unix-signals-extension signal(2) default");
|
||||||
|
return scheme_false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return scheme_false;
|
||||||
|
}
|
||||||
|
return scheme_true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scheme_Object *prim_send_signal(int argc, Scheme_Object **argv) {
|
||||||
|
pid_t pid = SCHEME_INT_VAL(argv[0]);
|
||||||
|
int sig = SCHEME_INT_VAL(argv[1]);
|
||||||
|
if (kill(pid, sig) == -1) {
|
||||||
|
perror("unix-signals-extension kill(2)");
|
||||||
|
return scheme_false;
|
||||||
|
}
|
||||||
|
return scheme_true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scheme_Object *scheme_reload(Scheme_Env *env) {
|
||||||
|
Scheme_Env *module_env;
|
||||||
|
Scheme_Object *proc;
|
||||||
|
|
||||||
|
if (!self_pipe_initialized) {
|
||||||
|
if (setup_self_pipe() == -1) {
|
||||||
|
return scheme_false;
|
||||||
|
}
|
||||||
|
self_pipe_initialized = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
module_env = scheme_primitive_module(scheme_intern_symbol("unix-signals-extension"), env);
|
||||||
|
|
||||||
|
proc = scheme_make_prim_w_arity(prim_get_signal_fd, "get-signal-fd", 0, 0);
|
||||||
|
scheme_add_global("get-signal-fd", proc, module_env);
|
||||||
|
|
||||||
|
proc = scheme_make_prim_w_arity(prim_get_signal_names, "get-signal-names", 0, 0);
|
||||||
|
scheme_add_global("get-signal-names", proc, module_env);
|
||||||
|
|
||||||
|
proc = scheme_make_prim_w_arity(prim_capture_signal, "set-signal-handler!", 2, 2);
|
||||||
|
scheme_add_global("set-signal-handler!", proc, module_env);
|
||||||
|
|
||||||
|
proc = scheme_make_prim_w_arity(prim_send_signal, "lowlevel-send-signal!", 2, 2);
|
||||||
|
scheme_add_global("lowlevel-send-signal!", proc, module_env);
|
||||||
|
|
||||||
|
scheme_finish_primitive_module(module_env);
|
||||||
|
return scheme_void;
|
||||||
|
}
|
||||||
|
|
||||||
|
Scheme_Object *scheme_initialize(Scheme_Env *env) {
|
||||||
|
return scheme_reload(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
Scheme_Object *scheme_module_name() {
|
||||||
|
return scheme_intern_symbol("unix-signals-extension");
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
#lang scribble/manual
|
||||||
|
|
||||||
|
@(require scriblib/footnote
|
||||||
|
(for-label racket unix-signals racket/os))
|
||||||
|
|
||||||
|
@title{unix-signals}
|
||||||
|
@author[(author+email "Tony Garnock-Jones" "tonyg@leastfixedpoint.com")]
|
||||||
|
|
||||||
|
If you find that this library lacks some feature you need, or you have
|
||||||
|
a suggestion for improving it, please don't hesitate to
|
||||||
|
@link["mailto:tonyg@leastfixedpoint.com"]{get in touch with me}!
|
||||||
|
|
||||||
|
@section{Introduction}
|
||||||
|
|
||||||
|
This library provides a means of sending and receiving Unix signals to
|
||||||
|
Racket programs.
|
||||||
|
|
||||||
|
Be warned that attempting to receive certain signals used by the
|
||||||
|
Racket runtime is dangerous, as the code here will conflict with the
|
||||||
|
code in Racket itself.
|
||||||
|
|
||||||
|
@section{What to require}
|
||||||
|
|
||||||
|
All the functionality below can be accessed with a single
|
||||||
|
@racket[require]:
|
||||||
|
|
||||||
|
@(defmodule unix-signals)
|
||||||
|
|
||||||
|
@subsection{Mapping between signal names and signal numbers}
|
||||||
|
|
||||||
|
This library represents signal names as symbols all in upper-case;
|
||||||
|
for example, @racket['SIGUSR1] and @racket['SIGKILL].
|
||||||
|
|
||||||
|
@defproc[(lookup-signal-number [sym symbol?]) (opt/c fixnum?)]{
|
||||||
|
Returns a fixnum if the symbol name is defined, or @racket[#f] if not. }
|
||||||
|
|
||||||
|
@defproc[(lookup-signal-name [num fixnum?]) (opt/c symbol?)]{ Returns
|
||||||
|
a symbol naming the given signal number, if one is defined, or
|
||||||
|
@racket[#f] if not. Note that in cases where multiple C identifiers
|
||||||
|
map to a given signal number, an arbitrary choice among the
|
||||||
|
possibilities is returned. }
|
||||||
|
|
||||||
|
@subsection{Waiting for a signal}
|
||||||
|
|
||||||
|
To receive Unix signals using this library, call
|
||||||
|
@racket[capture-signal!] once for each signal of interest, and then
|
||||||
|
use @racket[next-signal-evt] or @racket[read-signal]. Use
|
||||||
|
@racket[ignore-signal!] and @racket[release-signal!] to ignore a
|
||||||
|
signal (@tt{SIG_IGN}) or to install the default
|
||||||
|
signal-handler (@tt{SIG_DFL}), respectively.
|
||||||
|
|
||||||
|
Calls to @racket[capture-signal!] and friends have @emph{global} effect
|
||||||
|
within the Racket process. Likewise, use of @racket[next-signal-evt]
|
||||||
|
and @racket[read-signal] have global side-effects on the state of the
|
||||||
|
Racket process.
|
||||||
|
|
||||||
|
@defproc[(capture-signal! [sig (or/c fixnum? symbol?)]) boolean?]{
|
||||||
|
Installs a signal handler for the given signal. When the given signal
|
||||||
|
is received by the process, its signal number will be returned by uses
|
||||||
|
of @racket[next-signal-evt] and/or @racket[read-signal]. }
|
||||||
|
|
||||||
|
@defproc[(ignore-signal! [sig (or/c fixnum? symbol?)]) boolean?]{
|
||||||
|
Causes the given signal to be ignored (@tt{SIG_IGN}) by the process. }
|
||||||
|
|
||||||
|
@defproc[(release-signal! [sig (or/c fixnum? symbol?)]) boolean?]{
|
||||||
|
Installs the default handler (@tt{SIG_DFL}) for the given signal. }
|
||||||
|
|
||||||
|
@defthing[next-signal-evt evt?]{ @tech[#:doc
|
||||||
|
'(lib "scribblings/reference/reference.scrbl")]{Synchronizable event} which
|
||||||
|
becomes ready when a signal previously registered with
|
||||||
|
@racket[capture-signal!] is received, at which point it returns the
|
||||||
|
number of the received signal as its synchronization result by
|
||||||
|
yielding the result of a call to @racket[read-signal]. }
|
||||||
|
|
||||||
|
@defproc[(read-signal) fixnum?]{ Blocks until a signal previously
|
||||||
|
registered with @racket[capture-signal!] is received. Returns the
|
||||||
|
number of the received signal. Signals are buffered internally using
|
||||||
|
the @link["http://cr.yp.to/docs/selfpipe.html"]{self-pipe trick}, and
|
||||||
|
are therefore delivered in order of receipt. }
|
||||||
|
|
||||||
|
@subsection{Sending a signal}
|
||||||
|
|
||||||
|
This library provides @racket[getpid] from @racketmodname[racket/os]
|
||||||
|
for convenience.
|
||||||
|
|
||||||
|
@defproc[(send-signal! [pid fixnum?] [sig (or/c fixnum? symbol?)])
|
||||||
|
boolean?]{ Calls @tt{kill(2)} to deliver the given signal to the
|
||||||
|
given process ID. All special cases for @racket[pid] from the
|
||||||
|
@tt{kill(2)} manpage apply. }
|
Loading…
Reference in New Issue