From 9f8886c72ce02c9c4487a562a24a518c96043664 Mon Sep 17 00:00:00 2001 Message-Id: <9f8886c72ce02c9c4487a562a24a518c96043664.1715378445.git.mdw@distorted.org.uk> From: Mark Wooding Date: Mon, 26 Jul 1999 23:16:26 +0000 Subject: [PATCH] Signal handling integrated into I/O system. Organization: Straylight/Edgeware From: mdw --- man/sig.3 | 97 ++++++++++++++++++++ sig.c | 270 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ sig.h | 106 +++++++++++++++++++++ 3 files changed, 473 insertions(+) create mode 100644 man/sig.3 create mode 100644 sig.c create mode 100644 sig.h diff --git a/man/sig.3 b/man/sig.3 new file mode 100644 index 0000000..ddfbdb4 --- /dev/null +++ b/man/sig.3 @@ -0,0 +1,97 @@ +.\" -*-nroff-*- +.TH sel 3 "23 July 1999" mLib +.SH NAME +sig \- more controlled signal handling +.\" @sig_init +.\" @sig_add +.\" @sig_remove +.SH SYNOPSIS +.nf +.B "#include " + +.BI "void sig_add(sig *" s ", int " n , +.BI " void (*" proc ")(int " n ", void *" p "), void *" p ); +.BI "void sig_remove(sig *" s ); +.BI "void sig_init(sel_state *" s ); +.fi +.SH "DESCRIPTION" +The +.B sig +subsystem uses the I/O multiplexing capabilities of +.B mLib +(see +.BR sel (3) for details) +to provide a more convenient interface for handling signals which don't +need to be dealt with `right away'. Like the I/O system, +.B sig +doesn't allocate any memory for itself: you have to give it space to +work in. +.PP +The system needs to be initialized before use. To do this, you must +call +.BR sig_init , +passing it the address of an initialized multiplexor object. Signals +handled through this interface will only be delivered when +.BR sel_select (3) +is called on that multiplexor. +.PP +To register interest in a signal, call +.BR sig_add , +passing it the following arguments: +.TP +.I s +A pointer to an (uninitialized) object of type +.BR sig . +This will be used by the system to retain information about this signal +claim. You use the address of this object to remove the handler again +when you've finished. +.TP +.I n +The number of the signal you want to handle. +.PP +.TP +.I proc +A function to call when the signal is detected. The function is passed +the signal number and the pointer +.I p +passed to +.BR sig_add . +.TP +.I p +A pointer argument to be passed to +.I func +when the signal is detected. +.PP +Removing a handler is easy. Call +.B sig_remove +with the address of the +.B sig +structure you passed to +.BR sig_add . +.SS "Multiple signal handlers" +You may have multiple signal handlers for a signal. All of them are +called in some unspecified order when the signal occurs. +.PP +A signal's disposition is remembered when a handler for it is added and +there are no handlers already registered. When the last handler for a +signal is removed, its disposition is restored to its initial remembered +state. +.SH "BUGS AND CAVEATS" +The +.B sig +system attempts to set the +.B SA_RESTART +flag on signal handlers it creates that signal occurrences don't +interrupt system calls. This won't be done on systems which don't +define this flag, for obvious reasons. +.PP +The +.B SA_NOCLDSTOP +flag is also set, so that stopped child processes aren't reported by a +signal. This is normally right, but ought to be configurable. +.PP +The system uses writes to a nonblocking pipe to integrate with the I/O +multiplexing system. It's possible (though very unlikely) that signals +get lost because the pipe is full. +.SH "AUTHOR" +Mark Wooding, diff --git a/sig.c b/sig.c new file mode 100644 index 0000000..1bfddbf --- /dev/null +++ b/sig.c @@ -0,0 +1,270 @@ +/* -*-c-*- + * + * $Id: sig.c,v 1.1 1999/07/26 23:16:26 mdw Exp $ + * + * Signal handling + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with mLib; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: sig.c,v $ + * Revision 1.1 1999/07/26 23:16:26 mdw + * Signal handling integrated into I/O system. + * + */ + +/*----- Header files ------------------------------------------------------*/ + +#include +#include +#include +#include +#include + +#include + +#include "alloc.h" +#include "fdflags.h" +#include "report.h" +#include "sel.h" +#include "sig.h" + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct sigstate { + struct sigaction sa; + sig *list; +} sigstate; + +/*----- Static variables --------------------------------------------------*/ + +static sel_file sigsel; +static int sigfd; +static sigstate *sigs; + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @sig_handler@ --- * + * + * Arguments: @int n@ = signal number + * + * Returns: --- + * + * Use: Generic signal handler. Just writes a single byte to the + * signal pipe. The byte contains the signal number. + */ + +static void sig_handler(int n) +{ + unsigned char sch = (unsigned char)n; + write(sigfd, &sch, 1); +} + +/* --- @sig_read@ --- * + * + * Arguments: @int fd@ = file descriptor to read + * @unsigned mode@ = thing to do with descriptor + * @void *p@ = uninteresting argument + * + * Returns: --- + * + * Use: Dispatches signals to their handlers safely. + */ + +static void sig_read(int fd, unsigned mode, void *p) +{ + sigset_t ss; + unsigned char buf[256]; + int r; + + sigemptyset(&ss); + while ((r = read(fd, buf, sizeof(buf))) > 0) { + unsigned char *p, *q; + for (p = buf, q = buf + r; p < q; p++) { + sig *s; + int n = *p; + if (sigismember(&ss, n)) + continue; + sigaddset(&ss, n); + s = sigs[n].list; + while (s) { + sig *ss = s; + s = s->next; + ss->proc(n, ss->p); + } + } + } +} + +/* --- @sig_add@ --- * + * + * Arguments: @sig *s@ = pointer to signal handler block + * @int n@ = number of the signal + * @void (*proc)(int n, void *p)@ = signal handler function + * @void *p@ = argument to pass to handler + * + * Returns: --- + * + * Use: Adds a signal handler. + */ + +void sig_add(sig *s, int n, void (*proc)(int /*n*/, void */*p*/), void *p) +{ + sigstate *ss = &sigs[n]; + + /* --- Initialize the block --- */ + + s->proc = proc; + s->p = p; + s->sig = n; + s->next = ss->list; + s->prev = 0; + + /* --- Engage a signal handler, maybe --- */ + + if (!ss->list) { + struct sigaction sa; + sa.sa_handler = sig_handler; + sa.sa_flags = SA_NOCLDSTOP; +#ifdef SA_RESTART + sa.sa_flags |= SA_RESTART; +#endif + sigemptyset(&sa.sa_mask); + sigaction(n, &sa, &ss->sa); + } + + /* --- Link the block into the list --- */ + + if (ss->list) + ss->list->prev = s; + ss->list = s; +} + +/* --- @sig_remove@ --- * + * + * Arguments: @sig *s@ = pointer to signal handler block + * + * Returns: --- + * + * Use: Removes the signal handler from the list. + */ + +void sig_remove(sig *s) +{ + sigstate *ss = &sigs[s->sig]; + + /* --- Unlink the handler block --- */ + + if (s->next) + s->next->prev = s->prev; + if (s->prev) + s->prev->next = s->next; + else + ss->list = s->next; + + /* --- Maybe remove the handler --- */ + + if (!ss->list) + sigaction(s->sig, &ss->sa, 0); +} + +/* --- @sig_init@ --- * + * + * Arguments: @sel_state *s@ = pointer to select state + * + * Returns: --- + * + * Use: Initializes the signal handling system ready for use. + */ + +void sig_init(sel_state *s) +{ + int fd[2]; + unsigned nsig; + + /* --- Work out how many signals there are --- */ + + { + sigset_t ss; + unsigned min = 0, max = 0; + + sigemptyset(&ss); + + /* --- Get a good upper bound --- * + * + * Cap the search at 256. I'll be sending signal numbers as bytes. + */ + + nsig = 1; + while (sigaddset(&ss, nsig) == 0) { + if (nsig == 256) + goto counted; + nsig <<= 1; + } + + /* --- Now binary search until I find the actual limit --- */ + + min = nsig >> 1; + max = nsig; + for (;;) { + nsig = (min + max) >> 1; + if (nsig == min) + break; + if (sigaddset(&ss, nsig)) + max = nsig; + else + min = nsig; + } + counted:; + } + + /* --- Initialize the signal state table --- */ + + { + unsigned i; + + sigs = xmalloc(nsig * sizeof(*sigs)); + for (i = 0; i < nsig; i++) + sigs[i].list = 0; + } + + /* --- Create the signal pipe --- */ + + if (pipe(fd)) + die(1, "couldn't create pipe for signal handling"); + + /* --- Set both ends to nonblocking and close-on-exec --- */ + + fdflags(fd[0], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC); + fdflags(fd[1], O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC); + + /* --- Set everything up for the future --- */ + + sigfd = fd[1]; + sel_initfile(s, &sigsel, fd[0], SEL_READ, sig_read, 0); + sel_addfile(&sigsel); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/sig.h b/sig.h new file mode 100644 index 0000000..4aba789 --- /dev/null +++ b/sig.h @@ -0,0 +1,106 @@ +/* -*-c-*- + * + * $Id: sig.h,v 1.1 1999/07/26 23:16:26 mdw Exp $ + * + * Signal handling + * + * (c) 1999 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of the mLib utilities library. + * + * mLib is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * mLib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with mLib; if not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/*----- Revision history --------------------------------------------------* + * + * $Log: sig.h,v $ + * Revision 1.1 1999/07/26 23:16:26 mdw + * Signal handling integrated into I/O system. + * + */ + +#ifndef SIG_H +#define SIG_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----- Header files ------------------------------------------------------*/ + +#include + +#include "sel.h" + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct sig { + struct sig *next; + struct sig *prev; + int sig; + void (*proc)(int /*n*/, void */*p*/); + void *p; +} sig; + +/*----- Functions provided ------------------------------------------------*/ + +/* --- @sig_add@ --- * + * + * Arguments: @sig *s@ = pointer to signal handler block + * @int n@ = number of the signal + * @void (*proc)(int n, void *p)@ = signal handler function + * @void *p@ = argument to pass to handler + * + * Returns: --- + * + * Use: Adds a signal handler. + */ + +extern void sig_add(sig */*s*/, int /*n*/, + void (*/*proc*/)(int /*n*/, void */*p*/), void */*p*/); + +/* --- @sig_remove@ --- * + * + * Arguments: @sig *s@ = pointer to signal handler block + * + * Returns: --- + * + * Use: Removes the signal handler from the list. + */ + +extern void sig_remove(sig */*s*/); + +/* --- @sig_init@ --- * + * + * Arguments: @sel_state *s@ = pointer to select state + * + * Returns: --- + * + * Use: Initializes the signal handling system ready for use. + */ + +extern void sig_init(sel_state */*s*/); + +/*----- That's all, folks -------------------------------------------------*/ + +#ifdef __cplusplus + } +#endif + +#endif -- [mdw]