]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/attach.c
lxc-shutdown: Make all processes exit before timeout if shutdown works
[mirror_lxc.git] / src / lxc / attach.c
CommitLineData
e0732705
CS
1/*
2 * lxc: linux Container library
3 *
4 * (C) Copyright IBM Corp. 2007, 2008
5 *
6 * Authors:
9afe19d6 7 * Daniel Lezcano <daniel.lezcano at free.fr>
e0732705
CS
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 <stdio.h>
27#include <string.h>
28#include <stdlib.h>
29#include <errno.h>
30#include <fcntl.h>
31#include <sys/param.h>
32#include <sys/prctl.h>
7a0b0b56 33#include <sys/mount.h>
1ba0013f 34#include <sys/syscall.h>
905022f7 35#include <sys/wait.h>
910bb4fa 36#include <linux/unistd.h>
905022f7 37#include <pwd.h>
e0732705
CS
38
39#if !HAVE_DECL_PR_CAPBSET_DROP
40#define PR_CAPBSET_DROP 24
41#endif
42
43#include "namespace.h"
44#include "log.h"
45#include "attach.h"
46#include "caps.h"
e0732705 47#include "config.h"
9958532b 48#include "apparmor.h"
e0732705 49
e0732705
CS
50lxc_log_define(lxc_attach, lxc);
51
2d76d1d7
SG
52/* Define setns() if missing from the C library */
53#ifndef HAVE_SETNS
54static int setns(int fd, int nstype)
e0732705 55{
2d76d1d7
SG
56#ifdef __NR_setns
57return syscall(__NR_setns, fd, nstype);
e0732705 58#else
2d76d1d7
SG
59errno = ENOSYS;
60return -1;
e0732705
CS
61#endif
62}
2d76d1d7
SG
63#endif
64
65/* Define unshare() if missing from the C library */
66#ifndef HAVE_UNSHARE
67static int unshare(int flags)
68{
69#ifdef __NR_unshare
70return syscall(__NR_unshare, flags);
71#else
72errno = ENOSYS;
73return -1;
74#endif
75}
76#endif
e0732705 77
1ba0013f
SG
78/* Define getline() if missing from the C library */
79#ifndef HAVE_GETLINE
80#ifdef HAVE_FGETLN
81#include <../include/getline.h>
82#endif
83#endif
84
e0732705
CS
85struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid)
86{
87 struct lxc_proc_context_info *info = calloc(1, sizeof(*info));
88 FILE *proc_file;
89 char proc_fn[MAXPATHLEN];
460a1cf0 90 char *line = NULL;
e0732705 91 size_t line_bufsz = 0;
460a1cf0 92 int ret, found;
e0732705
CS
93
94 if (!info) {
95 SYSERROR("Could not allocate memory.");
96 return NULL;
97 }
98
99 /* read capabilities */
100 snprintf(proc_fn, MAXPATHLEN, "/proc/%d/status", pid);
101
102 proc_file = fopen(proc_fn, "r");
103 if (!proc_file) {
104 SYSERROR("Could not open %s", proc_fn);
105 goto out_error;
106 }
107
108 found = 0;
109 while (getline(&line, &line_bufsz, proc_file) != -1) {
110 ret = sscanf(line, "CapBnd: %llx", &info->capability_mask);
111 if (ret != EOF && ret > 0) {
112 found = 1;
113 break;
114 }
115 }
116
117 fclose(proc_file);
118
119 if (!found) {
120 SYSERROR("Could not read capability bounding set from %s", proc_fn);
121 errno = ENOENT;
122 goto out_error;
123 }
124
125 /* read personality */
126 snprintf(proc_fn, MAXPATHLEN, "/proc/%d/personality", pid);
127
128 proc_file = fopen(proc_fn, "r");
129 if (!proc_file) {
130 SYSERROR("Could not open %s", proc_fn);
131 goto out_error;
132 }
133
134 ret = fscanf(proc_file, "%lx", &info->personality);
135 fclose(proc_file);
136
137 if (ret == EOF || ret == 0) {
138 SYSERROR("Could not read personality from %s", proc_fn);
139 errno = ENOENT;
140 goto out_error;
141 }
9958532b 142 info->aa_profile = aa_get_profile(pid);
e0732705 143
e0732705
CS
144 return info;
145
146out_error:
460a1cf0 147 free(info);
e0732705
CS
148 free(line);
149 return NULL;
150}
151
fc763ab7 152int lxc_attach_to_ns(pid_t pid, int which)
99d50954
CS
153{
154 char path[MAXPATHLEN];
fc763ab7
CS
155 /* according to <http://article.gmane.org/gmane.linux.kernel.containers.lxc.devel/1429>,
156 * the file for user namepsaces in /proc/$pid/ns will be called
157 * 'user' once the kernel supports it
158 */
159 static char *ns[] = { "mnt", "pid", "uts", "ipc", "user", "net" };
160 static int flags[] = {
161 CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWUTS, CLONE_NEWIPC,
162 CLONE_NEWUSER, CLONE_NEWNET
163 };
164 static const int size = sizeof(ns) / sizeof(char *);
99d50954 165 int fd[size];
fc763ab7
CS
166 int i, j, saved_errno;
167
99d50954
CS
168
169 snprintf(path, MAXPATHLEN, "/proc/%d/ns", pid);
170 if (access(path, X_OK)) {
171 ERROR("Does this kernel version support 'attach' ?");
172 return -1;
173 }
174
175 for (i = 0; i < size; i++) {
fc763ab7
CS
176 /* ignore if we are not supposed to attach to that
177 * namespace
178 */
179 if (which != -1 && !(which & flags[i])) {
180 fd[i] = -1;
181 continue;
182 }
183
99d50954
CS
184 snprintf(path, MAXPATHLEN, "/proc/%d/ns/%s", pid, ns[i]);
185 fd[i] = open(path, O_RDONLY);
186 if (fd[i] < 0) {
fc763ab7
CS
187 saved_errno = errno;
188
189 /* close all already opened file descriptors before
190 * we return an error, so we don't leak them
191 */
192 for (j = 0; j < i; j++)
193 close(fd[j]);
194
195 errno = saved_errno;
99d50954
CS
196 SYSERROR("failed to open '%s'", path);
197 return -1;
198 }
199 }
200
201 for (i = 0; i < size; i++) {
fc763ab7
CS
202 if (fd[i] >= 0 && setns(fd[i], 0) != 0) {
203 saved_errno = errno;
204
205 for (j = i; j < size; j++)
206 close(fd[j]);
207
208 errno = saved_errno;
99d50954
CS
209 SYSERROR("failed to set namespace '%s'", ns[i]);
210 return -1;
211 }
212
213 close(fd[i]);
214 }
215
216 return 0;
217}
218
7a0b0b56
CS
219int lxc_attach_remount_sys_proc()
220{
221 int ret;
222
223 ret = unshare(CLONE_NEWNS);
224 if (ret < 0) {
225 SYSERROR("failed to unshare mount namespace");
226 return -1;
227 }
228
229 /* assume /proc is always mounted, so remount it */
230 ret = umount2("/proc", MNT_DETACH);
231 if (ret < 0) {
232 SYSERROR("failed to unmount /proc");
233 return -1;
234 }
235
236 ret = mount("none", "/proc", "proc", 0, NULL);
237 if (ret < 0) {
238 SYSERROR("failed to remount /proc");
239 return -1;
240 }
241
242 /* try to umount /sys - if it's not a mount point,
243 * we'll get EINVAL, then we ignore it because it
244 * may not have been mounted in the first place
245 */
246 ret = umount2("/sys", MNT_DETACH);
247 if (ret < 0 && errno != EINVAL) {
248 SYSERROR("failed to unmount /sys");
249 return -1;
250 } else if (ret == 0) {
251 /* remount it */
252 ret = mount("none", "/sys", "sysfs", 0, NULL);
253 if (ret < 0) {
254 SYSERROR("failed to remount /sys");
255 return -1;
256 }
257 }
258
259 return 0;
260}
261
e0732705
CS
262int lxc_attach_drop_privs(struct lxc_proc_context_info *ctx)
263{
264 int last_cap = lxc_caps_last_cap();
265 int cap;
266
267 for (cap = 0; cap <= last_cap; cap++) {
268 if (ctx->capability_mask & (1LL << cap))
269 continue;
270
271 if (prctl(PR_CAPBSET_DROP, cap, 0, 0, 0)) {
272 SYSERROR("failed to remove capability id %d", cap);
273 return -1;
274 }
275 }
276
277 return 0;
278}
905022f7 279
b3a39ba6
DW
280int lxc_attach_set_environment()
281{
282 if (clearenv()) {
283 SYSERROR("failed to clear environment");
284 /* don't error out though */
285 }
286
287 if (putenv("container=lxc")) {
288 SYSERROR("failed to set environment variable");
289 return -1;
290 }
291
292 return 0;
293}
294
905022f7
CS
295char *lxc_attach_getpwshell(uid_t uid)
296{
297 /* local variables */
298 pid_t pid;
299 int pipes[2];
300 int ret;
301 int fd;
302 char *result = NULL;
303
304 /* we need to fork off a process that runs the
305 * getent program, and we need to capture its
306 * output, so we use a pipe for that purpose
307 */
308 ret = pipe(pipes);
309 if (ret < 0)
310 return NULL;
311
312 pid = fork();
313 if (pid < 0) {
314 close(pipes[0]);
315 close(pipes[1]);
316 return NULL;
317 }
318
319 if (pid) {
320 /* parent process */
321 FILE *pipe_f;
322 char *line = NULL;
323 size_t line_bufsz = 0;
324 int found = 0;
325 int status;
326
327 close(pipes[1]);
328
329 pipe_f = fdopen(pipes[0], "r");
330 while (getline(&line, &line_bufsz, pipe_f) != -1) {
331 char *token;
332 char *saveptr = NULL;
333 long value;
334 char *endptr = NULL;
335 int i;
336
337 /* if we already found something, just continue
338 * to read until the pipe doesn't deliver any more
339 * data, but don't modify the existing data
340 * structure
341 */
342 if (found)
343 continue;
344
345 /* trim line on the right hand side */
346 for (i = strlen(line); line && i > 0 && (line[i - 1] == '\n' || line[i - 1] == '\r'); --i)
347 line[i - 1] = '\0';
348
349 /* split into tokens: first user name */
350 token = strtok_r(line, ":", &saveptr);
351 if (!token)
352 continue;
353 /* next: dummy password field */
354 token = strtok_r(NULL, ":", &saveptr);
355 if (!token)
356 continue;
357 /* next: user id */
358 token = strtok_r(NULL, ":", &saveptr);
359 value = token ? strtol(token, &endptr, 10) : 0;
360 if (!token || !endptr || *endptr || value == LONG_MIN || value == LONG_MAX)
361 continue;
362 /* dummy sanity check: user id matches */
363 if ((uid_t) value != uid)
364 continue;
365 /* skip fields: gid, gecos, dir, go to next field 'shell' */
366 for (i = 0; i < 4; i++) {
367 token = strtok_r(NULL, ":", &saveptr);
368 if (!token)
369 break;
370 }
371 if (!token)
372 continue;
53a54099
SH
373 if (result)
374 free(result);
905022f7
CS
375 result = strdup(token);
376
377 /* sanity check that there are no fields after that */
378 token = strtok_r(NULL, ":", &saveptr);
379 if (token)
380 continue;
381
382 found = 1;
383 }
384
385 free(line);
386 fclose(pipe_f);
387 again:
388 if (waitpid(pid, &status, 0) < 0) {
389 if (errno == EINTR)
390 goto again;
391 return NULL;
392 }
393
394 /* some sanity checks: if anything even hinted at going
395 * wrong: we can't be sure we have a valid result, so
396 * we assume we don't
397 */
398
399 if (!WIFEXITED(status))
400 return NULL;
401
402 if (WEXITSTATUS(status) != 0)
403 return NULL;
404
405 if (!found)
406 return NULL;
407
408 return result;
409 } else {
410 /* child process */
411 char uid_buf[32];
412 char *arguments[] = {
413 "getent",
414 "passwd",
415 uid_buf,
416 NULL
417 };
418
419 close(pipes[0]);
420
421 /* we want to capture stdout */
422 dup2(pipes[1], 1);
423 close(pipes[1]);
424
425 /* get rid of stdin/stderr, so we try to associate it
426 * with /dev/null
427 */
428 fd = open("/dev/null", O_RDWR);
429 if (fd < 0) {
430 close(0);
431 close(2);
432 } else {
433 dup2(fd, 0);
434 dup2(fd, 2);
435 close(fd);
436 }
437
438 /* finish argument list */
439 ret = snprintf(uid_buf, sizeof(uid_buf), "%ld", (long) uid);
440 if (ret <= 0)
441 exit(-1);
442
443 /* try to run getent program */
444 (void) execvp("getent", arguments);
445 exit(-1);
446 }
447}
cb3e61fa
CS
448
449void lxc_attach_get_init_uidgid(uid_t* init_uid, gid_t* init_gid)
450{
451 FILE *proc_file;
452 char proc_fn[MAXPATHLEN];
453 char *line = NULL;
454 size_t line_bufsz = 0;
455 int ret;
456 long value = -1;
457 uid_t uid = (uid_t)-1;
458 gid_t gid = (gid_t)-1;
459
460 /* read capabilities */
461 snprintf(proc_fn, MAXPATHLEN, "/proc/%d/status", 1);
462
463 proc_file = fopen(proc_fn, "r");
464 if (!proc_file)
465 return;
466
467 while (getline(&line, &line_bufsz, proc_file) != -1) {
468 /* format is: real, effective, saved set user, fs
469 * we only care about real uid
470 */
471 ret = sscanf(line, "Uid: %ld", &value);
472 if (ret != EOF && ret > 0) {
473 uid = (uid_t) value;
474 } else {
475 ret = sscanf(line, "Gid: %ld", &value);
476 if (ret != EOF && ret > 0)
477 gid = (gid_t) value;
478 }
479 if (uid != (uid_t)-1 && gid != (gid_t)-1)
480 break;
481 }
482
483 fclose(proc_file);
484 free(line);
485
486 /* only override arguments if we found something */
487 if (uid != (uid_t)-1)
488 *init_uid = uid;
489 if (gid != (gid_t)-1)
490 *init_gid = gid;
491
492 /* TODO: we should also parse supplementary groups and use
493 * setgroups() to set them */
494}