Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Option to restart the cild process #186 #187

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ ARG CC

# Persist ARGs into the image

RUN ln -s /usr/bin/python3 /usr/bin/python

ENV ARCH_SUFFIX="$ARCH_SUFFIX" \
ARCH_NATIVE="$ARCH_NATIVE" \
CC="$CC"
6 changes: 4 additions & 2 deletions ci/install_deps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ set -o xtrace
DEPS=(
build-essential git gdb valgrind cmake rpm file
libcap-dev python3-dev python3-pip python3-setuptools
hardening-includes gnupg
hardening-includes gnupg curl
)

case "${ARCH_SUFFIX-}" in
Expand All @@ -26,5 +26,7 @@ apt-get update
apt-get install --no-install-recommends --yes "${DEPS[@]}"
rm -rf /var/lib/apt/lists/*

python3 -m pip install --upgrade pip
curl -fsSL -o- https://bootstrap.pypa.io/pip/3.5/get-pip.py | python3.5
#python3 -m pip install --upgrade pip
python3 -m pip install virtualenv
python3 -m pip install importlib-metadata==2.1.0
83 changes: 73 additions & 10 deletions src/tini.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
#define STATUS_MAX 255
#define STATUS_MIN 0

#define RESPAWN_CHILD -2

typedef struct {
sigset_t* const sigmask_ptr;
struct sigaction* const sigttin_action_ptr;
Expand Down Expand Up @@ -90,11 +92,11 @@ static int32_t expect_status[(STATUS_MAX - STATUS_MIN + 1) / 32];

#ifdef PR_SET_CHILD_SUBREAPER
#define HAS_SUBREAPER 1
#define OPT_STRING "p:hvwgle:s"
#define OPT_STRING "p:hvwgle:r:t:s"
#define SUBREAPER_ENV_VAR "TINI_SUBREAPER"
#else
#define HAS_SUBREAPER 0
#define OPT_STRING "p:hvwgle:"
#define OPT_STRING "p:hvwgle:r:t:"
#endif

#define VERBOSITY_ENV_VAR "TINI_VERBOSITY"
Expand Down Expand Up @@ -128,6 +130,10 @@ To fix the problem, "
#endif
"run Tini as PID 1.";

static unsigned int restart_signal = 0;
static unsigned int child_term_signal = SIGTERM;
static bool is_restarting = false;

int restore_signals(const signal_configuration_t* const sigconf_ptr) {
if (sigprocmask(SIG_SETMASK, sigconf_ptr->sigmask_ptr, NULL)) {
PRINT_FATAL("Restoring child signal mask failed: '%s'", strerror(errno));
Expand Down Expand Up @@ -248,6 +254,9 @@ void print_usage(char* const name, FILE* const file) {
fprintf(file, " -w: Print a warning when processes are getting reaped.\n");
fprintf(file, " -g: Send signals to the child's process group.\n");
fprintf(file, " -e EXIT_CODE: Remap EXIT_CODE (from 0 to 255) to 0 (can be repeated).\n");
fprintf(file, " -r RESTART_SIGNAL: Restart(terminate and start) child process on RESTART_SIGNAL, e.g. \"-r SIGUSR1\".\n");
fprintf(file, " -t CHILD_TERM_SIGNAL: Signal to terminate the child process for a restart, defaults to SIGTERM, e.g. \"-t SIGTERM\".\n");
fprintf(file, " -e EXIT_CODE: Remap EXIT_CODE (from 0 to 255) to 0 (can be repeated).\n");
fprintf(file, " -l: Show license and exit.\n");
#endif

Expand All @@ -273,13 +282,13 @@ void print_license(FILE* const file) {
}
}

int set_pdeathsig(char* const arg) {
int set_signal_number(char* const signal_name, unsigned int* signal_number) {
size_t i;

for (i = 0; i < ARRAY_LEN(signal_names); i++) {
if (strcmp(signal_names[i].name, arg) == 0) {
if (strcmp(signal_names[i].name, signal_name) == 0) {
/* Signals start at value "1" */
parent_death_signal = signal_names[i].number;
*signal_number = signal_names[i].number;
return 0;
}
}
Expand Down Expand Up @@ -329,13 +338,35 @@ int parse_args(const int argc, char* const argv[], char* (**child_args_ptr_ptr)[
break;
#endif
case 'p':
if (set_pdeathsig(optarg)) {
if (set_signal_number(optarg, &parent_death_signal)) {
PRINT_FATAL("Not a valid option for -p: %s", optarg);
*parse_fail_exitcode_ptr = 1;
return 1;
}
break;

case 'r':
if (set_signal_number(optarg, &restart_signal)) {
PRINT_FATAL("Not a valid option for -r: %s", optarg);
*parse_fail_exitcode_ptr = 1;
return 1;
}

if (restart_signal == SIGCHLD) {
PRINT_FATAL("SIGCHLD not a valid option for -r: %s", optarg);
*parse_fail_exitcode_ptr = 1;
return 1;
}
break;

case 't':
if (set_signal_number(optarg, &child_term_signal)) {
PRINT_FATAL("Not a valid option for -t: %s", optarg);
*parse_fail_exitcode_ptr = 1;
return 1;
}
break;

case 'v':
verbosity++;
break;
Expand Down Expand Up @@ -498,6 +529,10 @@ int configure_signals(sigset_t* const parent_sigset_ptr, const signal_configurat
return 0;
}

bool is_restart_enabled() {
return restart_signal != 0;
}

int wait_and_forward_signal(sigset_t const* const parent_sigset_ptr, pid_t const child_pid) {
siginfo_t sig;

Expand All @@ -521,6 +556,17 @@ int wait_and_forward_signal(sigset_t const* const parent_sigset_ptr, pid_t const
PRINT_DEBUG("Received SIGCHLD");
break;
default:
if (restart_signal == (unsigned)sig.si_signo && is_restart_enabled()) {
PRINT_DEBUG("Received process restart signal: %d", sig.si_signo);
// Shutdown the child process.
// Success full termination will be known when corresponding
// SIGCHLD is received.
PRINT_DEBUG("Terminating child process with pid %d", child_pid);
kill(child_pid, child_term_signal);
is_restarting = true;
break;
}

PRINT_DEBUG("Passing signal: '%s'", strsignal(sig.si_signo));
/* Forward anything else */
if (kill(kill_process_group ? -child_pid : child_pid, sig.si_signo)) {
Expand All @@ -542,6 +588,8 @@ int reap_zombies(const pid_t child_pid, int* const child_exitcode_ptr) {
pid_t current_pid;
int current_status;

*child_exitcode_ptr = -1;

while (1) {
current_pid = waitpid(-1, &current_status, WNOHANG);

Expand Down Expand Up @@ -588,6 +636,14 @@ int reap_zombies(const pid_t child_pid, int* const child_exitcode_ptr) {
if (INT32_BITFIELD_TEST(expect_status, *child_exitcode_ptr)) {
*child_exitcode_ptr = 0;
}

if (is_restarting && *child_exitcode_ptr == 0) {
/* The child process exited normally with a success exit
* code 0 and we are restarting. Indicate repsawn of the child.
*/
*child_exitcode_ptr = RESPAWN_CHILD;
}
is_restarting = false;
} else if (warn_on_reap > 0) {
PRINT_WARNING("Reaped zombie process with pid=%i", current_pid);
}
Expand All @@ -603,7 +659,6 @@ int reap_zombies(const pid_t child_pid, int* const child_exitcode_ptr) {
return 0;
}


int main(int argc, char *argv[]) {
pid_t child_pid;

Expand Down Expand Up @@ -660,7 +715,10 @@ int main(int argc, char *argv[]) {
if (spawn_ret) {
return spawn_ret;
}
free(child_args_ptr);

if (!is_restart_enabled()) {
free(child_args_ptr);
}

while (1) {
/* Wait for one signal, and forward it */
Expand All @@ -673,8 +731,13 @@ int main(int argc, char *argv[]) {
return 1;
}

if (child_exitcode != -1) {
PRINT_TRACE("Exiting: child has exited");
if (is_restart_enabled() && child_exitcode == RESPAWN_CHILD) {
spawn_ret = spawn(&child_sigconf, *child_args_ptr, &child_pid);
if (spawn_ret) {
return spawn_ret;
}
} else if (child_exitcode != -1) {
PRINT_TRACE("Exiting: child has exited %d", child_exitcode);
return child_exitcode;
}
}
Expand Down
28 changes: 28 additions & 0 deletions test/restart/restart-test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env python
import signal
import os
import sys
import time


def sigterm_handler(sig, frame):
print("SIGTERM received - exiting gracefully")
sys.exit(0)


def sigusr1_handler(sig, frame):
print("SIGUSR1 received - exiting")
sys.exit(1)


def main():
signal.signal(signal.SIGTERM, sigterm_handler)
signal.signal(signal.SIGUSR1, sigusr1_handler)
print("#")
print("Starting")
while True:
time.sleep(1)


if __name__ == "__main__":
main()
Loading