]>
git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/cgmanager.c
2 * lxc: linux Container library
4 * (C) Copyright IBM Corp. 2007, 2008
7 * Daniel Lezcano <daniel.lezcano at free.fr>
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.
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.
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
33 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/inotify.h>
37 #include <sys/mount.h>
38 #include <netinet/in.h>
54 lxc_log_define(lxc_cgmanager
, lxc
);
56 #include <nih-dbus/dbus_connection.h>
57 #include <cgmanager-client/cgmanager-client.h>
58 #include <nih/alloc.h>
59 NihDBusProxy
*cgroup_manager
= NULL
;
61 extern struct cgroup_ops
*active_cg_ops
;
62 bool cgmanager_initialized
= false;
63 bool use_cgmanager
= true;
64 static struct cgroup_ops cgmanager_ops
;
66 bool lxc_init_cgmanager(void);
67 static void cgmanager_disconnected(DBusConnection
*connection
)
69 WARN("Cgroup manager connection was terminated");
70 cgroup_manager
= NULL
;
71 cgmanager_initialized
= false;
72 if (lxc_init_cgmanager()) {
73 cgmanager_initialized
= true;
74 INFO("New cgroup manager connection was opened");
78 static int send_creds(int sock
, int rpid
, int ruid
, int rgid
)
80 struct msghdr msg
= { 0 };
88 char cmsgbuf
[CMSG_SPACE(sizeof(cred
))];
92 msg
.msg_control
= cmsgbuf
;
93 msg
.msg_controllen
= sizeof(cmsgbuf
);
95 cmsg
= CMSG_FIRSTHDR(&msg
);
96 cmsg
->cmsg_len
= CMSG_LEN(sizeof(struct ucred
));
97 cmsg
->cmsg_level
= SOL_SOCKET
;
98 cmsg
->cmsg_type
= SCM_CREDENTIALS
;
99 memcpy(CMSG_DATA(cmsg
), &cred
, sizeof(cred
));
105 iov
.iov_len
= sizeof(buf
);
109 if (sendmsg(sock
, &msg
, 0) < 0) {
116 #define CGMANAGER_DBUS_SOCK "unix:path=/sys/fs/cgroup/cgmanager/sock"
117 bool lxc_init_cgmanager(void)
119 DBusError dbus_error
;
120 DBusConnection
*connection
;
121 dbus_error_init(&dbus_error
);
123 connection
= nih_dbus_connect(CGMANAGER_DBUS_SOCK
, cgmanager_disconnected
);
125 ERROR("Error opening cgmanager connection at %s", CGMANAGER_DBUS_SOCK
);
128 dbus_connection_set_exit_on_disconnect(connection
, FALSE
);
129 dbus_error_free(&dbus_error
);
130 cgroup_manager
= nih_dbus_proxy_new(NULL
, connection
,
132 "/org/linuxcontainers/cgmanager", NULL
, NULL
);
133 dbus_connection_unref(connection
);
134 if (!cgroup_manager
) {
137 active_cg_ops
= &cgmanager_ops
;
142 * Use the cgmanager to move a task into a cgroup for a particular
144 * All the subsystems in this hierarchy are co-mounted, so we only
145 * need to transition the task into one of the cgroups
147 static bool lxc_cgmanager_enter(pid_t pid
, char *controller
, char *cgroup_path
)
149 return cgmanager_move_pid_sync(NULL
, cgroup_manager
, controller
,
150 cgroup_path
, pid
) == 0;
153 static bool lxc_cgmanager_create(const char *controller
, const char *cgroup_path
, int32_t *existed
)
155 if ( cgmanager_create_sync(NULL
, cgroup_manager
, controller
,
156 cgroup_path
, existed
) != 0) {
157 ERROR("Failed to create %s:%s", controller
, cgroup_path
);
165 const char *controller
;
166 const char *cgroup_path
;
169 static int do_chown_cgroup(const char *controller
, const char *cgroup_path
)
171 int sv
[2] = {-1, -1}, optval
= 1;
175 WARN("Failed to setgid to 0");
177 WARN("Failed to setuid to 0");
179 if (socketpair(AF_UNIX
, SOCK_DGRAM
, 0, sv
) < 0) {
180 SYSERROR("Error creating socketpair");
183 if (setsockopt(sv
[1], SOL_SOCKET
, SO_PASSCRED
, &optval
, sizeof(optval
)) == -1) {
184 SYSERROR("setsockopt failed");
187 if (setsockopt(sv
[0], SOL_SOCKET
, SO_PASSCRED
, &optval
, sizeof(optval
)) == -1) {
188 SYSERROR("setsockopt failed");
191 if ( cgmanager_chown_scm_sync(NULL
, cgroup_manager
, controller
,
192 cgroup_path
, sv
[1]) != 0) {
193 ERROR("call to cgmanager_chown_scm_sync failed");
196 /* now send credentials */
200 FD_SET(sv
[0], &rfds
);
201 if (select(sv
[0]+1, &rfds
, NULL
, NULL
, NULL
) < 0) {
202 ERROR("Error getting go-ahead from server: %s", strerror(errno
));
205 if (read(sv
[0], &buf
, 1) != 1) {
206 ERROR("Error getting reply from server over socketpair");
209 if (send_creds(sv
[0], getpid(), getuid(), getgid())) {
210 ERROR("Error sending pid over SCM_CREDENTIAL");
214 FD_SET(sv
[0], &rfds
);
215 if (select(sv
[0]+1, &rfds
, NULL
, NULL
, NULL
) < 0) {
216 ERROR("Error getting go-ahead from server: %s", strerror(errno
));
219 if (read(sv
[0], &buf
, 1) != 1) {
220 ERROR("Error getting reply from server over socketpair");
223 if (send_creds(sv
[0], getpid(), 0, 0)) {
224 ERROR("Error sending pid over SCM_CREDENTIAL");
228 FD_SET(sv
[0], &rfds
);
229 if (select(sv
[0]+1, &rfds
, NULL
, NULL
, NULL
) < 0) {
230 ERROR("Error getting go-ahead from server: %s", strerror(errno
));
233 int ret
= read(sv
[0], buf
, 1);
236 if (ret
== 1 && *buf
== '1')
241 static int chown_cgroup_wrapper(void *data
)
243 struct chown_data
*arg
= data
;
244 return do_chown_cgroup(arg
->controller
, arg
->cgroup_path
);
247 static bool chown_cgroup(const char *controller
, const char *cgroup_path
,
248 struct lxc_conf
*conf
)
251 struct chown_data data
;
252 data
.controller
= controller
;
253 data
.cgroup_path
= cgroup_path
;
255 if (lxc_list_empty(&conf
->id_map
)) {
256 if (do_chown_cgroup(controller
, cgroup_path
) < 0)
261 if ((pid
= fork()) < 0) {
266 if (wait_for_pid(pid
)) {
267 ERROR("Error chowning cgroup");
272 if (userns_exec_1(conf
, chown_cgroup_wrapper
, &data
) < 0)
283 #define CG_REMOVE_RECURSIVE 1
284 void cgmanager_remove_cgroup(const char *controller
, const char *path
)
287 if ( cgmanager_remove_sync(NULL
, cgroup_manager
, controller
,
288 path
, CG_REMOVE_RECURSIVE
, &existed
) != 0)
289 ERROR("Error removing %s:%s", controller
, path
);
291 INFO("cgroup removal attempt: %s:%s did not exist", controller
, path
);
294 static void cgm_destroy(struct lxc_handler
*handler
)
296 struct cgm_data
*d
= handler
->cgroup_info
->data
;
301 for (i
=0; i
<d
->nr_subsystems
; i
++) {
303 cgmanager_remove_cgroup(d
->subsystems
[i
], d
->cgroup_path
);
304 free(d
->subsystems
[i
]);
307 free(d
->cgroup_path
);
309 handler
->cgroup_info
->data
= NULL
;
313 * remove all the cgroups created
315 static inline void cleanup_cgroups(struct cgm_data
*d
, char *path
)
318 for (i
= 0; i
< d
->nr_subsystems
; i
++) {
319 cgmanager_remove_cgroup(d
->subsystems
[i
], path
);
323 static inline bool cgm_create(struct lxc_handler
*handler
)
325 int i
, index
=0, baselen
, ret
;
327 char result
[MAXPATHLEN
], *tmp
;
328 struct cgm_data
*d
= handler
->cgroup_info
->data
;
330 // XXX we should send a hint to the cgmanager that when these
331 // cgroups become empty they should be deleted. Requires a cgmanager
334 memset(result
, 0, MAXPATHLEN
);
335 tmp
= lxc_string_replace("%n", handler
->name
, handler
->cgroup_info
->cgroup_pattern
);
338 if (strlen(tmp
) > MAXPATHLEN
)
341 baselen
= strlen(result
);
347 if (index
== 100) { // turn this into a warn later
348 ERROR("cgroup error? 100 cgroups with this name already running");
352 ret
= snprintf(result
+baselen
, MAXPATHLEN
-baselen
, "-%d", index
);
353 if (ret
< 0 || ret
>= MAXPATHLEN
-baselen
)
357 for (i
= 0; i
< d
->nr_subsystems
; i
++) {
358 if (!lxc_cgmanager_create(d
->subsystems
[i
], tmp
, &existed
)) {
359 ERROR("Error creating cgroup %s:%s", d
->subsystems
[i
], result
);
360 cleanup_cgroups(d
, tmp
);
367 d
->cgroup_path
= strdup(tmp
);
368 if (!d
->cgroup_path
) {
369 cleanup_cgroups(d
, tmp
);
374 cleanup_cgroups(d
, tmp
);
379 static inline bool cgm_enter(struct lxc_handler
*handler
)
381 struct cgm_data
*d
= handler
->cgroup_info
->data
;
384 for (i
= 0; i
< d
->nr_subsystems
; i
++) {
385 if (!lxc_cgmanager_enter(handler
->pid
, d
->subsystems
[i
], d
->cgroup_path
))
391 static char *cgm_get_cgroup(struct lxc_handler
*handler
, const char *subsystem
)
393 struct cgm_data
*d
= handler
->cgroup_info
->data
;
394 return d
->cgroup_path
;
397 int cgm_get(const char *filename
, char *value
, size_t len
, const char *name
, const char *lxcpath
)
399 char *result
, *controller
, *key
, *cgroup
;
402 controller
= alloca(strlen(filename
)+1);
403 strcpy(controller
, filename
);
404 key
= strchr(controller
, '.');
409 /* use the command interface to look for the cgroup */
410 cgroup
= lxc_cmd_get_cgroup_path(name
, lxcpath
, controller
);
413 if (cgmanager_get_value_sync(NULL
, cgroup_manager
, controller
, cgroup
, filename
, &result
) != 0) {
414 ERROR("Error getting value for %s from cgmanager for cgroup %s (%s:%s)",
415 filename
, cgroup
, lxcpath
, name
);
420 newlen
= strlen(result
);
422 // user queries the size
427 strncpy(value
, result
, len
);
431 } else if (newlen
+1 < len
) {
432 // cgmanager doesn't add eol to last entry
433 value
[newlen
++] = '\n';
434 value
[newlen
] = '\0';
440 static int cgm_do_set(const char *controller
, const char *file
,
441 const char *cgroup
, const char *value
)
444 ret
= cgmanager_set_value_sync(NULL
, cgroup_manager
, controller
,
445 cgroup
, file
, value
);
447 ERROR("Error setting cgroup %s limit %s", file
, cgroup
);
451 int cgm_set(const char *filename
, const char *value
, const char *name
, const char *lxcpath
)
453 char *controller
, *key
, *cgroup
;
456 controller
= alloca(strlen(filename
)+1);
457 strcpy(controller
, filename
);
458 key
= strchr(controller
, '.');
463 /* use the command interface to look for the cgroup */
464 cgroup
= lxc_cmd_get_cgroup_path(name
, lxcpath
, controller
);
466 ERROR("Failed to get cgroup for controller %s for %s:%s",
467 controller
, lxcpath
, name
);
470 ret
= cgm_do_set(controller
, filename
, cgroup
, value
);
476 * TODO really this should be done once for global data, not once
479 static inline bool cgm_init(struct lxc_handler
*handler
)
481 struct cgm_data
*d
= malloc(sizeof(*d
));
482 char *line
= NULL
, *tab1
;
488 d
->nr_subsystems
= 0;
489 d
->subsystems
= NULL
;
490 f
= fopen_cloexec("/proc/cgroups", "r");
495 while (getline(&line
, &sz
, f
) != -1) {
501 tab1
= strchr(line
, '\t');
505 tmp
= realloc(d
->subsystems
, (d
->nr_subsystems
+1)*sizeof(char *));
510 d
->subsystems
[d
->nr_subsystems
] = strdup(line
);
511 if (!d
->subsystems
[d
->nr_subsystems
])
517 d
->cgroup_path
= NULL
;
518 handler
->cgroup_info
->data
= d
;
522 for (i
=0; i
<d
->nr_subsystems
; i
++)
523 free(d
->subsystems
[i
]);
529 static int cgm_unfreeze_fromhandler(struct lxc_handler
*handler
)
531 struct cgm_data
*d
= handler
->cgroup_info
->data
;
533 if (cgmanager_set_value_sync(NULL
, cgroup_manager
, "freezer", d
->cgroup_path
,
534 "freezer.state", "THAWED") != 0) {
535 ERROR("Error unfreezing %s", d
->cgroup_path
);
541 static bool setup_limits(struct lxc_handler
*h
, bool do_devices
)
543 struct lxc_list
*iterator
;
544 struct lxc_cgroup
*cg
;
546 struct lxc_list
*cgroup_settings
= &h
->conf
->cgroup
;
547 struct cgm_data
*d
= h
->cgroup_info
->data
;
549 if (lxc_list_empty(cgroup_settings
))
552 lxc_list_for_each(iterator
, cgroup_settings
) {
553 char controller
[100], *p
;
555 if (do_devices
!= !strncmp("devices", cg
->subsystem
, 7))
557 if (strlen(cg
->subsystem
) > 100) // i smell a rat
559 strcpy(controller
, cg
->subsystem
);
560 p
= strchr(controller
, '.');
563 if (cgm_do_set(controller
, cg
->subsystem
, d
->cgroup_path
565 ERROR("Error setting %s to %s for %s\n",
566 cg
->subsystem
, cg
->value
, h
->name
);
570 DEBUG("cgroup '%s' set to '%s'", cg
->subsystem
, cg
->value
);
574 INFO("cgroup limits have been setup");
579 static bool cgm_setup_limits(struct lxc_handler
*handler
, bool with_devices
)
581 return setup_limits(handler
, with_devices
);
584 static bool cgm_chown(struct lxc_handler
*handler
)
586 struct cgm_data
*d
= handler
->cgroup_info
->data
;
589 for (i
= 0; i
< d
->nr_subsystems
; i
++) {
590 if (!chown_cgroup(d
->subsystems
[i
], d
->cgroup_path
, handler
->conf
))
591 WARN("Failed to chown %s:%s to container root",
592 d
->subsystems
[i
], d
->cgroup_path
);
597 static struct cgroup_ops cgmanager_ops
= {
598 .destroy
= cgm_destroy
,
600 .create
= cgm_create
,
602 .create_legacy
= NULL
,
603 .get_cgroup
= cgm_get_cgroup
,
606 .unfreeze_fromhandler
= cgm_unfreeze_fromhandler
,
607 .setup_limits
= cgm_setup_limits
,