]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/lxc_attach.c
pass lxcpath to lxc_command
[mirror_lxc.git] / src / lxc / lxc_attach.c
1 /*
2 * lxc: linux Container library
3 *
4 * (C) Copyright IBM Corp. 2007, 2010
5 *
6 * Authors:
7 * Daniel Lezcano <dlezcano at fr.ibm.com>
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #define _GNU_SOURCE
25 #include <unistd.h>
26 #include <errno.h>
27 #include <pwd.h>
28 #include <stdlib.h>
29 #include <sys/param.h>
30 #include <sys/types.h>
31 #include <sys/wait.h>
32
33 #include "attach.h"
34 #include "commands.h"
35 #include "arguments.h"
36 #include "caps.h"
37 #include "cgroup.h"
38 #include "config.h"
39 #include "confile.h"
40 #include "start.h"
41 #include "sync.h"
42 #include "log.h"
43 #include "namespace.h"
44
45 #if HAVE_SYS_PERSONALITY_H
46 #include <sys/personality.h>
47 #endif
48
49 lxc_log_define(lxc_attach_ui, lxc);
50
51 static const struct option my_longopts[] = {
52 {"elevated-privileges", no_argument, 0, 'e'},
53 {"arch", required_argument, 0, 'a'},
54 {"namespaces", required_argument, 0, 's'},
55 {"remount-sys-proc", no_argument, 0, 'R'},
56 LXC_COMMON_OPTIONS
57 };
58
59 static int elevated_privileges = 0;
60 static signed long new_personality = -1;
61 static int namespace_flags = -1;
62 static int remount_sys_proc = 0;
63
64 static int my_parser(struct lxc_arguments* args, int c, char* arg)
65 {
66 int ret;
67
68 switch (c) {
69 case 'e': elevated_privileges = 1; break;
70 case 'R': remount_sys_proc = 1; break;
71 case 'a':
72 new_personality = lxc_config_parse_arch(arg);
73 if (new_personality < 0) {
74 lxc_error(args, "invalid architecture specified: %s", arg);
75 return -1;
76 }
77 break;
78 case 's':
79 namespace_flags = 0;
80 ret = lxc_fill_namespace_flags(arg, &namespace_flags);
81 if (ret)
82 return -1;
83 /* -s implies -e */
84 elevated_privileges = 1;
85 break;
86 }
87
88 return 0;
89 }
90
91 static struct lxc_arguments my_args = {
92 .progname = "lxc-attach",
93 .help = "\
94 --name=NAME\n\
95 \n\
96 Execute the specified command - enter the container NAME\n\
97 \n\
98 Options :\n\
99 -n, --name=NAME NAME for name of the container\n\
100 -e, --elevated-privileges\n\
101 Use elevated privileges (capabilities, cgroup\n\
102 restrictions) instead of those of the container.\n\
103 WARNING: This may leak privleges into the container.\n\
104 Use with care.\n\
105 -a, --arch=ARCH Use ARCH for program instead of container's own\n\
106 architecture.\n\
107 -s, --namespaces=FLAGS\n\
108 Don't attach to all the namespaces of the container\n\
109 but just to the following OR'd list of flags:\n\
110 MOUNT, PID, UTSNAME, IPC, USER or NETWORK\n\
111 WARNING: Using -s implies -e, it may therefore\n\
112 leak privileges into the container. Use with care.\n\
113 -R, --remount-sys-proc\n\
114 Remount /sys and /proc if not attaching to the\n\
115 mount namespace when using -s in order to properly\n\
116 reflect the correct namespace context. See the\n\
117 lxc-attach(1) manual page for details.\n",
118 .options = my_longopts,
119 .parser = my_parser,
120 .checker = NULL,
121 };
122
123 int main(int argc, char *argv[])
124 {
125 int ret;
126 pid_t pid, init_pid;
127 struct passwd *passwd;
128 struct lxc_proc_context_info *init_ctx;
129 struct lxc_handler *handler;
130 void *cgroup_data = NULL;
131 uid_t uid;
132 char *curdir;
133 /* TODO: add cmdline arg to set lxcpath */
134 const char *lxcpath = NULL;
135
136 ret = lxc_caps_init();
137 if (ret)
138 return ret;
139
140 ret = lxc_arguments_parse(&my_args, argc, argv);
141 if (ret)
142 return ret;
143
144 ret = lxc_log_init(my_args.name, my_args.log_file, my_args.log_priority,
145 my_args.progname, my_args.quiet);
146 if (ret)
147 return ret;
148
149 init_pid = get_init_pid(my_args.name, lxcpath);
150 if (init_pid < 0) {
151 ERROR("failed to get the init pid");
152 return -1;
153 }
154
155 init_ctx = lxc_proc_get_context_info(init_pid);
156 if (!init_ctx) {
157 ERROR("failed to get context of the init process, pid = %d", init_pid);
158 return -1;
159 }
160
161 if (!elevated_privileges) {
162 /* we have to do this now since /sys/fs/cgroup may not
163 * be available inside the container or we may not have
164 * the required permissions anymore
165 */
166 ret = lxc_cgroup_prepare_attach(my_args.name, &cgroup_data);
167 if (ret < 0) {
168 ERROR("failed to prepare attaching to cgroup");
169 return -1;
170 }
171 }
172
173 curdir = getcwd(NULL, 0);
174
175 /* determine which namespaces the container was created with
176 * by asking lxc-start
177 */
178 if (namespace_flags == -1) {
179 namespace_flags = lxc_get_clone_flags(my_args.name, lxcpath);
180 /* call failed */
181 if (namespace_flags == -1) {
182 ERROR("failed to automatically determine the "
183 "namespaces which the container unshared");
184 return -1;
185 }
186 }
187
188 /* we need to attach before we fork since certain namespaces
189 * (such as pid namespaces) only really affect children of the
190 * current process and not the process itself
191 */
192 ret = lxc_attach_to_ns(init_pid, namespace_flags);
193 if (ret < 0) {
194 ERROR("failed to enter the namespace");
195 return -1;
196 }
197
198 if (curdir && chdir(curdir))
199 WARN("could not change directory to '%s'", curdir);
200
201 free(curdir);
202
203 /* hack: we need sync.h infrastructure - and that needs a handler */
204 handler = calloc(1, sizeof(*handler));
205
206 if (lxc_sync_init(handler)) {
207 ERROR("failed to initialize synchronization socket");
208 return -1;
209 }
210
211 pid = fork();
212
213 if (pid < 0) {
214 SYSERROR("failed to fork");
215 return -1;
216 }
217
218 if (pid) {
219 int status;
220
221 lxc_sync_fini_child(handler);
222
223 /* wait until the child has done configuring itself before
224 * we put it in a cgroup that potentially limits these
225 * possibilities */
226 if (lxc_sync_wait_child(handler, LXC_SYNC_CONFIGURE))
227 return -1;
228
229 /* now that we are done with all privileged operations,
230 * we can add ourselves to the cgroup. Since we smuggled in
231 * the fds earlier, we still have write permission
232 */
233 if (!elevated_privileges) {
234 /* since setns() for pid namespaces only really
235 * affects child processes, the pid we have is
236 * still valid outside the container, so this is
237 * fine
238 */
239 ret = lxc_cgroup_finish_attach(cgroup_data, pid);
240 if (ret < 0) {
241 ERROR("failed to attach process to cgroup");
242 return -1;
243 }
244 }
245
246 /* tell the child we are done initializing */
247 if (lxc_sync_wake_child(handler, LXC_SYNC_POST_CONFIGURE))
248 return -1;
249
250 lxc_sync_fini(handler);
251
252 again:
253 if (waitpid(pid, &status, 0) < 0) {
254 if (errno == EINTR)
255 goto again;
256 SYSERROR("failed to wait '%d'", pid);
257 return -1;
258 }
259
260 if (WIFEXITED(status))
261 return WEXITSTATUS(status);
262
263 return -1;
264 }
265
266 if (!pid) {
267 lxc_sync_fini_parent(handler);
268 lxc_cgroup_dispose_attach(cgroup_data);
269
270 /* A description of the purpose of this functionality is
271 * provided in the lxc-attach(1) manual page. We have to
272 * remount here and not in the parent process, otherwise
273 * /proc may not properly reflect the new pid namespace.
274 */
275 if (!(namespace_flags & CLONE_NEWNS) && remount_sys_proc) {
276 ret = lxc_attach_remount_sys_proc();
277 if (ret < 0) {
278 return -1;
279 }
280 }
281
282 #if HAVE_SYS_PERSONALITY_H
283 if (new_personality < 0)
284 new_personality = init_ctx->personality;
285
286 if (personality(new_personality) == -1) {
287 ERROR("could not ensure correct architecture: %s",
288 strerror(errno));
289 return -1;
290 }
291 #endif
292
293 if (!elevated_privileges && lxc_attach_drop_privs(init_ctx)) {
294 ERROR("could not drop privileges");
295 return -1;
296 }
297
298 /* tell parent we are done setting up the container and wait
299 * until we have been put in the container's cgroup, if
300 * applicable */
301 if (lxc_sync_barrier_parent(handler, LXC_SYNC_CONFIGURE))
302 return -1;
303
304 lxc_sync_fini(handler);
305
306 if (my_args.argc) {
307 execvp(my_args.argv[0], my_args.argv);
308 SYSERROR("failed to exec '%s'", my_args.argv[0]);
309 return -1;
310 }
311
312 uid = getuid();
313
314 passwd = getpwuid(uid);
315 if (!passwd) {
316 SYSERROR("failed to get passwd " \
317 "entry for uid '%d'", uid);
318 return -1;
319 }
320
321 {
322 char *const args[] = {
323 passwd->pw_shell,
324 NULL,
325 };
326
327 execvp(args[0], args);
328 SYSERROR("failed to exec '%s'", args[0]);
329 return -1;
330 }
331
332 }
333
334 return 0;
335 }