]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/cgmanager.c
log: Drop trailing \n from log messages
[mirror_lxc.git] / src / lxc / cgmanager.c
1 /*
2 * lxc: linux Container library
3 *
4 * (C) Copyright IBM Corp. 2007, 2008
5 *
6 * Authors:
7 * Daniel Lezcano <daniel.lezcano at free.fr>
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23 #include "config.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <dirent.h>
31 #include <fcntl.h>
32 #include <ctype.h>
33 #include <grp.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <sys/param.h>
37 #include <sys/inotify.h>
38 #include <sys/mount.h>
39 #include <netinet/in.h>
40 #include <net/if.h>
41
42 #include "error.h"
43 #include "commands.h"
44 #include "list.h"
45 #include "conf.h"
46 #include "utils.h"
47 #include "bdev.h"
48 #include "log.h"
49 #include "cgroup.h"
50 #include "start.h"
51 #include "state.h"
52
53 #ifdef HAVE_CGMANAGER
54 lxc_log_define(lxc_cgmanager, lxc);
55
56 #include <nih-dbus/dbus_connection.h>
57 #include <cgmanager/cgmanager-client.h>
58 #include <nih/alloc.h>
59 #include <nih/error.h>
60 #include <nih/string.h>
61
62 struct cgm_data {
63 char *name;
64 char *cgroup_path;
65 const char *cgroup_pattern;
66 };
67
68 static NihDBusProxy *cgroup_manager = NULL;
69 static struct cgroup_ops cgmanager_ops;
70 static int nr_subsystems;
71 static char **subsystems;
72
73 #define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock"
74 static void cgm_dbus_disconnected(DBusConnection *connection);
75 static bool cgm_dbus_connect(void)
76 {
77 DBusError dbus_error;
78 DBusConnection *connection;
79 dbus_error_init(&dbus_error);
80
81 connection = nih_dbus_connect(CGMANAGER_DBUS_SOCK, cgm_dbus_disconnected);
82 if (!connection) {
83 NihError *nerr;
84 nerr = nih_error_get();
85 DEBUG("Unable to open cgmanager connection at %s: %s", CGMANAGER_DBUS_SOCK,
86 nerr->message);
87 nih_free(nerr);
88 dbus_error_free(&dbus_error);
89 return false;
90 }
91 dbus_connection_set_exit_on_disconnect(connection, FALSE);
92 dbus_error_free(&dbus_error);
93 cgroup_manager = nih_dbus_proxy_new(NULL, connection,
94 NULL /* p2p */,
95 "/org/linuxcontainers/cgmanager", NULL, NULL);
96 dbus_connection_unref(connection);
97 if (!cgroup_manager) {
98 NihError *nerr;
99 nerr = nih_error_get();
100 ERROR("Error opening cgmanager proxy: %s", nerr->message);
101 nih_free(nerr);
102 return false;
103 }
104
105 // force fd passing negotiation
106 if (cgmanager_ping_sync(NULL, cgroup_manager, 0) != 0) {
107 NihError *nerr;
108 nerr = nih_error_get();
109 ERROR("Error pinging cgroup manager: %s", nerr->message);
110 nih_free(nerr);
111 }
112 return true;
113 }
114
115 static void cgm_dbus_disconnect(void)
116 {
117 nih_free(cgroup_manager);
118 cgroup_manager = NULL;
119 }
120
121 static void cgm_dbus_disconnected(DBusConnection *connection)
122 {
123 WARN("Cgroup manager connection was terminated");
124 cgroup_manager = NULL;
125 if (cgm_dbus_connect()) {
126 INFO("New cgroup manager connection was opened");
127 } else {
128 WARN("Cgroup manager unable to re-open connection");
129 }
130 }
131
132 static int send_creds(int sock, int rpid, int ruid, int rgid)
133 {
134 struct msghdr msg = { 0 };
135 struct iovec iov;
136 struct cmsghdr *cmsg;
137 struct ucred cred = {
138 .pid = rpid,
139 .uid = ruid,
140 .gid = rgid,
141 };
142 char cmsgbuf[CMSG_SPACE(sizeof(cred))];
143 char buf[1];
144 buf[0] = 'p';
145
146 msg.msg_control = cmsgbuf;
147 msg.msg_controllen = sizeof(cmsgbuf);
148
149 cmsg = CMSG_FIRSTHDR(&msg);
150 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
151 cmsg->cmsg_level = SOL_SOCKET;
152 cmsg->cmsg_type = SCM_CREDENTIALS;
153 memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred));
154
155 msg.msg_name = NULL;
156 msg.msg_namelen = 0;
157
158 iov.iov_base = buf;
159 iov.iov_len = sizeof(buf);
160 msg.msg_iov = &iov;
161 msg.msg_iovlen = 1;
162
163 if (sendmsg(sock, &msg, 0) < 0)
164 return -1;
165 return 0;
166 }
167
168 static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path, int32_t *existed)
169 {
170 if ( cgmanager_create_sync(NULL, cgroup_manager, controller,
171 cgroup_path, existed) != 0) {
172 NihError *nerr;
173 nerr = nih_error_get();
174 ERROR("call to cgmanager_create_sync failed: %s", nerr->message);
175 nih_free(nerr);
176 ERROR("Failed to create %s:%s", controller, cgroup_path);
177 return false;
178 }
179
180 return true;
181 }
182
183 static bool lxc_cgmanager_escape(void)
184 {
185 pid_t me = getpid();
186 int i;
187 for (i = 0; i < nr_subsystems; i++) {
188 if (cgmanager_move_pid_abs_sync(NULL, cgroup_manager,
189 subsystems[i], "/", me) != 0) {
190 NihError *nerr;
191 nerr = nih_error_get();
192 ERROR("call to cgmanager_move_pid_abs_sync(%s) failed: %s",
193 subsystems[i], nerr->message);
194 nih_free(nerr);
195 return false;
196 }
197 }
198
199 return true;
200 }
201
202 struct chown_data {
203 const char *controller;
204 const char *cgroup_path;
205 uid_t origuid;
206 };
207
208 static int do_chown_cgroup(const char *controller, const char *cgroup_path,
209 uid_t origuid)
210 {
211 int sv[2] = {-1, -1}, optval = 1, ret = -1;
212 char buf[1];
213
214 uid_t caller_nsuid = get_ns_uid(origuid);
215
216 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) {
217 SYSERROR("Error creating socketpair");
218 goto out;
219 }
220 if (setsockopt(sv[1], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
221 SYSERROR("setsockopt failed");
222 goto out;
223 }
224 if (setsockopt(sv[0], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
225 SYSERROR("setsockopt failed");
226 goto out;
227 }
228 if ( cgmanager_chown_scm_sync(NULL, cgroup_manager, controller,
229 cgroup_path, sv[1]) != 0) {
230 NihError *nerr;
231 nerr = nih_error_get();
232 ERROR("call to cgmanager_chown_scm_sync failed: %s", nerr->message);
233 nih_free(nerr);
234 goto out;
235 }
236 /* now send credentials */
237
238 fd_set rfds;
239 FD_ZERO(&rfds);
240 FD_SET(sv[0], &rfds);
241 if (select(sv[0]+1, &rfds, NULL, NULL, NULL) < 0) {
242 ERROR("Error getting go-ahead from server: %s", strerror(errno));
243 goto out;
244 }
245 if (read(sv[0], &buf, 1) != 1) {
246 ERROR("Error getting reply from server over socketpair");
247 goto out;
248 }
249 if (send_creds(sv[0], getpid(), getuid(), getgid())) {
250 SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__);
251 goto out;
252 }
253 FD_ZERO(&rfds);
254 FD_SET(sv[0], &rfds);
255 if (select(sv[0]+1, &rfds, NULL, NULL, NULL) < 0) {
256 ERROR("Error getting go-ahead from server: %s", strerror(errno));
257 goto out;
258 }
259 if (read(sv[0], &buf, 1) != 1) {
260 ERROR("Error getting reply from server over socketpair");
261 goto out;
262 }
263 if (send_creds(sv[0], getpid(), caller_nsuid, 0)) {
264 SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__);
265 goto out;
266 }
267 FD_ZERO(&rfds);
268 FD_SET(sv[0], &rfds);
269 if (select(sv[0]+1, &rfds, NULL, NULL, NULL) < 0) {
270 ERROR("Error getting go-ahead from server: %s", strerror(errno));
271 goto out;
272 }
273 ret = read(sv[0], buf, 1);
274 out:
275 close(sv[0]);
276 close(sv[1]);
277 if (ret == 1 && *buf == '1')
278 return 0;
279 return -1;
280 }
281
282 static int chown_cgroup_wrapper(void *data)
283 {
284 struct chown_data *arg = data;
285
286 if (setresgid(0,0,0) < 0)
287 SYSERROR("Failed to setgid to 0");
288 if (setresuid(0,0,0) < 0)
289 SYSERROR("Failed to setuid to 0");
290 if (setgroups(0, NULL) < 0)
291 SYSERROR("Failed to clear groups");
292 return do_chown_cgroup(arg->controller, arg->cgroup_path, arg->origuid);
293 }
294
295 static bool lxc_cgmanager_chmod(const char *controller,
296 const char *cgroup_path, const char *file, int mode)
297 {
298 if (cgmanager_chmod_sync(NULL, cgroup_manager, controller,
299 cgroup_path, file, mode) != 0) {
300 NihError *nerr;
301 nerr = nih_error_get();
302 ERROR("call to cgmanager_chmod_sync failed: %s", nerr->message);
303 nih_free(nerr);
304 return false;
305 }
306 return true;
307 }
308
309 static bool chown_cgroup(const char *controller, const char *cgroup_path,
310 struct lxc_conf *conf)
311 {
312 struct chown_data data;
313
314 if (lxc_list_empty(&conf->id_map))
315 /* If there's no mapping then we don't need to chown */
316 return true;
317
318 data.controller = controller;
319 data.cgroup_path = cgroup_path;
320 data.origuid = geteuid();
321
322 if (userns_exec_1(conf, chown_cgroup_wrapper, &data) < 0) {
323 ERROR("Error requesting cgroup chown in new namespace");
324 return false;
325 }
326
327 /* now chmod 775 the directory else the container cannot create cgroups */
328 if (!lxc_cgmanager_chmod(controller, cgroup_path, "", 0775))
329 return false;
330 if (!lxc_cgmanager_chmod(controller, cgroup_path, "tasks", 0775))
331 return false;
332 if (!lxc_cgmanager_chmod(controller, cgroup_path, "cgroup.procs", 0775))
333 return false;
334 return true;
335 }
336
337 #define CG_REMOVE_RECURSIVE 1
338 static void cgm_remove_cgroup(const char *controller, const char *path)
339 {
340 int existed;
341 if ( cgmanager_remove_sync(NULL, cgroup_manager, controller,
342 path, CG_REMOVE_RECURSIVE, &existed) != 0) {
343 NihError *nerr;
344 nerr = nih_error_get();
345 ERROR("call to cgmanager_remove_sync failed: %s", nerr->message);
346 nih_free(nerr);
347 ERROR("Error removing %s:%s", controller, path);
348 }
349 if (existed == -1)
350 INFO("cgroup removal attempt: %s:%s did not exist", controller, path);
351 }
352
353 static void *cgm_init(const char *name)
354 {
355 struct cgm_data *d;
356
357 d = malloc(sizeof(*d));
358 if (!d)
359 return NULL;
360
361 memset(d, 0, sizeof(*d));
362 d->name = strdup(name);
363 if (!d->name)
364 goto err1;
365
366 /* if we are running as root, use system cgroup pattern, otherwise
367 * just create a cgroup under the current one. But also fall back to
368 * that if for some reason reading the configuration fails and no
369 * default value is available
370 */
371 if (geteuid() == 0)
372 d->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
373 if (!d->cgroup_pattern)
374 d->cgroup_pattern = "%n";
375 return d;
376
377 err1:
378 free(d);
379 return NULL;
380 }
381
382 static void cgm_destroy(void *hdata)
383 {
384 struct cgm_data *d = hdata;
385 int i;
386
387 if (!d)
388 return;
389 for (i = 0; i < nr_subsystems; i++)
390 cgm_remove_cgroup(subsystems[i], d->cgroup_path);
391
392 free(d->name);
393 if (d->cgroup_path)
394 free(d->cgroup_path);
395 free(d);
396 }
397
398 /*
399 * remove all the cgroups created
400 */
401 static inline void cleanup_cgroups(char *path)
402 {
403 int i;
404 for (i = 0; i < nr_subsystems; i++)
405 cgm_remove_cgroup(subsystems[i], path);
406 }
407
408 static inline bool cgm_create(void *hdata)
409 {
410 struct cgm_data *d = hdata;
411 int i, index=0, baselen, ret;
412 int32_t existed;
413 char result[MAXPATHLEN], *tmp, *cgroup_path;
414
415 if (!d)
416 return false;
417 // XXX we should send a hint to the cgmanager that when these
418 // cgroups become empty they should be deleted. Requires a cgmanager
419 // extension
420
421 memset(result, 0, MAXPATHLEN);
422 tmp = lxc_string_replace("%n", d->name, d->cgroup_pattern);
423 if (!tmp)
424 return false;
425 if (strlen(tmp) > MAXPATHLEN)
426 return false;
427 strcpy(result, tmp);
428 baselen = strlen(result);
429 free(tmp);
430 tmp = result;
431 while (*tmp == '/')
432 tmp++;
433 again:
434 if (index == 100) { // turn this into a warn later
435 ERROR("cgroup error? 100 cgroups with this name already running");
436 return false;
437 }
438 if (index) {
439 ret = snprintf(result+baselen, MAXPATHLEN-baselen, "-%d", index);
440 if (ret < 0 || ret >= MAXPATHLEN-baselen)
441 return false;
442 }
443 existed = 0;
444 for (i = 0; i < nr_subsystems; i++) {
445 if (!lxc_cgmanager_create(subsystems[i], tmp, &existed)) {
446 ERROR("Error creating cgroup %s:%s", subsystems[i], result);
447 cleanup_cgroups(tmp);
448 return false;
449 }
450 if (existed == 1)
451 goto next;
452 }
453 // success
454 cgroup_path = strdup(tmp);
455 if (!cgroup_path) {
456 cleanup_cgroups(tmp);
457 return false;
458 }
459 d->cgroup_path = cgroup_path;
460 return true;
461 next:
462 cleanup_cgroups(tmp);
463 index++;
464 goto again;
465 }
466
467 /*
468 * Use the cgmanager to move a task into a cgroup for a particular
469 * hierarchy.
470 * All the subsystems in this hierarchy are co-mounted, so we only
471 * need to transition the task into one of the cgroups
472 */
473 static bool lxc_cgmanager_enter(pid_t pid, const char *controller,
474 const char *cgroup_path)
475 {
476 if (cgmanager_move_pid_sync(NULL, cgroup_manager, controller,
477 cgroup_path, pid) != 0) {
478 NihError *nerr;
479 nerr = nih_error_get();
480 ERROR("call to cgmanager_move_pid_sync failed: %s", nerr->message);
481 nih_free(nerr);
482 return false;
483 }
484 return true;
485 }
486
487 static bool do_cgm_enter(pid_t pid, const char *cgroup_path)
488 {
489 int i;
490
491 for (i = 0; i < nr_subsystems; i++) {
492 if (!lxc_cgmanager_enter(pid, subsystems[i], cgroup_path))
493 return false;
494 }
495 return true;
496 }
497
498 static inline bool cgm_enter(void *hdata, pid_t pid)
499 {
500 struct cgm_data *d = hdata;
501
502 if (!d || !d->cgroup_path)
503 return false;
504 return do_cgm_enter(pid, d->cgroup_path);
505 }
506
507 static const char *cgm_get_cgroup(void *hdata, const char *subsystem)
508 {
509 struct cgm_data *d = hdata;
510
511 if (!d || !d->cgroup_path)
512 return NULL;
513 return d->cgroup_path;
514 }
515
516 static int cgm_get_nrtasks(void *hdata)
517 {
518 struct cgm_data *d = hdata;
519 int32_t *pids;
520 size_t pids_len;
521
522 if (!d || !d->cgroup_path)
523 return false;
524
525 if (cgmanager_get_tasks_sync(NULL, cgroup_manager, subsystems[0],
526 d->cgroup_path, &pids, &pids_len) != 0) {
527 NihError *nerr;
528 nerr = nih_error_get();
529 ERROR("call to cgmanager_get_tasks_sync failed: %s", nerr->message);
530 nih_free(nerr);
531 return -1;
532 }
533 nih_free(pids);
534 return pids_len;
535 }
536
537 static int cgm_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
538 {
539 char *result, *controller, *key, *cgroup;
540 size_t newlen;
541
542 controller = alloca(strlen(filename)+1);
543 strcpy(controller, filename);
544 key = strchr(controller, '.');
545 if (!key)
546 return -1;
547 *key = '\0';
548
549 /* use the command interface to look for the cgroup */
550 cgroup = lxc_cmd_get_cgroup_path(name, lxcpath, controller);
551 if (!cgroup)
552 return -1;
553 if (cgmanager_get_value_sync(NULL, cgroup_manager, controller, cgroup, filename, &result) != 0) {
554 /*
555 * must consume the nih error
556 * However don't print out an error as the key may simply not exist
557 * on the host
558 */
559 NihError *nerr;
560 nerr = nih_error_get();
561 nih_free(nerr);
562 free(cgroup);
563 return -1;
564 }
565 free(cgroup);
566 newlen = strlen(result);
567 if (!value) {
568 // user queries the size
569 nih_free(result);
570 return newlen+1;
571 }
572
573 strncpy(value, result, len);
574 if (newlen >= len) {
575 value[len-1] = '\0';
576 newlen = len-1;
577 } else if (newlen+1 < len) {
578 // cgmanager doesn't add eol to last entry
579 value[newlen++] = '\n';
580 value[newlen] = '\0';
581 }
582 nih_free(result);
583 return newlen;
584 }
585
586 static int cgm_do_set(const char *controller, const char *file,
587 const char *cgroup, const char *value)
588 {
589 int ret;
590 ret = cgmanager_set_value_sync(NULL, cgroup_manager, controller,
591 cgroup, file, value);
592 if (ret != 0) {
593 NihError *nerr;
594 nerr = nih_error_get();
595 ERROR("call to cgmanager_remove_sync failed: %s", nerr->message);
596 nih_free(nerr);
597 ERROR("Error setting cgroup %s limit %s", file, cgroup);
598 }
599 return ret;
600 }
601
602 static int cgm_set(const char *filename, const char *value, const char *name, const char *lxcpath)
603 {
604 char *controller, *key, *cgroup;
605 int ret;
606
607 controller = alloca(strlen(filename)+1);
608 strcpy(controller, filename);
609 key = strchr(controller, '.');
610 if (!key)
611 return -1;
612 *key = '\0';
613
614 /* use the command interface to look for the cgroup */
615 cgroup = lxc_cmd_get_cgroup_path(name, lxcpath, controller);
616 if (!cgroup) {
617 ERROR("Failed to get cgroup for controller %s for %s:%s",
618 controller, lxcpath, name);
619 return -1;
620 }
621 ret = cgm_do_set(controller, filename, cgroup, value);
622 free(cgroup);
623 return ret;
624 }
625
626 static void free_subsystems(void)
627 {
628 int i;
629
630 for (i = 0; i < nr_subsystems; i++)
631 free(subsystems[i]);
632 free(subsystems);
633 subsystems = NULL;
634 nr_subsystems = 0;
635 }
636
637 static bool collect_subsytems(void)
638 {
639 char *line = NULL, *tab1;
640 size_t sz = 0;
641 FILE *f;
642
643 if (subsystems) // already initialized
644 return true;
645
646 f = fopen_cloexec("/proc/cgroups", "r");
647 if (!f)
648 return false;
649 while (getline(&line, &sz, f) != -1) {
650 char **tmp;
651 if (line[0] == '#')
652 continue;
653 if (!line[0])
654 continue;
655 tab1 = strchr(line, '\t');
656 if (!tab1)
657 continue;
658 *tab1 = '\0';
659 tmp = realloc(subsystems, (nr_subsystems+1)*sizeof(char *));
660 if (!tmp)
661 goto out_free;
662
663 subsystems = tmp;
664 tmp[nr_subsystems] = strdup(line);
665 if (!tmp[nr_subsystems])
666 goto out_free;
667 nr_subsystems++;
668 }
669 fclose(f);
670
671 if (!nr_subsystems) {
672 ERROR("No cgroup subsystems found");
673 return false;
674 }
675
676 return true;
677
678 out_free:
679 fclose(f);
680 free_subsystems();
681 return false;
682 }
683
684 struct cgroup_ops *cgm_ops_init(void)
685 {
686 if (!collect_subsytems())
687 return NULL;
688 if (!cgm_dbus_connect())
689 goto err1;
690
691 // root; try to escape to root cgroup
692 if (geteuid() == 0 && !lxc_cgmanager_escape())
693 goto err2;
694
695 return &cgmanager_ops;
696
697 err2:
698 cgm_dbus_disconnect();
699 err1:
700 free_subsystems();
701 return NULL;
702 }
703
704 static bool cgm_unfreeze(void *hdata)
705 {
706 struct cgm_data *d = hdata;
707
708 if (!d || !d->cgroup_path)
709 return false;
710
711 if (cgmanager_set_value_sync(NULL, cgroup_manager, "freezer", d->cgroup_path,
712 "freezer.state", "THAWED") != 0) {
713 NihError *nerr;
714 nerr = nih_error_get();
715 ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
716 nih_free(nerr);
717 ERROR("Error unfreezing %s", d->cgroup_path);
718 return false;
719 }
720 return true;
721 }
722
723 static bool cgm_setup_limits(void *hdata, struct lxc_list *cgroup_settings, bool do_devices)
724 {
725 struct cgm_data *d = hdata;
726 struct lxc_list *iterator;
727 struct lxc_cgroup *cg;
728 bool ret = false;
729
730 if (lxc_list_empty(cgroup_settings))
731 return true;
732
733 if (!d || !d->cgroup_path)
734 return false;
735
736 lxc_list_for_each(iterator, cgroup_settings) {
737 char controller[100], *p;
738 cg = iterator->elem;
739 if (do_devices != !strncmp("devices", cg->subsystem, 7))
740 continue;
741 if (strlen(cg->subsystem) > 100) // i smell a rat
742 goto out;
743 strcpy(controller, cg->subsystem);
744 p = strchr(controller, '.');
745 if (p)
746 *p = '\0';
747 if (cgm_do_set(controller, cg->subsystem, d->cgroup_path
748 , cg->value) < 0) {
749 ERROR("Error setting %s to %s for %s",
750 cg->subsystem, cg->value, d->name);
751 goto out;
752 }
753
754 DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value);
755 }
756
757 ret = true;
758 INFO("cgroup limits have been setup");
759 out:
760 return ret;
761 }
762
763 static bool cgm_chown(void *hdata, struct lxc_conf *conf)
764 {
765 struct cgm_data *d = hdata;
766 int i;
767
768 if (!d || !d->cgroup_path)
769 return false;
770 for (i = 0; i < nr_subsystems; i++) {
771 if (!chown_cgroup(subsystems[i], d->cgroup_path, conf))
772 WARN("Failed to chown %s:%s to container root",
773 subsystems[i], d->cgroup_path);
774 }
775 return true;
776 }
777
778 /*
779 * TODO: this should be re-written to use the get_config_item("lxc.id_map")
780 * cmd api instead of getting the idmap from c->lxc_conf. The reason is
781 * that the id_maps may be different if the container was started with a
782 * -f or -s argument.
783 * The reason I'm punting on that is because we'll need to parse the
784 * idmap results.
785 */
786 static bool cgm_attach(const char *name, const char *lxcpath, pid_t pid)
787 {
788 bool pass = false;
789 char *cgroup = NULL;
790 struct lxc_container *c;
791
792 c = lxc_container_new(name, lxcpath);
793 if (!c) {
794 ERROR("Could not load container %s:%s", lxcpath, name);
795 return false;
796 }
797 if (!collect_subsytems()) {
798 ERROR("Error collecting cgroup subsystems");
799 goto out;
800 }
801 // cgm_create makes sure that we have the same cgroup name for all
802 // subsystems, so since this is a slow command over the cmd socket,
803 // just get the cgroup name for the first one.
804 cgroup = lxc_cmd_get_cgroup_path(name, lxcpath, subsystems[0]);
805 if (!cgroup) {
806 ERROR("Failed to get cgroup for controller %s", subsystems[0]);
807 goto out;
808 }
809
810 if (!(pass = do_cgm_enter(pid, cgroup)))
811 ERROR("Failed to enter group %s", cgroup);
812
813 out:
814 free(cgroup);
815 lxc_container_put(c);
816 return pass;
817 }
818
819 static bool cgm_bind_dir(const char *root, const char *dirname)
820 {
821 nih_local char *cgpath = NULL;
822
823 /* /sys should have been mounted by now */
824 cgpath = NIH_MUST( nih_strdup(NULL, root) );
825 NIH_MUST( nih_strcat(&cgpath, NULL, "/sys/fs/cgroup") );
826
827 if (!dir_exists(cgpath)) {
828 ERROR("%s does not exist", cgpath);
829 return false;
830 }
831
832 /* mount a tmpfs there so we can create subdirs */
833 if (mount("cgroup", cgpath, "tmpfs", 0, "size=10000,mode=755")) {
834 SYSERROR("Failed to mount tmpfs at %s", cgpath);
835 return false;
836 }
837 NIH_MUST( nih_strcat(&cgpath, NULL, "/cgmanager") );
838
839 if (mkdir(cgpath, 0755) < 0) {
840 SYSERROR("Failed to create %s", cgpath);
841 return false;
842 }
843
844 if (mount(dirname, cgpath, "none", MS_BIND, 0)) {
845 SYSERROR("Failed to bind mount %s to %s", dirname, cgpath);
846 return false;
847 }
848
849 return true;
850 }
851
852 /*
853 * cgm_mount_cgroup:
854 * If /sys/fs/cgroup/cgmanager.lower/ exists, bind mount that to
855 * /sys/fs/cgroup/cgmanager/ in the container.
856 * Otherwise, if /sys/fs/cgroup/cgmanager exists, bind mount that.
857 * Else do nothing
858 */
859 #define CGMANAGER_LOWER_SOCK "/sys/fs/cgroup/cgmanager.lower"
860 #define CGMANAGER_UPPER_SOCK "/sys/fs/cgroup/cgmanager"
861 static bool cgm_mount_cgroup(void *hdata, const char *root, int type)
862 {
863 if (dir_exists(CGMANAGER_LOWER_SOCK))
864 return cgm_bind_dir(root, CGMANAGER_LOWER_SOCK);
865 if (dir_exists(CGMANAGER_UPPER_SOCK))
866 return cgm_bind_dir(root, CGMANAGER_UPPER_SOCK);
867 // Host doesn't have cgmanager running? Then how did we get here?
868 return false;
869 }
870
871 static struct cgroup_ops cgmanager_ops = {
872 .init = cgm_init,
873 .destroy = cgm_destroy,
874 .create = cgm_create,
875 .enter = cgm_enter,
876 .create_legacy = NULL,
877 .get_cgroup = cgm_get_cgroup,
878 .get = cgm_get,
879 .set = cgm_set,
880 .unfreeze = cgm_unfreeze,
881 .setup_limits = cgm_setup_limits,
882 .name = "cgmanager",
883 .chown = cgm_chown,
884 .attach = cgm_attach,
885 .mount_cgroup = cgm_mount_cgroup,
886 .nrtasks = cgm_get_nrtasks,
887 };
888 #endif