chiark / gitweb /
Signal handling integrated into I/O system.
authormdw <mdw>
Mon, 26 Jul 1999 23:16:26 +0000 (23:16 +0000)
committermdw <mdw>
Mon, 26 Jul 1999 23:16:26 +0000 (23:16 +0000)
man/sig.3 [new file with mode: 0644]
sig.c [new file with mode: 0644]
sig.h [new file with mode: 0644]

diff --git a/man/sig.3 b/man/sig.3
new file mode 100644 (file)
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 <mLib/sig.h>"
+
+.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, <mdw@nsict.org>
diff --git a/sig.c b/sig.c
new file mode 100644 (file)
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 <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <fcntl.h>
+
+#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 (file)
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 <signal.h>
+
+#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