system-init: set as subreaper
This commit is contained in:
parent
b07faab778
commit
f8568cf5fc
@ -17,7 +17,7 @@ var (
|
|||||||
func main() {
|
func main() {
|
||||||
configPath := flag.String("config", "config.yaml", "config to load (\"-\" for stdin)")
|
configPath := flag.String("config", "config.yaml", "config to load (\"-\" for stdin)")
|
||||||
doFiles := flag.Bool("files", false, "apply files")
|
doFiles := flag.Bool("files", false, "apply files")
|
||||||
filesFilters := flag.String("files-filters", "*", "comma-separated filters to select files to apply")
|
filesFilters := flag.String("files-filters", "", "comma-separated filters to select files to apply")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
log.SetConsole(os.Stderr)
|
log.SetConsole(os.Stderr)
|
||||||
@ -41,7 +41,10 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if *doFiles {
|
if *doFiles {
|
||||||
filters := strings.Split(*filesFilters, ",")
|
filters := []string{}
|
||||||
|
if *filesFilters != "" {
|
||||||
|
filters = strings.Split(*filesFilters, ",")
|
||||||
|
}
|
||||||
if err = apply.Files(cfg, log, filters...); err != nil {
|
if err = apply.Files(cfg, log, filters...); err != nil {
|
||||||
log.Taint(dlog.Fatal, "failed to apply files: ", err)
|
log.Taint(dlog.Fatal, "failed to apply files: ", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -1,8 +1,39 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
// void handleSignals();
|
import (
|
||||||
import "C"
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"novit.nc/direktil/pkg/log"
|
||||||
|
)
|
||||||
|
|
||||||
func handleChildren() {
|
func handleChildren() {
|
||||||
C.handleSignals()
|
// set us as a sub-reaper
|
||||||
|
if err := unix.Prctl(unix.PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0); err != nil {
|
||||||
|
initLog.Taintf(log.Error, "reaper: failed to set myself a child sub-reaper: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sigchld := make(chan os.Signal, 10)
|
||||||
|
signal.Notify(sigchld, unix.SIGCHLD)
|
||||||
|
|
||||||
|
for range sigchld {
|
||||||
|
// reap children
|
||||||
|
var (
|
||||||
|
ws unix.WaitStatus
|
||||||
|
rus unix.Rusage
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
pid, err := unix.Wait4(-1, &ws, unix.WNOHANG, &rus)
|
||||||
|
if err != nil {
|
||||||
|
if err == unix.ECHILD {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
initLog.Taintf(log.Warning, "reaper: wait4 failed: %v", err)
|
||||||
|
}
|
||||||
|
if pid <= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,139 +0,0 @@
|
|||||||
// extracted from https://raw.githubusercontent.com/Yelp/dumb-init/master/dumb-init.c
|
|
||||||
|
|
||||||
#include <assert.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#define PRINTERR(...) do { \
|
|
||||||
fprintf(stderr, "[dumb-init] " __VA_ARGS__); \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
#define DEBUG(...) do { \
|
|
||||||
if (debug) { \
|
|
||||||
PRINTERR(__VA_ARGS__); \
|
|
||||||
} \
|
|
||||||
} while (0)
|
|
||||||
|
|
||||||
// Signals we care about are numbered from 1 to 31, inclusive.
|
|
||||||
// (32 and above are real-time signals.)
|
|
||||||
// TODO: this is likely not portable outside of Linux, or on strange architectures
|
|
||||||
#define MAXSIG 31
|
|
||||||
|
|
||||||
// Indices are one-indexed (signal 1 is at index 1). Index zero is unused.
|
|
||||||
int signal_rewrite[MAXSIG + 1] = {[0 ... MAXSIG] = -1};
|
|
||||||
|
|
||||||
pid_t child_pid = -1;
|
|
||||||
char debug = 0;
|
|
||||||
char use_setsid = 1;
|
|
||||||
|
|
||||||
int translate_signal(int signum) {
|
|
||||||
if (signum <= 0 || signum > MAXSIG) {
|
|
||||||
return signum;
|
|
||||||
} else {
|
|
||||||
int translated = signal_rewrite[signum];
|
|
||||||
if (translated == -1) {
|
|
||||||
return signum;
|
|
||||||
} else {
|
|
||||||
DEBUG("Translating signal %d to %d.\n", signum, translated);
|
|
||||||
return translated;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void forward_signal(int signum) {
|
|
||||||
signum = translate_signal(signum);
|
|
||||||
if (signum != 0) {
|
|
||||||
kill(use_setsid ? -child_pid : child_pid, signum);
|
|
||||||
DEBUG("Forwarded signal %d to children.\n", signum);
|
|
||||||
} else {
|
|
||||||
DEBUG("Not forwarding signal %d to children (ignored).\n", signum);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The dumb-init signal handler.
|
|
||||||
*
|
|
||||||
* The main job of this signal handler is to forward signals along to our child
|
|
||||||
* process(es). In setsid mode, this means signaling the entire process group
|
|
||||||
* rooted at our child. In non-setsid mode, this is just signaling the primary
|
|
||||||
* child.
|
|
||||||
*
|
|
||||||
* In most cases, simply proxying the received signal is sufficient. If we
|
|
||||||
* receive a job control signal, however, we should not only forward it, but
|
|
||||||
* also sleep dumb-init itself.
|
|
||||||
*
|
|
||||||
* This allows users to run foreground processes using dumb-init and to
|
|
||||||
* control them using normal shell job control features (e.g. Ctrl-Z to
|
|
||||||
* generate a SIGTSTP and suspend the process).
|
|
||||||
*
|
|
||||||
* The libc manual is useful:
|
|
||||||
* https://www.gnu.org/software/libc/manual/html_node/Job-Control-Signals.html
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void handle_signal(int signum) {
|
|
||||||
DEBUG("Received signal %d.\n", signum);
|
|
||||||
if (signum == SIGCHLD) {
|
|
||||||
int status, exit_status;
|
|
||||||
pid_t killed_pid;
|
|
||||||
while ((killed_pid = waitpid(-1, &status, WNOHANG)) > 0) {
|
|
||||||
if (WIFEXITED(status)) {
|
|
||||||
exit_status = WEXITSTATUS(status);
|
|
||||||
DEBUG("A child with PID %d exited with exit status %d.\n", killed_pid, exit_status);
|
|
||||||
} else {
|
|
||||||
assert(WIFSIGNALED(status));
|
|
||||||
exit_status = 128 + WTERMSIG(status);
|
|
||||||
DEBUG("A child with PID %d was terminated by signal %d.\n", killed_pid, exit_status - 128);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (killed_pid == child_pid) {
|
|
||||||
forward_signal(SIGTERM); // send SIGTERM to any remaining children
|
|
||||||
DEBUG("Child exited with status %d. Goodbye.\n", exit_status);
|
|
||||||
exit(exit_status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
forward_signal(signum);
|
|
||||||
if (signum == SIGTSTP || signum == SIGTTOU || signum == SIGTTIN) {
|
|
||||||
DEBUG("Suspending self due to TTY signal.\n");
|
|
||||||
kill(getpid(), SIGSTOP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_rewrite_to_sigstop_if_not_defined(int signum) {
|
|
||||||
if (signal_rewrite[signum] == -1)
|
|
||||||
signal_rewrite[signum] = SIGSTOP;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A dummy signal handler used for signals we care about.
|
|
||||||
// On the FreeBSD kernel, ignored signals cannot be waited on by `sigwait` (but
|
|
||||||
// they can be on Linux). We must provide a dummy handler.
|
|
||||||
// https://lists.freebsd.org/pipermail/freebsd-ports/2009-October/057340.html
|
|
||||||
void dummy(int signum) {}
|
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// Go entry point
|
|
||||||
//
|
|
||||||
void handleSignals() {
|
|
||||||
sigset_t all_signals;
|
|
||||||
sigfillset(&all_signals);
|
|
||||||
sigprocmask(SIG_BLOCK, &all_signals, NULL);
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
for (i = 1; i <= MAXSIG; i++)
|
|
||||||
signal(i, dummy);
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
int signum;
|
|
||||||
sigwait(&all_signals, &signum);
|
|
||||||
handle_signal(signum);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user