[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[shepherd] 01/02: Add SIGSEGV/SIGABRT "crash" handler.
From: |
Ludovic Courtès |
Subject: |
[shepherd] 01/02: Add SIGSEGV/SIGABRT "crash" handler. |
Date: |
Mon, 9 Dec 2019 17:17:10 -0500 (EST) |
civodul pushed a commit to branch master
in repository shepherd.
commit dfb7c7ecdb2d12061073e6939ec6e765ae59c00c
Author: Ludovic Courtès <address@hidden>
Date: Mon Dec 9 23:09:23 2019 +0100
Add SIGSEGV/SIGABRT "crash" handler.
This allows shepherd to dump core when it's running as PID 1 on
GNU/Linux.
* etc/crash-handler.c: New file.
* configure.ac: Define 'BUILD_CRASH_HANDLER' Automake conditional.
* Makefile.am (EXTRA_DIST): Add 'etc/crash-handler.c'.
(all-local, install-crash-handler, etc/crash-handler.so)
[BUILD_CRASH_HANDLER]:
New targets.
(CLEANFILES) [BUILD_CRASH_HANDLER]: Add 'etc/crash-handler.so'.
(install-exec-hook): Depend on 'install-crash-handler'.
(uninstall-hook): Delete $(pkglibdir)/crash-handler.so.
(instantiate): Substitute "%pkglibdir%".
* modules/shepherd/config.scm.in (%pkglibdir): New variable.
* modules/shepherd.scm (main): Attempt to load "crash-handler.so".
---
Makefile.am | 38 +++++++++++++++--
configure.ac | 8 ++++
etc/crash-handler.c | 93 ++++++++++++++++++++++++++++++++++++++++++
modules/shepherd.scm | 7 +++-
modules/shepherd/config.scm.in | 2 +
5 files changed, 144 insertions(+), 4 deletions(-)
diff --git a/Makefile.am b/Makefile.am
index b6d4831..9a1f019 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -68,6 +68,36 @@ CLEANFILES = \
$(nodist_scriptsgo_DATA) \
$(bin_SCRIPTS) $(sbin_SCRIPTS)
+
+# Crash handler.
+
+EXTRA_DIST = etc/crash-handler.c
+
+if BUILD_CRASH_HANDLER
+
+# Build the crash handler "manually" rather than with Libtool.
+
+all-local: etc/crash-handler.so
+install-crash-handler:
+ $(MKDIR_P) $(DESTDIR)$(pkglibdir)
+ $(INSTALL) -m 755 etc/crash-handler.so \
+ $(DESTDIR)$(pkglibdir)/crash-handler.so
+
+etc/crash-handler.so: etc/crash-handler.c
+ $(AM_V_GEN)$(MKDIR_P) etc; \
+ $(CC) -O2 -g -Wall -shared -fPIC -o "$@" "$^"
+
+CLEANFILES += etc/crash-handler.so
+
+else !BUILD_CRASH_HANDLER
+
+install-crash-handler:
+
+endif !BUILD_CRASH_HANDLER
+
+.PHONY: install-crash-handler
+
+
# Documentation.
info_TEXINFOS = doc/shepherd.texi
doc_shepherd_TEXINFOS = doc/fdl-1.3.texi
@@ -93,7 +123,7 @@ dist_man1_MANS = doc/shepherd.1 doc/herd.1
dist_man8_MANS = doc/halt.8 doc/reboot.8
# Things not automatically included in the distribution.
-EXTRA_DIST = \
+EXTRA_DIST += \
build-aux/config.rpath \
ChangeLog-2003 \
QUESTIONS \
@@ -111,7 +141,7 @@ install-data-local:
# Relocate the script---i.e., have them refer to the installed module
# directory.
-install-exec-hook: install-executable-symlinks
+install-exec-hook: install-executable-symlinks install-crash-handler
for script in \
$(bin_SCRIPTS:%=$(DESTDIR)$(bindir)/%) \
$(sbin_SCRIPTS:%=$(DESTDIR)$(sbindir)/%) ; \
@@ -126,9 +156,10 @@ install-exec-hook: install-executable-symlinks
install-executable-symlinks:
cd $(DESTDIR)$(sbindir); ln -s halt shutdown
-# Remove the 'shutdown' symlink.
+# Remove the 'shutdown' symlink and 'crash-handler.so'.
uninstall-hook:
cd $(DESTDIR)$(sbindir); rm -f shutdown
+ cd $(DESTDIR)$(pkglibdir); rm -f crash-handler.so
# 'sed' expression to instantiate templates.
instantiate = \
@@ -136,6 +167,7 @@ instantiate =
\
-e 's,%modsrcdir%,${abs_top_srcdir}/modules,g' \
-e 's,%modbuilddir%,${abs_top_builddir}/modules,g' \
-e 's,%localstatedir%,${localstatedir},g' \
+ -e 's,%pkglibdir%,${pkglibdir},g' \
-e 's,%sysconfdir%,${sysconfdir},g' \
-e 's,%localedir%,${localedir},g' \
-e 's,%VERSION%,@VERSION@,g' \
diff --git a/configure.ac b/configure.ac
index 58c4978..1fc6c2f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -87,6 +87,14 @@ AC_COMPUTE_INT([PR_SET_CHILD_SUBREAPER],
[PR_SET_CHILD_SUBREAPER], [#include <sy
AC_SUBST([PR_SET_CHILD_SUBREAPER])
AC_MSG_RESULT([done])
+AC_MSG_CHECKING([whether to build crash handler])
+case "$host_os" in
+ linux-gnu*) build_crash_handler=yes;;
+ *) build_crash_handler=no;;
+esac
+AC_MSG_RESULT([$build_crash_handler])
+AM_CONDITIONAL([BUILD_CRASH_HANDLER], [test "x$build_crash_handler" = "xyes"])
+
dnl Manual pages.
AM_MISSING_PROG([HELP2MAN], [help2man])
diff --git a/etc/crash-handler.c b/etc/crash-handler.c
new file mode 100644
index 0000000..2921002
--- /dev/null
+++ b/etc/crash-handler.c
@@ -0,0 +1,93 @@
+/* crash-handler.c -- PID 1 helper to dump core upon a crash on GNU/Linux.
+ Copyright © 2019 Ludovic Courtès <address@hidden>
+
+ This file is part of the GNU Shepherd.
+
+ The GNU Shepherd is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or (at
+ your option) any later version.
+
+ The GNU Shepherd 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with the GNU Shepherd. If not, see <http://www.gnu.org/licenses/>.
*/
+
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <sched.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/syscall.h> /* For SYS_xxx definitions */
+#include <signal.h>
+
+/* Arrange to dump core. */
+static void
+handle_crash (int sig)
+{
+ static const char msg[] = "Shepherd crashed!\n";
+ write (2, msg, sizeof msg);
+
+#ifdef __sparc__
+ /* See 'raw_clone' in systemd. */
+# error "SPARC uses a different 'clone' syscall convention"
+#endif
+
+ /* Start a child process that will be able to dump core. */
+ pid_t pid = syscall (SYS_clone, SIGCHLD, NULL);
+ if (pid < 0)
+ abort ();
+
+ if (pid == 0)
+ {
+ /* Restore the default signal handler to get a core dump. */
+ signal (sig, SIG_DFL);
+
+ const struct rlimit infinity = { RLIM_INFINITY, RLIM_INFINITY };
+ setrlimit (RLIMIT_CORE, &infinity);
+ chdir ("/");
+
+ int pid = syscall (SYS_getpid);
+ kill (pid, sig);
+
+ /* As it turns out, 'kill' simply returns without doing anything, which
+ is consistent with the "Notes" section of kill(2). Thus, force a
+ crash. */
+ * (int *) 0 = 42;
+
+ _exit (254);
+ }
+ else
+ {
+ /* Wait for the child process and terminate. */
+ signal (sig, SIG_IGN);
+
+ int status;
+ waitpid (pid, &status, 0);
+
+ sync ();
+
+ _exit (255);
+ }
+
+ _exit (253);
+}
+
+static void initialize_crash_handler (void)
+ __attribute__ ((constructor));
+
+static void
+initialize_crash_handler (void)
+{
+ /* Register raw signal handlers. This cannot be done in Scheme because the
+ handler can only call async-signal-safe functions. */
+ signal (SIGSEGV, handle_crash);
+ signal (SIGABRT, handle_crash);
+}
diff --git a/modules/shepherd.scm b/modules/shepherd.scm
index 769085a..3812f8a 100644
--- a/modules/shepherd.scm
+++ b/modules/shepherd.scm
@@ -211,7 +211,12 @@ socket file at FILE-NAME upon exit of PROC. Return the
values of PROC."
;; 'reboot_pid_ns' in kernel/pid_namespace.c.) We get EPERM in
;; a user namespace that lacks CAP_SYS_BOOT.
(unless (member err (list EINVAL EPERM))
- (apply throw args))))))
+ (apply throw args)))))
+
+ ;; Load the SIGSEGV/SIGABRT handler. This is what allows PID 1 to
+ ;; dump core on "/", should something go wrong.
+ (false-if-exception
+ (dynamic-link (string-append %pkglibdir "/crash-handler"))))
;; Stop everything when we get SIGINT.
(sigaction SIGINT
diff --git a/modules/shepherd/config.scm.in b/modules/shepherd/config.scm.in
index 547b885..def5442 100644
--- a/modules/shepherd/config.scm.in
+++ b/modules/shepherd/config.scm.in
@@ -6,6 +6,7 @@
%localstatedir
%sysconfdir
%localedir
+ %pkglibdir
copyright
bug-address
package-name
@@ -16,6 +17,7 @@
(define %localstatedir "%localstatedir%")
(define %sysconfdir "%sysconfdir%")
(define %localedir "%localedir%")
+(define %pkglibdir "%pkglibdir%")
(define copyright "Copyright (C) 2002, 2003 Wolfgang J�hrling")
(define bug-address "%PACKAGE_BUGREPORT%")