+/* -*-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 -------------------------------------------------*/