Updated RunIn API to match the new console handling behavior in HEAD.

Docker-DCO-1.1-Signed-off-by: Vishnu Kannan <vishnuk@google.com> (github: vishh)
This commit is contained in:
Vishnu Kannan 2014-07-16 23:52:15 +00:00
parent 145299f9d8
commit 1f2828770d
3 changed files with 72 additions and 25 deletions

View File

@ -4,39 +4,37 @@ package namespaces
import ( import (
"encoding/json" "encoding/json"
"github.com/docker/libcontainer"
"github.com/docker/libcontainer/label"
"github.com/docker/libcontainer/system"
"io"
"os" "os"
"os/exec" "os/exec"
"strconv" "strconv"
"syscall" "syscall"
"github.com/docker/libcontainer"
"github.com/docker/libcontainer/label"
"github.com/docker/libcontainer/system"
) )
// Runs the command under 'args' inside an existing container referred to by 'container'. This API currently does not support a Tty based 'term'. // Runs the command under 'args' inside an existing container referred to by 'container'.
// Returns the exitcode of the command upon success and appropriate error on failure. // Returns the exitcode of the command upon success and appropriate error on failure.
func RunIn(container *libcontainer.Config, state *libcontainer.State, args []string, nsinitPath string, term Terminal, startCallback func()) (int, error) { func RunIn(container *libcontainer.Config, state *libcontainer.State, args []string, nsinitPath string, stdin io.Reader, stdout, stderr io.Writer, console string, startCallback func(*exec.Cmd)) (int, error) {
containerJson, err := getContainerJson(container) initArgs, err := getNsEnterCommand(strconv.Itoa(state.InitPid), container, console, args)
if err != nil { if err != nil {
return -1, err return -1, err
} }
var cmd exec.Cmd cmd := exec.Command(nsinitPath, initArgs...)
// Note: these are only used in non-tty mode
cmd.Path = nsinitPath // if there is a tty for the container it will be opened within the namespace and the
cmd.Args = getNsEnterCommand(nsinitPath, strconv.Itoa(state.InitPid), containerJson, args) // fds will be duped to stdin, stdiout, and stderr
cmd.Stdin = stdin
if err := term.Attach(&cmd); err != nil { cmd.Stdout = stdout
return -1, err cmd.Stderr = stderr
}
defer term.Close()
if err := cmd.Start(); err != nil { if err := cmd.Start(); err != nil {
return -1, err return -1, err
} }
if startCallback != nil { if startCallback != nil {
startCallback() startCallback(cmd)
} }
if err := cmd.Wait(); err != nil { if err := cmd.Wait(); err != nil {
@ -50,16 +48,18 @@ func RunIn(container *libcontainer.Config, state *libcontainer.State, args []str
// ExecIn uses an existing pid and joins the pid's namespaces with the new command. // ExecIn uses an existing pid and joins the pid's namespaces with the new command.
func ExecIn(container *libcontainer.Config, state *libcontainer.State, args []string) error { func ExecIn(container *libcontainer.Config, state *libcontainer.State, args []string) error {
containerJson, err := getContainerJson(container) // Enter the namespace and then finish setup
args, err := getNsEnterCommand(strconv.Itoa(state.InitPid), container, "", args)
if err != nil { if err != nil {
return err return err
} }
// Enter the namespace and then finish setup
finalArgs := getNsEnterCommand(os.Args[0], strconv.Itoa(state.InitPid), containerJson, args) finalArgs := append([]string{os.Args[0]}, args...)
if err := system.Execv(finalArgs[0], finalArgs[0:], os.Environ()); err != nil { if err := system.Execv(finalArgs[0], finalArgs[0:], os.Environ()); err != nil {
return err return err
} }
panic("unreachable") panic("unreachable")
} }
@ -73,14 +73,25 @@ func getContainerJson(container *libcontainer.Config) (string, error) {
return string(containerJson), nil return string(containerJson), nil
} }
func getNsEnterCommand(nsinitPath, initPid, containerJson string, args []string) []string { func getNsEnterCommand(initPid string, container *libcontainer.Config, console string, args []string) ([]string, error) {
return append([]string{ containerJson, err := getContainerJson(container)
nsinitPath, if err != nil {
return nil, err
}
out := []string{
"nsenter", "nsenter",
"--nspid", initPid, "--nspid", initPid,
"--containerjson", containerJson, "--containerjson", containerJson,
"--", }
}, args...)
if console != "" {
out = append(out, "--console", console)
}
out = append(out, "--")
out = append(out, args...)
return out, nil
} }
// Run a command in a container after entering the namespace. // Run a command in a container after entering the namespace.

View File

@ -88,6 +88,7 @@ void nsenter() {
static const struct option longopts[] = { static const struct option longopts[] = {
{ "nspid", required_argument, NULL, 'n' }, { "nspid", required_argument, NULL, 'n' },
{ "containerjson", required_argument, NULL, 'c' }, { "containerjson", required_argument, NULL, 'c' },
{ "console", required_argument, NULL, 't' },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
@ -95,6 +96,7 @@ void nsenter() {
pid_t init_pid = -1; pid_t init_pid = -1;
char *init_pid_str = NULL; char *init_pid_str = NULL;
char *container_json = NULL; char *container_json = NULL;
char *console = NULL;
while ((c = getopt_long_only(argc, argv, "n:s:c:", longopts, NULL)) != -1) { while ((c = getopt_long_only(argc, argv, "n:s:c:", longopts, NULL)) != -1) {
switch (c) { switch (c) {
case 'n': case 'n':
@ -103,6 +105,9 @@ void nsenter() {
case 'c': case 'c':
container_json = optarg; container_json = optarg;
break; break;
case 't':
console = optarg;
break;
} }
} }
@ -121,6 +126,21 @@ void nsenter() {
argc -= 3; argc -= 3;
argv += 3; argv += 3;
if (setsid() == -1) {
fprintf(stderr, "setsid failed. Error: %s\n", strerror(errno));
exit(1);
}
// before we setns we need to dup the console
int consolefd = -1;
if (console != NULL) {
consolefd = open(console, O_RDWR);
if (consolefd < 0) {
fprintf(stderr, "nsenter: failed to open console %s\n", console, strerror(errno));
exit(1);
}
}
// Setns on all supported namespaces. // Setns on all supported namespaces.
char ns_dir[PATH_MAX]; char ns_dir[PATH_MAX];
memset(ns_dir, 0, PATH_MAX); memset(ns_dir, 0, PATH_MAX);
@ -159,6 +179,21 @@ void nsenter() {
// We must fork to actually enter the PID namespace. // We must fork to actually enter the PID namespace.
int child = fork(); int child = fork();
if (child == 0) { if (child == 0) {
if (consolefd != -1) {
if (dup2(consolefd, STDIN_FILENO) != 0) {
fprintf(stderr, "nsenter: failed to dup 0 %s\n", strerror(errno));
exit(1);
}
if (dup2(consolefd, STDOUT_FILENO) != STDOUT_FILENO) {
fprintf(stderr, "nsenter: failed to dup 1 %s\n", strerror(errno));
exit(1);
}
if (dup2(consolefd, STDERR_FILENO) != STDERR_FILENO) {
fprintf(stderr, "nsenter: failed to dup 2 %s\n", strerror(errno));
exit(1);
}
}
// Finish executing, let the Go runtime take over. // Finish executing, let the Go runtime take over.
return; return;
} else { } else {

View File

@ -14,6 +14,7 @@ var nsenterCommand = cli.Command{
Flags: []cli.Flag{ Flags: []cli.Flag{
cli.IntFlag{Name: "nspid"}, cli.IntFlag{Name: "nspid"},
cli.StringFlag{Name: "containerjson"}, cli.StringFlag{Name: "containerjson"},
cli.StringFlag{Name: "console"},
}, },
} }