]>
Commit | Line | Data |
---|---|---|
576f946d | 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> |
576f946d | 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 | #define _GNU_SOURCE | |
24 | #include <stdio.h> | |
25 | #undef _GNU_SOURCE | |
26 | #include <stdlib.h> | |
27 | #include <errno.h> | |
576f946d | 28 | #include <unistd.h> |
29 | #include <string.h> | |
341a9bd8 | 30 | #include <dirent.h> |
576f946d | 31 | #include <fcntl.h> |
32 | #include <sys/types.h> | |
33 | #include <sys/stat.h> | |
34 | #include <sys/param.h> | |
35 | #include <sys/inotify.h> | |
36 | #include <netinet/in.h> | |
37 | #include <net/if.h> | |
38 | ||
e2bcd7db | 39 | #include "error.h" |
881450bb | 40 | #include "config.h" |
ae5c8b8e | 41 | #include "commands.h" |
36eb9bde | 42 | |
36eb9bde | 43 | #include <lxc/log.h> |
00b3c2e2 CLG |
44 | #include <lxc/cgroup.h> |
45 | #include <lxc/start.h> | |
36eb9bde | 46 | |
edaf8b1b SG |
47 | #if IS_BIONIC |
48 | #include <../include/lxcmntent.h> | |
49 | #else | |
50 | #include <mntent.h> | |
51 | #endif | |
52 | ||
36eb9bde | 53 | lxc_log_define(lxc_cgroup, lxc); |
576f946d | 54 | |
5193cc3d | 55 | #define MTAB "/proc/mounts" |
576f946d | 56 | |
1d39a065 DW |
57 | /* Check if a mount is a cgroup hierarchy for any subsystem. |
58 | * Return the first subsystem found (or NULL if none). | |
59 | */ | |
60 | static char *mount_has_subsystem(const struct mntent *mntent) | |
61 | { | |
62 | FILE *f; | |
5270bf4b | 63 | char *c, *ret = NULL; |
1d39a065 DW |
64 | char line[MAXPATHLEN]; |
65 | ||
66 | /* read the list of subsystems from the kernel */ | |
67 | f = fopen("/proc/cgroups", "r"); | |
68 | if (!f) | |
69 | return 0; | |
70 | ||
71 | /* skip the first line, which contains column headings */ | |
00b6be44 SH |
72 | if (!fgets(line, MAXPATHLEN, f)) { |
73 | fclose(f); | |
1d39a065 | 74 | return 0; |
00b6be44 | 75 | } |
1d39a065 DW |
76 | |
77 | while (fgets(line, MAXPATHLEN, f)) { | |
78 | c = strchr(line, '\t'); | |
79 | if (!c) | |
80 | continue; | |
81 | *c = '\0'; | |
82 | ||
83 | ret = hasmntopt(mntent, line); | |
84 | if (ret) | |
85 | break; | |
86 | } | |
87 | ||
88 | fclose(f); | |
89 | return ret; | |
90 | } | |
91 | ||
d08ba6ec | 92 | /* |
23622a2a | 93 | * Determine mountpoint for a cgroup subsystem. |
ae5c8b8e SH |
94 | * @subsystem: cgroup subsystem (i.e. freezer). If this is NULL, the first |
95 | * cgroup mountpoint with any subsystems is used. | |
96 | * @mnt: a passed-in buffer of at least size MAXPATHLEN into which the path | |
97 | * is copied. | |
98 | * | |
99 | * Returns 0 on success, -1 on error. | |
100 | */ | |
bcbd102c | 101 | static int get_cgroup_mount(const char *subsystem, char *mnt) |
576f946d | 102 | { |
bcbd102c SH |
103 | struct mntent *mntent; |
104 | FILE *file = NULL; | |
ae5c8b8e | 105 | int ret, err = -1; |
576f946d | 106 | |
bcbd102c SH |
107 | file = setmntent(MTAB, "r"); |
108 | if (!file) { | |
109 | SYSERROR("failed to open %s", MTAB); | |
5193cc3d | 110 | return -1; |
bcbd102c | 111 | } |
0d9f8e18 | 112 | |
bcbd102c | 113 | while ((mntent = getmntent(file))) { |
bcbd102c SH |
114 | if (strcmp(mntent->mnt_type, "cgroup")) |
115 | continue; | |
1d39a065 DW |
116 | |
117 | if (subsystem) { | |
118 | if (!hasmntopt(mntent, subsystem)) | |
119 | continue; | |
ae5c8b8e | 120 | } else { |
1d39a065 DW |
121 | if (!mount_has_subsystem(mntent)) |
122 | continue; | |
123 | } | |
ad08bbb7 | 124 | |
23622a2a | 125 | ret = snprintf(mnt, MAXPATHLEN, "%s", mntent->mnt_dir); |
ad08bbb7 DW |
126 | if (ret < 0 || ret >= MAXPATHLEN) |
127 | goto fail; | |
128 | ||
129 | DEBUG("using cgroup mounted at '%s'", mnt); | |
130 | err = 0; | |
131 | goto out; | |
bcbd102c | 132 | }; |
576f946d | 133 | |
d08ba6ec SH |
134 | fail: |
135 | DEBUG("Failed to find cgroup for %s\n", | |
136 | subsystem ? subsystem : "(NULL)"); | |
ad08bbb7 DW |
137 | out: |
138 | endmntent(file); | |
139 | return err; | |
5193cc3d DL |
140 | } |
141 | ||
ae5c8b8e SH |
142 | /* |
143 | * cgroup_path_get: Calculate the full path for a particular subsystem, plus | |
144 | * a passed-in (to be appended) relative cgpath for a container. | |
145 | * @path: a char** into which a pointer to the answer is copied | |
146 | * @subsystem: subsystem of interest (i.e. freezer). | |
147 | * @cgpath: a container's (relative) cgroup path, i.e. "/lxc/c1". | |
148 | * | |
149 | * Returns 0 on success, -1 on error. | |
150 | * | |
151 | * The answer is written in a static char[MAXPATHLEN] in this function and | |
152 | * should not be freed. | |
153 | */ | |
154 | extern int cgroup_path_get(char **path, const char *subsystem, const char *cgpath) | |
0b9c21ab SH |
155 | { |
156 | static char buf[MAXPATHLEN]; | |
ae5c8b8e SH |
157 | static char retbuf[MAXPATHLEN]; |
158 | int rc; | |
0b9c21ab | 159 | |
ae5c8b8e SH |
160 | /* lxc_cgroup_set passes a state object for the subsystem, |
161 | * so trim it to just the subsystem part */ | |
162 | if (subsystem) { | |
163 | rc = snprintf(retbuf, MAXPATHLEN, "%s", subsystem); | |
164 | if (rc < 0 || rc >= MAXPATHLEN) { | |
165 | ERROR("subsystem name too long"); | |
166 | return -1; | |
167 | } | |
168 | char *s = index(retbuf, '.'); | |
169 | if (s) | |
170 | *s = '\0'; | |
171 | DEBUG("%s: called for subsys %s name %s\n", __func__, retbuf, cgpath); | |
172 | } | |
173 | if (get_cgroup_mount(subsystem ? retbuf : NULL, buf)) { | |
174 | ERROR("cgroup is not mounted"); | |
175 | return -1; | |
176 | } | |
177 | ||
178 | rc = snprintf(retbuf, MAXPATHLEN, "%s/%s", buf, cgpath); | |
179 | if (rc < 0 || rc >= MAXPATHLEN) { | |
180 | ERROR("name too long"); | |
181 | return -1; | |
182 | } | |
183 | ||
184 | DEBUG("%s: returning %s for subsystem %s", __func__, retbuf, subsystem); | |
185 | ||
186 | *path = retbuf; | |
187 | return 0; | |
0b9c21ab SH |
188 | } |
189 | ||
ae5c8b8e SH |
190 | /* |
191 | * Calculate a container's cgroup path for a particular subsystem. This | |
192 | * is the cgroup path relative to the root of the cgroup filesystem. | |
193 | * @path: A char ** into which we copy the char* containing the answer | |
194 | * @subsystem: the cgroup subsystem of interest (i.e. freezer) | |
195 | * @name: container name | |
196 | * @lxcpath: the lxcpath in which the container is running. | |
197 | * | |
198 | * Returns 0 on success, -1 on error. | |
199 | * | |
200 | * Note that the char* copied into *path is a static char[MAXPATHLEN] in | |
201 | * commands.c:receive_answer(). It should not be freed. | |
202 | */ | |
203 | extern int lxc_get_cgpath(const char **path, const char *subsystem, const char *name, const char *lxcpath) | |
6203de18 | 204 | { |
ae5c8b8e SH |
205 | struct lxc_command command = { |
206 | .request = { .type = LXC_COMMAND_CGROUP }, | |
207 | }; | |
208 | ||
209 | int ret, stopped = 0; | |
fc3c7f7f | 210 | |
ae5c8b8e SH |
211 | ret = lxc_command(name, &command, &stopped, lxcpath); |
212 | if (ret < 0) { | |
213 | if (!stopped) | |
214 | ERROR("failed to send command"); | |
fc3c7f7f | 215 | return -1; |
ae5c8b8e | 216 | } |
6203de18 | 217 | |
ae5c8b8e SH |
218 | if (!ret) { |
219 | WARN("'%s' has stopped before sending its state", name); | |
fc3c7f7f | 220 | return -1; |
ae5c8b8e | 221 | } |
6203de18 | 222 | |
ae5c8b8e SH |
223 | if (command.answer.ret < 0 || command.answer.pathlen < 0) { |
224 | ERROR("failed to get state for '%s': %s", | |
225 | name, strerror(-command.answer.ret)); | |
ef342abb | 226 | return -1; |
0dd4566e DL |
227 | } |
228 | ||
ae5c8b8e | 229 | *path = command.answer.path; |
758437c5 | 230 | |
ef342abb | 231 | return 0; |
6203de18 DL |
232 | } |
233 | ||
ae5c8b8e SH |
234 | /* |
235 | * lxc_cgroup_path_get: determine full pathname for a cgroup | |
236 | * file for a specific container. | |
237 | * @path: char ** used to return the answer. The char * will point | |
238 | * into the static char* retuf from cgroup_path_get() (so no need | |
239 | * to free it). | |
240 | * @subsystem: cgroup subsystem (i.e. "freezer") for which to | |
241 | * return an answer. If NULL, then the first cgroup entry in | |
242 | * mtab will be used. | |
243 | * | |
244 | * This is the exported function, which determines cgpath from the | |
245 | * monitor running in lxcpath. | |
246 | * | |
247 | * Returns 0 on success, < 0 on error. | |
248 | */ | |
249 | int lxc_cgroup_path_get(char **path, const char *subsystem, const char *name, const char *lxcpath) | |
fd4f5a56 | 250 | { |
ae5c8b8e | 251 | const char *cgpath; |
fd4f5a56 | 252 | |
ae5c8b8e | 253 | if (lxc_get_cgpath(&cgpath, subsystem, name, lxcpath) < 0) |
fd4f5a56 | 254 | return -1; |
fd4f5a56 | 255 | |
ae5c8b8e SH |
256 | return cgroup_path_get(path, subsystem, cgpath); |
257 | } | |
258 | ||
259 | /* | |
260 | * small helper which simply write a value into a (cgroup) file | |
261 | */ | |
262 | static int do_cgroup_set(const char *path, const char *value) | |
263 | { | |
264 | int fd, ret; | |
265 | ||
266 | if ((fd = open(path, O_WRONLY)) < 0) { | |
267 | SYSERROR("open %s : %s", path, strerror(errno)); | |
268 | return -1; | |
fd4f5a56 DL |
269 | } |
270 | ||
ae5c8b8e SH |
271 | if ((ret = write(fd, value, strlen(value))) < 0) { |
272 | close(fd); | |
273 | SYSERROR("write %s : %s", path, strerror(errno)); | |
274 | return ret; | |
275 | } | |
fd4f5a56 | 276 | |
ae5c8b8e SH |
277 | if ((ret = close(fd)) < 0) { |
278 | SYSERROR("close %s : %s", path, strerror(errno)); | |
279 | return ret; | |
280 | } | |
281 | return 0; | |
fd4f5a56 DL |
282 | } |
283 | ||
ae5c8b8e SH |
284 | /* |
285 | * small helper to write a value into a file in a particular directory. | |
286 | * @cgpath: the directory in which to find the file | |
287 | * @filename: the file (under cgpath) to which to write | |
288 | * @value: what to write | |
289 | * | |
290 | * Returns 0 on success, < 0 on error. | |
291 | */ | |
292 | int lxc_cgroup_set_bypath(const char *cgpath, const char *filename, const char *value) | |
fd4f5a56 | 293 | { |
ae5c8b8e SH |
294 | int ret; |
295 | char *dirpath; | |
296 | char path[MAXPATHLEN]; | |
c8f7c563 | 297 | |
ae5c8b8e SH |
298 | ret = cgroup_path_get(&dirpath, filename, cgpath); |
299 | if (ret) | |
300 | return -1; | |
c8f7c563 | 301 | |
ae5c8b8e SH |
302 | ret = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename); |
303 | if (ret < 0 || ret >= MAXPATHLEN) { | |
304 | ERROR("pathname too long"); | |
305 | return -1; | |
306 | } | |
c8f7c563 | 307 | |
ae5c8b8e | 308 | return do_cgroup_set(path, value); |
c8f7c563 CS |
309 | } |
310 | ||
ae5c8b8e SH |
311 | /* |
312 | * set a cgroup value for a container | |
313 | * | |
314 | * @name: name of the container | |
315 | * @filename: the cgroup file (i.e. freezer.state) whose value to change | |
316 | * @value: the value to write to the file | |
317 | * @lxcpath: the lxcpath under which the container is running. | |
318 | * | |
319 | * Returns 0 on success, < 0 on error. | |
320 | */ | |
321 | ||
322 | int lxc_cgroup_set(const char *name, const char *filename, const char *value, | |
323 | const char *lxcpath) | |
c8f7c563 | 324 | { |
ae5c8b8e SH |
325 | int ret; |
326 | char *dirpath; | |
327 | char path[MAXPATHLEN]; | |
328 | ||
329 | ret = lxc_cgroup_path_get(&dirpath, filename, name, lxcpath); | |
330 | if (ret) | |
331 | return -1; | |
332 | ||
333 | ret = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename); | |
334 | if (ret < 0 || ret >= MAXPATHLEN) { | |
335 | ERROR("pathname too long"); | |
336 | return -1; | |
337 | } | |
338 | ||
339 | return do_cgroup_set(path, value); | |
c8f7c563 CS |
340 | } |
341 | ||
ae5c8b8e SH |
342 | /* |
343 | * Get value of a cgroup setting for a container. | |
344 | * | |
345 | * @name: name of the container | |
346 | * @filename: the cgroup file to read (i.e. 'freezer.state') | |
347 | * @value: a preallocated char* into which to copy the answer | |
348 | * @len: the length of pre-allocated @value | |
349 | * @lxcpath: the lxcpath in which the container is running (i.e. | |
350 | * /var/lib/lxc) | |
351 | * | |
352 | * Returns < 0 on error, or the number of bytes read. | |
353 | * | |
354 | * If you pass in NULL value or 0 len, then you are asking for the size of the | |
355 | * file. | |
356 | * | |
357 | * Note that we can't get the file size quickly through stat or lseek. | |
358 | * Therefore if you pass in len > 0 but less than the file size, your only | |
359 | * indication will be that the return value will be equal to the passed-in ret. | |
360 | * We will not return the actual full file size. | |
361 | */ | |
362 | int lxc_cgroup_get(const char *name, const char *filename, char *value, | |
363 | size_t len, const char *lxcpath) | |
c8f7c563 | 364 | { |
ae5c8b8e SH |
365 | int fd, ret = -1; |
366 | char *dirpath; | |
367 | char path[MAXPATHLEN]; | |
9ba8130c | 368 | int rc; |
460a1cf0 | 369 | |
ae5c8b8e SH |
370 | ret = lxc_cgroup_path_get(&dirpath, filename, name, lxcpath); |
371 | if (ret) | |
372 | return -1; | |
fd4f5a56 | 373 | |
ae5c8b8e | 374 | rc = snprintf(path, MAXPATHLEN, "%s/%s", dirpath, filename); |
9ba8130c SH |
375 | if (rc < 0 || rc >= MAXPATHLEN) { |
376 | ERROR("pathname too long"); | |
377 | return -1; | |
378 | } | |
fd4f5a56 | 379 | |
ae5c8b8e | 380 | fd = open(path, O_RDONLY); |
c8f7c563 | 381 | if (fd < 0) { |
ae5c8b8e | 382 | ERROR("open %s : %s", path, strerror(errno)); |
fd4f5a56 DL |
383 | return -1; |
384 | } | |
385 | ||
ae5c8b8e SH |
386 | if (!len || !value) { |
387 | char buf[100]; | |
388 | int count = 0; | |
389 | while ((ret = read(fd, buf, 100)) > 0) | |
390 | count += ret; | |
391 | if (ret >= 0) | |
392 | ret = count; | |
393 | } else { | |
394 | memset(value, 0, len); | |
395 | ret = read(fd, value, len); | |
fd4f5a56 DL |
396 | } |
397 | ||
ae5c8b8e SH |
398 | if (ret < 0) |
399 | ERROR("read %s : %s", path, strerror(errno)); | |
400 | ||
401 | close(fd); | |
402 | return ret; | |
c8f7c563 CS |
403 | } |
404 | ||
ae5c8b8e | 405 | int lxc_cgroup_nrtasks(const char *cgpath) |
c8f7c563 | 406 | { |
ae5c8b8e SH |
407 | char *dpath; |
408 | char path[MAXPATHLEN]; | |
409 | int pid, ret, count = 0; | |
410 | FILE *file; | |
411 | int rc; | |
c8f7c563 | 412 | |
ae5c8b8e SH |
413 | ret = cgroup_path_get(&dpath, NULL, cgpath); |
414 | if (ret) | |
415 | return -1; | |
c8f7c563 | 416 | |
ae5c8b8e SH |
417 | rc = snprintf(path, MAXPATHLEN, "%s/tasks", dpath); |
418 | if (rc < 0 || rc >= MAXPATHLEN) { | |
419 | ERROR("pathname too long"); | |
420 | return -1; | |
421 | } | |
c8f7c563 | 422 | |
ae5c8b8e SH |
423 | file = fopen(path, "r"); |
424 | if (!file) { | |
425 | SYSERROR("fopen '%s' failed", path); | |
426 | return -1; | |
c8f7c563 CS |
427 | } |
428 | ||
ae5c8b8e SH |
429 | while (fscanf(file, "%d", &pid) != EOF) |
430 | count++; | |
fd4f5a56 | 431 | |
ae5c8b8e SH |
432 | fclose(file); |
433 | ||
434 | return count; | |
fd4f5a56 DL |
435 | } |
436 | ||
fc7de561 SH |
437 | /* |
438 | * If first creating the /sys/fs/cgroup/$subsys/lxc container, then | |
439 | * try to set clone_children to 1. Some kernels don't support | |
440 | * clone_children, and cgroup maintainer wants to deprecate it. So | |
441 | * XXX TODO we should instead after each cgroup mkdir (here and in | |
442 | * hooks/mountcgroup) check if cpuset is in the subsystems, and if so | |
443 | * manually copy over mems and cpus. | |
444 | */ | |
23622a2a | 445 | static void set_clone_children(const char *mntdir) |
fc7de561 SH |
446 | { |
447 | char path[MAXPATHLEN]; | |
448 | FILE *fout; | |
449 | int ret; | |
450 | ||
23622a2a | 451 | ret = snprintf(path, MAXPATHLEN, "%s/cgroup.clone_children", mntdir); |
fc7de561 SH |
452 | INFO("writing to %s\n", path); |
453 | if (ret < 0 || ret > MAXPATHLEN) | |
454 | return; | |
455 | fout = fopen(path, "w"); | |
456 | if (!fout) | |
457 | return; | |
458 | fprintf(fout, "1\n"); | |
459 | fclose(fout); | |
460 | } | |
461 | ||
ae5c8b8e SH |
462 | /* |
463 | * Make sure the 'cgroup group' exists, so that we don't have to worry about | |
464 | * that later. | |
465 | * | |
466 | * @lxcgroup: the cgroup group, i.e. 'lxc' by default. | |
467 | * | |
468 | * See detailed comments at lxc_cgroup_path_create for more information. | |
469 | * | |
470 | * Returns 0 on success, -1 on error. | |
471 | */ | |
472 | static int create_lxcgroups(const char *lxcgroup) | |
460a1cf0 | 473 | { |
460a1cf0 | 474 | FILE *file = NULL; |
ae5c8b8e SH |
475 | struct mntent *mntent; |
476 | int ret, retv = -1; | |
23622a2a | 477 | char path[MAXPATHLEN]; |
460a1cf0 DW |
478 | |
479 | file = setmntent(MTAB, "r"); | |
480 | if (!file) { | |
481 | SYSERROR("failed to open %s", MTAB); | |
482 | return -1; | |
483 | } | |
484 | ||
485 | while ((mntent = getmntent(file))) { | |
460a1cf0 DW |
486 | |
487 | if (strcmp(mntent->mnt_type, "cgroup")) | |
488 | continue; | |
1d39a065 DW |
489 | if (!mount_has_subsystem(mntent)) |
490 | continue; | |
460a1cf0 | 491 | |
ae5c8b8e SH |
492 | /* |
493 | * TODO - handle case where lxcgroup has subdirs? (i.e. build/l1) | |
23622a2a SH |
494 | * We probably only want to support that for /users/joe |
495 | */ | |
496 | ret = snprintf(path, MAXPATHLEN, "%s/%s", | |
497 | mntent->mnt_dir, lxcgroup ? lxcgroup : "lxc"); | |
ae5c8b8e SH |
498 | if (ret < 0 || ret >= MAXPATHLEN) |
499 | goto fail; | |
500 | if (access(path, F_OK)) { | |
23622a2a | 501 | set_clone_children(mntent->mnt_dir); |
ae5c8b8e SH |
502 | ret = mkdir(path, 0755); |
503 | if (ret == -1 && errno != EEXIST) { | |
504 | SYSERROR("failed to create '%s' directory", path); | |
505 | goto fail; | |
506 | } | |
c8f7c563 | 507 | } |
c8f7c563 | 508 | |
c8f7c563 CS |
509 | } |
510 | ||
ae5c8b8e SH |
511 | retv = 0; |
512 | fail: | |
513 | endmntent(file); | |
514 | return retv; | |
c8f7c563 CS |
515 | } |
516 | ||
d08ba6ec | 517 | /* |
ae5c8b8e SH |
518 | * For a new container, find a cgroup path which is unique in all cgroup mounts. |
519 | * I.e. if r1 is already running, then /lxc/r1-1 may be used. | |
520 | * | |
521 | * @lxcgroup: the cgroup 'group' the contaienr should run in. By default, this | |
522 | * is just 'lxc'. Admins may wish to group some containers into other groups, | |
523 | * i.e. 'build', to take advantage of cgroup hierarchy to simplify group | |
524 | * administration. Also, unprivileged users who are placed into a cgroup by | |
525 | * libcgroup_pam will be using that cgroup rather than the system-wide 'lxc' | |
526 | * group. | |
527 | * @name: the name of the container | |
528 | * | |
529 | * The chosen cgpath is returned as a strdup'd string. The caller will have to | |
530 | * free that eventually, however the lxc monitor will keep that string so as to | |
531 | * return it in response to a LXC_COMMAND_CGROUP query. | |
532 | * | |
23622a2a SH |
533 | * Note the path is relative to cgroup mounts. I.e. if the freezer subsystem |
534 | * is at /sys/fs/cgroup/freezer, and this fn returns '/lxc/r1', then the | |
535 | * freezer cgroup's full path will be /sys/fs/cgroup/freezer/lxc/r1/. | |
ae5c8b8e SH |
536 | * |
537 | * XXX This should probably be locked globally | |
538 | * | |
539 | * Races won't be determintal, you'll just end up with leftover unused cgroups | |
d08ba6ec | 540 | */ |
ae5c8b8e | 541 | char *lxc_cgroup_path_create(const char *lxcgroup, const char *name) |
d08ba6ec | 542 | { |
ae5c8b8e | 543 | int i = 0, ret; |
23622a2a | 544 | char *retpath, path[MAXPATHLEN]; |
ae5c8b8e SH |
545 | char tail[12]; |
546 | FILE *file = NULL; | |
547 | struct mntent *mntent; | |
d08ba6ec | 548 | |
ae5c8b8e SH |
549 | if (create_lxcgroups(lxcgroup) < 0) |
550 | return NULL; | |
d08ba6ec | 551 | |
ae5c8b8e SH |
552 | again: |
553 | file = setmntent(MTAB, "r"); | |
554 | if (!file) { | |
555 | SYSERROR("failed to open %s", MTAB); | |
556 | return NULL; | |
d08ba6ec | 557 | } |
1ac470c0 | 558 | |
ae5c8b8e SH |
559 | if (i) |
560 | snprintf(tail, 12, "-%d", i); | |
561 | else | |
562 | *tail = '\0'; | |
257e5824 | 563 | |
ae5c8b8e | 564 | while ((mntent = getmntent(file))) { |
fd4f5a56 | 565 | |
ae5c8b8e SH |
566 | if (strcmp(mntent->mnt_type, "cgroup")) |
567 | continue; | |
568 | if (!mount_has_subsystem(mntent)) | |
569 | continue; | |
e4659536 | 570 | |
23622a2a SH |
571 | /* find unused mnt_dir + lxcgroup + name + -$i */ |
572 | ret = snprintf(path, MAXPATHLEN, "%s/%s/%s%s", mntent->mnt_dir, | |
ae5c8b8e SH |
573 | lxcgroup ? lxcgroup : "lxc", name, tail); |
574 | if (ret < 0 || ret >= MAXPATHLEN) | |
575 | goto fail; | |
5193cc3d | 576 | |
ae5c8b8e | 577 | if (access(path, F_OK) == 0) goto next; |
fd4f5a56 | 578 | |
ae5c8b8e SH |
579 | if (mkdir(path, 0755)) { |
580 | ERROR("Error creating cgroups"); | |
581 | goto fail; | |
e7f40d8a | 582 | } |
d08ba6ec | 583 | |
d08ba6ec SH |
584 | } |
585 | ||
ae5c8b8e | 586 | endmntent(file); |
fd4f5a56 | 587 | |
ae5c8b8e SH |
588 | // print out the cgpath part |
589 | ret = snprintf(path, MAXPATHLEN, "%s/%s%s", | |
590 | lxcgroup ? lxcgroup : "lxc", name, tail); | |
591 | if (ret < 0 || ret >= MAXPATHLEN) // can't happen | |
592 | goto fail; | |
bcbd102c | 593 | |
ae5c8b8e SH |
594 | retpath = strdup(path); |
595 | ||
596 | return retpath; | |
597 | ||
598 | next: | |
599 | endmntent(file); | |
600 | i++; | |
601 | goto again; | |
602 | ||
603 | fail: | |
604 | endmntent(file); | |
605 | return NULL; | |
36b86299 DL |
606 | } |
607 | ||
ae5c8b8e | 608 | int lxc_cgroup_enter(const char *cgpath, pid_t pid) |
36b86299 | 609 | { |
23622a2a | 610 | char path[MAXPATHLEN]; |
ae5c8b8e | 611 | FILE *file = NULL, *fout; |
bcbd102c | 612 | struct mntent *mntent; |
ae5c8b8e | 613 | int ret, retv = -1; |
ef342abb | 614 | |
bcbd102c SH |
615 | file = setmntent(MTAB, "r"); |
616 | if (!file) { | |
617 | SYSERROR("failed to open %s", MTAB); | |
ef342abb DL |
618 | return -1; |
619 | } | |
620 | ||
bcbd102c | 621 | while ((mntent = getmntent(file))) { |
ad08bbb7 DW |
622 | if (strcmp(mntent->mnt_type, "cgroup")) |
623 | continue; | |
1d39a065 DW |
624 | if (!mount_has_subsystem(mntent)) |
625 | continue; | |
23622a2a SH |
626 | ret = snprintf(path, MAXPATHLEN, "%s/%s/tasks", |
627 | mntent->mnt_dir, cgpath); | |
ae5c8b8e SH |
628 | if (ret < 0 || ret >= MAXPATHLEN) { |
629 | ERROR("entering cgroup"); | |
ad08bbb7 | 630 | goto out; |
ae5c8b8e SH |
631 | } |
632 | fout = fopen(path, "w"); | |
633 | if (!fout) { | |
634 | ERROR("entering cgroup"); | |
460a1cf0 | 635 | goto out; |
ae5c8b8e SH |
636 | } |
637 | fprintf(fout, "%d\n", (int)pid); | |
638 | fclose(fout); | |
639 | } | |
640 | retv = 0; | |
f0e64b8b | 641 | |
bcbd102c SH |
642 | out: |
643 | endmntent(file); | |
ae5c8b8e | 644 | return retv; |
bcbd102c SH |
645 | } |
646 | ||
341a9bd8 SH |
647 | int recursive_rmdir(char *dirname) |
648 | { | |
649 | struct dirent dirent, *direntp; | |
341a9bd8 SH |
650 | DIR *dir; |
651 | int ret; | |
652 | char pathname[MAXPATHLEN]; | |
653 | ||
654 | dir = opendir(dirname); | |
655 | if (!dir) { | |
656 | WARN("failed to open directory: %m"); | |
657 | return -1; | |
658 | } | |
659 | ||
341a9bd8 SH |
660 | while (!readdir_r(dir, &dirent, &direntp)) { |
661 | struct stat mystat; | |
9ba8130c | 662 | int rc; |
341a9bd8 SH |
663 | |
664 | if (!direntp) | |
665 | break; | |
666 | ||
667 | if (!strcmp(direntp->d_name, ".") || | |
668 | !strcmp(direntp->d_name, "..")) | |
669 | continue; | |
670 | ||
9ba8130c SH |
671 | rc = snprintf(pathname, MAXPATHLEN, "%s/%s", dirname, direntp->d_name); |
672 | if (rc < 0 || rc >= MAXPATHLEN) { | |
673 | ERROR("pathname too long"); | |
674 | continue; | |
675 | } | |
341a9bd8 SH |
676 | ret = stat(pathname, &mystat); |
677 | if (ret) | |
678 | continue; | |
679 | if (S_ISDIR(mystat.st_mode)) | |
680 | recursive_rmdir(pathname); | |
681 | } | |
682 | ||
683 | ret = rmdir(dirname); | |
684 | ||
685 | if (closedir(dir)) | |
686 | ERROR("failed to close directory"); | |
687 | return ret; | |
688 | ||
689 | ||
690 | } | |
bcbd102c | 691 | |
ae5c8b8e | 692 | static int lxc_one_cgroup_destroy(struct mntent *mntent, const char *cgpath) |
bcbd102c | 693 | { |
23622a2a | 694 | char cgname[MAXPATHLEN]; |
d08ba6ec | 695 | char *cgmnt = mntent->mnt_dir; |
9ba8130c | 696 | int rc; |
bcbd102c | 697 | |
23622a2a | 698 | rc = snprintf(cgname, MAXPATHLEN, "%s/%s", cgmnt, cgpath); |
9ba8130c SH |
699 | if (rc < 0 || rc >= MAXPATHLEN) { |
700 | ERROR("name too long"); | |
701 | return -1; | |
702 | } | |
d08ba6ec | 703 | DEBUG("destroying %s\n", cgname); |
341a9bd8 | 704 | if (recursive_rmdir(cgname)) { |
ef342abb DL |
705 | SYSERROR("failed to remove cgroup '%s'", cgname); |
706 | return -1; | |
707 | } | |
708 | ||
709 | DEBUG("'%s' unlinked", cgname); | |
710 | ||
711 | return 0; | |
36b86299 DL |
712 | } |
713 | ||
bcbd102c SH |
714 | /* |
715 | * for each mounted cgroup, destroy the cgroup for the container | |
716 | */ | |
ae5c8b8e | 717 | int lxc_cgroup_destroy(const char *cgpath) |
a6ddef61 | 718 | { |
bcbd102c SH |
719 | struct mntent *mntent; |
720 | FILE *file = NULL; | |
ae5c8b8e | 721 | int err, retv = 0; |
bcbd102c SH |
722 | |
723 | file = setmntent(MTAB, "r"); | |
724 | if (!file) { | |
725 | SYSERROR("failed to open %s", MTAB); | |
726 | return -1; | |
727 | } | |
0411a752 | 728 | |
bcbd102c | 729 | while ((mntent = getmntent(file))) { |
ad08bbb7 DW |
730 | if (strcmp(mntent->mnt_type, "cgroup")) |
731 | continue; | |
1d39a065 DW |
732 | if (!mount_has_subsystem(mntent)) |
733 | continue; | |
a6ddef61 | 734 | |
ae5c8b8e SH |
735 | err = lxc_one_cgroup_destroy(mntent, cgpath); |
736 | if (err) // keep trying to clean up the others | |
737 | retv = -1; | |
ad08bbb7 | 738 | } |
bcbd102c | 739 | |
ad08bbb7 | 740 | endmntent(file); |
ae5c8b8e | 741 | return retv; |
a6ddef61 MN |
742 | } |
743 | ||
ae5c8b8e | 744 | int lxc_cgroup_attach(pid_t pid, const char *name, const char *lxcpath) |
576f946d | 745 | { |
ae5c8b8e | 746 | const char *dirpath; |
576f946d | 747 | |
ae5c8b8e SH |
748 | if (lxc_get_cgpath(&dirpath, NULL, name, lxcpath) < 0) { |
749 | ERROR("Error getting cgroup for container %s: %s", lxcpath, name); | |
576f946d | 750 | return -1; |
8b92dc3a | 751 | } |
ae5c8b8e | 752 | INFO("joining pid %d to cgroup %s", pid, dirpath); |
576f946d | 753 | |
ae5c8b8e | 754 | return lxc_cgroup_enter(dirpath, pid); |
e0f888d9 | 755 | } |