[lxc-devel] [go-lxc/v2] [RFC] add c->attach() wrapper returning PID of attached process

brauner on Github lxc-bot at linuxcontainers.org
Mon Oct 10 23:21:58 UTC 2016


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 850 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20161010/1d6f4893/attachment.bin>
-------------- next part --------------
From 1cb769b3008688981da7074656255056b0f78a29 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at canonical.com>
Date: Tue, 11 Oct 2016 01:12:40 +0200
Subject: [PATCH 1/3] lxc-binding: add binding for c->attach()

Add a wrapper to call low-level c->attach() which stores the PID of the attached
process in one of its arguments. In contrast to all the other wrappers
go_lxc_attach_no_wait() will not wait on the attached process. This will allow
more flexibility in LXD when executing commands in a container.

Signed-off-by: Christian Brauner <christian.brauner at canonical.com>
---
 lxc-binding.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
 lxc-binding.h | 11 +++++++++++
 2 files changed, 56 insertions(+)

diff --git a/lxc-binding.c b/lxc-binding.c
index 110f952..d8841ef 100644
--- a/lxc-binding.c
+++ b/lxc-binding.c
@@ -216,6 +216,51 @@ int wait_for_pid_status(pid_t pid)
         return status;
 }
 
+int go_lxc_attach_no_wait(struct lxc_container *c,
+		bool clear_env,
+		int namespaces,
+		long personality,
+		uid_t uid, gid_t gid,
+		int stdinfd, int stdoutfd, int stderrfd,
+		char *initial_cwd,
+		char **extra_env_vars,
+		char **extra_keep_env,
+		const char * const argv[],
+		pid_t *attached_pid) {
+	int ret;
+
+	lxc_attach_options_t attach_options = LXC_ATTACH_OPTIONS_DEFAULT;
+	lxc_attach_command_t command = (lxc_attach_command_t){.program = NULL};
+
+	attach_options.env_policy = LXC_ATTACH_KEEP_ENV;
+	if (clear_env) {
+		attach_options.env_policy = LXC_ATTACH_CLEAR_ENV;
+	}
+
+	attach_options.namespaces = namespaces;
+	attach_options.personality = personality;
+
+	attach_options.uid = uid;
+	attach_options.gid = gid;
+
+	attach_options.stdin_fd = stdinfd;
+	attach_options.stdout_fd = stdoutfd;
+	attach_options.stderr_fd = stderrfd;
+
+	attach_options.initial_cwd = initial_cwd;
+	attach_options.extra_env_vars = extra_env_vars;
+	attach_options.extra_keep_env = extra_keep_env;
+
+	command.program = (char *)argv[0];
+	command.argv = (char **)argv;
+
+	ret = c->attach(c, lxc_attach_run_command, &command, &attach_options, attached_pid);
+	if (ret < 0)
+		return -1;
+
+	return 0;
+}
+
 int go_lxc_attach(struct lxc_container *c,
 		bool clear_env,
 		int namespaces,
diff --git a/lxc-binding.h b/lxc-binding.h
index f7ccd84..46ad5c9 100644
--- a/lxc-binding.h
+++ b/lxc-binding.h
@@ -60,6 +60,17 @@ extern int go_lxc_attach(struct lxc_container *c,
 		char *initial_cwd,
 		char **extra_env_vars,
 		char **extra_keep_env);
+extern int go_lxc_attach_no_wait(struct lxc_container *c,
+		bool clear_env,
+		int namespaces,
+		long personality,
+		uid_t uid, gid_t gid,
+		int stdinfd, int stdoutfd, int stderrfd,
+		char *initial_cwd,
+		char **extra_env_vars,
+		char **extra_keep_env,
+		const char * const argv[],
+		pid_t *attached_pid);
 extern int go_lxc_console_getfd(struct lxc_container *c, int ttynum);
 extern int go_lxc_snapshot_list(struct lxc_container *c, struct lxc_snapshot **ret);
 extern int go_lxc_snapshot(struct lxc_container *c);

From 136de728597615f07d86584a7fdffd552d371581 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at canonical.com>
Date: Tue, 11 Oct 2016 01:16:44 +0200
Subject: [PATCH 2/3] container: add RunCommandNoWait()

RunCommandNoWait() executes a command in a running container without waiting on
the attached process. Rather, RunCommandNoWait() returns the PID of the
executing process to the caller.

Signed-off-by: Christian Brauner <christian.brauner at canonical.com>
---
 container.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 58 insertions(+)

diff --git a/container.go b/container.go
index c8684e6..d44315a 100644
--- a/container.go
+++ b/container.go
@@ -1164,6 +1164,64 @@ func (c *Container) RunCommandStatus(args []string, options AttachOptions) (int,
 	return ret, nil
 }
 
+func (c *Container) RunCommandNoWait(args []string, options AttachOptions) (int, error) {
+	if len(args) == 0 {
+		return -1, ErrInsufficientNumberOfArguments
+	}
+
+	if err := c.makeSure(isRunning); err != nil {
+		return -1, err
+	}
+
+	c.mu.Lock()
+	defer c.mu.Unlock()
+
+	cargs := makeNullTerminatedArgs(args)
+	if cargs == nil {
+		return -1, ErrAllocationFailed
+	}
+	defer freeNullTerminatedArgs(cargs, len(args))
+
+	cenv := makeNullTerminatedArgs(options.Env)
+	if cenv == nil {
+		return -1, ErrAllocationFailed
+	}
+	defer freeNullTerminatedArgs(cenv, len(options.Env))
+
+	cenvToKeep := makeNullTerminatedArgs(options.EnvToKeep)
+	if cenvToKeep == nil {
+		return -1, ErrAllocationFailed
+	}
+	defer freeNullTerminatedArgs(cenvToKeep, len(options.EnvToKeep))
+
+	cwd := C.CString(options.Cwd)
+	defer C.free(unsafe.Pointer(cwd))
+
+	var attachedPid C.pid_t
+	ret := int(C.go_lxc_attach_no_wait(
+		c.container,
+		C.bool(options.ClearEnv),
+		C.int(options.Namespaces),
+		C.long(options.Arch),
+		C.uid_t(options.UID),
+		C.gid_t(options.GID),
+		C.int(options.StdinFd),
+		C.int(options.StdoutFd),
+		C.int(options.StderrFd),
+		cwd,
+		cenv,
+		cenvToKeep,
+		cargs,
+		&attachedPid,
+	))
+
+	if ret < 0 {
+		return -1, ErrAttachFailed
+	}
+
+	return int(attachedPid), nil
+}
+
 // RunCommand attachs a shell and runs the command within the container.
 // The process will wait for the command to finish and return a success status. An error
 // is returned only when invocation of the command completely fails.

From 4347d3861264e5075cbb08d5bfb66be49b514019 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at canonical.com>
Date: Tue, 11 Oct 2016 01:18:05 +0200
Subject: [PATCH 3/3] lxc_test: add test for RunCommandNoWait()

Signed-off-by: Christian Brauner <christian.brauner at canonical.com>
---
 lxc_test.go | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 63 insertions(+)

diff --git a/lxc_test.go b/lxc_test.go
index 94091ff..c5990ce 100644
--- a/lxc_test.go
+++ b/lxc_test.go
@@ -926,6 +926,69 @@ func TestCPUStats(t *testing.T) {
 	}
 }
 
+func TestRunCommandNoWait(t *testing.T) {
+	c, err := NewContainer("TestRunCommandNoWait")
+	if err != nil {
+		t.Errorf(err.Error())
+		t.FailNow()
+	}
+
+	options := DownloadTemplateOptions
+	if !unprivileged() {
+		options = BusyboxTemplateOptions
+	}
+
+	if err := c.Create(options); err != nil {
+		t.Errorf(err.Error())
+		t.FailNow()
+	}
+	defer c.Destroy()
+
+	err = c.Start()
+	if err != nil {
+		t.Errorf(err.Error())
+		t.FailNow()
+	}
+	defer c.Stop()
+
+	argsThree := []string{"/bin/sh", "-c", "exit 0"}
+	pid, err := c.RunCommandNoWait(argsThree, DefaultAttachOptions)
+	if err != nil {
+		t.Errorf(err.Error())
+	}
+
+	proc, err := os.FindProcess(pid)
+	if err != nil {
+		t.Errorf(err.Error())
+	}
+
+	procState, err := proc.Wait()
+	if err != nil {
+		t.Errorf(err.Error())
+	}
+	if !procState.Success() {
+		t.Errorf("Expected success")
+	}
+
+	argsThree = []string{"/bin/sh", "-c", "exit 0"}
+	pid, err = c.RunCommandNoWait(argsThree, DefaultAttachOptions)
+	if err != nil {
+		t.Errorf(err.Error())
+	}
+	proc, err = os.FindProcess(pid)
+	if err != nil {
+		t.Errorf(err.Error())
+	}
+
+	procState, err = proc.Wait()
+	if err != nil {
+		t.Errorf(err.Error())
+	}
+	if procState.Success() {
+		t.Errorf("Expected failure")
+	}
+}
+
 func TestRunCommand(t *testing.T) {
 	c, err := NewContainer(ContainerName)
 	if err != nil {


More information about the lxc-devel mailing list