]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/cgmanager.c
cgroup: change unfreeze_fromhandler to return bool
[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-client/cgmanager-client.h>
58 #include <nih/alloc.h>
59 #include <nih/error.h>
60 #include <nih/string.h>
61 NihDBusProxy *cgroup_manager = NULL;
62
63 extern struct cgroup_ops *active_cg_ops;
64 bool cgmanager_initialized = false;
65 bool use_cgmanager = true;
66 static struct cgroup_ops cgmanager_ops;
67 static int nr_subsystems;
68 static char **subsystems;
69
70 bool lxc_init_cgmanager(void);
71 static void cgmanager_disconnected(DBusConnection *connection)
72 {
73 WARN("Cgroup manager connection was terminated");
74 cgroup_manager = NULL;
75 cgmanager_initialized = false;
76 if (lxc_init_cgmanager()) {
77 cgmanager_initialized = true;
78 INFO("New cgroup manager connection was opened");
79 }
80 }
81
82 static int send_creds(int sock, int rpid, int ruid, int rgid)
83 {
84 struct msghdr msg = { 0 };
85 struct iovec iov;
86 struct cmsghdr *cmsg;
87 struct ucred cred = {
88 .pid = rpid,
89 .uid = ruid,
90 .gid = rgid,
91 };
92 char cmsgbuf[CMSG_SPACE(sizeof(cred))];
93 char buf[1];
94 buf[0] = 'p';
95
96 msg.msg_control = cmsgbuf;
97 msg.msg_controllen = sizeof(cmsgbuf);
98
99 cmsg = CMSG_FIRSTHDR(&msg);
100 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
101 cmsg->cmsg_level = SOL_SOCKET;
102 cmsg->cmsg_type = SCM_CREDENTIALS;
103 memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred));
104
105 msg.msg_name = NULL;
106 msg.msg_namelen = 0;
107
108 iov.iov_base = buf;
109 iov.iov_len = sizeof(buf);
110 msg.msg_iov = &iov;
111 msg.msg_iovlen = 1;
112
113 if (sendmsg(sock, &msg, 0) < 0)
114 return -1;
115 return 0;
116 }
117
118 #define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock"
119 bool lxc_init_cgmanager(void)
120 {
121 DBusError dbus_error;
122 DBusConnection *connection;
123 dbus_error_init(&dbus_error);
124
125 connection = nih_dbus_connect(CGMANAGER_DBUS_SOCK, cgmanager_disconnected);
126 if (!connection) {
127 NihError *nerr;
128 nerr = nih_error_get();
129 ERROR("Error opening cgmanager connection at %s: %s", CGMANAGER_DBUS_SOCK,
130 nerr->message);
131 nih_free(nerr);
132 dbus_error_free(&dbus_error);
133 return false;
134 }
135 dbus_connection_set_exit_on_disconnect(connection, FALSE);
136 dbus_error_free(&dbus_error);
137 cgroup_manager = nih_dbus_proxy_new(NULL, connection,
138 NULL /* p2p */,
139 "/org/linuxcontainers/cgmanager", NULL, NULL);
140 dbus_connection_unref(connection);
141 if (!cgroup_manager) {
142 NihError *nerr;
143 nerr = nih_error_get();
144 ERROR("Error opening cgmanager proxy: %s", nerr->message);
145 nih_free(nerr);
146 return false;
147 }
148 active_cg_ops = &cgmanager_ops;
149 // force fd passing negotiation
150 if (cgmanager_ping_sync(NULL, cgroup_manager, 0) != 0) {
151 NihError *nerr;
152 nerr = nih_error_get();
153 ERROR("Error pinging cgroup manager: %s", nerr->message);
154 nih_free(nerr);
155 }
156 return true;
157 }
158
159 static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path, int32_t *existed)
160 {
161 if ( cgmanager_create_sync(NULL, cgroup_manager, controller,
162 cgroup_path, existed) != 0) {
163 NihError *nerr;
164 nerr = nih_error_get();
165 ERROR("call to cgmanager_create_sync failed: %s", nerr->message);
166 nih_free(nerr);
167 ERROR("Failed to create %s:%s", controller, cgroup_path);
168 return false;
169 }
170
171 return true;
172 }
173
174 struct chown_data {
175 const char *controller;
176 const char *cgroup_path;
177 uid_t origuid;
178 };
179
180 static int do_chown_cgroup(const char *controller, const char *cgroup_path,
181 uid_t origuid)
182 {
183 int sv[2] = {-1, -1}, optval = 1, ret = -1;
184 char buf[1];
185
186 uid_t caller_nsuid = get_ns_uid(origuid);
187
188 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) {
189 SYSERROR("Error creating socketpair");
190 goto out;
191 }
192 if (setsockopt(sv[1], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
193 SYSERROR("setsockopt failed");
194 goto out;
195 }
196 if (setsockopt(sv[0], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
197 SYSERROR("setsockopt failed");
198 goto out;
199 }
200 if ( cgmanager_chown_scm_sync(NULL, cgroup_manager, controller,
201 cgroup_path, sv[1]) != 0) {
202 NihError *nerr;
203 nerr = nih_error_get();
204 ERROR("call to cgmanager_chown_scm_sync failed: %s", nerr->message);
205 nih_free(nerr);
206 goto out;
207 }
208 /* now send credentials */
209
210 fd_set rfds;
211 FD_ZERO(&rfds);
212 FD_SET(sv[0], &rfds);
213 if (select(sv[0]+1, &rfds, NULL, NULL, NULL) < 0) {
214 ERROR("Error getting go-ahead from server: %s", strerror(errno));
215 goto out;
216 }
217 if (read(sv[0], &buf, 1) != 1) {
218 ERROR("Error getting reply from server over socketpair");
219 goto out;
220 }
221 if (send_creds(sv[0], getpid(), getuid(), getgid())) {
222 SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__);
223 goto out;
224 }
225 FD_ZERO(&rfds);
226 FD_SET(sv[0], &rfds);
227 if (select(sv[0]+1, &rfds, NULL, NULL, NULL) < 0) {
228 ERROR("Error getting go-ahead from server: %s", strerror(errno));
229 goto out;
230 }
231 if (read(sv[0], &buf, 1) != 1) {
232 ERROR("Error getting reply from server over socketpair");
233 goto out;
234 }
235 if (send_creds(sv[0], getpid(), caller_nsuid, 0)) {
236 SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__);
237 goto out;
238 }
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 ret = read(sv[0], buf, 1);
246 out:
247 close(sv[0]);
248 close(sv[1]);
249 if (ret == 1 && *buf == '1')
250 return 0;
251 return -1;
252 }
253
254 static int chown_cgroup_wrapper(void *data)
255 {
256 struct chown_data *arg = data;
257
258 if (setresgid(0,0,0) < 0)
259 SYSERROR("Failed to setgid to 0");
260 if (setresuid(0,0,0) < 0)
261 SYSERROR("Failed to setuid to 0");
262 if (setgroups(0, NULL) < 0)
263 SYSERROR("Failed to clear groups");
264 return do_chown_cgroup(arg->controller, arg->cgroup_path, arg->origuid);
265 }
266
267 static bool chown_cgroup(const char *controller, const char *cgroup_path,
268 struct lxc_conf *conf)
269 {
270 struct chown_data data;
271
272 if (lxc_list_empty(&conf->id_map))
273 /* If there's no mapping then we don't need to chown */
274 return true;
275
276 data.controller = controller;
277 data.cgroup_path = cgroup_path;
278 data.origuid = geteuid();
279
280 if (userns_exec_1(conf, chown_cgroup_wrapper, &data) < 0) {
281 ERROR("Error requesting cgroup chown in new namespace");
282 return false;
283 }
284 return true;
285 }
286
287 #define CG_REMOVE_RECURSIVE 1
288 static void cgm_remove_cgroup(const char *controller, const char *path)
289 {
290 int existed;
291 if ( cgmanager_remove_sync(NULL, cgroup_manager, controller,
292 path, CG_REMOVE_RECURSIVE, &existed) != 0) {
293 NihError *nerr;
294 nerr = nih_error_get();
295 ERROR("call to cgmanager_remove_sync failed: %s", nerr->message);
296 nih_free(nerr);
297 ERROR("Error removing %s:%s", controller, path);
298 }
299 if (existed == -1)
300 INFO("cgroup removal attempt: %s:%s did not exist", controller, path);
301 }
302
303 static void cgm_destroy(struct lxc_handler *handler)
304 {
305 char *cgroup_path = handler->cgroup_info->data;
306 int i;
307
308 if (!cgroup_path)
309 return;
310
311 for (i = 0; i < nr_subsystems; i++)
312 cgm_remove_cgroup(subsystems[i], cgroup_path);
313
314 free(cgroup_path);
315 handler->cgroup_info->data = NULL;
316 }
317
318 /*
319 * remove all the cgroups created
320 */
321 static inline void cleanup_cgroups(char *path)
322 {
323 int i;
324 for (i = 0; i < nr_subsystems; i++)
325 cgm_remove_cgroup(subsystems[i], path);
326 }
327
328 static inline bool cgm_create(struct lxc_handler *handler)
329 {
330 int i, index=0, baselen, ret;
331 int32_t existed;
332 char result[MAXPATHLEN], *tmp;
333 char *cgroup_path = handler->cgroup_info->data;
334
335 // XXX we should send a hint to the cgmanager that when these
336 // cgroups become empty they should be deleted. Requires a cgmanager
337 // extension
338
339 memset(result, 0, MAXPATHLEN);
340 tmp = lxc_string_replace("%n", handler->name, handler->cgroup_info->cgroup_pattern);
341 if (!tmp)
342 return false;
343 if (strlen(tmp) > MAXPATHLEN)
344 return false;
345 strcpy(result, tmp);
346 baselen = strlen(result);
347 free(tmp);
348 tmp = result;
349 while (*tmp == '/')
350 tmp++;
351 again:
352 if (index == 100) { // turn this into a warn later
353 ERROR("cgroup error? 100 cgroups with this name already running");
354 return false;
355 }
356 if (index) {
357 ret = snprintf(result+baselen, MAXPATHLEN-baselen, "-%d", index);
358 if (ret < 0 || ret >= MAXPATHLEN-baselen)
359 return false;
360 }
361 existed = 0;
362 for (i = 0; i < nr_subsystems; i++) {
363 if (!lxc_cgmanager_create(subsystems[i], tmp, &existed)) {
364 ERROR("Error creating cgroup %s:%s", subsystems[i], result);
365 cleanup_cgroups(tmp);
366 return false;
367 }
368 if (existed == 1)
369 goto next;
370 }
371 // success
372 cgroup_path = strdup(tmp);
373 if (!cgroup_path) {
374 cleanup_cgroups(tmp);
375 return false;
376 }
377 handler->cgroup_info->data = cgroup_path;
378 return true;
379 next:
380 cleanup_cgroups(tmp);
381 index++;
382 goto again;
383 }
384
385 /*
386 * Use the cgmanager to move a task into a cgroup for a particular
387 * hierarchy.
388 * All the subsystems in this hierarchy are co-mounted, so we only
389 * need to transition the task into one of the cgroups
390 */
391 static bool lxc_cgmanager_enter(pid_t pid, const char *controller,
392 const char *cgroup_path)
393 {
394 if (cgmanager_move_pid_sync(NULL, cgroup_manager, controller,
395 cgroup_path, pid) != 0) {
396 NihError *nerr;
397 nerr = nih_error_get();
398 ERROR("call to cgmanager_move_pid_sync failed: %s", nerr->message);
399 nih_free(nerr);
400 return false;
401 }
402 return true;
403 }
404
405 static bool do_cgm_enter(pid_t pid, const char *cgroup_path)
406 {
407 int i;
408
409 for (i = 0; i < nr_subsystems; i++) {
410 if (!lxc_cgmanager_enter(pid, subsystems[i], cgroup_path))
411 return false;
412 }
413 return true;
414 }
415
416 static inline bool cgm_enter(struct lxc_handler *handler)
417 {
418 char *cgroup_path = handler->cgroup_info->data;
419 return do_cgm_enter(handler->pid, cgroup_path);
420 }
421
422 static char *cgm_get_cgroup(struct lxc_handler *handler, const char *subsystem)
423 {
424 char *cgroup_path = handler->cgroup_info->data;
425 return cgroup_path;
426 }
427
428 int cgm_get(const char *filename, char *value, size_t len, const char *name, const char *lxcpath)
429 {
430 char *result, *controller, *key, *cgroup;
431 size_t newlen;
432
433 controller = alloca(strlen(filename)+1);
434 strcpy(controller, filename);
435 key = strchr(controller, '.');
436 if (!key)
437 return -1;
438 *key = '\0';
439
440 /* use the command interface to look for the cgroup */
441 cgroup = lxc_cmd_get_cgroup_path(name, lxcpath, controller);
442 if (!cgroup)
443 return -1;
444 if (cgmanager_get_value_sync(NULL, cgroup_manager, controller, cgroup, filename, &result) != 0) {
445 /*
446 * must consume the nih error
447 * However don't print out an error as the key may simply not exist
448 * on the host
449 */
450 NihError *nerr;
451 nerr = nih_error_get();
452 nih_free(nerr);
453 free(cgroup);
454 return -1;
455 }
456 free(cgroup);
457 newlen = strlen(result);
458 if (!value) {
459 // user queries the size
460 nih_free(result);
461 return newlen+1;
462 }
463
464 strncpy(value, result, len);
465 if (newlen >= len) {
466 value[len-1] = '\0';
467 newlen = len-1;
468 } else if (newlen+1 < len) {
469 // cgmanager doesn't add eol to last entry
470 value[newlen++] = '\n';
471 value[newlen] = '\0';
472 }
473 nih_free(result);
474 return newlen;
475 }
476
477 static int cgm_do_set(const char *controller, const char *file,
478 const char *cgroup, const char *value)
479 {
480 int ret;
481 ret = cgmanager_set_value_sync(NULL, cgroup_manager, controller,
482 cgroup, file, value);
483 if (ret != 0) {
484 NihError *nerr;
485 nerr = nih_error_get();
486 ERROR("call to cgmanager_remove_sync failed: %s", nerr->message);
487 nih_free(nerr);
488 ERROR("Error setting cgroup %s limit %s", file, cgroup);
489 }
490 return ret;
491 }
492
493 int cgm_set(const char *filename, const char *value, const char *name, const char *lxcpath)
494 {
495 char *controller, *key, *cgroup;
496 int ret;
497
498 controller = alloca(strlen(filename)+1);
499 strcpy(controller, filename);
500 key = strchr(controller, '.');
501 if (!key)
502 return -1;
503 *key = '\0';
504
505 /* use the command interface to look for the cgroup */
506 cgroup = lxc_cmd_get_cgroup_path(name, lxcpath, controller);
507 if (!cgroup) {
508 ERROR("Failed to get cgroup for controller %s for %s:%s",
509 controller, lxcpath, name);
510 return -1;
511 }
512 ret = cgm_do_set(controller, filename, cgroup, value);
513 free(cgroup);
514 return ret;
515 }
516
517 static bool collect_subsytems(void)
518 {
519 char *line = NULL, *tab1;
520 size_t sz = 0, i;
521 FILE *f;
522
523 if (subsystems) // already initialized
524 return true;
525
526 f = fopen_cloexec("/proc/cgroups", "r");
527 if (!f)
528 return false;
529 while (getline(&line, &sz, f) != -1) {
530 char **tmp;
531 if (line[0] == '#')
532 continue;
533 if (!line[0])
534 continue;
535 tab1 = strchr(line, '\t');
536 if (!tab1)
537 continue;
538 *tab1 = '\0';
539 tmp = realloc(subsystems, (nr_subsystems+1)*sizeof(char *));
540 if (!tmp)
541 goto out_free;
542
543 subsystems = tmp;
544 tmp[nr_subsystems] = strdup(line);
545 if (!tmp[nr_subsystems])
546 goto out_free;
547 nr_subsystems++;
548 }
549 fclose(f);
550
551 if (!nr_subsystems) {
552 ERROR("No cgroup subsystems found");
553 return false;
554 }
555
556 return true;
557
558 out_free:
559 fclose(f);
560 for (i = 0; i < nr_subsystems; i++)
561 free(subsystems[i]);
562 free(subsystems);
563 subsystems = NULL;
564 nr_subsystems = 0;
565 return false;
566 }
567
568 static inline bool cgm_init(struct lxc_handler *handler)
569 {
570 return collect_subsytems();
571 }
572
573 static bool cgm_unfreeze_fromhandler(struct lxc_handler *handler)
574 {
575 char *cgroup_path = handler->cgroup_info->data;
576
577 if (cgmanager_set_value_sync(NULL, cgroup_manager, "freezer", cgroup_path,
578 "freezer.state", "THAWED") != 0) {
579 NihError *nerr;
580 nerr = nih_error_get();
581 ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
582 nih_free(nerr);
583 ERROR("Error unfreezing %s", cgroup_path);
584 return false;
585 }
586 return true;
587 }
588
589 static bool setup_limits(struct lxc_handler *h, bool do_devices)
590 {
591 struct lxc_list *iterator;
592 struct lxc_cgroup *cg;
593 bool ret = false;
594 struct lxc_list *cgroup_settings = &h->conf->cgroup;
595 char *cgroup_path = h->cgroup_info->data;
596
597 if (lxc_list_empty(cgroup_settings))
598 return true;
599
600 lxc_list_for_each(iterator, cgroup_settings) {
601 char controller[100], *p;
602 cg = iterator->elem;
603 if (do_devices != !strncmp("devices", cg->subsystem, 7))
604 continue;
605 if (strlen(cg->subsystem) > 100) // i smell a rat
606 goto out;
607 strcpy(controller, cg->subsystem);
608 p = strchr(controller, '.');
609 if (p)
610 *p = '\0';
611 if (cgm_do_set(controller, cg->subsystem, cgroup_path
612 , cg->value) < 0) {
613 ERROR("Error setting %s to %s for %s\n",
614 cg->subsystem, cg->value, h->name);
615 goto out;
616 }
617
618 DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value);
619 }
620
621 ret = true;
622 INFO("cgroup limits have been setup");
623 out:
624 return ret;
625 }
626
627 static bool cgm_setup_limits(struct lxc_handler *handler, bool with_devices)
628 {
629 return setup_limits(handler, with_devices);
630 }
631
632 static bool cgm_chown(struct lxc_handler *handler)
633 {
634 char *cgroup_path = handler->cgroup_info->data;
635 int i;
636
637 for (i = 0; i < nr_subsystems; i++) {
638 if (!chown_cgroup(subsystems[i], cgroup_path, handler->conf))
639 WARN("Failed to chown %s:%s to container root",
640 subsystems[i], cgroup_path);
641 }
642 return true;
643 }
644
645 /*
646 * TODO: this should be re-written to use the get_config_item("lxc.id_map")
647 * cmd api instead of getting the idmap from c->lxc_conf. The reason is
648 * that the id_maps may be different if the container was started with a
649 * -f or -s argument.
650 * The reason I'm punting on that is because we'll need to parse the
651 * idmap results.
652 */
653 static bool cgm_attach(const char *name, const char *lxcpath, pid_t pid)
654 {
655 bool pass = false;
656 char *cgroup = NULL;
657 struct lxc_container *c;
658
659 c = lxc_container_new(name, lxcpath);
660 if (!c) {
661 ERROR("Could not load container %s:%s", lxcpath, name);
662 return false;
663 }
664 if (!collect_subsytems()) {
665 ERROR("Error collecting cgroup subsystems");
666 goto out;
667 }
668 // cgm_create makes sure that we have the same cgroup name for all
669 // subsystems, so since this is a slow command over the cmd socket,
670 // just get the cgroup name for the first one.
671 cgroup = lxc_cmd_get_cgroup_path(name, lxcpath, subsystems[0]);
672 if (!cgroup) {
673 ERROR("Failed to get cgroup for controller %s", subsystems[0]);
674 goto out;
675 }
676
677 if (!(pass = do_cgm_enter(pid, cgroup)))
678 ERROR("Failed to enter group %s", cgroup);
679
680 out:
681 free(cgroup);
682 lxc_container_put(c);
683 return pass;
684 }
685
686 static bool cgm_bind_dir(const char *root, const char *dirname)
687 {
688 nih_local char *cgpath = NULL;
689
690 /* /sys should have been mounted by now */
691 cgpath = NIH_MUST( nih_strdup(NULL, root) );
692 NIH_MUST( nih_strcat(&cgpath, NULL, "/sys/fs/cgroup") );
693
694 if (!dir_exists(cgpath)) {
695 ERROR("%s does not exist", cgpath);
696 return false;
697 }
698
699 /* mount a tmpfs there so we can create subdirs */
700 if (mount("cgroup", cgpath, "tmpfs", 0, "size=10000")) {
701 SYSERROR("Failed to mount tmpfs at %s", cgpath);
702 return false;
703 }
704 NIH_MUST( nih_strcat(&cgpath, NULL, "/cgmanager") );
705
706 if (mkdir(cgpath, 0755) < 0) {
707 SYSERROR("Failed to create %s", cgpath);
708 return false;
709 }
710
711 if (mount(dirname, cgpath, "none", MS_BIND, 0)) {
712 SYSERROR("Failed to bind mount %s to %s", dirname, cgpath);
713 return false;
714 }
715
716 return true;
717 }
718
719 /*
720 * cgm_mount_cgroup:
721 * If /sys/fs/cgroup/cgmanager.lower/ exists, bind mount that to
722 * /sys/fs/cgroup/cgmanager/ in the container.
723 * Otherwise, if /sys/fs/cgroup/cgmanager exists, bind mount that.
724 * Else do nothing
725 */
726 #define CGMANAGER_LOWER_SOCK "/sys/fs/cgroup/cgmanager.lower"
727 #define CGMANAGER_UPPER_SOCK "/sys/fs/cgroup/cgmanager"
728 static bool cgm_mount_cgroup(const char *root,
729 struct lxc_cgroup_info *cgroup_info, int type)
730 {
731 if (dir_exists(CGMANAGER_LOWER_SOCK))
732 return cgm_bind_dir(root, CGMANAGER_LOWER_SOCK);
733 if (dir_exists(CGMANAGER_UPPER_SOCK))
734 return cgm_bind_dir(root, CGMANAGER_UPPER_SOCK);
735 // Host doesn't have cgmanager running? Then how did we get here?
736 return false;
737 }
738
739 static struct cgroup_ops cgmanager_ops = {
740 .destroy = cgm_destroy,
741 .init = cgm_init,
742 .create = cgm_create,
743 .enter = cgm_enter,
744 .create_legacy = NULL,
745 .get_cgroup = cgm_get_cgroup,
746 .get = cgm_get,
747 .set = cgm_set,
748 .unfreeze_fromhandler = cgm_unfreeze_fromhandler,
749 .setup_limits = cgm_setup_limits,
750 .name = "cgmanager",
751 .chown = cgm_chown,
752 .attach = cgm_attach,
753 .mount_cgroup = cgm_mount_cgroup,
754 };
755 #endif