Basic POSIX signal handling (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 23 Jan 2013 00:30:32 +0000 (16:30 -0800)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 23 Jan 2013 01:07:39 +0000 (17:07 -0800)
Can register sighandlers/sigactions for POSIX signals.  You can signal
with kill or raise.

Check signal.c for details, esp concerning all the things we don't do.

Rebuild your cross compiler.

tests/raise.c [new file with mode: 0644]
tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/bits/sigaction.h [new file with mode: 0644]
user/parlib/include/parlib.h
user/parlib/signal.c
user/parlib/uthread.c

diff --git a/tests/raise.c b/tests/raise.c
new file mode 100644 (file)
index 0000000..83f58d3
--- /dev/null
@@ -0,0 +1,77 @@
+/* Copyright (C) 2003 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Jakub Jelinek <jakub@redhat.com>, 2003.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#include <errno.h>
+#include <error.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+volatile int count;
+
+void sighand(int sig)
+{
+       assert(SIGUSR1 == sig);
+       ++count;
+}
+
+void sigact(int sig, siginfo_t *info, void *null)
+{
+       assert(SIGUSR1 == sig);
+       assert(sig == info->si_signo);
+       ++count;
+}
+
+int main(void)
+{
+       struct sigaction sa;
+       sigemptyset(&sa.sa_mask);
+       int nr_tests = 0;
+
+       sa.sa_handler = sighand;
+       sa.sa_flags = 0;
+       nr_tests++;
+       if (sigaction(SIGUSR1, &sa, NULL) < 0) {
+               printf("first sigaction failed: %m\n");
+               exit(1);
+       }
+       if (raise(SIGUSR1) < 0) {
+               printf("first raise failed: %m\n");
+               exit(1);
+       }
+
+       sa.sa_sigaction = sigact;
+       sa.sa_flags = SA_SIGINFO;
+       nr_tests++;
+       if (sigaction(SIGUSR1, &sa, NULL) < 0) {
+               printf("second sigaction failed: %m\n");
+               exit(1);
+       }
+       if (raise(SIGUSR1) < 0) {
+               printf("second raise failed: %m\n");
+               exit(1);
+       }
+       if (count != nr_tests) {
+               printf("signal handler not called %d times\n", nr_tests);
+               exit(1);
+       }
+       printf("Passed, exiting\n");
+       exit(0);
+}
diff --git a/tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/bits/sigaction.h b/tools/compilers/gcc-glibc/glibc-2.14.1-ros/sysdeps/ros/bits/sigaction.h
new file mode 100644 (file)
index 0000000..536055b
--- /dev/null
@@ -0,0 +1,81 @@
+/* The proper definitions for Linux's sigaction.
+   Copyright (C) 1993-1999, 2000, 2010 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
+
+#ifndef _SIGNAL_H
+# error "Never include <bits/sigaction.h> directly; use <signal.h> instead."
+#endif
+
+/* ROS: Copy of sysdeps/unix/sysv/linux/bits/sigaction.h */
+
+/* Structure describing the action to be taken when a signal arrives.  */
+struct sigaction
+  {
+    /* Signal handler.  */
+#ifdef __USE_POSIX199309
+    union
+      {
+       /* Used if SA_SIGINFO is not set.  */
+       __sighandler_t sa_handler;
+       /* Used if SA_SIGINFO is set.  */
+       void (*sa_sigaction) (int, siginfo_t *, void *);
+      }
+    __sigaction_handler;
+# define sa_handler    __sigaction_handler.sa_handler
+# define sa_sigaction  __sigaction_handler.sa_sigaction
+#else
+    __sighandler_t sa_handler;
+#endif
+
+    /* Additional set of signals to be blocked.  */
+    __sigset_t sa_mask;
+
+    /* Special flags.  */
+    int sa_flags;
+
+    /* Restore handler.  */
+    void (*sa_restorer) (void);
+  };
+
+/* Bits in `sa_flags'.  */
+#define        SA_NOCLDSTOP  1          /* Don't send SIGCHLD when children stop.  */
+#define SA_NOCLDWAIT  2                 /* Don't create zombie on child death.  */
+#define SA_SIGINFO    4                 /* Invoke signal-catching function with
+                                   three arguments instead of one.  */
+#if defined __USE_UNIX98 || defined __USE_MISC
+# define SA_ONSTACK   0x08000000 /* Use signal stack by using `sa_restorer'. */
+#endif
+#if defined __USE_UNIX98 || defined __USE_MISC || defined __USE_XOPEN2K8
+# define SA_RESTART   0x10000000 /* Restart syscall on signal return.  */
+# define SA_NODEFER   0x40000000 /* Don't automatically block the signal when
+                                   its handler is being executed.  */
+# define SA_RESETHAND 0x80000000 /* Reset to SIG_DFL on entry to handler.  */
+#endif
+#ifdef __USE_MISC
+# define SA_INTERRUPT 0x20000000 /* Historical no-op.  */
+
+/* Some aliases for the SA_ constants.  */
+# define SA_NOMASK    SA_NODEFER
+# define SA_ONESHOT   SA_RESETHAND
+# define SA_STACK     SA_ONSTACK
+#endif
+
+/* Values for the HOW argument to `sigprocmask'.  */
+#define        SIG_BLOCK     0          /* Block signals.  */
+#define        SIG_UNBLOCK   1          /* Unblock signals.  */
+#define        SIG_SETMASK   2          /* Set the set of blocked signals.  */
index ed5949c..95f9f44 100644 (file)
@@ -57,6 +57,7 @@ int         sys_change_vcore(uint32_t vcoreid, bool enable_my_notif);
 int         sys_change_to_m(void);
 int         sys_poke_ksched(int res_type);
 
+void           init_posix_signals(void);       /* in signal.c */
 #ifdef __cplusplus
 }
 #endif
index 5c458be..df3c7b8 100644 (file)
-#include <signal.h>
-
-// Process wide table of signal handlers
-__sighandler_t sighandlers[_NSIG-1];
+/* Copyright (c) 2013 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * POSIX signal handling glue.  All glibc programs link against parlib, so they
+ * will get this mixed in.  Mostly just registration of signal handlers.
+ *
+ * POSIX signal handling caveats:
+ *     - We don't copy signal handling tables or anything across forks or execs
+ *     - We don't send meaningful info in the siginfos, nor do we pass pid/uids on
+ *     signals coming from a kill.  This is especially pertinent for sigqueue,
+ *     which needs a payload (value) and sending PID
+ *     - We run handlers in vcore context, so any blocking syscall will spin.
+ *     Regular signals have restrictions on their syscalls too, though not this
+ *     great.  We could spawn off a uthread to run the handler, given that we have
+ *     a 2LS (which we don't for SCPs).
+ *     - We don't do anything with signal blocking/masking.  When in a signal
+ *     handler, you won't get interrupted with another signal handler (so long as
+ *     you run it in vcore context!).  With uthreads, you could get interrupted.
+ *     There is also no process wide signal blocking yet (sigprocmask()).  If this
+ *     is desired, we can abort certain signals when we h_p_signal(), 
+ *     - Likewise, we don't do waiting for particular signals yet.  Just about the
+ *     only thing we do is allow the registration of signal handlers. 
+ *     - Check each function for further notes.  */
 
+#include <signal.h>
+#include <stdio.h>
+
+#include <event.h>
+#include <assert.h>
+
+struct sigaction sigactions[_NSIG - 1];
+
+void handle_posix_signal(struct event_msg *ev_msg, unsigned int ev_type)
+{
+       int sig_nr;
+       struct sigaction *action;
+       struct siginfo info = {0};
+       assert(ev_msg);
+       sig_nr = ev_msg->ev_arg1;
+       if (sig_nr > _NSIG - 1 || sig_nr < 0)
+               return;
+       action = &sigactions[sig_nr];
+       /* Would like a switch/case here, but they are pointers.  We can also get
+        * away with this check early since sa_handler and sa_sigaction are macros
+        * referencing the same union.  The man page isn't specific about whether or
+        * not you need to care about SA_SIGINFO when sending DFL/ERR/IGN. */
+       if (action->sa_handler == SIG_ERR)
+               return;
+       if (action->sa_handler == SIG_IGN)
+               return;
+       if (action->sa_handler == SIG_DFL)      /* aka, 0 */
+               return;
+       if (action->sa_flags & SA_SIGINFO) {
+               info.si_signo = sig_nr;
+               /* TODO: consider pid and whatnot */
+               action->sa_sigaction(sig_nr, &info, 0);
+       } else {
+               action->sa_handler(sig_nr);
+       }
+}
+
+/* Called from uthread_slim_init() */
+void init_posix_signals(void)
+{
+       struct event_queue *posix_sig_ev_q;
+       ev_handlers[EV_POSIX_SIGNAL] = handle_posix_signal;
+       posix_sig_ev_q = get_big_event_q();
+       assert(posix_sig_ev_q);
+       posix_sig_ev_q->ev_flags = EVENT_IPI | EVENT_INDIR | EVENT_FALLBACK;
+       register_kevent_q(posix_sig_ev_q, EV_POSIX_SIGNAL);
+}
+
+/* Will need to do these if we have signal masks (sigprocmask style) */
 int sigaddset(sigset_t *__set, int __signo)
 {
+       return 0;
 }
 
 int sigdelset(sigset_t *__set, int __signo)
 {
+       return 0;
 }
 
 int sigismember(__const sigset_t *__set, int __signo)
 {
+       return 0;
 }
 
-int sigprocmask(int __how, __const sigset_t *__restrict __set, sigset_t *__restrict __oset)
+/* Would need a layer/interposition to ignore blocked signals when they come in,
+ * and then to manually play them when they are unblocked, like how x86 does
+ * with the IRR and the ISR for interrupt delivery. */
+int sigprocmask(int __how, __const sigset_t *__restrict __set,
+                sigset_t *__restrict __oset)
 {
+       return 0;
 }
 
+/* Could do this with a loop on delivery of the signal, sleeping and getting
+ * woken up by the kernel on any event, like we do with async syscalls. */
 int sigsuspend(__const sigset_t *__set)
 {
+       return 0;
 }
 
 int sigaction(int __sig, __const struct sigaction *__restrict __act,
- struct sigaction *__restrict __oact)
             struct sigaction *__restrict __oact)
 {
+       if (__sig > _NSIG - 1 || __sig < 0)
+               return -1;
+       if (__oact) {
+               *__oact = sigactions[__sig];
+       }
+       if (!__act)
+               return 0;
+       sigactions[__sig] = *__act;
+       return 0;
 }
 
+/* Not really possible or relevant - you'd need to walk/examine the event UCQ */
 int sigpending(sigset_t *__set)
 {
+       return 0;
 }
 
+/* Can be done similar to sigsuspend */
 int sigwait(__const sigset_t *__restrict __set, int *__restrict __sig)
 {
+       return 0;
 }
 
-int sigwaitinfo(__const sigset_t *__restrict __set, siginfo_t *__restrict __info)
+/* Can be done similar to sigsuspend */
+int sigwaitinfo(__const sigset_t *__restrict __set,
+                siginfo_t *__restrict __info)
 {
+       return 0;
 }
 
+/* Can be done similar to sigsuspend, with an extra alarm syscall */
 int sigtimedwait(__const sigset_t *__restrict __set,
-    siginfo_t *__restrict __info,
-    __const struct timespec *__restrict __timeout)
+                 siginfo_t *__restrict __info,
+                 __const struct timespec *__restrict __timeout)
 {
+       return 0;
 }
 
+/* Needs support with handle_posix_signal to deal with passing values with POSIX
+ * signals. */
 int sigqueue(__pid_t __pid, int __sig, __const union sigval __val)
 {
+       return 0;
 }
 
-
+/* Old BSD interface, deprecated */
 int sigvec(int __sig, __const struct sigvec *__vec, struct sigvec *__ovec)
 {
+       return 0;
 }
 
-
+/* Linux specific, and not really needed for us */
 int sigreturn(struct sigcontext *__scp)
 {
+       return 0;
 }
 
-
+/* Akaros can't have signals interrupt syscalls to need a restart, though we can
+ * re-wake-up the process while it is waiting for its syscall. */
 int siginterrupt(int __sig, int __interrupt)
 {
+       return 0;
 }
 
-
+/* This is managed by vcore / 2LS code */
 int sigstack(struct sigstack *__ss, struct sigstack *__oss)
 {
+       return 0;
 }
 
-
+/* This is managed by vcore / 2LS code */
 int sigaltstack(__const struct sigaltstack *__restrict __ss,
-   struct sigaltstack *__restrict __oss)
+                struct sigaltstack *__restrict __oss)
 {
+       return 0;
 }
-
index f4d759e..32d3c8f 100644 (file)
@@ -104,33 +104,17 @@ static void scp_vcctx_ready(void)
                             old_flags & ~VC_SCP_NOVCCTX));
 }
 
-/* TODO: dumb helper, put it in glibc or something */
-static void handle_posix_signal(struct event_msg *ev_msg, unsigned int ev_type)
-{
-       int sig_nr;
-       assert(ev_msg);
-       sig_nr = ev_msg->ev_arg1;
-       printf("Received POSIX signal number %d\n", sig_nr);
-}
-
 /* Slim-init - sets up basic uthreading for when we are in _S mode and before
  * we set up the 2LS.  Some apps may not have a 2LS and thus never do the full
  * vcore/2LS/uthread init. */
 void uthread_slim_init(void)
 {
        struct uthread *uthread = malloc(sizeof(*uthread));
-       struct event_queue *posix_sig_ev_q;
        /* TODO: consider a vcore_init_vc0 call. */
        vcore_init();
        uthread_manage_thread0(uthread);
        scp_vcctx_ready();
-       /* Register an ev_q for posix signals */
-       /* TODO: probably put this handler in glibc */
-       ev_handlers[EV_POSIX_SIGNAL] = handle_posix_signal;
-       posix_sig_ev_q = get_big_event_q();
-       assert(posix_sig_ev_q);
-       posix_sig_ev_q->ev_flags = EVENT_IPI | EVENT_INDIR | EVENT_FALLBACK;
-       register_kevent_q(posix_sig_ev_q, EV_POSIX_SIGNAL);
+       init_posix_signals();
        /* change our blockon from glibc's internal one to the mcp one (which can
         * handle SCPs too).  we must do this before switching to _M, or at least
         * before blocking while an _M.  it's harmless (and probably saner) to do it