summaryrefslogtreecommitdiff
path: root/cmd/libsnap-confine-private
diff options
Diffstat (limited to 'cmd/libsnap-confine-private')
-rw-r--r--cmd/libsnap-confine-private/panic-test.c88
-rw-r--r--cmd/libsnap-confine-private/panic.c67
-rw-r--r--cmd/libsnap-confine-private/panic.h91
3 files changed, 246 insertions, 0 deletions
diff --git a/cmd/libsnap-confine-private/panic-test.c b/cmd/libsnap-confine-private/panic-test.c
new file mode 100644
index 0000000000..4e9659ab97
--- /dev/null
+++ b/cmd/libsnap-confine-private/panic-test.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2019 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "panic.h"
+#include "panic.c"
+
+#include <glib.h>
+
+static void test_panic(void)
+{
+ if (g_test_subprocess()) {
+ errno = 0;
+ sc_panic("death message");
+ g_test_message("expected die not to return");
+ g_test_fail();
+ return;
+ }
+ g_test_trap_subprocess(NULL, 0, 0);
+ g_test_trap_assert_failed();
+ g_test_trap_assert_stderr("death message\n");
+}
+
+static void test_panic_with_errno(void)
+{
+ if (g_test_subprocess()) {
+ errno = EPERM;
+ sc_panic("death message");
+ g_test_message("expected die not to return");
+ g_test_fail();
+ return;
+ }
+ g_test_trap_subprocess(NULL, 0, 0);
+ g_test_trap_assert_failed();
+ g_test_trap_assert_stderr("death message: Operation not permitted\n");
+}
+
+static void custom_panic_msg(const char *fmt, va_list ap, int errno_copy)
+{
+ fprintf(stderr, "PANIC: ");
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, " (errno: %d)", errno_copy);
+ fprintf(stderr, "\n");
+}
+
+static void custom_panic_exit(void)
+{
+ fprintf(stderr, "EXITING\n");
+ exit(2);
+}
+
+static void test_panic_customization(void)
+{
+ if (g_test_subprocess()) {
+ sc_set_panic_msg_fn(custom_panic_msg);
+ sc_set_panic_exit_fn(custom_panic_exit);
+ errno = 123;
+ sc_panic("death message");
+ g_test_message("expected die not to return");
+ g_test_fail();
+ return;
+ }
+ g_test_trap_subprocess(NULL, 0, 0);
+ g_test_trap_assert_failed();
+ g_test_trap_assert_stderr("PANIC: death message (errno: 123)\n"
+ "EXITING\n");
+ // NOTE: g_test doesn't offer facilities to observe the exit code.
+}
+
+static void __attribute__((constructor)) init(void)
+{
+ g_test_add_func("/panic/panic", test_panic);
+ g_test_add_func("/panic/panic_with_errno", test_panic_with_errno);
+ g_test_add_func("/panic/panic_customization", test_panic_customization);
+}
diff --git a/cmd/libsnap-confine-private/panic.c b/cmd/libsnap-confine-private/panic.c
new file mode 100644
index 0000000000..5a80b89c45
--- /dev/null
+++ b/cmd/libsnap-confine-private/panic.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2019 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "panic.h"
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static sc_panic_exit_fn panic_exit_fn = NULL;
+static sc_panic_msg_fn panic_msg_fn = NULL;
+
+void sc_panic(const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ sc_panicv(fmt, ap);
+ va_end(ap);
+}
+
+void sc_panicv(const char *fmt, va_list ap) {
+ int errno_copy = errno;
+
+ if (panic_msg_fn != NULL) {
+ panic_msg_fn(fmt, ap, errno_copy);
+ } else {
+ vfprintf(stderr, fmt, ap);
+ if (errno != 0) {
+ fprintf(stderr, ": %s\n", strerror(errno_copy));
+ } else {
+ fprintf(stderr, "\n");
+ }
+ }
+
+ if (panic_exit_fn != NULL) {
+ panic_exit_fn();
+ }
+ exit(1);
+}
+
+sc_panic_exit_fn sc_set_panic_exit_fn(sc_panic_exit_fn fn) {
+ sc_panic_exit_fn old = panic_exit_fn;
+ panic_exit_fn = fn;
+ return old;
+}
+
+sc_panic_msg_fn sc_set_panic_msg_fn(sc_panic_msg_fn fn) {
+ sc_panic_msg_fn old = panic_msg_fn;
+ panic_msg_fn = fn;
+ return old;
+}
diff --git a/cmd/libsnap-confine-private/panic.h b/cmd/libsnap-confine-private/panic.h
new file mode 100644
index 0000000000..c25fcd78ea
--- /dev/null
+++ b/cmd/libsnap-confine-private/panic.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2019 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef SC_PANIC_H
+#define SC_PANIC_H
+
+#include <stdarg.h>
+
+/**
+ * sc_panic is an exit-with-message utility function.
+ *
+ * The function takes a printf-like format string that is formatted and printed
+ * somehow. The function then terminates the process by calling exit. Both
+ * aspects can be customized.
+ *
+ * The particular nature of the exit can be customized by calling
+ * sc_set_panic_action. The panic action is a function that is called before
+ * attempting to exit.
+ *
+ * The way the error message is formatted and printed can be customized by
+ * calling sc_set_panic_format_fn(). By default the error is printed to
+ * standard error. If the error is related to a system call failure then errno
+ * can be set to a non-zero value just prior to calling sc_panic. The value
+ * will then be used when crafting the error message.
+ **/
+__attribute__((noreturn, format(printf, 1, 2))) void sc_panic(const char *fmt, ...);
+
+/**
+ * sc_panicv is a variant of sc_panic with an argument list.
+ **/
+__attribute__((noreturn)) void sc_panicv(const char *fmt, va_list ap);
+
+/**
+ * sc_panic_exit_fn is the type of the exit function used by sc_panic().
+ **/
+typedef void (*sc_panic_exit_fn)(void);
+
+/**
+ * sc_set_panic_exit_fn sets the panic exit function.
+ *
+ * When sc_panic is called it will eventually exit the running process. Just
+ * prior to that, it will call the panic exit function, if one has been set.
+ *
+ * If exiting the process is undesired, for example while running in intrd as
+ * pid 1, during the system shutdown phase, then a process can set the panic
+ * exit function. Note that if the specified function returns then panic will
+ * proceed to call exit(3) anyway.
+ *
+ * The old exit function, if any, is returned.
+ **/
+sc_panic_exit_fn sc_set_panic_exit_fn(sc_panic_exit_fn fn);
+
+/**
+ * sc_panic_msg_fn is the type of the format function used by sc_panic().
+ **/
+typedef void (*sc_panic_msg_fn)(const char *fmt, va_list ap, int errno_copy);
+
+/**
+ * sc_set_panic_msg_fn sets the panic message function.
+ *
+ * When sc_panic is called it will attempt to print an error message to
+ * standard error. The message includes information provided by the caller: the
+ * format string, the argument vector for a printf-like function as well as a
+ * copy of the system errno value, which may be zero if the error is not
+ * originated by a system call error.
+ *
+ * If custom formatting of the error message is desired, for example while
+ * running in initrd as pid 1, during the system shutdown phase, then a process
+ * can set the panic message function. Once set the function takes over the
+ * responsibility of printing an error message (in whatever form is
+ * appropriate).
+ *
+ * The old message function, if any, is returned.
+ **/
+sc_panic_msg_fn sc_set_panic_msg_fn(sc_panic_msg_fn fn);
+
+#endif