Initial commit

This commit is contained in:
Tony Garnock-Jones 2016-09-13 17:07:27 -04:00
commit c9cf16740a
8 changed files with 415 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
compiled/
htmldocs/

44
Makefile Normal file
View File

@ -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

5
info.rkt Normal file
View File

@ -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")

4
unix-signals/info.rkt Normal file
View File

@ -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"))

48
unix-signals/main.rkt Normal file
View File

@ -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)))

View File

@ -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))

View File

@ -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");
}

View File

@ -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. }