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