From 62859010cb607daddd4d361d415f62558b3b744c Mon Sep 17 00:00:00 2001 From: "Hongli Lai (Phusion)" Date: Fri, 31 Jan 2014 15:44:50 +0100 Subject: [PATCH] Allow running scripts during startup. --- Changelog.md | 1 + README.md | 19 ++++++++++++++++ image/my_init | 60 +++++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 76 insertions(+), 4 deletions(-) diff --git a/Changelog.md b/Changelog.md index 3cbd758..d2456c4 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ## 0.9.3 (release date: pending) * It looks like Docker changed their Ubuntu 12.04 base image, thereby breaking our Dockerfile. This has been fixed. + * The init system (`/sbin/my_init`) now supports running scripts during startup. You can put startup scripts `/etc/my_init.d`. `/etc/rc.local` is also run during startup. ## 0.9.2 (release date: 2013-12-11) diff --git a/README.md b/README.md index e52fdb6..9dfcabb 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,25 @@ Here's an example showing you how to a memached server runit entry can be made. Note that the shell script must run the daemon **without letting it daemonize/fork it**. Usually, daemons provide a command line flag or a config file option for that. +### Running scripts during container startup + +The baseimage-docker init system, `/sbin/my_init`, runs the following scripts during startup, in the following order: + + * All executable scripts in `/etc/my_init.d`, if this directory exists. The scripts are run during in lexicographic order. + * The script `/etc/rc.local`, if this file exists. + +All scripts must exit correctly, e.g. with exit code 0. If any script exits with a non-zero exit code, the booting will fail. + +The following example shows how you can add a startup script. This script simply logs the time of boot to the file /tmp/boottime.txt. + + ### In logtime.sh (make sure this file is chmod +x): + #!/bin/sh + date > /tmp/boottime.txt + + ### In Dockerfile: + RUN mkdir -p /etc/my_init.d + ADD logtime.sh /etc/my_init.d/logtime.sh + ### Login to the container You can use SSH to login to any container that is based on baseimage-docker. diff --git a/image/my_init b/image/my_init index d11db60..b06db69 100755 --- a/image/my_init +++ b/image/my_init @@ -1,5 +1,24 @@ #!/usr/bin/python2 -import os, sys, signal, errno +import os, sys, stat, signal, errno + +pid = None +status = None + +def listdir(path): + try: + result = os.stat(path) + except OSError: + return [] + if stat.S_ISDIR(result.st_mode): + return sorted(os.listdir(path)) + else: + return [] + +def is_exe(path): + try: + return os.path.isfile(path) and os.access(path, os.X_OK) + except OSError: + return False def reap_child(signum, frame): global pid, status, waiting_for_runit @@ -10,20 +29,52 @@ def reap_child(signum, frame): except OSError: pass -def stop_runit(signum, frame): +def stop_child_process(name): global pid - print("*** Shutting down runit (PID %d)..." % pid) + print("*** Shutting down %s (PID %d)..." % (name, pid)) try: os.kill(pid, signal.SIGHUP) except OSError: pass +def run_command_killable(*argv): + global pid + filename = argv[0] + pid = os.spawnvp(os.P_NOWAIT, filename, argv) + signal.signal(signal.SIGINT, lambda signum, frame: stop_child_process(filename)) + signal.signal(signal.SIGTERM, lambda signum, frame: stop_child_process(filename)) + try: + this_pid, status = os.waitpid(pid, 0) + except OSError as e: + if e.errno == errno.EINTR: + sys.exit(2) + else: + raise + finally: + signal.signal(signal.SIGINT, signal.SIG_DFL) + signal.signal(signal.SIGTERM, signal.SIG_DFL) + if status != 0: + sys.stderr.write("*** %s failed with exit code %d\n" % (filename, status)) + sys.exit(1) + +# Run /etc/my_init.d/* +for name in listdir("/etc/my_init.d"): + filename = "/etc/my_init.d/" + name + if is_exe(filename): + print("*** Running %s..." % filename) + run_command_killable(filename) + +# Run /etc/rc.local. +if is_exe("/etc/rc.local"): + print("*** Running /etc/rc.local...") + run_command_killable("/etc/rc.local") + # Start runit. signal.signal(signal.SIGCHLD, reap_child) print("*** Booting runit...") pid = os.spawnl(os.P_NOWAIT, "/usr/sbin/runsvdir-start", "/usr/sbin/runsvdir-start") print("*** Runit started as PID %d" % pid) -signal.signal(signal.SIGTERM, stop_runit) +signal.signal(signal.SIGTERM, lambda signum, frame: stop_child_process("runit")) # Wait for runit, and while waiting, reap any adopted orphans. done = False @@ -62,3 +113,4 @@ while not done: if not done: time.sleep(0.5) shutdown() +