]> git.proxmox.com Git - mirror_lxcfs.git/blame - cgmanager.c
remove stale comments, update some
[mirror_lxcfs.git] / cgmanager.c
CommitLineData
2183082c 1/*
2c51f8dd 2 * Copyright © 2015 Canonical Limited
2183082c
SH
3 *
4 * Authors:
2c51f8dd 5 * Serge Hallyn <serge.hallyn@ubuntu.com>
2183082c
SH
6 *
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.
11 *
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.
16 *
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
20 */
21#include "config.h"
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <errno.h>
26#include <unistd.h>
27#include <string.h>
28#include <dirent.h>
29#include <fcntl.h>
30#include <ctype.h>
31#include <pthread.h>
32#include <grp.h>
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <sys/param.h>
36#include <sys/inotify.h>
37#include <sys/mount.h>
ab54b798 38#include <sys/wait.h>
2183082c
SH
39#include <netinet/in.h>
40#include <net/if.h>
41#include <stdbool.h>
2c51f8dd
SH
42#include "cgmanager.h"
43#include <assert.h>
2183082c 44
2c51f8dd
SH
45#include <glib.h>
46#include <gio/gio.h>
2183082c 47
2c51f8dd
SH
48#define CGM_DBUS_ADDRESS "unix:path=/sys/fs/cgroup/cgmanager/sock"
49#define CGM_REQUIRED_VERSION 9 // we need list_keys
50
51static GDBusConnection *cgroup_manager = NULL;
2183082c 52
2c51f8dd 53static pthread_mutex_t cgm_mutex = PTHREAD_MUTEX_INITIALIZER;
2183082c 54
2c51f8dd 55static void lock_mutex(pthread_mutex_t *l)
2183082c 56{
2c51f8dd
SH
57 int ret;
58 if ((ret = pthread_mutex_lock(l)) != 0) {
59 fprintf(stderr, "pthread_mutex_lock returned:%d %s\n", ret, strerror(ret));
60 exit(1);
61 }
2183082c
SH
62}
63
2c51f8dd 64static void unlock_mutex(pthread_mutex_t *l)
2183082c 65{
2c51f8dd
SH
66 int ret;
67 if ((ret = pthread_mutex_unlock(l)) != 0) {
68 fprintf(stderr, "pthread_mutex_unlock returned:%d %s\n", ret, strerror(ret));
69 exit(1);
70 }
71}
2183082c 72
2c51f8dd
SH
73void cgm_lock(void)
74{
75 lock_mutex(&cgm_mutex);
76}
2183082c 77
2c51f8dd
SH
78void cgm_unlock(void)
79{
80 unlock_mutex(&cgm_mutex);
81}
2183082c 82
2c51f8dd
SH
83/* only called on exit, no need to lock */
84void cgm_dbus_disconnect(void)
85{
86 GError *error = NULL;
87
88 if (!cgroup_manager)
89 return;
90
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);
94 g_error_free(error);
2183082c 95 }
2c51f8dd
SH
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);
99 g_error_free(error);
100 }
101 g_object_unref(cgroup_manager);
102 cgroup_manager = NULL;
2183082c
SH
103}
104
2c51f8dd 105bool cgm_dbus_connect(void)
2183082c 106{
2c51f8dd
SH
107 GDBusConnection *connection;
108 GVariant *reply;
109 GVariant *version;
110 GError *error = NULL;
111
112 // fastpath - don't lock if we have the manager
113 if (cgroup_manager && !g_dbus_connection_is_closed(cgroup_manager))
114 return true;
115
116 cgm_lock();
117
118 // TODO - do we want to add some limit to nretries?
119retry:
120 if (cgroup_manager) {
121 if (!g_dbus_connection_is_closed(cgroup_manager)) {
40b8c791 122 // someone else reconnected us
2c51f8dd
SH
123 cgm_unlock();
124 return true;
125 }
126 fprintf(stderr, "cgmanager connection was closed\n");
127 g_object_unref(cgroup_manager);
128 cgroup_manager = NULL;
2183082c
SH
129 }
130
2c51f8dd
SH
131 connection = g_dbus_connection_new_for_address_sync (CGM_DBUS_ADDRESS,
132 G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
133 NULL, NULL, &error);
134 if (!connection) {
135 g_warning("Could not connect to cgmanager: %s\n"
136 "Use G_DBUS_DEBUG=message for more info.", error->message);
137 g_error_free(error);
138 error = NULL;
139 fprintf(stderr, "Retrying...\n");
140 sleep(1);
141 goto retry;
2183082c
SH
142 }
143
2c51f8dd
SH
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);
148 if (!reply)
149 {
150 g_warning("Failed to get cgmanager api version: %s\n"
151 "Use G_DBUS_DEBUG=message for more info.", error->message);
152 g_error_free(error);
153 g_object_unref (connection);
154 cgm_unlock();
155 return false;
156 }
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)
160 {
161 g_warning("Cgmanager does not meet minimal API version");
162 g_object_unref (connection);
163 g_variant_unref (version);
164 cgm_unlock();
165 return false; }
166 g_variant_unref (version);
167 cgroup_manager = connection;
168
169 cgm_unlock();
2183082c
SH
170 return true;
171}
172
2c51f8dd
SH
173static bool cgcall(const gchar *method_name, GVariant *parameters,
174 const GVariantType *reply_type, GVariant **reply)
2183082c 175{
2c51f8dd
SH
176 GVariant *my_reply = NULL;
177 GError *error = NULL;
178
2183082c 179 if (!cgm_dbus_connect()) {
2c51f8dd 180 g_warning("Error: unable to connect to cgmanager");
2183082c
SH
181 return false;
182 }
183
2c51f8dd
SH
184 if (!reply)
185 reply = &my_reply;
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.
188 */
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,
192 -1, NULL, &error);
193 if (!*reply)
194 {
195 if (reply_type)
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);
2183082c
SH
199 return false;
200 }
2c51f8dd
SH
201 if (my_reply)
202 g_variant_unref (my_reply);
2183082c
SH
203 return true;
204}
205
2c51f8dd
SH
206// todo - can we avoid some of this alloc/copy/free when copying
207// from iters?
208
209#define MAX_CONTROLLERS 20
210bool cgm_get_controllers(char ***contrls)
2183082c 211{
2c51f8dd
SH
212 char **list = NULL;
213 GVariantIter *iter = NULL;
214 GVariant *reply = NULL;
215 gchar *ctrl;
216 int i = 0;
2183082c 217
2c51f8dd 218 if (!cgcall("ListControllers", NULL, G_VARIANT_TYPE("(as)"), &reply))
2183082c 219 return false;
2c51f8dd
SH
220
221 do {
222 list = malloc(MAX_CONTROLLERS * sizeof(*list));
223 } while (!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");
229 exit(1);
230 }
231 do {
232 list[i] = strdup(ctrl);
233 } while (!list[i]);
234 i++;
235 g_free(ctrl);
2183082c 236 }
2c51f8dd
SH
237 g_variant_iter_free(iter);
238 g_variant_unref(reply);
2183082c 239
2c51f8dd 240 *contrls = list;
2183082c
SH
241 return true;
242}
243
2c51f8dd 244void free_key(struct cgm_keys *k)
2183082c 245{
2c51f8dd
SH
246 if (!k)
247 return;
248 free(k->name);
249 free(k);
2183082c
SH
250}
251
2c51f8dd 252void free_keys(struct cgm_keys **keys)
2183082c 253{
2c51f8dd 254 int i;
2183082c 255
2c51f8dd
SH
256 if (!keys)
257 return;
258 for (i = 0; keys[i]; i++) {
259 free_key(keys[i]);
2183082c 260 }
2c51f8dd 261 free(keys);
2183082c 262}
99978832 263
2c51f8dd
SH
264#define BATCH_SIZE 50
265void append_key(struct cgm_keys ***keys, struct cgm_keys *newk, size_t *sz, size_t *asz)
4775fba1 266{
2c51f8dd
SH
267 assert(keys);
268 if (sz == 0) {
269 *asz = BATCH_SIZE;
270 *sz = 1;
271 do {
272 *keys = malloc(*asz * sizeof(struct cgm_keys *));
273 } while (!*keys);
274 (*keys)[0] = newk;
275 (*keys)[1] = NULL;
276 return;
4775fba1 277 }
2c51f8dd
SH
278 if (*sz + 2 >= *asz) {
279 struct cgm_keys **tmp;
280 *asz += BATCH_SIZE;
281 do {
282 tmp = realloc(*keys, *asz * sizeof(struct cgm_keys *));
283 } while (!tmp);
284 *keys = tmp;
4775fba1 285 }
2c51f8dd
SH
286 (*keys)[(*sz)++] = newk;
287 (*keys)[(*sz)] = NULL;
4775fba1
SH
288}
289
2c51f8dd 290bool cgm_list_keys(const char *controller, const char *cgroup, struct cgm_keys ***keys)
99978832 291{
2c51f8dd
SH
292 GVariantIter *iter = NULL;
293 GVariant *reply = NULL;
294 size_t sz = 0, asz = 0;
295 gchar *name;
296 guint32 uid, gid, mode;
297
298 if (!cgcall("ListKeys", g_variant_new("(ss)", controller, cgroup),
299 G_VARIANT_TYPE("(a(suuu))"), &reply))
99978832 300 return false;
99978832 301
2c51f8dd
SH
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) */
305 struct cgm_keys *k;
306
307 do {
308 k = malloc(sizeof(*k));
309 } while (!k);
310 do {
311 k->name = strdup(name);
312 } while (!k->name);
313 k->uid = uid;
314 k->gid = gid;
315 k->mode = mode;
316 g_free(name);
317 append_key(keys, k, &sz, &asz);
99978832
SH
318 }
319
2c51f8dd
SH
320 g_variant_iter_free(iter);
321 g_variant_unref(reply);
322
99978832
SH
323 return true;
324}
ab54b798 325
2c51f8dd 326bool cgm_list_children(const char *controller, const char *cgroup, char ***list)
2ad6d2bd 327{
2c51f8dd
SH
328 GVariantIter *iter = NULL;
329 GVariant *reply = NULL;
330 gchar *child;
331 size_t sz = 0, asz = 0;
2ad6d2bd 332
2c51f8dd
SH
333 if (!cgcall("ListChildren", g_variant_new("(ss)", controller, cgroup),
334 G_VARIANT_TYPE("(as)"), &reply))
2ad6d2bd 335 return false;
2c51f8dd
SH
336
337 g_variant_get(reply, "(as)", &iter);
338 do {
339 *list = malloc(BATCH_SIZE * sizeof(char *));
340 } while (!*list);
341 (*list)[0] = NULL;
342 while (g_variant_iter_next(iter, "s", &child)) {
343 if (sz+2 >= asz) {
344 char **tmp;
345 asz += BATCH_SIZE;
346 do {
347 tmp = realloc(*list, asz * sizeof(char *));
348 } while (!tmp);
349 *list = tmp;
350 }
351 do {
352 (*list)[sz] = strdup(child);
353 } while (!(*list)[sz]);
354 (*list)[sz+1] = NULL;
355 sz++;
356 g_free(child);
2ad6d2bd
SH
357 }
358
2c51f8dd
SH
359 g_variant_iter_free(iter);
360 g_variant_unref(reply);
361
2ad6d2bd
SH
362 return true;
363}
364
2c51f8dd 365bool cgm_escape_cgroup(void)
ab54b798 366{
2c51f8dd
SH
367 return cgcall("MovePidAbs", g_variant_new("(ssi)", "all", "/", getpid()),
368 G_VARIANT_TYPE_UNIT, NULL);
ab54b798
SH
369}
370
2c51f8dd 371bool cgm_move_pid(const char *controller, const char *cgroup, pid_t pid)
ab54b798 372{
2c51f8dd
SH
373 return cgcall("MovePid", g_variant_new("(ssi)", controller, cgroup, pid),
374 G_VARIANT_TYPE_UNIT, NULL);
375}
ab54b798 376
2c51f8dd
SH
377bool cgm_get_value(const char *controller, const char *cgroup, const char *file,
378 char **value)
379{
380 GVariant *reply = NULL;
381 gchar *str;
ab54b798 382
2c51f8dd
SH
383 if (!cgcall("GetValue", g_variant_new("(sss)", controller, cgroup, file),
384 G_VARIANT_TYPE("(s)"), &reply))
385 return false;
ab54b798 386
2c51f8dd
SH
387 g_variant_get(reply, "(s)", &str);
388 g_variant_unref(reply);
ab54b798 389
2c51f8dd
SH
390 do {
391 *value = strdup(str);
392 } while (!*value);
393 g_free(str);
ab54b798 394
2c51f8dd 395 return true;
ab54b798
SH
396}
397
2c51f8dd
SH
398bool cgm_set_value(const char *controller, const char *cgroup, const char *file,
399 const char *value)
ab54b798 400{
2c51f8dd
SH
401 return cgcall("SetValue", g_variant_new("(ssss)", controller, cgroup, file, value),
402 G_VARIANT_TYPE_UNIT, NULL);
403}
ab54b798 404
2c51f8dd
SH
405bool cgm_create(const char *controller, const char *cg)
406{
407 if (!cgcall("Create", g_variant_new("(ss)", controller, cg),
408 G_VARIANT_TYPE ("(i)"), NULL))
ab54b798 409 return false;
ab54b798 410
ab54b798
SH
411 return true;
412}
413
2c51f8dd 414bool cgm_chown_file(const char *controller, const char *cg, uid_t uid, gid_t gid)
0a1bb5ea 415{
2c51f8dd
SH
416 return cgcall("Chown", g_variant_new("(ssii)", controller, cg, uid, gid),
417 G_VARIANT_TYPE_UNIT, NULL);
418}
0a1bb5ea 419
2c51f8dd
SH
420bool cgm_chmod_file(const char *controller, const char *file, mode_t mode)
421{
422 return cgcall("Chmod", g_variant_new("(sssi)", controller, file, "", mode), G_VARIANT_TYPE_UNIT, NULL);
0a1bb5ea
SH
423}
424
ab54b798
SH
425bool cgm_remove(const char *controller, const char *cg)
426{
2c51f8dd 427 return cgcall("Remove", g_variant_new ("(ssi)", "all", cg, 1), G_VARIANT_TYPE ("(i)"), NULL);
ab54b798 428}