Subject: [bug#41507] [PATCH Shepherd 1/2] system: Add support for 'signalfd'.

From: Ludovic Courtès <[email-masked]>
Date: Sun, 24 May 2020 16:36:59 +0200

* configure.ac: Add 'AC_CHECK_SIZEOF' calls for 'struct
signalfd_siginfo' and 'sigset_t'.  Add 'AC_COMPUTE_INT' for
'SFD_CLOEXEC', 'SIG_BLOCK', and 'SIG_UNBLOCK'.  Substitute the results.
* modules/shepherd/system.scm.in (allocate-sigset, sigemptyset)
(sigaddset, sigset): New procedures.
(%sizeof-struct-signalfd-siginfo, SFD_CLOEXEC): New variables.
(signalfd, consume-signalfd-siginfo): New procedures.
(SIG_BLOCK, SIG_UNBLOCK): New variables.
(sigprocmask, block-signals, unblock-signals): New procedures.
---
 configure.ac                   | 18 ++++++++++
 modules/shepherd/system.scm.in | 65 ++++++++++++++++++++++++++++++++++
 2 files changed, 83 insertions(+)

diff --git a/configure.ac b/configure.ac
index 5d11846..052a826 100644
--- a/configure.ac
+++ b/configure.ac
@@ -87,6 +87,24 @@ AC_COMPUTE_INT([PR_SET_CHILD_SUBREAPER], [PR_SET_CHILD_SUBREAPER], [#include <sy
 AC_SUBST([PR_SET_CHILD_SUBREAPER])
 AC_MSG_RESULT([done])
 
+dnl Check the size of 'signalfd_siginfo'.  If it's undefined, returns zero.
+AC_CHECK_SIZEOF([struct signalfd_siginfo], [], [#include <sys/signalfd.h>])
+AC_CHECK_SIZEOF([sigset_t], [], [#include <signal.h>])
+
+AC_MSG_CHECKING([<sys/signalfd.h> and <sys/signal.h> constants])
+AC_COMPUTE_INT([SFD_CLOEXEC], [SFD_CLOEXEC], [#include <sys/signalfd.h>])
+AC_COMPUTE_INT([SIG_BLOCK], [SIG_BLOCK], [#include <sys/signal.h>])
+AC_COMPUTE_INT([SIG_UNBLOCK], [SIG_UNBLOCK], [#include <sys/signal.h>])
+AC_MSG_RESULT([done])
+
+SIZEOF_STRUCT_SIGNALFD_SIGINFO="$ac_cv_sizeof_struct_signalfd_siginfo"
+SIZEOF_SIGSET_T="$ac_cv_sizeof_sigset_t"
+AC_SUBST([SIZEOF_STRUCT_SIGNALFD_SIGINFO])
+AC_SUBST([SIZEOF_SIGSET_T])
+AC_SUBST([SFD_CLOEXEC])
+AC_SUBST([SIG_BLOCK])
+AC_SUBST([SIG_UNBLOCK])
+
 AC_MSG_CHECKING([whether to build crash handler])
 case "$host_os" in
   linux-gnu*)  build_crash_handler=yes;;
diff --git a/modules/shepherd/system.scm.in b/modules/shepherd/system.scm.in
index e5ecd1f..872fad4 100644
--- a/modules/shepherd/system.scm.in
+++ b/modules/shepherd/system.scm.in
@@ -20,8 +20,10 @@
 
 (define-module (shepherd system)
   #:use-module (system foreign)
+  #:use-module (ice-9 binary-ports)
   #:use-module (rnrs bytevectors)
   #:use-module (srfi srfi-11)
+  #:use-module (srfi srfi-26)
   #:export (disable-reboot-on-ctrl-alt-del
             reboot
             halt
@@ -30,6 +32,11 @@
             prctl
             PR_SET_CHILD_SUBREAPER
             getpgid
+            SFD_CLOEXEC
+            signalfd
+            consume-signalfd-siginfo
+            block-signals
+            unblock-signals
             without-automatic-finalization))
 
 ;; The <sys/reboot.h> constants.
@@ -132,6 +139,64 @@ ctrlaltdel(8) and see kernel/reboot.c in Linux."
                    (list err))
             result)))))
 
+(define (allocate-sigset)
+  (bytevector->pointer (make-bytevector @SIZEOF_SIGSET_T@)))
+
+(define sigemptyset
+  (syscall->procedure int "sigemptyset" '(*)))
+
+(define sigaddset
+  (syscall->procedure int "sigaddset" `(* ,int)))
+
+(define (sigset signals)
+  "Return a pointer to a fresh 'sigset_t' for SIGNALS."
+  (let ((set (allocate-sigset)))
+    (sigemptyset set)
+    (for-each (cut sigaddset set <>) signals)
+    set))
+
+(define %sizeof-struct-signalfd-siginfo
+  ;; Size of 'struct signalfd_siginfo' or zero if it doesn't exist, as is the
+  ;; case on GNU/Hurd.
+  @SIZEOF_STRUCT_SIGNALFD_SIGINFO@)
+
+(define SFD_CLOEXEC @SFD_CLOEXEC@)
+
+(define signalfd
+  (let ((proc (syscall->procedure int "signalfd" `(,int * ,int))))
+    (lambda* (fd signals #:optional (flags SFD_CLOEXEC))
+      "Return an open input port over a signal file descriptor for SIGNALS, a
+list of signal constants; if FD is -1, a new file descriptor is allocated,
+otherwise FD is returned and its associated state is updated.  FLAGS must be a
+bitmask of SFD_CLOEXEC or SFD_NONBLOCK."
+      (fdopen (proc fd (sigset signals) flags) "r0"))))
+
+(define (consume-signalfd-siginfo port)
+  "Read a 'signalfd_siginfo' structure from PORT and discard it.  Return the
+number of the signal received."
+  (let ((bv (get-bytevector-n port %sizeof-struct-signalfd-siginfo)))
+    ;; The first 'uint32_t' field of 'struct signalfd_siginfo' is the signal
+    ;; number.
+    (bytevector-u32-native-ref bv 0)))
+
+(define SIG_BLOCK @SIG_BLOCK@)
+(define SIG_UNBLOCK @SIG_UNBLOCK@)
+
+(define sigprocmask
+  (let ((proc (syscall->procedure int "pthread_sigmask" `(,int * *))))
+    (lambda (how signals)
+      "Add SIGNALS, a list of SIG* values, to the set of blocked signals if
+HOW is SIG_BLOCK, or unblock them if HOW is SIG_UNBLOCK."
+      (proc how (sigset signals) %null-pointer))))
+
+(define (block-signals signals)
+  "Block SIGNALS, a list of SIG* values, in the current thread."
+  (sigprocmask SIG_BLOCK signals))
+
+(define (unblock-signals signals)
+  "Unblock SIGNALS, a list of SIG* values, in the current thread."
+  (sigprocmask SIG_UNBLOCK signals))
+
 
 ;;;
 ;;; Guile shenanigans.
-- 
2.26.2