]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
lxc: enter / exec a command inside a container V2
authorDaniel Lezcano <dlezcano@fr.ibm.com>
Mon, 22 Mar 2010 10:08:34 +0000 (11:08 +0100)
committerDaniel Lezcano <dlezcano@fr.ibm.com>
Mon, 22 Mar 2010 10:08:34 +0000 (11:08 +0100)
This patch allows to execute a command or enter inside the container:
  * lxc-attach -n <name> [command]

If the <command is not specified>, the lxc-attach will retrieve your uid
and get your shell name and exec it in the container.

Signed-off-by: Daniel Lezcano <dlezcano@fr.ibm.com>
src/lxc/Makefile.am
src/lxc/commands.c
src/lxc/commands.h
src/lxc/lxc-setcap.in
src/lxc/lxc_attach.c [new file with mode: 0644]
src/lxc/namespace.c
src/lxc/namespace.h
src/lxc/start.c

index f5168411d6b9a3856a5d1d4a52120bc83a4bbffc..890f706bf05ceb17f9ffb95ea639e386c32f8b25 100644 (file)
@@ -67,6 +67,7 @@ bin_SCRIPTS = \
        lxc-destroy
 
 bin_PROGRAMS = \
+       lxc-attach \
        lxc-unshare \
        lxc-stop \
        lxc-start \
@@ -87,6 +88,7 @@ libexec_PROGRAMS = \
 AM_LDFLAGS=-Wl,-E -Wl,-rpath -Wl,$(libdir)
 LDADD=liblxc.so
 
+lxc_attach_SOURCES = lxc_attach.c
 lxc_cgroup_SOURCES = lxc_cgroup.c
 lxc_checkpoint_SOURCES = lxc_checkpoint.c
 lxc_console_SOURCES = lxc_console.c
index 4c48571a2a816d73daa46fdd5990d45e888e8085..fe055ed1035fe7bd5bc4302c7b89c54d8cf7cdd4 100644 (file)
@@ -114,6 +114,7 @@ extern void lxc_console_remove_fd(int, struct lxc_tty_info *);
 extern int  lxc_console_callback(int, struct lxc_request *, struct lxc_handler *);
 extern int  lxc_stop_callback(int, struct lxc_request *, struct lxc_handler *);
 extern int  lxc_state_callback(int, struct lxc_request *, struct lxc_handler *);
+extern int  lxc_pid_callback(int, struct lxc_request *, struct lxc_handler *);
 
 static int trigger_command(int fd, struct lxc_request *request,
                           struct lxc_handler *handler)
@@ -124,6 +125,7 @@ static int trigger_command(int fd, struct lxc_request *request,
                [LXC_COMMAND_TTY]   = lxc_console_callback,
                [LXC_COMMAND_STOP]  = lxc_stop_callback,
                [LXC_COMMAND_STATE] = lxc_state_callback,
+               [LXC_COMMAND_PID]   = lxc_pid_callback,
        };
 
        if (request->type < 0 || request->type >= LXC_COMMAND_MAX)
index 3c6c08271b26938fdbdd9d33b25ba3e62913e450..e191b779e92bf18a02251c23c5949ff3e04eac7c 100644 (file)
@@ -27,6 +27,7 @@ enum {
        LXC_COMMAND_TTY,
        LXC_COMMAND_STOP,
        LXC_COMMAND_STATE,
+       LXC_COMMAND_PID,
        LXC_COMMAND_MAX,
 };
 
@@ -38,6 +39,7 @@ struct lxc_request {
 struct lxc_answer {
        int fd;
        int ret; /* 0 on success, -errno on failure */
+       pid_t pid;
 };
 
 struct lxc_command {
index 4731035b97e9129931f18bd173cd1587d5f764ac..5954ade19c66ad567a4d341106e933a49da14afa 100644 (file)
@@ -25,6 +25,7 @@
 # When the capabilities are set, a non root user can manage the containers.
 #
 
+LXC_ATTACH_CAP="cap_sys_admin"
 LXC_CREATE_CAPS="cap_sys_admin"
 LXC_NETSTAT_CAPS="cap_sys_admin"
 LXC_INIT_CAPS="cap_sys_admin,cap_dac_override"
@@ -34,7 +35,6 @@ LXC_START_CAPS="$LXC_COMMON_CAPS,cap_fowner,cap_sys_chroot,cap_setpcap"
 LXC_EXECUTE_CAPS=$LXC_START_CAPS
 LXC_RESTART_CAPS="$LXC_START_CAPS,cap_mknod"
 LXC_CHECKPOINT_CAPS="$LXC_COMMON_CAPS,cap_sys_ptrace,cap_mknod"
-
 LXC_DROP_CAPS=""
 
 usage()
@@ -44,6 +44,7 @@ usage()
 
 lxc_setcaps()
 {
+    setcap $LXC_ATTACH_CAPS=ep @BINDIR@/lxc-attach
     setcap $LXC_CREATE_CAPS=ep @BINDIR@/lxc-create
     setcap $LXC_EXECUTE_CAPS=ep @BINDIR@/lxc-execute
     setcap $LXC_START_CAPS=ep @BINDIR@/lxc-start
diff --git a/src/lxc/lxc_attach.c b/src/lxc/lxc_attach.c
new file mode 100644 (file)
index 0000000..1d32332
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2010
+ *
+ * Authors:
+ * Daniel Lezcano <dlezcano at fr.ibm.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <pwd.h>
+#include "commands.h"
+#include "arguments.h"
+#include "namespace.h"
+#include "log.h"
+
+lxc_log_define(lxc_attach_ui, lxc);
+
+static const struct option my_longopts[] = {
+       LXC_COMMON_OPTIONS
+};
+
+static struct lxc_arguments my_args = {
+       .progname = "lxc-attach",
+       .help     = "\
+--name=NAME\n\
+\n\
+Execute the specified command - enter the container NAME\n\
+\n\
+Options :\n\
+  -n, --name=NAME   NAME for name of the container\n",
+       .options  = my_longopts,
+       .parser   = NULL,
+       .checker  = NULL,
+};
+
+pid_t get_init_pid(const char *name)
+{
+       struct lxc_command command = {
+               .request = { .type = LXC_COMMAND_PID },
+       };
+
+       int ret, stopped = 0;
+
+       ret = lxc_command(name, &command, &stopped);
+       if (ret < 0 && stopped) {
+               INFO("'%s' is already stopped", name);
+               return 0;
+       }
+
+       if (ret < 0) {
+               ERROR("failed to send command");
+               return -1;
+       }
+
+       if (command.answer.ret) {
+               ERROR("failed to retrieve the init pid: %s",
+                     strerror(command.answer.ret));
+               return -1;
+       }
+
+       return command.answer.pid;
+}
+
+int main(int argc, char *argv[], char *envp[])
+{
+       int ret;
+       pid_t pid;
+       struct passwd *passwd;
+       uid_t uid;
+
+       ret = lxc_arguments_parse(&my_args, argc, argv);
+       if (ret)
+               return ret;
+
+       ret = lxc_log_init(my_args.log_file, my_args.log_priority,
+                          my_args.progname, my_args.quiet);
+       if (ret)
+               return ret;
+
+       pid = get_init_pid(my_args.name);
+       if (pid < 0) {
+               ERROR("failed to get the init pid");
+               return -1;
+       }
+
+       ret = lxc_attach(pid);
+       if (ret < 0) {
+               ERROR("failed to enter the namespace");
+               return -1;
+       }
+
+       if (my_args.argc) {
+               execve(my_args.argv[0], my_args.argv, envp);
+               SYSERROR("failed to exec '%s'", my_args.argv[0]);
+               return -1;
+       }
+
+       uid = getuid();
+
+       passwd = getpwuid(uid);
+       if (!passwd) {
+               SYSERROR("failed to get passwd entry for uid '%d'", uid);
+               return -1;
+       }
+
+       {
+               char *const args[] = {
+                       passwd->pw_shell,
+                       NULL,
+               };
+
+               execve(args[0], args, envp);
+               SYSERROR("failed to exec '%s'", args[0]);
+               return -1;
+       }
+
+       return 0;
+}
index 72e47e1ea45512c0cb9f703b3c56e85b5dfddbce..a68e15e442d4020a3f9055544953b4fbc6722e9d 100644 (file)
 #include <alloca.h>
 #include <errno.h>
 #include <signal.h>
-#include <namespace.h>
+#include <syscall.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 
-#include <lxc/log.h>
+#include "namespace.h"
+#include "log.h"
+
+#ifndef __NR_setns
+#  if __i386__
+#    define __NR_setns 338
+#  elif __x86_64__
+#    define __NR_setns 300
+#  elif __powerpc__
+#    define __NR_setns 323
+#  elif __s390__
+#    define __NR_setns 332
+#  else
+#    warning "architecture not supported for setns"
+#  endif
+#endif
 
 lxc_log_define(lxc_namespace, lxc);
 
@@ -36,6 +55,16 @@ struct clone_arg {
        void *arg;
 };
 
+int setns(int nstype, int fd)
+{
+#ifndef __NR_setns
+       errno = ENOSYS;
+       return -1;
+#else
+       return syscall(__NR_setns, nstype, fd);
+#endif
+}
+
 static int do_clone(void *arg)
 {
        struct clone_arg *clone_arg = arg;
@@ -64,3 +93,32 @@ pid_t lxc_clone(int (*fn)(void *), void *arg, int flags)
 
        return ret;
 }
+
+int lxc_attach(pid_t pid)
+{
+       char path[MAXPATHLEN];
+       char *ns[] = { "pid", "mnt", "net", "pid", "uts" };
+       const int size = sizeof(ns) / sizeof(char *);
+       int fd[size];
+       int i;
+
+       for (i = 0; i < size; i++) {
+               sprintf(path, "/proc/%d/ns/%s", pid, ns[i]);
+               fd[i] = open(path, O_RDONLY);
+               if (fd[i] < 0) {
+                       SYSERROR("failed to open '%s'", path);
+                       return -1;
+               }
+       }
+
+       for (i = 0; i < size; i++) {
+               if (setns(0, fd[i])) {
+                       SYSERROR("failed to set namespace '%s'", ns[i]);
+                       return -1;
+               }
+
+               close(fd[i]);
+       }
+
+       return 0;
+}
index 5442dd3b7eecea22509f0f4949c957232b29354c..9c6b7ec2bb4c627c58cc9995f7117703a12d74f4 100644 (file)
@@ -49,5 +49,6 @@
 #endif
 
 extern pid_t lxc_clone(int (*fn)(void *), void *arg, int flags);
+extern int lxc_attach(pid_t pid);
 
 #endif
index 48fffe35ca3df43b3268718efa450ba13048f5d7..4a4755d059ac328779dfbe9ff3656df324c069d4 100644 (file)
@@ -204,6 +204,28 @@ static int sigchld_handler(int fd, void *data,
        return 1;
 }
 
+int lxc_pid_callback(int fd, struct lxc_request *request, struct lxc_handler *handler)
+{
+       struct lxc_answer answer;
+       int ret;
+
+       answer.pid = handler->pid;
+       answer.ret = 0;
+
+       ret = send(fd, &answer, sizeof(answer), 0);
+       if (ret < 0) {
+               WARN("failed to send answer to the peer");
+               return -1;
+       }
+
+       if (ret != sizeof(answer)) {
+               ERROR("partial answer sent");
+               return -1;
+       }
+
+       return 0;
+}
+
 int lxc_set_state(const char *name, struct lxc_handler *handler, lxc_state_t state)
 {
        handler->state = state;