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