stage1: remove --pid-file patches from systemd-nspawn

Instead of asking systemd-nspawn to write its "pid" file, rkt can write
the parent pid "ppid". Then, update getPID() to get the pid file from
/proc/$ppid/task/$ppid/children. In this way, we don't need to patch
systemd-nspawn.

getPID() is able to read both the "pid" file and the "ppid" file, in
order to work with alternative stage1s which choose to implement one or
the other method.

coreos/rkt#910
This commit is contained in:
Alban Crequy
2015-06-03 12:58:09 +02:00
parent be40b0eb67
commit 2c25275523
8 changed files with 107 additions and 422 deletions
+90 -3
View File
@@ -19,10 +19,13 @@ package main
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"syscall"
"time"
@@ -731,16 +734,97 @@ func (p *pod) getState() string {
return state
}
type ErrChildNotReady struct {
}
func (e ErrChildNotReady) Error() string {
return fmt.Sprintf("Child not ready")
}
// Returns the pid of the child, or ErrChildNotReady if not ready
func getChildPID(ppid int) (int, error) {
var pid int
// If possible, get the child in O(1). Fallback on O(n) when the kernel does not have
// either CONFIG_PROC_CHILDREN or CONFIG_CHECKPOINT_RESTORE
_, err := os.Stat("/proc/1/task/1/children")
if err == nil {
b, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/task/%d/children", ppid, ppid))
if err == nil {
children := strings.SplitN(string(b), " ", 2)
if len(children) == 2 && children[1] != "" {
return -1, fmt.Errorf("too many children of pid %d", ppid)
}
if _, err := fmt.Sscanf(children[0], "%d ", &pid); err == nil {
return pid, nil
}
}
return -1, ErrChildNotReady{}
}
// Fallback on the slower method
fdir, err := os.Open(`/proc`)
if err != nil {
return -1, err
}
defer fdir.Close()
for {
fi, err := fdir.Readdir(1)
if err == io.EOF {
break
}
if err != nil {
return -1, err
}
var pid64 int64
if pid64, err = strconv.ParseInt(fi[0].Name(), 10, 0); err != nil {
continue
}
filename := fmt.Sprintf("/proc/%d/stat", pid64)
statBytes, err := ioutil.ReadFile(filename)
if err != nil {
// The process just died? It's not the one we want then.
continue
}
statFields := strings.SplitN(string(statBytes), " ", 5)
if len(statFields) != 5 {
return -1, fmt.Errorf("incomplete file %q", filename)
}
if statFields[3] == fmt.Sprintf("%d", ppid) {
return int(pid64), nil
}
}
return -1, ErrChildNotReady{}
}
// getPID returns the pid of the pod.
func (p *pod) getPID() (pid int, err error) {
// rkt supports two methods to find the container's PID 1:
// the pid file and the ppid file.
// See Documentation/devel/stage1-implementors-guide.md
for {
var ppid int
// No do { } while() Golang, seriously?
for first := true; first || (os.IsNotExist(err) && p.isRunning()); first = false {
pid, err = p.readIntFromFile("pid")
if err == nil {
return
}
ppid, err = p.readIntFromFile("ppid")
if err == nil {
pid, err = getChildPID(ppid)
if err == nil {
return pid, nil
}
if _, ok := err.(ErrChildNotReady); ok {
err = nil
} else {
return -1, err
}
}
// There's a window between a pod transitioning to run and the pid file being created by stage1.
// The window shouldn't be large so we just delay and retry here. If stage1 fails to reach the
// point of pid file creation, it will exit and p.isRunning() becomes false since we refreshState below.
@@ -749,8 +833,11 @@ func (p *pod) getPID() (pid int, err error) {
if err := p.refreshState(); err != nil {
return -1, err
}
if !os.IsNotExist(err) || !p.isRunning() {
return -1, err
}
}
return
}
// getStage1Hash returns the hash of the stage1 image used in this pod
+2 -1
View File
@@ -52,5 +52,6 @@ for link in ${BUSYBOX_LINKS}; do
done
echo "Busybox installed. Use the following command to enter pod's stage1:"
SYSTEMD_PID=$(sudo cat "${RKT_RUN_DIR}/${UUID}/pid")
SYSTEMD_PPID=$(sudo cat "${RKT_RUN_DIR}/${UUID}/ppid")
SYSTEMD_PID=$(sudo cat /proc/$SYSTEMD_PPID/task/$SYSTEMD_PPID/children)
echo sudo nsenter -m -u -i -p -t ${SYSTEMD_PID}
+15 -6
View File
@@ -192,12 +192,6 @@ func getArgsEnv(p *Pod, flavor string, systemdStage1Version string, debug bool)
// before execing init"), fds remain open, so --keep-fd is not needed.
}
out, err := os.Getwd()
if err != nil {
return nil, nil, err
}
args = append(args, fmt.Sprintf("--pid-file=%v", filepath.Join(out, "pid")))
if machinedRegister() {
args = append(args, fmt.Sprintf("--register=true"))
} else {
@@ -381,6 +375,21 @@ func stage1() int {
return 3
}
// write ppid file as specified in
// Documentation/devel/stage1-implementors-guide.md
out, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "Cannot get current working directory: %v\n", err)
return 4
}
// we are the parent of the process that is PID 1 in the container so we write our PID to "ppid"
err = ioutil.WriteFile(filepath.Join(out, "ppid"),
[]byte(fmt.Sprintf("%d\n", os.Getpid())), 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "Cannot write ppid file: %v\n", err)
return 4
}
var execFn func() error
if privNet.Any() {
-39
View File
@@ -28,16 +28,12 @@
/* hack to make systemd-nspawn execute on non-sysd systems:
* - intercept lstat() so lstat of /run/systemd/system always succeeds and returns a directory
* - intercept close() to prevent nspawn closing the rkt lock, set it to CLOEXEC instead
* - intercept syscall(SYS_clone) to record the pod's pid
*/
#define ENV_LOCKFD "RKT_LOCK_FD"
#define PIDFILE_TMP "pid.tmp"
#define PIDFILE "pid"
static int (*libc_lxstat)(int, const char *, struct stat *);
static int (*libc_close)(int);
static long (*libc_syscall)(long number, ...);
static int lock_fd = -1;
static __attribute__((constructor)) void wrapper_init(void)
@@ -47,7 +43,6 @@ static __attribute__((constructor)) void wrapper_init(void)
lock_fd = atoi(env);
libc_lxstat = dlsym(RTLD_NEXT, "__lxstat");
libc_close = dlsym(RTLD_NEXT, "close");
libc_syscall = dlsym(RTLD_NEXT, "syscall");
}
int __lxstat(int ver, const char *path, struct stat *stat)
@@ -69,37 +64,3 @@ int close(int fd)
return libc_close(fd);
}
long syscall(long number, ...)
{
unsigned long clone_flags;
va_list ap;
long ret;
/* XXX: we're targeting systemd-nspawn with this shim, its only syscall() use is __NR_clone */
if(number != __NR_clone)
return -1;
va_start(ap, number);
clone_flags = va_arg(ap, unsigned long);
va_end(ap);
ret = libc_syscall(number, clone_flags, NULL);
if(ret > 0) {
int fd;
/* in parent; try record the pod's pid */
if((fd = open(PIDFILE_TMP, O_CREAT|O_WRONLY|O_SYNC, 0640)) != -1) {
int len;
char buf[20];
if((len = snprintf(buf, sizeof(buf), "%li\n", ret)) != -1)
if(write(fd, buf, len) == len)
rename(PIDFILE_TMP, PIDFILE);
libc_close(fd);
}
}
return ret;
}
@@ -1,95 +0,0 @@
From 54802a88288c44c098d0cbe884e698cdbd778de2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iago=20L=C3=B3pez=20Galeiras?= <iago@endocode.com>
Date: Thu, 21 May 2015 16:52:48 +0200
Subject: [PATCH] nspawn: add --pid-file
Simple mechanism for recording the container's pid independent of any
external daemons/systemd services.
[patch ported to v219 -alban]
[patch ported to git master 2015-05-20 -alban]
[patch ported to git master 2015-05-21 -iaguis]
---
src/nspawn/nspawn.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index d6b24c6..be69172 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -190,6 +190,7 @@ static bool arg_quiet = false;
static bool arg_share_system = false;
static bool arg_register = true;
static bool arg_keep_unit = false;
+static const char *arg_pid_file = NULL;
static char **arg_network_interfaces = NULL;
static char **arg_network_macvlan = NULL;
static char **arg_network_ipvlan = NULL;
@@ -271,6 +272,7 @@ static void help(void) {
" --keep-unit Do not register a scope for the machine, reuse\n"
" the service unit nspawn is running in\n"
" --volatile[=MODE] Run the system in volatile mode\n"
+ " --pid-file=FILE Write child pid to FILE\n"
, program_invocation_short_name);
}
@@ -397,6 +399,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_SHARE_SYSTEM,
ARG_REGISTER,
ARG_KEEP_UNIT,
+ ARG_PID_FILE,
ARG_NETWORK_INTERFACE,
ARG_NETWORK_MACVLAN,
ARG_NETWORK_IPVLAN,
@@ -437,6 +440,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "share-system", no_argument, NULL, ARG_SHARE_SYSTEM },
{ "register", required_argument, NULL, ARG_REGISTER },
{ "keep-unit", no_argument, NULL, ARG_KEEP_UNIT },
+ { "pid-file", required_argument, NULL, ARG_PID_FILE },
{ "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE },
{ "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN },
{ "network-ipvlan", required_argument, NULL, ARG_NETWORK_IPVLAN },
@@ -820,6 +824,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_keep_unit = true;
break;
+ case ARG_PID_FILE:
+ arg_pid_file = optarg;
+ break;
+
case ARG_PERSONALITY:
arg_personality = personality_from_string(optarg);
@@ -3920,6 +3928,14 @@ static int wait_for_container(pid_t pid, ContainerStatus *container) {
return r;
}
+static int write_pid(const char *f, int pid) {
+ char pid_str[13]; /* -2147483646\n\0 */
+
+ snprintf(pid_str, sizeof(pid_str), "%i\n", pid);
+ return write_string_file_atomic(f, pid_str);
+}
+
+
static void nop_handler(int sig) {}
static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
@@ -4875,6 +4891,14 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ if (arg_pid_file) {
+ r = write_pid(arg_pid_file, pid);
+ if (r < 0) {
+ log_error_errno(r, "Failed to write pid file: %m");
+ goto finish;
+ }
+ }
+
sd_notifyf(false,
"READY=1\n"
"STATUS=Container running.\n"
--
2.4.1
@@ -1,90 +0,0 @@
From b7c75246350bd7af55f80dd9ac89c23682765aaf Mon Sep 17 00:00:00 2001
From: Vito Caputo <vito.caputo@coreos.com>
Date: Mon, 16 Mar 2015 15:13:49 -0700
Subject: [PATCH 3/3] nspawn: add --pid-file
Simple mechanism for recording the container's pid independent of any external
daemons/systemd services.
---
src/nspawn/nspawn.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 0277a12..6650629 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -154,6 +154,7 @@ static bool arg_share_system = false;
static bool arg_register = true;
static bool arg_keep_unit = false;
static int arg_keep_fd = -1;
+static const char *arg_pid_file = NULL;
static char **arg_network_interfaces = NULL;
static char **arg_network_macvlan = NULL;
static bool arg_network_veth = false;
@@ -209,7 +210,8 @@ static int help(void) {
" --register=BOOLEAN Register container as machine\n"
" --keep-unit Do not register a scope for the machine, reuse\n"
" the service unit nspawn is running in\n"
- " --keep-fd=FDNUM Do not close the specified file descriptor\n",
+ " --keep-fd=FDNUM Do not close the specified file descriptor\n"
+ " --pid-file=FILE Write child pid to FILE\n",
program_invocation_short_name);
return 0;
@@ -233,6 +235,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_REGISTER,
ARG_KEEP_UNIT,
ARG_KEEP_FD,
+ ARG_PID_FILE,
ARG_NETWORK_INTERFACE,
ARG_NETWORK_MACVLAN,
ARG_NETWORK_VETH,
@@ -265,6 +268,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "register", required_argument, NULL, ARG_REGISTER },
{ "keep-unit", no_argument, NULL, ARG_KEEP_UNIT },
{ "keep-fd", required_argument, NULL, ARG_KEEP_FD },
+ { "pid-file", required_argument, NULL, ARG_PID_FILE },
{ "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE },
{ "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN },
{ "network-veth", no_argument, NULL, ARG_NETWORK_VETH },
@@ -561,6 +565,10 @@ static int parse_argv(int argc, char *argv[]) {
}
break;
+ case ARG_PID_FILE:
+ arg_pid_file = optarg;
+ break;
+
case ARG_PERSONALITY:
arg_personality = personality_from_string(optarg);
@@ -2757,6 +2765,13 @@ static int wait_for_container(pid_t pid, ContainerStatus *container) {
return r;
}
+static int write_pid(const char *f, int pid) {
+ char pid_str[13]; /* -2147483646\n\0 */
+
+ snprintf(pid_str, sizeof(pid_str), "%i\n", pid);
+ return write_string_file_atomic(f, pid_str);
+}
+
static void nop_handler(int sig) {}
int main(int argc, char *argv[]) {
@@ -3317,6 +3332,12 @@ int main(int argc, char *argv[]) {
eventfds[1] = safe_close(eventfds[1]);
if (r >= 0) {
+ if (arg_pid_file) {
+ r = write_pid(arg_pid_file, pid);
+ if (r < 0)
+ goto finish;
+ }
+
r = register_machine(pid);
if (r < 0)
goto finish;
--
2.1.4
@@ -1,92 +0,0 @@
From 30b585a02cfad4c0f420f70146bb6bc4566def85 Mon Sep 17 00:00:00 2001
From: Vito Caputo <vito.caputo@coreos.com>
Date: Mon, 16 Mar 2015 15:13:49 -0700
Subject: [PATCH 2/4] nspawn: add --pid-file
Simple mechanism for recording the container's pid independent of any external
daemons/systemd services.
[patch ported to v219 -alban]
---
src/nspawn/nspawn.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index b7c9da5..5ea07c4 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -179,6 +179,7 @@ static bool arg_share_system = false;
static bool arg_register = true;
static bool arg_keep_unit = false;
static int arg_keep_fd = -1;
+static const char *arg_pid_file = NULL;
static char **arg_network_interfaces = NULL;
static char **arg_network_macvlan = NULL;
static char **arg_network_ipvlan = NULL;
@@ -248,6 +249,7 @@ static void help(void) {
" the service unit nspawn is running in\n"
" --volatile[=MODE] Run the system in volatile mode\n"
" --keep-fd=FDNUM Do not close the specified file descriptor\n"
+ " --pid-file=FILE Write child pid to FILE\n"
, program_invocation_short_name);
}
@@ -290,6 +292,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_REGISTER,
ARG_KEEP_UNIT,
ARG_KEEP_FD,
+ ARG_PID_FILE,
ARG_NETWORK_INTERFACE,
ARG_NETWORK_MACVLAN,
ARG_NETWORK_IPVLAN,
@@ -326,6 +329,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "register", required_argument, NULL, ARG_REGISTER },
{ "keep-unit", no_argument, NULL, ARG_KEEP_UNIT },
{ "keep-fd", required_argument, NULL, ARG_KEEP_FD },
+ { "pid-file", required_argument, NULL, ARG_PID_FILE },
{ "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE },
{ "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN },
{ "network-ipvlan", required_argument, NULL, ARG_NETWORK_IPVLAN },
@@ -655,6 +659,10 @@ static int parse_argv(int argc, char *argv[]) {
}
break;
+ case ARG_PID_FILE:
+ arg_pid_file = optarg;
+ break;
+
case ARG_PERSONALITY:
arg_personality = personality_from_string(optarg);
@@ -3495,6 +3503,13 @@ static int wait_for_container(pid_t pid, ContainerStatus *container) {
return r;
}
+static int write_pid(const char *f, int pid) {
+ char pid_str[13]; /* -2147483646\n\0 */
+
+ snprintf(pid_str, sizeof(pid_str), "%i\n", pid);
+ return write_string_file_atomic(f, pid_str);
+}
+
static void nop_handler(int sig) {}
static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
@@ -4237,6 +4252,14 @@ int main(int argc, char *argv[]) {
_cleanup_rtnl_unref_ sd_rtnl *rtnl = NULL;
char last_char = 0;
+ if (arg_pid_file) {
+ r = write_pid(arg_pid_file, pid);
+ if (r < 0) {
+ log_error_errno(r, "Failed to write pid file: %m");
+ goto finish;
+ }
+ }
+
sd_notifyf(false,
"READY=1\n"
"STATUS=Container running.\n"
--
2.1.4
@@ -1,96 +0,0 @@
From a7bfa4b16a833ff5e2b25e28b406c300a8d3e318 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Iago=20L=C3=B3pez=20Galeiras?= <iago@endocode.com>
Date: Thu, 21 May 2015 16:52:48 +0200
Subject: [PATCH] nspawn: add --pid-file
Simple mechanism for recording the container's pid independent of any
external daemons/systemd services.
[patch ported to v219 -alban]
[patch ported to git master 2015-05-20 -alban]
[patch ported to git master 2015-05-21 -iaguis]
[patch ported to v220 2015-05-26 -alban]
---
src/nspawn/nspawn.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
index 5009363..41c034c 100644
--- a/src/nspawn/nspawn.c
+++ b/src/nspawn/nspawn.c
@@ -190,6 +190,7 @@ static bool arg_quiet = false;
static bool arg_share_system = false;
static bool arg_register = true;
static bool arg_keep_unit = false;
+static const char *arg_pid_file = NULL;
static char **arg_network_interfaces = NULL;
static char **arg_network_macvlan = NULL;
static char **arg_network_ipvlan = NULL;
@@ -271,6 +272,7 @@ static void help(void) {
" --keep-unit Do not register a scope for the machine, reuse\n"
" the service unit nspawn is running in\n"
" --volatile[=MODE] Run the system in volatile mode\n"
+ " --pid-file=FILE Write child pid to FILE\n"
, program_invocation_short_name);
}
@@ -397,6 +399,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_SHARE_SYSTEM,
ARG_REGISTER,
ARG_KEEP_UNIT,
+ ARG_PID_FILE,
ARG_NETWORK_INTERFACE,
ARG_NETWORK_MACVLAN,
ARG_NETWORK_IPVLAN,
@@ -437,6 +440,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "share-system", no_argument, NULL, ARG_SHARE_SYSTEM },
{ "register", required_argument, NULL, ARG_REGISTER },
{ "keep-unit", no_argument, NULL, ARG_KEEP_UNIT },
+ { "pid-file", required_argument, NULL, ARG_PID_FILE },
{ "network-interface", required_argument, NULL, ARG_NETWORK_INTERFACE },
{ "network-macvlan", required_argument, NULL, ARG_NETWORK_MACVLAN },
{ "network-ipvlan", required_argument, NULL, ARG_NETWORK_IPVLAN },
@@ -820,6 +824,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_keep_unit = true;
break;
+ case ARG_PID_FILE:
+ arg_pid_file = optarg;
+ break;
+
case ARG_PERSONALITY:
arg_personality = personality_from_string(optarg);
@@ -3921,6 +3929,14 @@ static int wait_for_container(pid_t pid, ContainerStatus *container) {
return r;
}
+static int write_pid(const char *f, int pid) {
+ char pid_str[13]; /* -2147483646\n\0 */
+
+ snprintf(pid_str, sizeof(pid_str), "%i\n", pid);
+ return write_string_file_atomic(f, pid_str);
+}
+
+
static void nop_handler(int sig) {}
static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
@@ -4876,6 +4892,14 @@ int main(int argc, char *argv[]) {
goto finish;
}
+ if (arg_pid_file) {
+ r = write_pid(arg_pid_file, pid);
+ if (r < 0) {
+ log_error_errno(r, "Failed to write pid file: %m");
+ goto finish;
+ }
+ }
+
sd_notifyf(false,
"READY=1\n"
"STATUS=Container running.\n"
--
2.1.4