]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/cgroups/cgmanager.c
tree-wide: log function called in userns_exec_1()
[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 "bdev.h"
45 #include "error.h"
46 #include "commands.h"
47 #include "list.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
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_all_controllers_same;
187
188 /*
189 * Check whether we can use "all" when talking to cgmanager.
190 * We check two things:
191 * 1. whether cgmanager is new enough to support this.
192 * 2. whether the task we are interested in is in the same
193 * cgroup for all controllers.
194 * In cgm_init (before an lxc-start) we care about our own
195 * cgroup. In cgm_attach, we care about the target task's
196 * cgroup.
197 */
198 static void check_supports_multiple_controllers(pid_t pid)
199 {
200 FILE *f;
201 char *line = NULL, *prevpath = NULL;
202 size_t sz = 0;
203 char path[100];
204
205 cgm_all_controllers_same = false;
206
207 if (pid == -1)
208 sprintf(path, "/proc/self/cgroup");
209 else
210 sprintf(path, "/proc/%d/cgroup", pid);
211 f = fopen(path, "r");
212 if (!f)
213 return;
214
215 cgm_all_controllers_same = true;
216
217 while (getline(&line, &sz, f) != -1) {
218 /* file format: hierarchy:subsystems:group */
219 char *colon;
220 if (!line[0])
221 continue;
222
223 colon = strchr(line, ':');
224 if (!colon)
225 continue;
226 colon = strchr(colon+1, ':');
227 if (!colon)
228 continue;
229 colon++;
230 if (!prevpath) {
231 prevpath = alloca(strlen(colon)+1);
232 strcpy(prevpath, colon);
233 continue;
234 }
235 if (strcmp(prevpath, colon) != 0) {
236 cgm_all_controllers_same = false;
237 break;
238 }
239 }
240
241 fclose(f);
242 free(line);
243 }
244
245 static int send_creds(int sock, int rpid, int ruid, int rgid)
246 {
247 struct msghdr msg = { 0 };
248 struct iovec iov;
249 struct cmsghdr *cmsg;
250 struct ucred cred = {
251 .pid = rpid,
252 .uid = ruid,
253 .gid = rgid,
254 };
255 char cmsgbuf[CMSG_SPACE(sizeof(cred))];
256 char buf[1];
257 buf[0] = 'p';
258
259 msg.msg_control = cmsgbuf;
260 msg.msg_controllen = sizeof(cmsgbuf);
261
262 cmsg = CMSG_FIRSTHDR(&msg);
263 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
264 cmsg->cmsg_level = SOL_SOCKET;
265 cmsg->cmsg_type = SCM_CREDENTIALS;
266 memcpy(CMSG_DATA(cmsg), &cred, sizeof(cred));
267
268 msg.msg_name = NULL;
269 msg.msg_namelen = 0;
270
271 iov.iov_base = buf;
272 iov.iov_len = sizeof(buf);
273 msg.msg_iov = &iov;
274 msg.msg_iovlen = 1;
275
276 if (sendmsg(sock, &msg, 0) < 0)
277 return -1;
278 return 0;
279 }
280
281 static bool lxc_cgmanager_create(const char *controller, const char *cgroup_path, int32_t *existed)
282 {
283 bool ret = true;
284 if ( cgmanager_create_sync(NULL, cgroup_manager, controller,
285 cgroup_path, existed) != 0) {
286 NihError *nerr;
287 nerr = nih_error_get();
288 ERROR("call to cgmanager_create_sync failed: %s", nerr->message);
289 nih_free(nerr);
290 ERROR("Failed to create %s:%s", controller, cgroup_path);
291 ret = false;
292 }
293
294 return ret;
295 }
296
297 /*
298 * Escape to the root cgroup if we are root, so that the container will
299 * be in "/lxc/c1" rather than "/user/..../c1"
300 * called internally with connection already open
301 */
302 static bool cgm_escape(void *hdata)
303 {
304 bool ret = true, cgm_needs_disconnect = false;
305 pid_t me = getpid();
306 char **slist = subsystems;
307 int i;
308
309 if (!cgroup_manager) {
310 if (!cgm_dbus_connect()) {
311 ERROR("Error connecting to cgroup manager");
312 return false;
313 }
314 cgm_needs_disconnect = true;
315 }
316
317
318 if (cgm_all_controllers_same)
319 slist = subsystems_inone;
320
321 for (i = 0; slist[i]; i++) {
322 if (cgmanager_move_pid_abs_sync(NULL, cgroup_manager,
323 slist[i], "/", me) != 0) {
324 NihError *nerr;
325 nerr = nih_error_get();
326 ERROR("call to cgmanager_move_pid_abs_sync(%s) failed: %s",
327 slist[i], nerr->message);
328 nih_free(nerr);
329 ret = false;
330 break;
331 }
332 }
333
334 if (cgm_needs_disconnect)
335 cgm_dbus_disconnect();
336
337 return ret;
338 }
339
340 static int cgm_num_hierarchies(void)
341 {
342 /* not implemented */
343 return -1;
344 }
345
346 static bool cgm_get_hierarchies(int i, char ***out)
347 {
348 /* not implemented */
349 return false;
350 }
351
352 struct chown_data {
353 const char *cgroup_path;
354 uid_t origuid;
355 };
356
357 static int do_chown_cgroup(const char *controller, const char *cgroup_path,
358 uid_t newuid)
359 {
360 int sv[2] = {-1, -1}, optval = 1, ret = -1;
361 char buf[1];
362 struct pollfd fds;
363
364 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sv) < 0) {
365 SYSERROR("Error creating socketpair");
366 goto out;
367 }
368 if (setsockopt(sv[1], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
369 SYSERROR("setsockopt failed");
370 goto out;
371 }
372 if (setsockopt(sv[0], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
373 SYSERROR("setsockopt failed");
374 goto out;
375 }
376 if ( cgmanager_chown_scm_sync(NULL, cgroup_manager, controller,
377 cgroup_path, sv[1]) != 0) {
378 NihError *nerr;
379 nerr = nih_error_get();
380 ERROR("call to cgmanager_chown_scm_sync failed: %s", nerr->message);
381 nih_free(nerr);
382 goto out;
383 }
384 /* now send credentials */
385
386 fds.fd = sv[0];
387 fds.events = POLLIN;
388 fds.revents = 0;
389 if (poll(&fds, 1, -1) <= 0) {
390 ERROR("Error getting go-ahead from server: %s", strerror(errno));
391 goto out;
392 }
393 if (read(sv[0], &buf, 1) != 1) {
394 ERROR("Error getting reply from server over socketpair");
395 goto out;
396 }
397 if (send_creds(sv[0], getpid(), getuid(), getgid())) {
398 SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__);
399 goto out;
400 }
401 fds.fd = sv[0];
402 fds.events = POLLIN;
403 fds.revents = 0;
404 if (poll(&fds, 1, -1) <= 0) {
405 ERROR("Error getting go-ahead from server: %s", strerror(errno));
406 goto out;
407 }
408 if (read(sv[0], &buf, 1) != 1) {
409 ERROR("Error getting reply from server over socketpair");
410 goto out;
411 }
412 if (send_creds(sv[0], getpid(), newuid, 0)) {
413 SYSERROR("%s: Error sending pid over SCM_CREDENTIAL", __func__);
414 goto out;
415 }
416 fds.fd = sv[0];
417 fds.events = POLLIN;
418 fds.revents = 0;
419 if (poll(&fds, 1, -1) <= 0) {
420 ERROR("Error getting go-ahead from server: %s", strerror(errno));
421 goto out;
422 }
423 ret = read(sv[0], buf, 1);
424 out:
425 close(sv[0]);
426 close(sv[1]);
427 if (ret == 1 && *buf == '1')
428 return 0;
429 return -1;
430 }
431
432 static int chown_cgroup_wrapper(void *data)
433 {
434 struct chown_data *arg = data;
435 char **slist = subsystems;
436 int i, ret = -1;
437 uid_t destuid;
438
439 if (setresgid(0,0,0) < 0)
440 SYSERROR("Failed to setgid to 0");
441 if (setresuid(0,0,0) < 0)
442 SYSERROR("Failed to setuid to 0");
443 if (setgroups(0, NULL) < 0)
444 SYSERROR("Failed to clear groups");
445 cgm_dbus_disconnect();
446 if (!cgm_dbus_connect()) {
447 ERROR("Error connecting to cgroup manager");
448 return -1;
449 }
450 destuid = get_ns_uid(arg->origuid);
451
452 if (cgm_all_controllers_same)
453 slist = subsystems_inone;
454
455 for (i = 0; slist[i]; i++) {
456 if (do_chown_cgroup(slist[i], arg->cgroup_path, destuid) < 0) {
457 ERROR("Failed to chown %s:%s to container root",
458 slist[i], arg->cgroup_path);
459 goto fail;
460 }
461 }
462 ret = 0;
463 fail:
464 cgm_dbus_disconnect();
465 return ret;
466 }
467
468 /* Internal helper. Must be called with the cgmanager dbus socket open */
469 static bool lxc_cgmanager_chmod(const char *controller,
470 const char *cgroup_path, const char *file, int mode)
471 {
472 if (cgmanager_chmod_sync(NULL, cgroup_manager, controller,
473 cgroup_path, file, mode) != 0) {
474 NihError *nerr;
475 nerr = nih_error_get();
476 ERROR("call to cgmanager_chmod_sync failed: %s", nerr->message);
477 nih_free(nerr);
478 return false;
479 }
480 return true;
481 }
482
483 /* Internal helper. Must be called with the cgmanager dbus socket open */
484 static bool chown_cgroup(const char *cgroup_path, struct lxc_conf *conf)
485 {
486 struct chown_data data;
487 char **slist = subsystems;
488 int i;
489
490 if (lxc_list_empty(&conf->id_map))
491 /* If there's no mapping then we don't need to chown */
492 return true;
493
494 data.cgroup_path = cgroup_path;
495 data.origuid = geteuid();
496
497 /* Unpriv users can't chown it themselves, so chown from
498 * a child namespace mapping both our own and the target uid
499 */
500 if (userns_exec_1(conf, chown_cgroup_wrapper, &data,
501 "chown_cgroup_wrapper") < 0) {
502 ERROR("Error requesting cgroup chown in new namespace");
503 return false;
504 }
505
506 /*
507 * Now chmod 775 the directory else the container cannot create cgroups.
508 * This can't be done in the child namespace because it only group-owns
509 * the cgroup
510 */
511 if (cgm_all_controllers_same)
512 slist = subsystems_inone;
513
514 for (i = 0; slist[i]; i++) {
515 if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "", 0775))
516 return false;
517 if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "tasks", 0664))
518 return false;
519 if (!lxc_cgmanager_chmod(slist[i], cgroup_path, "cgroup.procs", 0664))
520 return false;
521 }
522
523 return true;
524 }
525
526 #define CG_REMOVE_RECURSIVE 1
527 /* Internal helper. Must be called with the cgmanager dbus socket open */
528 static void cgm_remove_cgroup(const char *controller, const char *path)
529 {
530 int existed;
531 if ( cgmanager_remove_sync(NULL, cgroup_manager, controller,
532 path, CG_REMOVE_RECURSIVE, &existed) != 0) {
533 NihError *nerr;
534 nerr = nih_error_get();
535 ERROR("call to cgmanager_remove_sync failed: %s", nerr->message);
536 nih_free(nerr);
537 ERROR("Error removing %s:%s", controller, path);
538 }
539 if (existed == -1)
540 INFO("cgroup removal attempt: %s:%s did not exist", controller, path);
541 }
542
543 static void *cgm_init(const char *name)
544 {
545 struct cgm_data *d;
546
547 d = malloc(sizeof(*d));
548 if (!d)
549 return NULL;
550
551 if (!cgm_dbus_connect()) {
552 ERROR("Error connecting to cgroup manager");
553 goto err1;
554 }
555
556 memset(d, 0, sizeof(*d));
557 d->name = strdup(name);
558 if (!d->name) {
559 cgm_dbus_disconnect();
560 goto err1;
561 }
562
563 d->cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
564
565 // cgm_create immediately gets called so keep the connection open
566 return d;
567
568 err1:
569 free(d);
570 return NULL;
571 }
572
573 /* Called after a failed container startup */
574 static void cgm_destroy(void *hdata, struct lxc_conf *conf)
575 {
576 struct cgm_data *d = hdata;
577 char **slist = subsystems;
578 int i;
579
580 if (!d || !d->cgroup_path)
581 return;
582 if (!cgm_dbus_connect()) {
583 ERROR("Error connecting to cgroup manager");
584 return;
585 }
586
587 if (cgm_all_controllers_same)
588 slist = subsystems_inone;
589 for (i = 0; slist[i]; i++)
590 cgm_remove_cgroup(slist[i], d->cgroup_path);
591
592 free(d->name);
593 free(d->cgroup_path);
594 free(d);
595 cgm_dbus_disconnect();
596 }
597
598 /*
599 * remove all the cgroups created
600 * called internally with dbus connection open
601 */
602 static inline void cleanup_cgroups(char *path)
603 {
604 int i;
605 char **slist = subsystems;
606
607 if (cgm_all_controllers_same)
608 slist = subsystems_inone;
609 for (i = 0; slist[i]; i++)
610 cgm_remove_cgroup(slist[i], path);
611 }
612
613 static inline bool cgm_create(void *hdata)
614 {
615 struct cgm_data *d = hdata;
616 char **slist = subsystems;
617 int i, index=0, baselen, ret;
618 int32_t existed;
619 char result[MAXPATHLEN], *tmp, *cgroup_path;
620
621 if (!d)
622 return false;
623 // XXX we should send a hint to the cgmanager that when these
624 // cgroups become empty they should be deleted. Requires a cgmanager
625 // extension
626
627 memset(result, 0, MAXPATHLEN);
628 tmp = lxc_string_replace("%n", d->name, d->cgroup_pattern);
629 if (!tmp)
630 goto bad;
631 if (strlen(tmp) >= MAXPATHLEN) {
632 free(tmp);
633 goto bad;
634 }
635 strcpy(result, tmp);
636 baselen = strlen(result);
637 free(tmp);
638 tmp = result;
639 while (*tmp == '/')
640 tmp++;
641 again:
642 if (index == 100) { // turn this into a warn later
643 ERROR("cgroup error? 100 cgroups with this name already running");
644 goto bad;
645 }
646 if (index) {
647 ret = snprintf(result+baselen, MAXPATHLEN-baselen, "-%d", index);
648 if (ret < 0 || ret >= MAXPATHLEN-baselen)
649 goto bad;
650 }
651 existed = 0;
652
653 if (cgm_all_controllers_same)
654 slist = subsystems_inone;
655
656 for (i = 0; slist[i]; i++) {
657 if (!lxc_cgmanager_create(slist[i], tmp, &existed)) {
658 ERROR("Error creating cgroup %s:%s", slist[i], result);
659 cleanup_cgroups(tmp);
660 goto bad;
661 }
662 if (existed == 1)
663 goto next;
664 }
665 // success
666 cgroup_path = strdup(tmp);
667 if (!cgroup_path) {
668 cleanup_cgroups(tmp);
669 goto bad;
670 }
671 d->cgroup_path = cgroup_path;
672 cgm_dbus_disconnect();
673 return true;
674
675 next:
676 index++;
677 goto again;
678 bad:
679 cgm_dbus_disconnect();
680 return false;
681 }
682
683 /*
684 * Use the cgmanager to move a task into a cgroup for a particular
685 * hierarchy.
686 * All the subsystems in this hierarchy are co-mounted, so we only
687 * need to transition the task into one of the cgroups
688 *
689 * Internal helper, must be called with cgmanager dbus socket open
690 */
691 static bool lxc_cgmanager_enter(pid_t pid, const char *controller,
692 const char *cgroup_path, bool abs)
693 {
694 int ret;
695
696 if (abs)
697 ret = cgmanager_move_pid_abs_sync(NULL, cgroup_manager,
698 controller, cgroup_path, pid);
699 else
700 ret = cgmanager_move_pid_sync(NULL, cgroup_manager,
701 controller, cgroup_path, pid);
702 if (ret != 0) {
703 NihError *nerr;
704 nerr = nih_error_get();
705 WARN("call to cgmanager_move_pid_%ssync failed: %s",
706 abs ? "abs_" : "", nerr->message);
707 nih_free(nerr);
708 return false;
709 }
710 return true;
711 }
712
713 static inline bool cgm_enter(void *hdata, pid_t pid)
714 {
715 struct cgm_data *d = hdata;
716 char **slist = subsystems;
717 bool ret = false;
718 int i;
719
720 if (!d || !d->cgroup_path)
721 return false;
722
723 if (!cgm_dbus_connect()) {
724 ERROR("Error connecting to cgroup manager");
725 return false;
726 }
727
728 if (cgm_all_controllers_same)
729 slist = subsystems_inone;
730
731 for (i = 0; slist[i]; i++) {
732 if (!lxc_cgmanager_enter(pid, slist[i], d->cgroup_path, false))
733 goto out;
734 }
735 ret = true;
736 out:
737 cgm_dbus_disconnect();
738 return ret;
739 }
740
741 static const char *cgm_get_cgroup(void *hdata, const char *subsystem)
742 {
743 struct cgm_data *d = hdata;
744
745 if (!d || !d->cgroup_path)
746 return NULL;
747 return d->cgroup_path;
748 }
749
750 #if HAVE_CGMANAGER_GET_PID_CGROUP_ABS_SYNC
751 static inline bool abs_cgroup_supported(void) {
752 return api_version >= CGM_SUPPORTS_GET_ABS;
753 }
754 #else
755 static inline bool abs_cgroup_supported(void) {
756 return false;
757 }
758 #define cgmanager_get_pid_cgroup_abs_sync(...) -1
759 #endif
760
761 static char *try_get_abs_cgroup(const char *name, const char *lxcpath,
762 const char *controller)
763 {
764 char *cgroup = NULL;
765
766 if (abs_cgroup_supported()) {
767 /* get the container init pid and ask for its abs cgroup */
768 pid_t pid = lxc_cmd_get_init_pid(name, lxcpath);
769 if (pid < 0)
770 return NULL;
771 if (cgmanager_get_pid_cgroup_abs_sync(NULL, cgroup_manager,
772 controller, pid, &cgroup) != 0) {
773 cgroup = NULL;
774 NihError *nerr;
775 nerr = nih_error_get();
776 nih_free(nerr);
777 } else
778 prune_init_scope(cgroup);
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 WARN("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 /*
1123 * return true if inword is in the comma-delimited list cgroup_use
1124 */
1125 static bool in_comma_list(const char *inword, const char *cgroup_use)
1126 {
1127 char *e;
1128 size_t inlen = strlen(inword), len;
1129
1130 do {
1131 e = strchr(cgroup_use, ',');
1132 len = e ? e - cgroup_use : strlen(cgroup_use);
1133 if (len == inlen && strncmp(inword, cgroup_use, len) == 0)
1134 return true;
1135 cgroup_use = e + 1;
1136 } while (e);
1137
1138 return false;
1139 }
1140
1141 /*
1142 * inlist is a comma-delimited list of cgroups; so is checklist. Return
1143 * true if any member of inlist is in checklist.
1144 */
1145 static bool any_in_comma_list(const char *inlist, const char *checklist)
1146 {
1147 char *tmp = alloca(strlen(inlist) + 1), *tok, *saveptr = NULL;
1148
1149 strcpy(tmp, inlist);
1150 for (tok = strtok_r(tmp, ",", &saveptr); tok; tok = strtok_r(NULL, ",", &saveptr)) {
1151 if (in_comma_list(tok, checklist))
1152 return true;
1153 }
1154
1155 return false;
1156 }
1157
1158 static bool in_subsystem_list(const char *c)
1159 {
1160 int i;
1161
1162 for (i = 0; i < nr_subsystems; i++) {
1163 if (strcmp(c, subsystems[i]) == 0)
1164 return true;
1165 }
1166
1167 return false;
1168 }
1169
1170 /*
1171 * If /etc/lxc/lxc.conf specifies lxc.cgroup.use = "freezer,memory",
1172 * then clear out any other subsystems, and make sure that freezer
1173 * and memory are both enabled
1174 */
1175 static bool verify_and_prune(const char *cgroup_use)
1176 {
1177 const char *p;
1178 char *e;
1179 int i, j;
1180
1181 for (p = cgroup_use; p && *p; p = e + 1) {
1182 e = strchr(p, ',');
1183 if (e)
1184 *e = '\0';
1185
1186 if (!in_subsystem_list(p)) {
1187 ERROR("Controller %s required by lxc.cgroup.use but not available\n", p);
1188 return false;
1189 }
1190
1191 if (e)
1192 *e = ',';
1193 if (!e)
1194 break;
1195 }
1196
1197 for (i = 0; i < nr_subsystems;) {
1198 if (in_comma_list(subsystems[i], cgroup_use)) {
1199 i++;
1200 continue;
1201 }
1202 free(subsystems[i]);
1203 for (j = i; j < nr_subsystems-1; j++)
1204 subsystems[j] = subsystems[j+1];
1205 subsystems[nr_subsystems-1] = NULL;
1206 nr_subsystems--;
1207 }
1208
1209 return true;
1210 }
1211
1212 static void drop_subsystem(int which)
1213 {
1214 int i;
1215
1216 if (which < 0 || which >= nr_subsystems) {
1217 ERROR("code error: dropping invalid subsystem index\n");
1218 exit(1);
1219 }
1220
1221 free(subsystems[which]);
1222 /* note - we have nr_subsystems+1 entries, last one a NULL */
1223 for (i = which; i < nr_subsystems; i++)
1224 subsystems[i] = subsystems[i+1];
1225 nr_subsystems -= 1;
1226 }
1227
1228 /*
1229 * Check whether we can create the cgroups we would want
1230 */
1231 static bool subsys_is_writeable(const char *controller, const char *probe)
1232 {
1233 int32_t existed;
1234 bool ret = true;
1235
1236 if ( cgmanager_create_sync(NULL, cgroup_manager, controller,
1237 probe, &existed) != 0) {
1238 NihError *nerr;
1239 nerr = nih_error_get();
1240 ERROR("call to cgmanager_create_sync failed: %s", nerr->message);
1241 nih_free(nerr);
1242 ERROR("Failed to create %s:%s", controller, probe);
1243 ret = false;
1244 }
1245
1246 return ret;
1247 }
1248
1249 static char *get_last_controller_in_list(char *list)
1250 {
1251 char *p;
1252
1253 while ((p = strchr(list, ',')) != NULL)
1254 list = p + 1;
1255
1256 return list;
1257 }
1258
1259 /*
1260 * Make sure that all the controllers are writeable.
1261 * If any are not, then
1262 * - if they are listed in lxc.cgroup.use, refuse to start
1263 * - else if they are crucial subsystems, refuse to start
1264 * - else warn and do not use them
1265 */
1266 static bool verify_final_subsystems(const char *cgroup_use)
1267 {
1268 int i;
1269 bool dropped_any = false;
1270 bool bret = false;
1271 const char *cgroup_pattern;
1272 char tmpnam[50], *probe;
1273
1274 if (!cgm_dbus_connect()) {
1275 ERROR("Error connecting to cgroup manager");
1276 return false;
1277 }
1278
1279 cgroup_pattern = lxc_global_config_value("lxc.cgroup.pattern");
1280 i = snprintf(tmpnam, 50, "lxcprobe-%d", getpid());
1281 if (i < 0 || i >= 50) {
1282 ERROR("Attack - format string modified?");
1283 return false;
1284 }
1285 probe = lxc_string_replace("%n", tmpnam, cgroup_pattern);
1286 if (!probe)
1287 goto out;
1288
1289 i = 0;
1290 while (i < nr_subsystems) {
1291 char *p = get_last_controller_in_list(subsystems[i]);
1292
1293 if (!subsys_is_writeable(p, probe)) {
1294 if (is_crucial_cgroup_subsystem(p)) {
1295 ERROR("Cannot write to crucial subsystem %s\n",
1296 subsystems[i]);
1297 goto out;
1298 }
1299 if (cgroup_use && any_in_comma_list(subsystems[i], cgroup_use)) {
1300 ERROR("Cannot write to subsystem %s which is requested in lxc.cgroup.use\n",
1301 subsystems[i]);
1302 goto out;
1303 }
1304 WARN("Cannot write to subsystem %s, continuing with out it\n",
1305 subsystems[i]);
1306 dropped_any = true;
1307 drop_subsystem(i);
1308 } else {
1309 cgm_remove_cgroup(subsystems[i], probe);
1310 i++;
1311 }
1312 }
1313
1314 if (dropped_any)
1315 cgm_all_controllers_same = false;
1316 bret = true;
1317
1318 out:
1319 free(probe);
1320 cgm_dbus_disconnect();
1321 return bret;
1322 }
1323
1324 static bool collect_subsystems(void)
1325 {
1326 char *line = NULL;
1327 nih_local char **cgm_subsys_list = NULL;
1328 size_t sz = 0;
1329 FILE *f = NULL;
1330
1331 if (subsystems) // already initialized
1332 return true;
1333
1334 subsystems_inone = malloc(2 * sizeof(char *));
1335 if (!subsystems_inone)
1336 return false;
1337 subsystems_inone[0] = "all";
1338 subsystems_inone[1] = NULL;
1339
1340 if (lxc_list_controllers(&cgm_subsys_list)) {
1341 while (cgm_subsys_list[nr_subsystems]) {
1342 char **tmp = NIH_MUST( realloc(subsystems,
1343 (nr_subsystems+2)*sizeof(char *)) );
1344 tmp[nr_subsystems] = NIH_MUST(
1345 strdup(cgm_subsys_list[nr_subsystems++]) );
1346 subsystems = tmp;
1347 }
1348 if (nr_subsystems)
1349 subsystems[nr_subsystems] = NULL;
1350 goto collected;
1351 }
1352
1353 INFO("cgmanager_list_controllers failed, falling back to /proc/self/cgroups");
1354 f = fopen_cloexec("/proc/self/cgroup", "r");
1355 if (!f) {
1356 f = fopen_cloexec("/proc/1/cgroup", "r");
1357 if (!f)
1358 return false;
1359 }
1360 while (getline(&line, &sz, f) != -1) {
1361 /* file format: hierarchy:subsystems:group,
1362 * with multiple subsystems being ,-separated */
1363 char *slist, *end, *p, *saveptr = NULL, **tmp;
1364
1365 if (!line[0])
1366 continue;
1367
1368 slist = strchr(line, ':');
1369 if (!slist)
1370 continue;
1371 slist++;
1372 end = strchr(slist, ':');
1373 if (!end)
1374 continue;
1375 *end = '\0';
1376
1377 for (p = strtok_r(slist, ",", &saveptr);
1378 p;
1379 p = strtok_r(NULL, ",", &saveptr)) {
1380 tmp = realloc(subsystems, (nr_subsystems+2)*sizeof(char *));
1381 if (!tmp)
1382 goto out_free;
1383
1384 subsystems = tmp;
1385 tmp[nr_subsystems] = strdup(p);
1386 tmp[nr_subsystems+1] = NULL;
1387 if (!tmp[nr_subsystems])
1388 goto out_free;
1389 nr_subsystems++;
1390 }
1391 }
1392 fclose(f);
1393 f = NULL;
1394
1395 free(line);
1396 line = NULL;
1397
1398 collected:
1399 if (!nr_subsystems) {
1400 ERROR("No cgroup subsystems found");
1401 return false;
1402 }
1403
1404 /* make sure that cgroup.use can be and is honored */
1405 const char *cgroup_use = lxc_global_config_value("lxc.cgroup.use");
1406 if (!cgroup_use && errno != 0)
1407 goto final_verify;
1408 if (cgroup_use) {
1409 if (!verify_and_prune(cgroup_use)) {
1410 free_subsystems();
1411 return false;
1412 }
1413 subsystems_inone[0] = NIH_MUST( strdup(cgroup_use) );
1414 cgm_all_controllers_same = false;
1415 }
1416
1417 final_verify:
1418 return verify_final_subsystems(cgroup_use);
1419
1420 out_free:
1421 free(line);
1422 if (f)
1423 fclose(f);
1424 free_subsystems();
1425 return false;
1426 }
1427
1428 /*
1429 * called during cgroup.c:cgroup_ops_init(), at startup. No threads.
1430 * We check whether we can talk to cgmanager, escape to root cgroup if
1431 * we are root, then close the connection.
1432 */
1433 struct cgroup_ops *cgm_ops_init(void)
1434 {
1435 check_supports_multiple_controllers(-1);
1436 if (!collect_subsystems())
1437 return NULL;
1438
1439 if (api_version < CGM_SUPPORTS_MULT_CONTROLLERS)
1440 cgm_all_controllers_same = false;
1441
1442 // if root, try to escape to root cgroup
1443 if (geteuid() == 0 && !cgm_escape(NULL)) {
1444 free_subsystems();
1445 return NULL;
1446 }
1447
1448 return &cgmanager_ops;
1449 }
1450
1451 /* unfreeze is called by the command api after killing a container. */
1452 static bool cgm_unfreeze(void *hdata)
1453 {
1454 struct cgm_data *d = hdata;
1455 bool ret = true;
1456
1457 if (!d || !d->cgroup_path)
1458 return false;
1459
1460 if (!cgm_dbus_connect()) {
1461 ERROR("Error connecting to cgroup manager");
1462 return false;
1463 }
1464 if (cgmanager_set_value_sync(NULL, cgroup_manager, "freezer", d->cgroup_path,
1465 "freezer.state", "THAWED") != 0) {
1466 NihError *nerr;
1467 nerr = nih_error_get();
1468 ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
1469 nih_free(nerr);
1470 ERROR("Error unfreezing %s", d->cgroup_path);
1471 ret = false;
1472 }
1473 cgm_dbus_disconnect();
1474 return ret;
1475 }
1476
1477 static bool cgm_setup_limits(void *hdata, struct lxc_list *cgroup_settings, bool do_devices)
1478 {
1479 struct cgm_data *d = hdata;
1480 struct lxc_list *iterator, *sorted_cgroup_settings, *next;
1481 struct lxc_cgroup *cg;
1482 bool ret = false;
1483
1484 if (lxc_list_empty(cgroup_settings))
1485 return true;
1486
1487 if (!d || !d->cgroup_path)
1488 return false;
1489
1490 if (!cgm_dbus_connect()) {
1491 ERROR("Error connecting to cgroup manager");
1492 return false;
1493 }
1494
1495 sorted_cgroup_settings = sort_cgroup_settings(cgroup_settings);
1496 if (!sorted_cgroup_settings) {
1497 return false;
1498 }
1499
1500 lxc_list_for_each(iterator, sorted_cgroup_settings) {
1501 char controller[100], *p;
1502 cg = iterator->elem;
1503 if (do_devices != !strncmp("devices", cg->subsystem, 7))
1504 continue;
1505 if (strlen(cg->subsystem) > 100) // i smell a rat
1506 goto out;
1507 strcpy(controller, cg->subsystem);
1508 p = strchr(controller, '.');
1509 if (p)
1510 *p = '\0';
1511 if (cgmanager_set_value_sync(NULL, cgroup_manager, controller,
1512 d->cgroup_path, cg->subsystem, cg->value) != 0) {
1513 NihError *nerr;
1514 nerr = nih_error_get();
1515 if (do_devices) {
1516 WARN("call to cgmanager_set_value_sync failed: %s", nerr->message);
1517 nih_free(nerr);
1518 WARN("Error setting cgroup %s:%s limit type %s", controller,
1519 d->cgroup_path, cg->subsystem);
1520 continue;
1521 }
1522
1523 ERROR("call to cgmanager_set_value_sync failed: %s", nerr->message);
1524 nih_free(nerr);
1525 ERROR("Error setting cgroup %s:%s limit type %s", controller,
1526 d->cgroup_path, cg->subsystem);
1527 goto out;
1528 }
1529
1530 DEBUG("cgroup '%s' set to '%s'", cg->subsystem, cg->value);
1531 }
1532
1533 ret = true;
1534 INFO("cgroup limits have been setup");
1535 out:
1536 lxc_list_for_each_safe(iterator, sorted_cgroup_settings, next) {
1537 lxc_list_del(iterator);
1538 free(iterator);
1539 }
1540 free(sorted_cgroup_settings);
1541 cgm_dbus_disconnect();
1542 return ret;
1543 }
1544
1545 static bool cgm_chown(void *hdata, struct lxc_conf *conf)
1546 {
1547 struct cgm_data *d = hdata;
1548
1549 if (!d || !d->cgroup_path)
1550 return false;
1551 if (!cgm_dbus_connect()) {
1552 ERROR("Error connecting to cgroup manager");
1553 return false;
1554 }
1555 if (!chown_cgroup(d->cgroup_path, conf))
1556 WARN("Failed to chown %s to container root", d->cgroup_path);
1557 cgm_dbus_disconnect();
1558 return true;
1559 }
1560
1561 /*
1562 * TODO: this should be re-written to use the get_config_item("lxc.id_map")
1563 * cmd api instead of getting the idmap from c->lxc_conf. The reason is
1564 * that the id_maps may be different if the container was started with a
1565 * -f or -s argument.
1566 * The reason I'm punting on that is because we'll need to parse the
1567 * idmap results.
1568 */
1569 static bool cgm_attach(const char *name, const char *lxcpath, pid_t pid)
1570 {
1571 bool pass = true;
1572 char *cgroup = NULL;
1573 char **slist = subsystems;
1574 int i;
1575
1576 if (!cgm_dbus_connect()) {
1577 ERROR("Error connecting to cgroup manager");
1578 return false;
1579 }
1580
1581 for (i = 0; slist[i]; i++) {
1582 cgroup = try_get_abs_cgroup(name, lxcpath, slist[i]);
1583 if (!cgroup) {
1584 ERROR("Failed to get cgroup for controller %s", slist[i]);
1585 cgm_dbus_disconnect();
1586 return false;
1587 }
1588
1589 if (!lxc_cgmanager_enter(pid, slist[i], cgroup, abs_cgroup_supported())) {
1590 pass = false;
1591 break;
1592 }
1593
1594 }
1595 cgm_dbus_disconnect();
1596 if (!pass)
1597 ERROR("Failed to enter group %s", cgroup);
1598
1599 free_abs_cgroup(cgroup);
1600 return pass;
1601 }
1602
1603 static bool cgm_bind_dir(const char *root, const char *dirname)
1604 {
1605 nih_local char *cgpath = NULL;
1606
1607 /* /sys should have been mounted by now */
1608 cgpath = NIH_MUST( nih_strdup(NULL, root) );
1609 NIH_MUST( nih_strcat(&cgpath, NULL, "/sys/fs/cgroup") );
1610
1611 if (!dir_exists(cgpath)) {
1612 ERROR("%s does not exist", cgpath);
1613 return false;
1614 }
1615
1616 /* mount a tmpfs there so we can create subdirs */
1617 if (safe_mount("cgroup", cgpath, "tmpfs", 0, "size=10000,mode=755", root)) {
1618 SYSERROR("Failed to mount tmpfs at %s", cgpath);
1619 return false;
1620 }
1621 NIH_MUST( nih_strcat(&cgpath, NULL, "/cgmanager") );
1622
1623 if (mkdir(cgpath, 0755) < 0) {
1624 SYSERROR("Failed to create %s", cgpath);
1625 return false;
1626 }
1627
1628 if (safe_mount(dirname, cgpath, "none", MS_BIND, 0, root)) {
1629 SYSERROR("Failed to bind mount %s to %s", dirname, cgpath);
1630 return false;
1631 }
1632
1633 return true;
1634 }
1635
1636 /*
1637 * cgm_mount_cgroup:
1638 * If /sys/fs/cgroup/cgmanager.lower/ exists, bind mount that to
1639 * /sys/fs/cgroup/cgmanager/ in the container.
1640 * Otherwise, if /sys/fs/cgroup/cgmanager exists, bind mount that.
1641 * Else do nothing
1642 */
1643 #define CGMANAGER_LOWER_SOCK "/sys/fs/cgroup/cgmanager.lower"
1644 #define CGMANAGER_UPPER_SOCK "/sys/fs/cgroup/cgmanager"
1645 static bool cgm_mount_cgroup(void *hdata, const char *root, int type)
1646 {
1647 if (dir_exists(CGMANAGER_LOWER_SOCK))
1648 return cgm_bind_dir(root, CGMANAGER_LOWER_SOCK);
1649 if (dir_exists(CGMANAGER_UPPER_SOCK))
1650 return cgm_bind_dir(root, CGMANAGER_UPPER_SOCK);
1651 // Host doesn't have cgmanager running? Then how did we get here?
1652 return false;
1653 }
1654
1655 static struct cgroup_ops cgmanager_ops = {
1656 .init = cgm_init,
1657 .destroy = cgm_destroy,
1658 .create = cgm_create,
1659 .enter = cgm_enter,
1660 .create_legacy = NULL,
1661 .get_cgroup = cgm_get_cgroup,
1662 .escape = cgm_escape,
1663 .num_hierarchies = cgm_num_hierarchies,
1664 .get_hierarchies = cgm_get_hierarchies,
1665 .get = cgm_get,
1666 .set = cgm_set,
1667 .unfreeze = cgm_unfreeze,
1668 .setup_limits = cgm_setup_limits,
1669 .name = "cgmanager",
1670 .chown = cgm_chown,
1671 .attach = cgm_attach,
1672 .mount_cgroup = cgm_mount_cgroup,
1673 .nrtasks = cgm_get_nrtasks,
1674 .disconnect = NULL,
1675 .driver = CGMANAGER,
1676 };
1677 #endif