Merge pull request #141 from vishh/runin_support
Change nsenter to support docker 'runin'
This commit is contained in:
commit
f79bccbcf0
|
@ -18,8 +18,8 @@ var createCommand = cli.Command{
|
|||
Name: "create",
|
||||
Usage: "Create a cgroup container using the supplied configuration and initial process.",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{"config, c", "cgroup.json", "path to container configuration (cgroups.Cgroup object)"},
|
||||
cli.IntFlag{"pid, p", 0, "pid of the initial process in the container"},
|
||||
cli.StringFlag{Name: "config, c", Value: "cgroup.json", Usage: "path to container configuration (cgroups.Cgroup object)"},
|
||||
cli.IntFlag{Name: "pid, p", Value: 0, Usage: "pid of the initial process in the container"},
|
||||
},
|
||||
Action: createAction,
|
||||
}
|
||||
|
@ -28,8 +28,8 @@ var destroyCommand = cli.Command{
|
|||
Name: "destroy",
|
||||
Usage: "Destroy an existing cgroup container.",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{"name, n", "", "container name"},
|
||||
cli.StringFlag{"parent, p", "", "container parent"},
|
||||
cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"},
|
||||
cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"},
|
||||
},
|
||||
Action: destroyAction,
|
||||
}
|
||||
|
@ -38,8 +38,8 @@ var statsCommand = cli.Command{
|
|||
Name: "stats",
|
||||
Usage: "Get stats for cgroup",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{"name, n", "", "container name"},
|
||||
cli.StringFlag{"parent, p", "", "container parent"},
|
||||
cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"},
|
||||
cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"},
|
||||
},
|
||||
Action: statsAction,
|
||||
}
|
||||
|
@ -48,8 +48,8 @@ var pauseCommand = cli.Command{
|
|||
Name: "pause",
|
||||
Usage: "Pause cgroup",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{"name, n", "", "container name"},
|
||||
cli.StringFlag{"parent, p", "", "container parent"},
|
||||
cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"},
|
||||
cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"},
|
||||
},
|
||||
Action: pauseAction,
|
||||
}
|
||||
|
@ -58,8 +58,8 @@ var resumeCommand = cli.Command{
|
|||
Name: "resume",
|
||||
Usage: "Resume a paused cgroup",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{"name, n", "", "container name"},
|
||||
cli.StringFlag{"parent, p", "", "container parent"},
|
||||
cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"},
|
||||
cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"},
|
||||
},
|
||||
Action: resumeAction,
|
||||
}
|
||||
|
@ -68,8 +68,8 @@ var psCommand = cli.Command{
|
|||
Name: "ps",
|
||||
Usage: "Get list of pids for a cgroup",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{"name, n", "", "container name"},
|
||||
cli.StringFlag{"parent, p", "", "container parent"},
|
||||
cli.StringFlag{Name: "name, n", Value: "", Usage: "container name"},
|
||||
cli.StringFlag{Name: "parent, p", Value: "", Usage: "container parent"},
|
||||
},
|
||||
Action: psAction,
|
||||
}
|
||||
|
|
|
@ -4,52 +4,18 @@ package namespaces
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/docker/libcontainer"
|
||||
"github.com/docker/libcontainer/label"
|
||||
"github.com/docker/libcontainer/system"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// 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.
|
||||
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) {
|
||||
initArgs, err := getNsEnterCommand(strconv.Itoa(state.InitPid), container, console, args)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
cmd := exec.Command(nsinitPath, initArgs...)
|
||||
// Note: these are only used in non-tty mode
|
||||
// if there is a tty for the container it will be opened within the namespace and the
|
||||
// fds will be duped to stdin, stdiout, and stderr
|
||||
cmd.Stdin = stdin
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
|
||||
if err := cmd.Start(); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if startCallback != nil {
|
||||
startCallback(cmd)
|
||||
}
|
||||
|
||||
if err := cmd.Wait(); err != nil {
|
||||
if _, ok := err.(*exec.ExitError); !ok {
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
|
||||
return cmd.ProcessState.Sys().(syscall.WaitStatus).ExitStatus(), nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// Enter the namespace and then finish setup
|
||||
args, err := getNsEnterCommand(strconv.Itoa(state.InitPid), container, "", args)
|
||||
args, err := GetNsEnterCommand(strconv.Itoa(state.InitPid), container, "", args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -73,14 +39,13 @@ func getContainerJson(container *libcontainer.Config) (string, error) {
|
|||
return string(containerJson), nil
|
||||
}
|
||||
|
||||
func getNsEnterCommand(initPid string, container *libcontainer.Config, console string, args []string) ([]string, error) {
|
||||
func GetNsEnterCommand(initPid string, container *libcontainer.Config, console string, args []string) ([]string, error) {
|
||||
containerJson, err := getContainerJson(container)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := []string{
|
||||
"nsenter",
|
||||
"--nspid", initPid,
|
||||
"--containerjson", containerJson,
|
||||
}
|
||||
|
@ -88,6 +53,7 @@ func getNsEnterCommand(initPid string, container *libcontainer.Config, console s
|
|||
if console != "" {
|
||||
out = append(out, "--console", console)
|
||||
}
|
||||
out = append(out, "nsenter")
|
||||
out = append(out, "--")
|
||||
out = append(out, args...)
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ package namespaces
|
|||
#include <getopt.h>
|
||||
|
||||
static const kBufSize = 256;
|
||||
static const char *kNsEnter = "nsenter";
|
||||
|
||||
void get_args(int *argc, char ***argv) {
|
||||
// Read argv
|
||||
|
@ -58,7 +59,7 @@ void get_args(int *argc, char ***argv) {
|
|||
#include "syscall.h"
|
||||
#ifdef SYS_setns
|
||||
int setns(int fd, int nstype) {
|
||||
return syscall(SYS_setns, fd, nstype);
|
||||
return syscall(SYS_setns, fd, nstype);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
@ -68,34 +69,37 @@ void print_usage() {
|
|||
}
|
||||
|
||||
void nsenter() {
|
||||
int argc;
|
||||
int argc, c;
|
||||
char **argv;
|
||||
get_args(&argc, &argv);
|
||||
|
||||
// Ignore if this is not for us.
|
||||
if (argc < 2 || strcmp(argv[1], "nsenter") != 0) {
|
||||
if (argc < 6) {
|
||||
return;
|
||||
}
|
||||
|
||||
// USAGE: <binary> nsenter <PID> <process label> <container JSON> <argv>...
|
||||
if (argc < 6) {
|
||||
fprintf(stderr, "nsenter: Incorrect usage, not enough arguments\n");
|
||||
exit(1);
|
||||
int found_nsenter = 0;
|
||||
for (c = 0; c < argc; ++c) {
|
||||
if (strcmp(argv[c], kNsEnter) == 0) {
|
||||
found_nsenter = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_nsenter) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const struct option longopts[] = {
|
||||
{ "nspid", required_argument, NULL, 'n' },
|
||||
{ "nspid", required_argument, NULL, 'n' },
|
||||
{ "containerjson", required_argument, NULL, 'c' },
|
||||
{ "console", required_argument, NULL, 't' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
{ "console", optional_argument, NULL, 't' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
int c;
|
||||
pid_t init_pid = -1;
|
||||
char *init_pid_str = NULL;
|
||||
char *container_json = NULL;
|
||||
char *console = NULL;
|
||||
while ((c = getopt_long_only(argc, argv, "n:s:c:", longopts, NULL)) != -1) {
|
||||
char *console = NULL;
|
||||
opterr = 0;
|
||||
while ((c = getopt_long_only(argc, argv, "-n:s:c:", longopts, NULL)) != -1) {
|
||||
switch (c) {
|
||||
case 'n':
|
||||
init_pid_str = optarg;
|
||||
|
@ -109,14 +113,18 @@ void nsenter() {
|
|||
}
|
||||
}
|
||||
|
||||
if (strcmp(argv[optind - 2], kNsEnter) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (container_json == NULL || init_pid_str == NULL) {
|
||||
print_usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
init_pid = strtol(init_pid_str, NULL, 10);
|
||||
if (errno != 0 || init_pid <= 0) {
|
||||
fprintf(stderr, "nsenter: Failed to parse PID from \"%s\" with error: \"%s\"\n", init_pid_str, strerror(errno));
|
||||
if ((init_pid == 0 && errno == EINVAL) || errno == ERANGE) {
|
||||
fprintf(stderr, "nsenter: Failed to parse PID from \"%s\" with output \"%d\" and error: \"%s\"\n", init_pid_str, init_pid, strerror(errno));
|
||||
print_usage();
|
||||
exit(1);
|
||||
}
|
||||
|
@ -124,20 +132,20 @@ void nsenter() {
|
|||
argc -= 3;
|
||||
argv += 3;
|
||||
|
||||
if (setsid() == -1) {
|
||||
fprintf(stderr, "setsid failed. Error: %s\n", strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
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 %s\n", console, 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 %s\n", console, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Setns on all supported namespaces.
|
||||
char ns_dir[PATH_MAX];
|
||||
|
@ -172,20 +180,20 @@ void nsenter() {
|
|||
// We must fork to actually enter the PID namespace.
|
||||
int child = fork();
|
||||
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);
|
||||
}
|
||||
}
|
||||
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.
|
||||
return;
|
||||
|
|
|
@ -24,7 +24,10 @@ func NsInit() {
|
|||
app.Name = "nsinit"
|
||||
app.Version = "0.1"
|
||||
app.Author = "libcontainer maintainers"
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{Name: "nspid"},
|
||||
cli.StringFlag{Name: "containerjson"},
|
||||
cli.StringFlag{Name: "console"}}
|
||||
app.Before = preload
|
||||
app.Commands = []cli.Command{
|
||||
execCommand,
|
||||
|
|
|
@ -36,7 +36,7 @@ func execAction(context *cli.Context) {
|
|||
}
|
||||
|
||||
if state != nil {
|
||||
exitCode, err = runIn(container, state, []string(context.Args()))
|
||||
err = namespaces.ExecIn(container, state, []string(context.Args()))
|
||||
} else {
|
||||
exitCode, err = startContainer(container, dataPath, []string(context.Args()))
|
||||
}
|
||||
|
@ -48,59 +48,6 @@ func execAction(context *cli.Context) {
|
|||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
func runIn(container *libcontainer.Config, state *libcontainer.State, args []string) (int, error) {
|
||||
var (
|
||||
master *os.File
|
||||
console string
|
||||
err error
|
||||
|
||||
stdin = os.Stdin
|
||||
stdout = os.Stdout
|
||||
stderr = os.Stderr
|
||||
sigc = make(chan os.Signal, 10)
|
||||
)
|
||||
|
||||
signal.Notify(sigc)
|
||||
|
||||
if container.Tty {
|
||||
stdin = nil
|
||||
stdout = nil
|
||||
stderr = nil
|
||||
|
||||
master, console, err = consolepkg.CreateMasterAndConsole()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
go io.Copy(master, os.Stdin)
|
||||
go io.Copy(os.Stdout, master)
|
||||
|
||||
state, err := term.SetRawTerminal(os.Stdin.Fd())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
defer term.RestoreTerminal(os.Stdin.Fd(), state)
|
||||
}
|
||||
|
||||
startCallback := func(cmd *exec.Cmd) {
|
||||
go func() {
|
||||
resizeTty(master)
|
||||
|
||||
for sig := range sigc {
|
||||
switch sig {
|
||||
case syscall.SIGWINCH:
|
||||
resizeTty(master)
|
||||
default:
|
||||
cmd.Process.Signal(sig)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
return namespaces.RunIn(container, state, args, os.Args[0], stdin, stdout, stderr, console, startCallback)
|
||||
}
|
||||
|
||||
// startContainer starts the container. Returns the exit status or -1 and an
|
||||
// error.
|
||||
//
|
||||
|
|
|
@ -2,6 +2,7 @@ package nsinit
|
|||
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/docker/libcontainer/namespaces"
|
||||
|
@ -11,11 +12,6 @@ var nsenterCommand = cli.Command{
|
|||
Name: "nsenter",
|
||||
Usage: "init process for entering an existing namespace",
|
||||
Action: nsenterAction,
|
||||
Flags: []cli.Flag{
|
||||
cli.IntFlag{Name: "nspid"},
|
||||
cli.StringFlag{Name: "containerjson"},
|
||||
cli.StringFlag{Name: "console"},
|
||||
},
|
||||
}
|
||||
|
||||
func nsenterAction(context *cli.Context) {
|
||||
|
@ -25,14 +21,14 @@ func nsenterAction(context *cli.Context) {
|
|||
args = []string{"/bin/bash"}
|
||||
}
|
||||
|
||||
container, err := loadContainerFromJson(context.String("containerjson"))
|
||||
container, err := loadContainerFromJson(context.GlobalString("containerjson"))
|
||||
if err != nil {
|
||||
log.Fatalf("unable to load container: %s", err)
|
||||
}
|
||||
|
||||
nspid := context.Int("nspid")
|
||||
if nspid <= 0 {
|
||||
log.Fatalf("cannot enter into namespaces without valid pid: %q", nspid)
|
||||
nspid, err := strconv.Atoi(context.GlobalString("nspid"))
|
||||
if nspid <= 0 || err != nil {
|
||||
log.Fatalf("cannot enter into namespaces without valid pid: %q - %s", nspid, err)
|
||||
}
|
||||
|
||||
if err := namespaces.NsEnter(container, args); err != nil {
|
||||
|
|
Loading…
Reference in New Issue