]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/cgmanager.c
detect whether cgmanager_list_controllers is available
[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 <pthread.h>
34 #include <grp.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <sys/param.h>
38 #include <sys/inotify.h>
39 #include <sys/mount.h>
40 #include <netinet/in.h>
41 #include <net/if.h>
42 #include <poll.h>
43
44 #include "error.h"
45 #include "commands.h"
46 #include "list.h"
47 #include "conf.h"
48 #include "utils.h"
49 #include "bdev.h"
50 #include "log.h"
51 #include "cgroup.h"
52 #include "start.h"
53 #include "state.h"
54
55 #define CGM_SUPPORTS_GET_ABS 3
56 #define CGM_SUPPORTS_NAMED 4
57 #define CGM_SUPPORTS_MULT_CONTROLLERS 10
58
59 #ifdef HAVE_CGMANAGER
60 lxc_log_define(lxc_cgmanager, lxc);
61
62 #include <nih-dbus/dbus_connection.h>
63 #include <cgmanager/cgmanager-client.h>
64 #include <nih/alloc.h>
65 #include <nih/error.h>
66 #include <nih/string.h>
67
68 struct cgm_data {
69 char *name;
70 char *cgroup_path;
71 const char *cgroup_pattern;
72 };
73
74 static pthread_mutex_t cgm_mutex = PTHREAD_MUTEX_INITIALIZER;
75
76 static void lock_mutex(pthread_mutex_t *l)
77 {
78 int ret;
79
80 if ((ret = pthread_mutex_lock(l)) != 0) {
81 fprintf(stderr, "pthread_mutex_lock returned:%d %s\n", ret, strerror(ret));
82 exit(1);
83 }
84 }
85
86 static void unlock_mutex(pthread_mutex_t *l)
87 {
88 int ret;
89
90 if ((ret = pthread_mutex_unlock(l)) != 0) {
91 fprintf(stderr, "pthread_mutex_unlock returned:%d %s\n", ret, strerror(ret));
92 exit(1);
93 }
94 }
95
96 void cgm_lock(void)
97 {
98 lock_mutex(&cgm_mutex);
99 }
100
101 void cgm_unlock(void)
102 {
103 unlock_mutex(&cgm_mutex);
104 }
105
106 #ifdef HAVE_PTHREAD_ATFORK
107 __attribute__((constructor))
108 static void process_lock_setup_atfork(void)
109 {
110 pthread_atfork(cgm_lock, cgm_unlock, cgm_unlock);
111 }
112 #endif
113
114 static NihDBusProxy *cgroup_manager = NULL;
115 static int32_t api_version;
116
117 static struct cgroup_ops cgmanager_ops;
118 static int nr_subsystems;
119 static char **subsystems, **subsystems_inone;
120 static bool dbus_threads_initialized = false;
121 static void cull_user_controllers(void);
122
123 static void cgm_dbus_disconnect(void)
124 {
125 if (cgroup_manager) {
126 dbus_connection_flush(cgroup_manager->connection);
127 dbus_connection_close(cgroup_manager->connection);
128 nih_free(cgroup_manager);
129 }
130 cgroup_manager = NULL;
131 cgm_unlock();
132 }
133
134 #define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock"
135 static bool cgm_dbus_connect(void)
136 {
137 DBusError dbus_error;
138 static DBusConnection *connection;
139
140 cgm_lock();
141 if (!dbus_threads_initialized) {
142 // tell dbus to do struct locking for thread safety
143 dbus_threads_init_default();
144 dbus_threads_initialized = true;
145 }
146
147 dbus_error_init(&dbus_error);
148
149 connection = dbus_connection_open_private(CGMANAGER_DBUS_SOCK, &dbus_error);
150 if (!connection) {
151 DEBUG("Failed opening dbus connection: %s: %s",
152 dbus_error.name, dbus_error.message);
153 dbus_error_free(&dbus_error);
154 cgm_unlock();
155 return false;
156 }
157 dbus_connection_set_exit_on_disconnect(connection, FALSE);
158 dbus_error_free(&dbus_error);
159 cgroup_manager = nih_dbus_proxy_new(NULL, connection,
160 NULL /* p2p */,
161 "/org/linuxcontainers/cgmanager", NULL, NULL);
162 dbus_connection_unref(connection);
163 if (!cgroup_manager) {
164 NihError *nerr;
165 nerr = nih_error_get();
166 ERROR("Error opening cgmanager proxy: %s", nerr->message);
167 nih_free(nerr);
168 cgm_dbus_disconnect();
169 return false;
170 }
171
172 // get the api version
173 if (cgmanager_get_api_version_sync(NULL, cgroup_manager, &api_version) != 0) {
174 NihError *nerr;
175 nerr = nih_error_get();
176 ERROR("Error cgroup manager api version: %s", nerr->message);
177 nih_free(nerr);
178 cgm_dbus_disconnect();
179 return false;
180 }
181 if (api_version < CGM_SUPPORTS_NAMED)
182 cull_user_controllers();
183 return true;
184 }
185
186 static bool cgm_supports_multiple_controllers;
187 /*
188 * if cgm_all_controllers_same is true, then cgm_supports_multiple_controllers
189 * is true
190 */
191 static bool cgm_all_controllers_same;
192
193 /*
194 * Check whether we can use "all" when talking to cgmanager.
195 * We check two things:
196 * 1. whether cgmanager is new enough to support this.
197 * 2. whether the task we are interested in is in the same
198 * cgroup for all controllers.
199 * In cgm_init (before an lxc-start) we care about our own
200 * cgroup. In cgm_attach, we care about the target task's
201 * cgroup.
202 */
203 static void check_supports_multiple_controllers(pid_t pid)
204 {
205 FILE *f;
206 char *line = NULL, *prevpath = NULL;
207 size_t sz = 0;
208 char path[100];
209
210 cgm_supports_multiple_controllers = false;
211 cgm_all_controllers_same = false;
212
213 if (api_version < CGM_SUPPORTS_MULT_CONTROLLERS) {
214 cgm_supports_multiple_controllers = false;
215 return;
216 }
217
218 cgm_supports_multiple_controllers = true;
219
220 if (pid == -1)
221 sprintf(path, "/proc/self/cgroup");
222 else
223 sprintf(path, "/proc/%d/cgroup", pid);
224 f = fopen(path, "r");
225 if (!f)
226 return;
227
228 cgm_all_controllers_same = true;
229
230 while (getline(&line, &sz, f) != -1) {
231 /* file format: hierarchy:subsystems:group */
232 char *colon;
233 if (!line[0])
234 continue;
235
236 colon = strchr(line, ':');
237 if (!colon)
238 continue;
239 colon = strchr(colon+1, ':');
240 if (!colon)
241 continue;
242 colon++;
243 if (!prevpath) {
244 prevpath = alloca(strlen(colon)+1);
245 strcpy(prevpath, colon);
246 continue;
247 }
248 if (strcmp(prevpath, colon) != 0) {
249 cgm_all_controllers_same = false;
250 break;
251 }
252 }
253
254 fclose(f);
255 free(line);
256 }
257
258 static int send_creds(int sock, int rpid, int ruid, int rgid)
259 {
260 struct msghdr msg = { 0 };
261 struct iovec iov;
262 struct cmsghdr *cmsg;
263 struct ucred cred = {
264 .pid = rpid,
265 .uid = ruid,
266 .gid = rgid,
267 };
268 char cmsgbuf[CMSG_SPACE(sizeof(cred))];
269 char buf[1];
270 buf[0] = 'p';
271
272 msg.msg_control = cmsgbuf;
273 msg.msg_controllen = sizeof(cmsgbuf);
274
275 cmsg = CMSG_FIRSTHDR(&msg);
276 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
277 cmsg->cmsg_level = SOL_SOCKET;
278 cmsg->cmsg_type = SCM_CREDENTIALS;
279 memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred));
280
281 msg.msg_name = NULL;
282 msg.msg_namelen = 0;
283
284 iov.iov_base = buf;
285 iov.iov_len = sizeof(buf);
286 msg.msg_iov = &iov;
287 msg.msg_iovlen = 1;
288
289 if (sendmsg(sock, &msg, 0) < 0)
290 return -1;
291 return 0;
292 }
293
294 static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path, int32_t *existed)
295 {
296 bool ret = true;
297 if ( cgmanager_create_sync(NULL, cgroup_manager, controller,
298 cgroup_path, existed) != 0) {
299 NihError *nerr;
300 nerr = nih_error_get();
301 ERROR("call to cgmanager_create_sync failed: %s", nerr->message);
302 nih_free(nerr);
303 ERROR("Failed to create %s:%s", controller, cgroup_path);
304 ret = false;
305 }
306
307 return ret;
308 }
309
310 /*
311 * Escape to the root cgroup if we are root, so that the container will
312 * be in "/lxc/c1" rather than "/user/..../c1"
313 * called internally with connection already open
314 */
315 static bool lxc_cgmanager_escape(void)
316 {
317 bool ret = true;
318 pid_t me = getpid();
319 char **slist = subsystems;
320 int i;
321
322 if (cgm_all_controllers_same)
323 slist = subsystems_inone;
324
325 for (i = 0; slist[i]; i++) {
326 if (cgmanager_move_pid_abs_sync(NULL, cgroup_manager,
327 slist[i], "/", me) != 0) {
328 NihError *nerr;
329 nerr = nih_error_get();
330 ERROR("call to cgmanager_move_pid_abs_sync(%s) failed: %s",
331 slist[i], nerr->message);
332 nih_free(nerr);
333 ret = false;
334 break;
335 }
336 }
337
338 return ret;
339 }
340
341 struct chown_data {
342 const char *cgroup_path;
343 uid_t origuid;
344 };
345
346 static int do_chown_cgroup(const char *controller, const char *cgroup_path,
347 uid_t newuid)
348 {
349 int sv[2] = {-1, -1}, optval = 1, ret = -1;
350 char buf[1];
351 struct pollfd fds;
352
353 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) {
354 SYSERROR("Error creating socketpair");
355 goto out;
356 }
357 if (setsockopt(sv[1], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
358 SYSERROR("setsockopt failed");
359 goto out;
360 }
361 if (setsockopt(sv[0], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
362 SYSERROR("setsockopt failed");
363 goto out;
364 }
365 if ( cgmanager_chown_scm_sync(NULL, cgroup_manager, controller,
366 cgroup_path, sv[1]) != 0) {
367 NihError *nerr;
368 nerr = nih_error_get();
369 ERROR("call to cgmanager_chown_scm_sync failed: %s", nerr->message);
370 nih_free(nerr);
371 goto out;
372 }
373 /* now send credentials */
374
375 fds.fd = sv[0];
376 fds.events = POLLIN;
377 fds.revents = 0;
378 if (poll(&fds, 1, -1) <= 0) {
379 ERROR("Error getting go-ahead from server: %s", strerror(errno));
380 goto out;
381 }
382 if (read(sv[0], &buf, 1) != 1) {
383 ERROR("Error getting reply from server over socketpair");
384 goto out;
385 }
386 if (send_creds(sv[0], getpid(), getuid(), getgid())) {
387 SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__);
388 goto out;
389 }
390 fds.fd = sv[0];
391 fds.events = POLLIN;
392 fds.revents = 0;
393 if (poll(&fds, 1, -1) <= 0) {
394 ERROR("Error getting go-ahead from server: %s", strerror(errno));
395 goto out;
396 }
397 if (read(sv[0], &buf, 1) != 1) {
398 ERROR("Error getting reply from server over socketpair");
399 goto out;
400 }
401 if (send_creds(sv[0], getpid(), newuid, 0)) {
402 SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__);
403 goto out;
404 }
405 fds.fd = sv[0];
406 fds.events = POLLIN;
407 fds.revents = 0;
408 if (poll(&fds, 1, -1) <= 0) {
409 ERROR("Error getting go-ahead from server: %s", strerror(errno));
410 goto out;
411 }
412 ret = read(sv[0], buf, 1);
413 out:
414 close(sv[0]);
415 close(sv[1]);
416 if (ret == 1 && *buf == '1')
417 return 0;
418 return -1;
419 }
420
421 static int chown_cgroup_wrapper(void *data)
422 {
423 struct chown_data *arg = data;
424 char **slist = subsystems;
425 int i, ret = -1;
426 uid_t destuid;
427
428 if (setresgid(0,0,0) < 0)
429 SYSERROR("Failed to setgid to 0");
430 if (setresuid(0,0,0) < 0)
431 SYSERROR("Failed to setuid to 0");
432 if (setgroups(0, NULL) < 0)
433 SYSERROR("Failed to clear groups");
434 cgm_dbus_disconnect();
435 if (!cgm_dbus_connect()) {
436 ERROR("Error connecting to cgroup manager");
437 return -1;
438 }
439 destuid = get_ns_uid(arg->origuid);
440
441 if (cgm_supports_multiple_controllers)
442 slist = subsystems_inone;
443
444 for (i = 0; slist[i]; i++) {
445 if (do_chown_cgroup(slist[i], arg->cgroup_path, destuid) < 0) {
446 ERROR("Failed to chown %s:%s to container root",
447 slist[i], arg->cgroup_path);
448 goto fail;
449 }
450 }
451 ret = 0;
452 fail:
453 cgm_dbus_disconnect();
454 return ret;
455 }
456
457 /* Internal helper. Must be called with the cgmanager dbus socket open */
458 static bool lxc_cgmanager_chmod(const char *controller,
459 const char *cgroup_path, const char *file, int mode)
460 {
461 if (cgmanager_chmod_sync(NULL, cgroup_manager, controller,
462 cgroup_path, file, mode) != 0) {
463 NihError *nerr;
464 nerr = nih_error_get();
465 ERROR("call to cgmanager_chmod_sync failed: %s", nerr->message);
466 nih_free(nerr);
467 return false;
468 }
469 return true;
470 }
471
472 /* Internal helper. Must be called with the cgmanager dbus socket open */
473 static bool chown_cgroup(const char *cgroup_path, struct lxc_conf *conf)
474 {
475 struct chown_data data;
476 char **slist = subsystems;
477 int i;
478
479 if (lxc_list_empty(&conf->id_map))
480 /* If there's no mapping then we don't need to chown */
481 return true;
482
483 data.cgroup_path = cgroup_path;
484 data.origuid = geteuid();
485
486 /* Unpriv users can't chown it themselves, so chown from
487 * a child namespace mapping both our own and the target uid
488 */
489 if (userns_exec_1(conf, chown_cgroup_wrapper, &data) < 0) {
490 ERROR("Error requesting cgroup chown in new namespace");
491 return false;
492 }
493
494 /*
495 * Now chmod 775 the directory else the container cannot create cgroups.
496 * This can't be done in the child namespace because it only group-owns
497 * the cgroup
498 */
499 if (cgm_supports_multiple_controllers)
500 slist = subsystems_inone;
501
502 for (i = 0; slist[i]; i++) {
503 if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "", 0775))
504 return false;
505 if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "tasks", 0775))
506 return false;
507 if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "cgroup.procs", 0775))
508 return false;
509 }
510
511 return true;
512 }
513
514 #define CG_REMOVE_RECURSIVE 1
515 /* Internal helper. Must be called with the cgmanager dbus socket open */
516 static void cgm_remove_cgroup(const char *controller, const char *path)
517 {
518 int existed;
519 if ( cgmanager_remove_sync(NULL, cgroup_manager, controller,
520 path, CG_REMOVE_RECURSIVE, &existed) != 0) {
521 NihError *nerr;
522 nerr = nih_error_get();
523 ERROR("call to cgmanager_remove_sync failed: %s", nerr->message);
524 nih_free(nerr);
525 ERROR("Error removing %s:%s", controller, path);
526 }
527 if (existed == -1)
528 INFO("cgroup removal attempt: %s:%s did not exist", controller, path);
529 }
530
531 static void *cgm_init(const char *name)
532 {
533 struct cgm_data *d;
534
535 if (!cgm_dbus_connect()) {
536 ERROR("Error connecting to cgroup manager");
537 return NULL;
538 }
539
540 check_supports_multiple_controllers(-1);
541
542 d = malloc(sizeof(*d));
543 if (!d) {
544 cgm_dbus_disconnect();
545 return NULL;
546 }
547
548 memset(d, 0, sizeof(*d));
549 d->name = strdup(name);
550 if (!d->name) {
551 cgm_dbus_disconnect();
552 goto err1;
553 }
554
555 d->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
556
557 // cgm_create immediately gets called so keep the connection open
558 return d;
559
560 err1:
561 free(d);
562 return NULL;
563 }
564
565 /* Called after a failed container startup */
566 static void cgm_destroy(void *hdata)
567 {
568 struct cgm_data *d = hdata;
569 char **slist = subsystems;
570 int i;
571
572 if (!d || !d->cgroup_path)
573 return;
574 if (!cgm_dbus_connect()) {
575 ERROR("Error connecting to cgroup manager");
576 return;
577 }
578
579 if (cgm_supports_multiple_controllers)
580 slist = subsystems_inone;
581 for (i = 0; slist[i]; i++)
582 cgm_remove_cgroup(slist[i], d->cgroup_path);
583
584 free(d->name);
585 free(d->cgroup_path);
586 free(d);
587 cgm_dbus_disconnect();
588 }
589
590 /*
591 * remove all the cgroups created
592 * called internally with dbus connection open
593 */
594 static inline void cleanup_cgroups(char *path)
595 {
596 int i;
597 char **slist = subsystems;
598
599 if (cgm_supports_multiple_controllers)
600 slist = subsystems_inone;
601 for (i = 0; slist[i]; i++)
602 cgm_remove_cgroup(slist[i], path);
603 }
604
605 static inline bool cgm_create(void *hdata)
606 {
607 struct cgm_data *d = hdata;
608 char **slist = subsystems;
609 int i, index=0, baselen, ret;
610 int32_t existed;
611 char result[MAXPATHLEN], *tmp, *cgroup_path;
612
613 if (!d)
614 return false;
615 // XXX we should send a hint to the cgmanager that when these
616 // cgroups become empty they should be deleted. Requires a cgmanager
617 // extension
618
619 memset(result, 0, MAXPATHLEN);
620 tmp = lxc_string_replace("%n", d->name, d->cgroup_pattern);
621 if (!tmp)
622 goto bad;
623 if (strlen(tmp) >= MAXPATHLEN) {
624 free(tmp);
625 goto bad;
626 }
627 strcpy(result, tmp);
628 baselen = strlen(result);
629 free(tmp);
630 tmp = result;
631 while (*tmp == '/')
632 tmp++;
633 again:
634 if (index == 100) { // turn this into a warn later
635 ERROR("cgroup error? 100 cgroups with this name already running");
636 goto bad;
637 }
638 if (index) {
639 ret = snprintf(result+baselen, MAXPATHLEN-baselen, "-%d", index);
640 if (ret < 0 || ret >= MAXPATHLEN-baselen)
641 goto bad;
642 }
643 existed = 0;
644
645 if (cgm_supports_multiple_controllers)
646 slist = subsystems_inone;
647
648 for (i = 0; slist[i]; i++) {
649 if (!lxc_cgmanager_create(slist[i], tmp, &existed)) {
650 ERROR("Error creating cgroup %s:%s", slist[i], result);
651 cleanup_cgroups(tmp);
652 goto bad;
653 }
654 if (existed == 1)
655 goto next;
656 }
657 // success
658 cgroup_path = strdup(tmp);
659 if (!cgroup_path) {
660 cleanup_cgroups(tmp);
661 goto bad;
662 }
663 d->cgroup_path = cgroup_path;
664 cgm_dbus_disconnect();
665 return true;
666
667 next:
668 index++;
669 goto again;
670 bad:
671 cgm_dbus_disconnect();
672 return false;
673 }
674
675 /*
676 * Use the cgmanager to move a task into a cgroup for a particular
677 * hierarchy.
678 * All the subsystems in this hierarchy are co-mounted, so we only
679 * need to transition the task into one of the cgroups
680 *
681 * Internal helper, must be called with cgmanager dbus socket open
682 */
683 static bool lxc_cgmanager_enter(pid_t pid, const char *controller,
684 const char *cgroup_path, bool abs)
685 {
686 int ret;
687
688 if (abs)
689 ret = cgmanager_move_pid_abs_sync(NULL, cgroup_manager,
690 controller, cgroup_path, pid);
691 else
692 ret = cgmanager_move_pid_sync(NULL, cgroup_manager,
693 controller, cgroup_path, pid);
694 if (ret != 0) {
695 NihError *nerr;
696 nerr = nih_error_get();
697 ERROR("call to cgmanager_move_pid_%ssync failed: %s",
698 abs ? "abs_" : "", nerr->message);
699 nih_free(nerr);
700 return false;
701 }
702 return true;
703 }
704
705 static inline bool cgm_enter(void *hdata, pid_t pid)
706 {
707 struct cgm_data *d = hdata;
708 char **slist = subsystems;
709 bool ret = false;
710 int i;
711
712 if (!d || !d->cgroup_path)
713 return false;
714
715 if (!cgm_dbus_connect()) {
716 ERROR("Error connecting to cgroup manager");
717 return false;
718 }
719
720 if (cgm_all_controllers_same)
721 slist = subsystems_inone;
722
723 for (i = 0; slist[i]; i++) {
724 if (!lxc_cgmanager_enter(pid, slist[i], d->cgroup_path, false))
725 goto out;
726 }
727 ret = true;
728 out:
729 cgm_dbus_disconnect();
730 return ret;
731 }
732
733 static const char *cgm_get_cgroup(void *hdata, const char *subsystem)
734 {
735 struct cgm_data *d = hdata;
736
737 if (!d || !d->cgroup_path)
738 return NULL;
739 return d->cgroup_path;
740 }
741
742 static const char *cgm_canonical_path(void *hdata)
743 {
744 struct cgm_data *d = hdata;
745
746 if (!d || !d->cgroup_path)
747 return NULL;
748 return d->cgroup_path;
749 }
750
751 #if HAVE_CGMANAGER_GET_PID_CGROUP_ABS_SYNC
752 static inline bool abs_cgroup_supported(void) {
753 return api_version >= CGM_SUPPORTS_GET_ABS;
754 }
755 #else
756 static inline bool abs_cgroup_supported(void) {
757 return false;
758 }
759 #define cgmanager_get_pid_cgroup_abs_sync(...) -1
760 #endif
761
762 static char *try_get_abs_cgroup(const char *name, const char *lxcpath,
763 const char *controller)
764 {
765 char *cgroup = NULL;
766
767 if (abs_cgroup_supported()) {
768 /* get the container init pid and ask for its abs cgroup */
769 pid_t pid = lxc_cmd_get_init_pid(name, lxcpath);
770 if (pid < 0)
771 return NULL;
772 if (cgmanager_get_pid_cgroup_abs_sync(NULL, cgroup_manager,
773 controller, pid, &cgroup) != 0) {
774 cgroup = NULL;
775 NihError *nerr;
776 nerr = nih_error_get();
777 nih_free(nerr);
778 }
779 return cgroup;
780 }
781
782 /* use the command interface to look for the cgroup */
783 return lxc_cmd_get_cgroup_path(name, lxcpath, controller);
784 }
785
786 /*
787 * nrtasks is called by the utmp helper by the container monitor.
788 * cgmanager socket was closed after cgroup setup was complete, so we need
789 * to reopen here.
790 *
791 * Return -1 on error.
792 */
793 static int cgm_get_nrtasks(void *hdata)
794 {
795 struct cgm_data *d = hdata;
796 int32_t *pids;
797 size_t pids_len;
798
799 if (!d || !d->cgroup_path)
800 return -1;
801
802 if (!cgm_dbus_connect()) {
803 ERROR("Error connecting to cgroup manager");
804 return -1;
805 }
806 if (cgmanager_get_tasks_sync(NULL, cgroup_manager, subsystems[0],
807 d->cgroup_path, &pids, &pids_len) != 0) {
808 NihError *nerr;
809 nerr = nih_error_get();
810 ERROR("call to cgmanager_get_tasks_sync failed: %s", nerr->message);
811 nih_free(nerr);
812 pids_len = -1;
813 goto out;
814 }
815 nih_free(pids);
816 out:
817 cgm_dbus_disconnect();
818 return pids_len;
819 }
820
821 #if HAVE_CGMANAGER_LIST_CONTROLLERS
822 static bool lxc_list_controllers(char ***list)
823 {
824 if (!cgm_dbus_connect()) {
825 ERROR("Error connecting to cgroup manager");
826 return false;
827 }
828 if (cgmanager_list_controllers_sync(NULL, cgroup_manager, list) != 0) {
829 NihError *nerr;
830 nerr = nih_error_get();
831 ERROR("call to cgmanager_list_controllers_sync failed: %s", nerr->message);
832 nih_free(nerr);
833 cgm_dbus_disconnect();
834 return false;
835 }
836
837 cgm_dbus_disconnect();
838 return true;
839 }
840 #else
841 static bool lxc_list_controllers(char ***list)
842 {
843 return false;
844 }
845 #endif
846
847 static inline void free_abs_cgroup(char *cgroup)
848 {
849 if (!cgroup)
850 return;
851 if (abs_cgroup_supported())
852 nih_free(cgroup);
853 else
854 free(cgroup);
855 }
856
857 static void do_cgm_get(const char *name, const char *lxcpath, const char *filename, int outp, bool sendvalue)
858 {
859 char *controller, *key, *cgroup = NULL, *cglast;
860 int len = -1;
861 int ret;
862 nih_local char *result = NULL;
863
864 controller = alloca(strlen(filename)+1);
865 strcpy(controller, filename);
866 key = strchr(controller, '.');
867 if (!key) {
868 ret = write(outp, &len, sizeof(len));
869 if (ret != sizeof(len))
870 WARN("Failed to warn cgm_get of error; parent may hang");
871 exit(1);
872 }
873 *key = '\0';
874
875 if (!cgm_dbus_connect()) {
876 ERROR("Error connecting to cgroup manager");
877 ret = write(outp, &len, sizeof(len));
878 if (ret != sizeof(len))
879 WARN("Failed to warn cgm_get of error; parent may hang");
880 exit(1);
881 }
882 cgroup = try_get_abs_cgroup(name, lxcpath, controller);
883 if (!cgroup) {
884 cgm_dbus_disconnect();
885 ret = write(outp, &len, sizeof(len));
886 if (ret != sizeof(len))
887 WARN("Failed to warn cgm_get of error; parent may hang");
888 exit(1);
889 }
890 cglast = strrchr(cgroup, '/');
891 if (!cglast) {
892 cgm_dbus_disconnect();
893 free_abs_cgroup(cgroup);
894 ret = write(outp, &len, sizeof(len));
895 if (ret != sizeof(len))
896 WARN("Failed to warn cgm_get of error; parent may hang");
897 exit(1);
898 }
899 *cglast = '\0';
900 if (!lxc_cgmanager_enter(getpid(), controller, cgroup, abs_cgroup_supported())) {
901 ERROR("Failed to enter container cgroup %s:%s", controller, cgroup);
902 ret = write(outp, &len, sizeof(len));
903 if (ret != sizeof(len))
904 WARN("Failed to warn cgm_get of error; parent may hang");
905 cgm_dbus_disconnect();
906 free_abs_cgroup(cgroup);
907 exit(1);
908 }
909 if (cgmanager_get_value_sync(NULL, cgroup_manager, controller, cglast+1, filename, &result) != 0) {
910 NihError *nerr;
911 nerr = nih_error_get();
912 nih_free(nerr);
913 free_abs_cgroup(cgroup);
914 cgm_dbus_disconnect();
915 ret = write(outp, &len, sizeof(len));
916 if (ret != sizeof(len))
917 WARN("Failed to warn cgm_get of error; parent may hang");
918 exit(1);
919 }
920 free_abs_cgroup(cgroup);
921 cgm_dbus_disconnect();
922 len = strlen(result);
923 ret = write(outp, &len, sizeof(len));
924 if (ret != sizeof(len)) {
925 WARN("Failed to send length to parent");
926 exit(1);
927 }
928 if (!len || !sendvalue) {
929 exit(0);
930 }
931 ret = write(outp, result, len);
932 if (ret < 0)
933 exit(1);
934 exit(0);
935 }
936
937 /* cgm_get is called to get container cgroup settings, not during startup */
938 static int cgm_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
939 {
940 pid_t pid;
941 int p[2], ret, newlen, readlen;
942
943 if (pipe(p) < 0)
944 return -1;
945 if ((pid = fork()) < 0) {
946 close(p[0]);
947 close(p[1]);
948 return -1;
949 }
950 if (!pid) // do_cgm_get exits
951 do_cgm_get(name, lxcpath, filename, p[1], len && value);
952 close(p[1]);
953 ret = read(p[0], &newlen, sizeof(newlen));
954 if (ret != sizeof(newlen)) {
955 close(p[0]);
956 ret = -1;
957 goto out;
958 }
959 if (!len || !value) {
960 close(p[0]);
961 ret = newlen;
962 goto out;
963 }
964 memset(value, 0, len);
965 if (newlen < 0) { // child is reporting an error
966 close(p[0]);
967 ret = -1;
968 goto out;
969 }
970 if (newlen == 0) { // empty read
971 close(p[0]);
972 ret = 0;
973 goto out;
974 }
975 readlen = newlen > len ? len : newlen;
976 ret = read(p[0], value, readlen);
977 close(p[0]);
978 if (ret != readlen) {
979 ret = -1;
980 goto out;
981 }
982 if (newlen >= len) {
983 value[len-1] = '\0';
984 newlen = len-1;
985 } else if (newlen+1 < len) {
986 // cgmanager doesn't add eol to last entry
987 value[newlen++] = '\n';
988 value[newlen] = '\0';
989 }
990 ret = newlen;
991 out:
992 if (wait_for_pid(pid))
993 WARN("do_cgm_get exited with error");
994 return ret;
995 }
996
997 static void do_cgm_set(const char *name, const char *lxcpath, const char *filename, const char *value, int outp)
998 {
999 char *controller, *key, *cgroup = NULL;
1000 int retval = 0; // value we are sending to the parent over outp
1001 int ret;
1002 char *cglast;
1003
1004 controller = alloca(strlen(filename)+1);
1005 strcpy(controller, filename);
1006 key = strchr(controller, '.');
1007 if (!key) {
1008 ret = write(outp, &retval, sizeof(retval));
1009 if (ret != sizeof(retval))
1010 WARN("Failed to warn cgm_set of error; parent may hang");
1011 exit(1);
1012 }
1013 *key = '\0';
1014
1015 if (!cgm_dbus_connect()) {
1016 ERROR("Error connecting to cgroup manager");
1017 ret = write(outp, &retval, sizeof(retval));
1018 if (ret != sizeof(retval))
1019 WARN("Failed to warn cgm_set of error; parent may hang");
1020 exit(1);
1021 }
1022 cgroup = try_get_abs_cgroup(name, lxcpath, controller);
1023 if (!cgroup) {
1024 cgm_dbus_disconnect();
1025 ret = write(outp, &retval, sizeof(retval));
1026 if (ret != sizeof(retval))
1027 WARN("Failed to warn cgm_set of error; parent may hang");
1028 exit(1);
1029 }
1030 cglast = strrchr(cgroup, '/');
1031 if (!cglast) {
1032 cgm_dbus_disconnect();
1033 free_abs_cgroup(cgroup);
1034 ret = write(outp, &retval, sizeof(retval));
1035 if (ret != sizeof(retval))
1036 WARN("Failed to warn cgm_set of error; parent may hang");
1037 exit(1);
1038 }
1039 *cglast = '\0';
1040 if (!lxc_cgmanager_enter(getpid(), controller, cgroup, abs_cgroup_supported())) {
1041 ERROR("Failed to enter container cgroup %s:%s", controller, cgroup);
1042 ret = write(outp, &retval, sizeof(retval));
1043 if (ret != sizeof(retval))
1044 WARN("Failed to warn cgm_set of error; parent may hang");
1045 cgm_dbus_disconnect();
1046 free_abs_cgroup(cgroup);
1047 exit(1);
1048 }
1049 if (cgmanager_set_value_sync(NULL, cgroup_manager, controller, cglast+1, filename, value) != 0) {
1050 NihError *nerr;
1051 nerr = nih_error_get();
1052 ERROR("Error setting cgroup value %s for %s:%s", filename, controller, cgroup);
1053 ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
1054 nih_free(nerr);
1055 free_abs_cgroup(cgroup);
1056 cgm_dbus_disconnect();
1057 ret = write(outp, &retval, sizeof(retval));
1058 if (ret != sizeof(retval))
1059 WARN("Failed to warn cgm_set of error; parent may hang");
1060 exit(1);
1061 }
1062 free_abs_cgroup(cgroup);
1063 cgm_dbus_disconnect();
1064 /* tell parent that we are done */
1065 retval = 1;
1066 ret = write(outp, &retval, sizeof(retval));
1067 if (ret != sizeof(retval)) {
1068 exit(1);
1069 }
1070 exit(0);
1071 }
1072
1073 /* cgm_set is called to change cgroup settings, not during startup */
1074 static int cgm_set(const char *filename, const char *value, const char *name, const char *lxcpath)
1075 {
1076 pid_t pid;
1077 int p[2], ret, v;
1078
1079 if (pipe(p) < 0)
1080 return -1;
1081 if ((pid = fork()) < 0) {
1082 close(p[1]);
1083 close(p[0]);
1084 return -1;
1085 }
1086 if (!pid) // do_cgm_set exits
1087 do_cgm_set(name, lxcpath, filename, value, p[1]);
1088 close(p[1]);
1089 ret = read(p[0], &v, sizeof(v));
1090 close(p[0]);
1091 if (wait_for_pid(pid))
1092 WARN("do_cgm_set exited with error");
1093 if (ret != sizeof(v) || !v)
1094 return -1;
1095 return 0;
1096 }
1097
1098 static void free_subsystems(void)
1099 {
1100 int i;
1101
1102 for (i = 0; i < nr_subsystems; i++)
1103 free(subsystems[i]);
1104 free(subsystems);
1105 subsystems = NULL;
1106 nr_subsystems = 0;
1107 }
1108
1109 static void cull_user_controllers(void)
1110 {
1111 int i, j;
1112
1113 for (i = 0; i < nr_subsystems; i++) {
1114 if (strncmp(subsystems[i], "name=", 5) != 0)
1115 continue;
1116 for (j = i; j < nr_subsystems-1; j++)
1117 subsystems[j] = subsystems[j+1];
1118 nr_subsystems--;
1119 }
1120 }
1121
1122 static bool in_comma_list(const char *inword, const char *cgroup_use)
1123 {
1124 char *e;
1125 size_t inlen = strlen(inword), len;
1126
1127 do {
1128 e = strchr(cgroup_use, ',');
1129 len = e ? e - cgroup_use : strlen(cgroup_use);
1130 if (len == inlen && strncmp(inword, cgroup_use, len) == 0)
1131 return true;
1132 cgroup_use = e + 1;
1133 } while (e);
1134
1135 return false;
1136 }
1137
1138 static bool in_subsystem_list(const char *c)
1139 {
1140 int i;
1141
1142 for (i = 0; i < nr_subsystems; i++) {
1143 if (strcmp(c, subsystems[i]) == 0)
1144 return true;
1145 }
1146
1147 return false;
1148 }
1149
1150 /*
1151 * If /etc/lxc/lxc.conf specifies lxc.cgroup.use = "freezer,memory",
1152 * then clear out any other subsystems, and make sure that freezer
1153 * and memory are both enabled
1154 */
1155 static bool verify_and_prune(const char *cgroup_use)
1156 {
1157 const char *p;
1158 char *e;
1159 int i, j;
1160
1161 for (p = cgroup_use; p && *p; p = e + 1) {
1162 e = strchr(p, ',');
1163 if (e)
1164 *e = '\0';
1165
1166 if (!in_subsystem_list(p)) {
1167 ERROR("Controller %s required by lxc.cgroup.use but not available\n", p);
1168 return false;
1169 }
1170
1171 if (e)
1172 *e = ',';
1173 if (!e)
1174 break;
1175 }
1176
1177 for (i = 0; i < nr_subsystems;) {
1178 if (in_comma_list(subsystems[i], cgroup_use)) {
1179 i++;
1180 continue;
1181 }
1182 free(subsystems[i]);
1183 for (j = i; j < nr_subsystems-1; j++)
1184 subsystems[j] = subsystems[j+1];
1185 subsystems[nr_subsystems-1] = NULL;
1186 nr_subsystems--;
1187 }
1188
1189 return true;
1190 }
1191
1192 static bool collect_subsytems(void)
1193 {
1194 char *line = NULL;
1195 nih_local char **cgm_subsys_list = NULL;
1196 size_t sz = 0;
1197 FILE *f = NULL;
1198
1199 if (subsystems) // already initialized
1200 return true;
1201
1202 subsystems_inone = malloc(2 * sizeof(char *));
1203 if (!subsystems_inone)
1204 return false;
1205 subsystems_inone[0] = "all";
1206 subsystems_inone[1] = NULL;
1207
1208 if (lxc_list_controllers(&cgm_subsys_list)) {
1209 while (cgm_subsys_list[nr_subsystems]) {
1210 char **tmp = NIH_MUST( realloc(subsystems,
1211 (nr_subsystems+2)*sizeof(char *)) );
1212 tmp[nr_subsystems] = NIH_MUST(
1213 strdup(cgm_subsys_list[nr_subsystems++]) );
1214 subsystems = tmp;
1215 }
1216 if (nr_subsystems)
1217 subsystems[nr_subsystems] = NULL;
1218 goto collected;
1219 }
1220
1221 INFO("cgmanager_list_controllers failed, falling back to /proc/self/cgroups");
1222 f = fopen_cloexec("/proc/self/cgroup", "r");
1223 if (!f) {
1224 f = fopen_cloexec("/proc/1/cgroup", "r");
1225 if (!f)
1226 return false;
1227 }
1228 while (getline(&line, &sz, f) != -1) {
1229 /* file format: hierarchy:subsystems:group,
1230 * with multiple subsystems being ,-separated */
1231 char *slist, *end, *p, *saveptr = NULL, **tmp;
1232
1233 if (!line[0])
1234 continue;
1235
1236 slist = strchr(line, ':');
1237 if (!slist)
1238 continue;
1239 slist++;
1240 end = strchr(slist, ':');
1241 if (!end)
1242 continue;
1243 *end = '\0';
1244
1245 for (p = strtok_r(slist, ",", &saveptr);
1246 p;
1247 p = strtok_r(NULL, ",", &saveptr)) {
1248 tmp = realloc(subsystems, (nr_subsystems+2)*sizeof(char *));
1249 if (!tmp)
1250 goto out_free;
1251
1252 subsystems = tmp;
1253 tmp[nr_subsystems] = strdup(p);
1254 tmp[nr_subsystems+1] = NULL;
1255 if (!tmp[nr_subsystems])
1256 goto out_free;
1257 nr_subsystems++;
1258 }
1259 }
1260 fclose(f);
1261
1262 free(line);
1263
1264 collected:
1265 if (!nr_subsystems) {
1266 ERROR("No cgroup subsystems found");
1267 return false;
1268 }
1269
1270 /* make sure that cgroup.use can be and is honored */
1271 const char *cgroup_use = lxc_global_config_value("lxc.cgroup.use");
1272 if (!cgroup_use && errno != 0)
1273 goto out_good;
1274 if (cgroup_use) {
1275 if (!verify_and_prune(cgroup_use))
1276 goto out_free;
1277 subsystems_inone[0] = NIH_MUST( strdup(cgroup_use) );
1278 cgm_all_controllers_same = false;
1279 }
1280
1281 out_good:
1282 return true;
1283
1284 out_free:
1285 free(line);
1286 if (f)
1287 fclose(f);
1288 free_subsystems();
1289 return false;
1290 }
1291
1292 /*
1293 * called during cgroup.c:cgroup_ops_init(), at startup. No threads.
1294 * We check whether we can talk to cgmanager, escape to root cgroup if
1295 * we are root, then close the connection.
1296 */
1297 struct cgroup_ops *cgm_ops_init(void)
1298 {
1299 if (!collect_subsytems())
1300 return NULL;
1301 if (!cgm_dbus_connect())
1302 goto err1;
1303
1304 // root; try to escape to root cgroup
1305 if (geteuid() == 0 && !lxc_cgmanager_escape())
1306 goto err2;
1307 cgm_dbus_disconnect();
1308
1309 return &cgmanager_ops;
1310
1311 err2:
1312 cgm_dbus_disconnect();
1313 err1:
1314 free_subsystems();
1315 return NULL;
1316 }
1317
1318 /* unfreeze is called by the command api after killing a container. */
1319 static bool cgm_unfreeze(void *hdata)
1320 {
1321 struct cgm_data *d = hdata;
1322 bool ret = true;
1323
1324 if (!d || !d->cgroup_path)
1325 return false;
1326
1327 if (!cgm_dbus_connect()) {
1328 ERROR("Error connecting to cgroup manager");
1329 return false;
1330 }
1331 if (cgmanager_set_value_sync(NULL, cgroup_manager, "freezer", d->cgroup_path,
1332 "freezer.state", "THAWED") != 0) {
1333 NihError *nerr;
1334 nerr = nih_error_get();
1335 ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
1336 nih_free(nerr);
1337 ERROR("Error unfreezing %s", d->cgroup_path);
1338 ret = false;
1339 }
1340 cgm_dbus_disconnect();
1341 return ret;
1342 }
1343
1344 static bool cgm_setup_limits(void *hdata, struct lxc_list *cgroup_settings, bool do_devices)
1345 {
1346 struct cgm_data *d = hdata;
1347 struct lxc_list *iterator, *sorted_cgroup_settings, *next;
1348 struct lxc_cgroup *cg;
1349 bool ret = false;
1350
1351 if (lxc_list_empty(cgroup_settings))
1352 return true;
1353
1354 if (!d || !d->cgroup_path)
1355 return false;
1356
1357 if (!cgm_dbus_connect()) {
1358 ERROR("Error connecting to cgroup manager");
1359 return false;
1360 }
1361
1362 sorted_cgroup_settings = sort_cgroup_settings(cgroup_settings);
1363 if (!sorted_cgroup_settings) {
1364 return false;
1365 }
1366
1367 lxc_list_for_each(iterator, sorted_cgroup_settings) {
1368 char controller[100], *p;
1369 cg = iterator->elem;
1370 if (do_devices != !strncmp("devices", cg->subsystem, 7))
1371 continue;
1372 if (strlen(cg->subsystem) > 100) // i smell a rat
1373 goto out;
1374 strcpy(controller, cg->subsystem);
1375 p = strchr(controller, '.');
1376 if (p)
1377 *p = '\0';
1378 if (cgmanager_set_value_sync(NULL, cgroup_manager, controller,
1379 d->cgroup_path, cg->subsystem, cg->value) != 0) {
1380 NihError *nerr;
1381 nerr = nih_error_get();
1382 ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
1383 nih_free(nerr);
1384 ERROR("Error setting cgroup %s:%s limit type %s", controller,
1385 d->cgroup_path, cg->subsystem);
1386 goto out;
1387 }
1388
1389 DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value);
1390 }
1391
1392 ret = true;
1393 INFO("cgroup limits have been setup");
1394 out:
1395 lxc_list_for_each_safe(iterator, sorted_cgroup_settings, next) {
1396 lxc_list_del(iterator);
1397 free(iterator);
1398 }
1399 free(sorted_cgroup_settings);
1400 cgm_dbus_disconnect();
1401 return ret;
1402 }
1403
1404 static bool cgm_chown(void *hdata, struct lxc_conf *conf)
1405 {
1406 struct cgm_data *d = hdata;
1407
1408 if (!d || !d->cgroup_path)
1409 return false;
1410 if (!cgm_dbus_connect()) {
1411 ERROR("Error connecting to cgroup manager");
1412 return false;
1413 }
1414 if (!chown_cgroup(d->cgroup_path, conf))
1415 WARN("Failed to chown %s to container root", d->cgroup_path);
1416 cgm_dbus_disconnect();
1417 return true;
1418 }
1419
1420 /*
1421 * TODO: this should be re-written to use the get_config_item("lxc.id_map")
1422 * cmd api instead of getting the idmap from c->lxc_conf. The reason is
1423 * that the id_maps may be different if the container was started with a
1424 * -f or -s argument.
1425 * The reason I'm punting on that is because we'll need to parse the
1426 * idmap results.
1427 */
1428 static bool cgm_attach(const char *name, const char *lxcpath, pid_t pid)
1429 {
1430 bool pass = true;
1431 char *cgroup = NULL;
1432 char **slist = subsystems;
1433 int i;
1434
1435 if (!cgm_dbus_connect()) {
1436 ERROR("Error connecting to cgroup manager");
1437 return false;
1438 }
1439
1440 for (i = 0; slist[i]; i++) {
1441 cgroup = try_get_abs_cgroup(name, lxcpath, slist[i]);
1442 if (!cgroup) {
1443 ERROR("Failed to get cgroup for controller %s", slist[i]);
1444 cgm_dbus_disconnect();
1445 return false;
1446 }
1447
1448 if (!lxc_cgmanager_enter(pid, slist[i], cgroup, abs_cgroup_supported())) {
1449 pass = false;
1450 break;
1451 }
1452
1453 }
1454 cgm_dbus_disconnect();
1455 if (!pass)
1456 ERROR("Failed to enter group %s", cgroup);
1457
1458 free_abs_cgroup(cgroup);
1459 return pass;
1460 }
1461
1462 static bool cgm_bind_dir(const char *root, const char *dirname)
1463 {
1464 nih_local char *cgpath = NULL;
1465
1466 /* /sys should have been mounted by now */
1467 cgpath = NIH_MUST( nih_strdup(NULL, root) );
1468 NIH_MUST( nih_strcat(&cgpath, NULL, "/sys/fs/cgroup") );
1469
1470 if (!dir_exists(cgpath)) {
1471 ERROR("%s does not exist", cgpath);
1472 return false;
1473 }
1474
1475 /* mount a tmpfs there so we can create subdirs */
1476 if (mount("cgroup", cgpath, "tmpfs", 0, "size=10000,mode=755")) {
1477 SYSERROR("Failed to mount tmpfs at %s", cgpath);
1478 return false;
1479 }
1480 NIH_MUST( nih_strcat(&cgpath, NULL, "/cgmanager") );
1481
1482 if (mkdir(cgpath, 0755) < 0) {
1483 SYSERROR("Failed to create %s", cgpath);
1484 return false;
1485 }
1486
1487 if (mount(dirname, cgpath, "none", MS_BIND, 0)) {
1488 SYSERROR("Failed to bind mount %s to %s", dirname, cgpath);
1489 return false;
1490 }
1491
1492 return true;
1493 }
1494
1495 /*
1496 * cgm_mount_cgroup:
1497 * If /sys/fs/cgroup/cgmanager.lower/ exists, bind mount that to
1498 * /sys/fs/cgroup/cgmanager/ in the container.
1499 * Otherwise, if /sys/fs/cgroup/cgmanager exists, bind mount that.
1500 * Else do nothing
1501 */
1502 #define CGMANAGER_LOWER_SOCK "/sys/fs/cgroup/cgmanager.lower"
1503 #define CGMANAGER_UPPER_SOCK "/sys/fs/cgroup/cgmanager"
1504 static bool cgm_mount_cgroup(void *hdata, const char *root, int type)
1505 {
1506 if (dir_exists(CGMANAGER_LOWER_SOCK))
1507 return cgm_bind_dir(root, CGMANAGER_LOWER_SOCK);
1508 if (dir_exists(CGMANAGER_UPPER_SOCK))
1509 return cgm_bind_dir(root, CGMANAGER_UPPER_SOCK);
1510 // Host doesn't have cgmanager running? Then how did we get here?
1511 return false;
1512 }
1513
1514 static struct cgroup_ops cgmanager_ops = {
1515 .init = cgm_init,
1516 .destroy = cgm_destroy,
1517 .create = cgm_create,
1518 .enter = cgm_enter,
1519 .create_legacy = NULL,
1520 .get_cgroup = cgm_get_cgroup,
1521 .canonical_path = cgm_canonical_path,
1522 .get = cgm_get,
1523 .set = cgm_set,
1524 .unfreeze = cgm_unfreeze,
1525 .setup_limits = cgm_setup_limits,
1526 .name = "cgmanager",
1527 .chown = cgm_chown,
1528 .attach = cgm_attach,
1529 .mount_cgroup = cgm_mount_cgroup,
1530 .nrtasks = cgm_get_nrtasks,
1531 .disconnect = NULL,
1532 .driver = CGMANAGER,
1533 };
1534 #endif