2 * Copyright © 2015 Canonical Limited
5 * Serge Hallyn <serge.hallyn@ubuntu.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * 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>
39 #include <netinet/in.h>
42 #include "cgmanager.h"
48 #define CGM_DBUS_ADDRESS "unix:path=/sys/fs/cgroup/cgmanager/sock"
49 #define CGM_REQUIRED_VERSION 9 // we need list_keys
51 static GDBusConnection
*cgroup_manager
= NULL
;
53 static pthread_mutex_t cgm_mutex
= PTHREAD_MUTEX_INITIALIZER
;
55 static void lock_mutex(pthread_mutex_t
*l
)
58 if ((ret
= pthread_mutex_lock(l
)) != 0) {
59 fprintf(stderr
, "pthread_mutex_lock returned:%d %s\n", ret
, strerror(ret
));
64 static void unlock_mutex(pthread_mutex_t
*l
)
67 if ((ret
= pthread_mutex_unlock(l
)) != 0) {
68 fprintf(stderr
, "pthread_mutex_unlock returned:%d %s\n", ret
, strerror(ret
));
75 lock_mutex(&cgm_mutex
);
80 unlock_mutex(&cgm_mutex
);
83 /* only called on exit, no need to lock */
84 void cgm_dbus_disconnect(void)
91 if (!g_dbus_connection_flush_sync(cgroup_manager
, NULL
, &error
)) {
92 g_warning("failed to flush connection: %s."
93 "Use G_DBUS_DEBUG=message for more info.", error
->message
);
96 if (!g_dbus_connection_close_sync(cgroup_manager
, NULL
, &error
)) {
97 g_warning("failed to close connection: %s."
98 "Use G_DBUS_DEBUG=message for more info.", error
->message
);
101 g_object_unref(cgroup_manager
);
102 cgroup_manager
= NULL
;
105 bool cgm_dbus_connect(void)
107 GDBusConnection
*connection
;
110 GError
*error
= NULL
;
112 // fastpath - don't lock if we have the manager
113 if (cgroup_manager
&& !g_dbus_connection_is_closed(cgroup_manager
))
118 // TODO - do we want to add some limit to nretries?
120 if (cgroup_manager
) {
121 if (!g_dbus_connection_is_closed(cgroup_manager
)) {
122 // someone else reconnected us
126 fprintf(stderr
, "cgmanager connection was closed\n");
127 g_object_unref(cgroup_manager
);
128 cgroup_manager
= NULL
;
131 connection
= g_dbus_connection_new_for_address_sync (CGM_DBUS_ADDRESS
,
132 G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT
,
135 g_warning("Could not connect to cgmanager: %s\n"
136 "Use G_DBUS_DEBUG=message for more info.", error
->message
);
139 fprintf(stderr
, "Retrying...\n");
144 reply
= g_dbus_connection_call_sync (connection
, NULL
, "/org/linuxcontainers/cgmanager",
145 "org.freedesktop.DBus.Properties", "Get",
146 g_variant_new ("(ss)", "org.linuxcontainers.cgmanager0_0", "api_version"),
147 G_VARIANT_TYPE ("(v)"), G_DBUS_CALL_FLAGS_NONE
, -1, NULL
, &error
);
150 g_warning("Failed to get cgmanager api version: %s\n"
151 "Use G_DBUS_DEBUG=message for more info.", error
->message
);
153 g_object_unref (connection
);
157 g_variant_get (reply
, "(v)", &version
);
158 g_variant_unref (reply
);
159 if (!g_variant_is_of_type (version
, G_VARIANT_TYPE_INT32
) || g_variant_get_int32 (version
) < CGM_REQUIRED_VERSION
)
161 g_warning("Cgmanager does not meet minimal API version");
162 g_object_unref (connection
);
163 g_variant_unref (version
);
166 g_variant_unref (version
);
167 cgroup_manager
= connection
;
173 static bool cgcall(const gchar
*method_name
, GVariant
*parameters
,
174 const GVariantType
*reply_type
, GVariant
**reply
)
176 GVariant
*my_reply
= NULL
;
177 GError
*error
= NULL
;
179 if (!cgm_dbus_connect()) {
180 g_warning("Error: unable to connect to cgmanager");
186 /* We do this sync because we need to ensure that the calls finish
187 * before we return to _our_ caller saying that this is done.
189 *reply
= g_dbus_connection_call_sync (cgroup_manager
, NULL
, "/org/linuxcontainers/cgmanager",
190 "org.linuxcontainers.cgmanager0_0", method_name
,
191 parameters
, reply_type
, G_DBUS_CALL_FLAGS_NONE
,
196 g_warning ("cgmanager method call org.linuxcontainers.cgmanager0_0.%s failed: %s. "
197 "Use G_DBUS_DEBUG=message for more info.", method_name
, error
->message
);
198 g_error_free (error
);
202 g_variant_unref (my_reply
);
206 // todo - can we avoid some of this alloc/copy/free when copying
209 #define MAX_CONTROLLERS 20
210 bool cgm_get_controllers(char ***contrls
)
213 GVariantIter
*iter
= NULL
;
214 GVariant
*reply
= NULL
;
218 if (!cgcall("ListControllers", NULL
, G_VARIANT_TYPE("(as)"), &reply
))
222 list
= malloc(MAX_CONTROLLERS
* sizeof(*list
));
224 memset(list
, 0, MAX_CONTROLLERS
* sizeof(*list
));
225 g_variant_get(reply
, "(as)", &iter
);
226 while (g_variant_iter_next(iter
, "s", &ctrl
)) {
227 if (i
>= MAX_CONTROLLERS
) {
228 g_warning("Too many cgroup subsystems");
232 list
[i
] = strdup(ctrl
);
237 g_variant_iter_free(iter
);
238 g_variant_unref(reply
);
244 void free_key(struct cgm_keys
*k
)
252 void free_keys(struct cgm_keys
**keys
)
258 for (i
= 0; keys
[i
]; i
++) {
264 #define BATCH_SIZE 50
265 void append_key(struct cgm_keys
***keys
, struct cgm_keys
*newk
, size_t *sz
, size_t *asz
)
272 *keys
= malloc(*asz
* sizeof(struct cgm_keys
*));
278 if (*sz
+ 2 >= *asz
) {
279 struct cgm_keys
**tmp
;
282 tmp
= realloc(*keys
, *asz
* sizeof(struct cgm_keys
*));
286 (*keys
)[(*sz
)++] = newk
;
287 (*keys
)[(*sz
)] = NULL
;
290 bool cgm_list_keys(const char *controller
, const char *cgroup
, struct cgm_keys
***keys
)
292 GVariantIter
*iter
= NULL
;
293 GVariant
*reply
= NULL
;
294 size_t sz
= 0, asz
= 0;
296 guint32 uid
, gid
, mode
;
298 if (!cgcall("ListKeys", g_variant_new("(ss)", controller
, cgroup
),
299 G_VARIANT_TYPE("(a(suuu))"), &reply
))
302 g_variant_get(reply
, "(a(suuu))", &iter
);
303 while (g_variant_iter_next(iter
, "(suuu)", &name
, &uid
, &gid
, &mode
)) {
304 /* name, owner, groupid, mode) */
308 k
= malloc(sizeof(*k
));
311 k
->name
= strdup(name
);
317 append_key(keys
, k
, &sz
, &asz
);
320 g_variant_iter_free(iter
);
321 g_variant_unref(reply
);
326 bool cgm_list_children(const char *controller
, const char *cgroup
, char ***list
)
328 GVariantIter
*iter
= NULL
;
329 GVariant
*reply
= NULL
;
331 size_t sz
= 0, asz
= 0;
333 if (!cgcall("ListChildren", g_variant_new("(ss)", controller
, cgroup
),
334 G_VARIANT_TYPE("(as)"), &reply
))
337 g_variant_get(reply
, "(as)", &iter
);
339 *list
= malloc(BATCH_SIZE
* sizeof(char *));
342 while (g_variant_iter_next(iter
, "s", &child
)) {
347 tmp
= realloc(*list
, asz
* sizeof(char *));
352 (*list
)[sz
] = strdup(child
);
353 } while (!(*list
)[sz
]);
354 (*list
)[sz
+1] = NULL
;
359 g_variant_iter_free(iter
);
360 g_variant_unref(reply
);
365 bool cgm_escape_cgroup(void)
367 return cgcall("MovePidAbs", g_variant_new("(ssi)", "all", "/", getpid()),
368 G_VARIANT_TYPE_UNIT
, NULL
);
371 bool cgm_move_pid(const char *controller
, const char *cgroup
, pid_t pid
)
373 return cgcall("MovePid", g_variant_new("(ssi)", controller
, cgroup
, pid
),
374 G_VARIANT_TYPE_UNIT
, NULL
);
377 bool cgm_get_value(const char *controller
, const char *cgroup
, const char *file
,
380 GVariant
*reply
= NULL
;
383 if (!cgcall("GetValue", g_variant_new("(sss)", controller
, cgroup
, file
),
384 G_VARIANT_TYPE("(s)"), &reply
))
387 g_variant_get(reply
, "(s)", &str
);
388 g_variant_unref(reply
);
391 *value
= strdup(str
);
398 bool cgm_set_value(const char *controller
, const char *cgroup
, const char *file
,
401 return cgcall("SetValue", g_variant_new("(ssss)", controller
, cgroup
, file
, value
),
402 G_VARIANT_TYPE_UNIT
, NULL
);
405 bool cgm_create(const char *controller
, const char *cg
)
407 if (!cgcall("Create", g_variant_new("(ss)", controller
, cg
),
408 G_VARIANT_TYPE ("(i)"), NULL
))
414 bool cgm_chown_file(const char *controller
, const char *cg
, uid_t uid
, gid_t gid
)
416 return cgcall("Chown", g_variant_new("(ssii)", controller
, cg
, uid
, gid
),
417 G_VARIANT_TYPE_UNIT
, NULL
);
420 bool cgm_chmod_file(const char *controller
, const char *file
, mode_t mode
)
422 return cgcall("Chmod", g_variant_new("(sssi)", controller
, file
, "", mode
), G_VARIANT_TYPE_UNIT
, NULL
);
425 bool cgm_remove(const char *controller
, const char *cg
)
427 return cgcall("Remove", g_variant_new ("(ssi)", "all", cg
, 1), G_VARIANT_TYPE ("(i)"), NULL
);