]> git.ipfire.org Git - pakfire.git/blame - src/libpakfire/cgroup.c
tests: jail: Actually run a useful command
[pakfire.git] / src / libpakfire / cgroup.c
CommitLineData
9dd11ed0
MT
1/*#############################################################################
2# #
3# Pakfire - The IPFire package management system #
e3ddb498 4# Copyright (C) 2022 Pakfire development team #
9dd11ed0
MT
5# #
6# This program is free software: you can redistribute it and/or modify #
7# it under the terms of the GNU General Public License as published by #
8# the Free Software Foundation, either version 3 of the License, or #
9# (at your option) any later version. #
10# #
11# This program is distributed in the hope that it will be useful, #
12# but WITHOUT ANY WARRANTY; without even the implied warranty of #
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14# GNU General Public License for more details. #
15# #
16# You should have received a copy of the GNU General Public License #
17# along with this program. If not, see <http://www.gnu.org/licenses/>. #
18# #
19#############################################################################*/
20
21#include <errno.h>
e3ddb498 22#include <fcntl.h>
9dd11ed0 23#include <stdlib.h>
9dd11ed0 24#include <sys/types.h>
9dd11ed0
MT
25
26#include <pakfire/cgroup.h>
27#include <pakfire/logging.h>
e3ddb498 28#include <pakfire/pakfire.h>
9dd11ed0
MT
29#include <pakfire/util.h>
30
e3ddb498
MT
31#define ROOT "/sys/fs/cgroup"
32#define BUFFER_SIZE 64 * 1024
9dd11ed0 33
2901c3a7
MT
34enum pakfire_cgroup_controllers {
35 PAKFIRE_CGROUP_CONTROLLER_CPU = (1 << 0),
36 PAKFIRE_CGROUP_CONTROLLER_MEMORY = (1 << 1),
37 PAKFIRE_CGROUP_CONTROLLER_PIDS = (1 << 2),
38 PAKFIRE_CGROUP_CONTROLLER_IO = (1 << 3),
39};
40
41static const enum pakfire_cgroup_controllers pakfire_cgroup_accounting_controllers =
42 PAKFIRE_CGROUP_CONTROLLER_CPU |
43 PAKFIRE_CGROUP_CONTROLLER_MEMORY |
44 PAKFIRE_CGROUP_CONTROLLER_PIDS |
45 PAKFIRE_CGROUP_CONTROLLER_IO;
46
e3ddb498
MT
47struct pakfire_cgroup {
48 struct pakfire* pakfire;
49 int nrefs;
9dd11ed0 50
2901c3a7
MT
51 // Flags
52 int flags;
53
e3ddb498
MT
54 // Store the path
55 char path[PATH_MAX];
9dd11ed0 56
e3ddb498
MT
57 // File descriptor to cgroup
58 int fd;
59};
9e1e7985 60
e3ddb498
MT
61// Returns true if this is the root cgroup
62static int pakfire_cgroup_is_root(struct pakfire_cgroup* cgroup) {
63 return !*cgroup->path;
64}
9e1e7985 65
2901c3a7
MT
66static int pakfire_cgroup_has_flag(struct pakfire_cgroup* cgroup, int flag) {
67 return cgroup->flags & flag;
68}
69
e3ddb498
MT
70static const char* pakfire_cgroup_name(struct pakfire_cgroup* cgroup) {
71 if (pakfire_cgroup_is_root(cgroup))
72 return "(root)";
9e1e7985 73
e3ddb498 74 return cgroup->path;
9e1e7985
MT
75}
76
2901c3a7
MT
77static const char* pakfire_cgroup_controller_name(
78 enum pakfire_cgroup_controllers controller) {
79 switch (controller) {
80 case PAKFIRE_CGROUP_CONTROLLER_CPU:
81 return "cpu";
82
83 case PAKFIRE_CGROUP_CONTROLLER_MEMORY:
84 return "memory";
85
86 case PAKFIRE_CGROUP_CONTROLLER_PIDS:
87 return "pids";
88
89 case PAKFIRE_CGROUP_CONTROLLER_IO:
90 return "io";
91 }
92
93 return NULL;
94}
95
96static enum pakfire_cgroup_controllers pakfire_cgroup_find_controller_by_name(
97 const char* name) {
98 const char* n = NULL;
99
100 // Walk through the bitmap
101 for (unsigned int i = 1; i; i <<= 1) {
102 n = pakfire_cgroup_controller_name(i);
103 if (!n)
104 break;
105
106 // Match
107 if (strcmp(name, n) == 0)
108 return i;
109 }
110
111 // Nothing found
112 return 0;
113}
114
115static struct pakfire_cgroup* pakfire_cgroup_parent(struct pakfire_cgroup* cgroup) {
116 struct pakfire_cgroup* parent = NULL;
117 int r;
118
119 // Cannot return parent for root group
120 if (pakfire_cgroup_is_root(cgroup))
121 return NULL;
122
123 // Determine the path of the parent
124 char* path = pakfire_dirname(cgroup->path);
125 if (!path) {
126 ERROR(cgroup->pakfire, "Could not determine path for parent cgroup: %m\n");
127 return NULL;
128 }
129
130 // dirname() returns . if no directory component could be found
131 if (strcmp(path, ".") == 0)
132 *path = '\0';
133
134 // Open the cgroup
135 r = pakfire_cgroup_open(&parent, cgroup->pakfire, path, 0);
136 if (r) {
137 ERROR(cgroup->pakfire, "Could not open parent cgroup: %m\n");
138 parent = NULL;
139 }
140
141 // Cleanup
142 free(path);
143
144 return parent;
145}
146
e3ddb498
MT
147static void pakfire_cgroup_free(struct pakfire_cgroup* cgroup) {
148 DEBUG(cgroup->pakfire, "Releasing cgroup %s at %p\n",
149 pakfire_cgroup_name(cgroup), cgroup);
9e1e7985 150
e3ddb498
MT
151 // Close the file descriptor
152 if (cgroup->fd)
153 close(cgroup->fd);
9e1e7985 154
e3ddb498
MT
155 pakfire_unref(cgroup->pakfire);
156 free(cgroup);
9e1e7985
MT
157}
158
e3ddb498
MT
159static int __pakfire_cgroup_create(struct pakfire_cgroup* cgroup) {
160 char path[PATH_MAX];
161 int r;
9dd11ed0 162
e3ddb498 163 DEBUG(cgroup->pakfire, "Trying to create cgroup %s\n", pakfire_cgroup_name(cgroup));
9dd11ed0 164
e3ddb498
MT
165 // Compose the absolute path
166 r = pakfire_path_join(path, ROOT, cgroup->path);
167 if (r < 0)
168 return 1;
9dd11ed0 169
e3ddb498
MT
170 // Try creating the directory
171 return pakfire_mkdir(path, 0755);
172}
9dd11ed0 173
e3ddb498
MT
174/*
175 Opens the cgroup and returns a file descriptor.
9dd11ed0 176
e3ddb498 177 If the cgroup does not exist, it will try to create it.
9dd11ed0 178
e3ddb498
MT
179 This function returns a negative value on error.
180*/
181static int __pakfire_cgroup_open(struct pakfire_cgroup* cgroup) {
182 int rootfd = -1;
183 int fd = -1;
184 int r;
9dd11ed0 185
e3ddb498
MT
186 // Open file descriptor of the cgroup root
187 rootfd = open(ROOT, O_DIRECTORY|O_PATH|O_CLOEXEC);
188 if (rootfd < 0) {
189 ERROR(cgroup->pakfire, "Could not open %s: %m\n", ROOT);
190 return -1;
9dd11ed0
MT
191 }
192
e3ddb498
MT
193 // Return the rootfd for the root group
194 if (pakfire_cgroup_is_root(cgroup))
195 return rootfd;
9dd11ed0 196
e3ddb498
MT
197RETRY:
198 fd = openat(rootfd, cgroup->path, O_DIRECTORY|O_PATH|O_CLOEXEC);
199 if (fd < 0) {
200 switch (errno) {
201 // If the cgroup doesn't exist yet, try to create it
202 case ENOENT:
203 r = __pakfire_cgroup_create(cgroup);
204 if (r)
205 goto ERROR;
9dd11ed0 206
e3ddb498
MT
207 // Retry open after successful creation
208 goto RETRY;
9dd11ed0 209
e3ddb498
MT
210 // Exit on all other errors
211 default:
212 ERROR(cgroup->pakfire, "Could not open cgroup %s: %m\n",
213 pakfire_cgroup_name(cgroup));
214 goto ERROR;
215 }
216 }
9dd11ed0 217
e3ddb498
MT
218ERROR:
219 if (rootfd > 0)
220 close(rootfd);
9dd11ed0 221
e3ddb498
MT
222 return fd;
223}
9dd11ed0 224
2901c3a7 225static ssize_t pakfire_cgroup_read(struct pakfire_cgroup* cgroup, const char* path,
e3ddb498 226 char* buffer, size_t length) {
2901c3a7 227 ssize_t bytes_read = -1;
9dd11ed0 228
e3ddb498
MT
229 // Check if this cgroup has been destroyed already
230 if (!cgroup->fd) {
231 ERROR(cgroup->pakfire, "Trying to read from destroyed cgroup\n");
2901c3a7 232 return -1;
9dd11ed0
MT
233 }
234
e3ddb498
MT
235 // Open the file
236 int fd = openat(cgroup->fd, path, O_CLOEXEC);
237 if (fd < 0) {
238 DEBUG(cgroup->pakfire, "Could not open %s/%s: %m\n",
239 pakfire_cgroup_name(cgroup), path);
240 goto ERROR;
241 }
9dd11ed0 242
e3ddb498 243 // Read file content into buffer
2901c3a7
MT
244 bytes_read = read(fd, buffer, length);
245 if (bytes_read < 0) {
e3ddb498
MT
246 DEBUG(cgroup->pakfire, "Could not read from %s/%s: %m\n",
247 pakfire_cgroup_name(cgroup), path);
248 goto ERROR;
249 }
9dd11ed0 250
2901c3a7
MT
251 // Terminate the buffer
252 if (bytes_read < length)
253 buffer[bytes_read] = '\0';
9dd11ed0 254
e3ddb498
MT
255ERROR:
256 if (fd > 0)
257 close(fd);
9dd11ed0 258
2901c3a7 259 return bytes_read;
9dd11ed0
MT
260}
261
e3ddb498
MT
262static int pakfire_cgroup_write(struct pakfire_cgroup* cgroup,
263 const char* path, const char* format, ...) {
305de320 264 va_list args;
e3ddb498
MT
265 int r = 0;
266
267 // Check if this cgroup has been destroyed already
268 if (!cgroup->fd) {
269 ERROR(cgroup->pakfire, "Trying to write to destroyed cgroup\n");
270 errno = EPERM;
271 return 1;
272 }
9dd11ed0 273
e3ddb498
MT
274 // Open the file
275 int fd = openat(cgroup->fd, path, O_WRONLY|O_CLOEXEC);
276 if (fd < 0) {
277 DEBUG(cgroup->pakfire, "Could not open %s/%s for writing: %m\n",
278 pakfire_cgroup_name(cgroup), path);
9dd11ed0 279 return 1;
e3ddb498 280 }
9dd11ed0 281
e3ddb498 282 // Write buffer
305de320 283 va_start(args, format);
e3ddb498 284 ssize_t bytes_written = vdprintf(fd, format, args);
305de320
MT
285 va_end(args);
286
e3ddb498
MT
287 // Check if content was written okay
288 if (bytes_written < 0) {
289 DEBUG(cgroup->pakfire, "Could not write to %s/%s: %m\n",
290 pakfire_cgroup_name(cgroup), path);
291 r = 1;
292 }
820c32c7 293
e3ddb498
MT
294 // Close fd
295 close(fd);
305de320
MT
296
297 return r;
298}
299
2901c3a7
MT
300static int pakfire_cgroup_read_controllers(
301 struct pakfire_cgroup* cgroup, const char* name) {
302 char buffer[BUFFER_SIZE];
303 char* p = NULL;
9dd11ed0
MT
304 int r;
305
2901c3a7
MT
306 // Discovered controllers
307 int controllers = 0;
69cfa22d 308
2901c3a7
MT
309 // Read cgroup.controllers file
310 ssize_t bytes_read = pakfire_cgroup_read(cgroup, name, buffer, sizeof(buffer));
311 if (bytes_read < 0)
312 return -1;
313
314 // If the file was empty, there is nothing more to do
315 if (bytes_read == 0)
316 return 0;
317
318 char* token = strtok_r(buffer, " \n", &p);
319
320 while (token) {
321 DEBUG(cgroup->pakfire, "Found controller '%s'\n", token);
322
323 // Try finding this controller
324 int controller = pakfire_cgroup_find_controller_by_name(token);
325 if (controller)
326 controllers |= controller;
327
328 // Move on to next token
329 token = strtok_r(NULL, " \n", &p);
9dd11ed0
MT
330 }
331
2901c3a7
MT
332 // Return discovered controllers
333 return controllers;
334}
9dd11ed0 335
2901c3a7
MT
336/*
337 Returns a bitmap of all available controllers
338*/
339static int pakfire_cgroup_available_controllers(struct pakfire_cgroup* cgroup) {
340 return pakfire_cgroup_read_controllers(cgroup, "cgroup.controllers");
341}
342
343/*
344 Returns a bitmap of all enabled controllers
345*/
346static int pakfire_cgroup_enabled_controllers(struct pakfire_cgroup* cgroup) {
347 return pakfire_cgroup_read_controllers(cgroup, "cgroup.subtree_control");
348}
349
350/*
351 This function takes a bitmap of controllers that should be enabled.
352*/
353static int pakfire_cgroup_enable_controllers(struct pakfire_cgroup* cgroup,
354 enum pakfire_cgroup_controllers controllers) {
355 struct pakfire_cgroup* parent = NULL;
356 int r = 1;
357
358 // Find all enabled controllers
359 const int enabled_controllers = pakfire_cgroup_enabled_controllers(cgroup);
360 if (enabled_controllers < 0) {
361 ERROR(cgroup->pakfire, "Could not fetch enabled controllers: %m\n");
362 goto ERROR;
9dd11ed0
MT
363 }
364
2901c3a7
MT
365 // Filter out anything that is already enabled
366 controllers = (controllers & ~enabled_controllers);
9dd11ed0 367
2901c3a7
MT
368 // Exit if everything is already enabled
369 if (!controllers) {
370 DEBUG(cgroup->pakfire, "All controllers are already enabled\n");
371 return 0;
372 }
373
374 // Find all available controllers
375 const int available_controllers = pakfire_cgroup_available_controllers(cgroup);
376 if (available_controllers < 0) {
377 ERROR(cgroup->pakfire, "Could not fetch available controllers: %m\n");
378 goto ERROR;
379 }
380
381 // Are all controllers we need available, yet?
382 if (controllers & ~available_controllers) {
383 DEBUG(cgroup->pakfire, "Not all controllers are available, yet\n");
384
385 parent = pakfire_cgroup_parent(cgroup);
386
387 // Enable everything we need on the parent group
388 if (parent) {
389 r = pakfire_cgroup_enable_controllers(parent, controllers);
390 if (r)
391 goto ERROR;
392 }
393 }
394
395 // Determine how many iterations we will need
396 const int iterations = 1 << (sizeof(controllers) * 8 - __builtin_clz(controllers));
397
398 // Iterate over all known controllers
399 for (int controller = 1; controller < iterations; controller <<= 1) {
400 // Skip enabling this controller if not requested
401 if (!(controller & controllers))
402 continue;
403
404 // Fetch name
405 const char* name = pakfire_cgroup_controller_name(controller);
406
407 DEBUG(cgroup->pakfire, "Enabling controller %s in cgroup %s\n",
408 name, pakfire_cgroup_name(cgroup));
409
410 // Try enabling the controller (this will succeed if it already is enabled)
411 r = pakfire_cgroup_write(cgroup, "cgroup.subtree_control", "+%s\n", name);
412 if (r) {
413 ERROR(cgroup->pakfire, "Could not enable controller %s in cgroup %s\n",
414 name, pakfire_cgroup_name(cgroup));
415 goto ERROR;
416 }
417 }
418
419ERROR:
420 if (parent)
421 pakfire_cgroup_unref(parent);
422
423 return r;
424}
425
426static int pakfire_cgroup_enable_accounting(struct pakfire_cgroup* cgroup) {
427 // Enable all accounting controllers
428 return pakfire_cgroup_enable_controllers(cgroup,
429 pakfire_cgroup_accounting_controllers);
9dd11ed0
MT
430}
431
e3ddb498
MT
432/*
433 Entry function to open a new cgroup.
9dd11ed0 434
e3ddb498
MT
435 If the cgroup doesn't exist, it will be created including any parent cgroups.
436*/
437int pakfire_cgroup_open(struct pakfire_cgroup** cgroup,
2901c3a7 438 struct pakfire* pakfire, const char* path, int flags) {
e3ddb498 439 int r = 1;
9dd11ed0 440
e3ddb498
MT
441 // Allocate the cgroup struct
442 struct pakfire_cgroup* c = calloc(1, sizeof(*c));
443 if (!c)
444 return 1;
9dd11ed0 445
e3ddb498 446 DEBUG(pakfire, "Allocated cgroup %s at %p\n", path, c);
9dd11ed0 447
e3ddb498
MT
448 // Keep a reference to pakfire
449 c->pakfire = pakfire_ref(pakfire);
4630031c 450
e3ddb498
MT
451 // Initialize reference counter
452 c->nrefs = 1;
5ae21aa1 453
e3ddb498
MT
454 // Copy path
455 pakfire_string_set(c->path, path);
5ae21aa1 456
2901c3a7
MT
457 // Copy flags
458 c->flags = flags;
459
e3ddb498
MT
460 // Open a file descriptor
461 c->fd = __pakfire_cgroup_open(c);
462 if (c->fd < 0)
463 goto ERROR;
4630031c 464
2901c3a7
MT
465 // Enable accounting if requested
466 if (pakfire_cgroup_has_flag(c, PAKFIRE_CGROUP_ENABLE_ACCOUNTING)) {
467 r = pakfire_cgroup_enable_accounting(c);
468 if (r)
469 goto ERROR;
470 }
471
e3ddb498 472 *cgroup = c;
4630031c 473 return 0;
4630031c 474
e3ddb498
MT
475ERROR:
476 pakfire_cgroup_free(c);
477 return r;
4630031c
MT
478}
479
e3ddb498
MT
480struct pakfire_cgroup* pakfire_cgroup_ref(struct pakfire_cgroup* cgroup) {
481 ++cgroup->nrefs;
4630031c 482
e3ddb498 483 return cgroup;
1b41d3b1
MT
484}
485
e3ddb498
MT
486struct pakfire_cgroup* pakfire_cgroup_unref(struct pakfire_cgroup* cgroup) {
487 if (--cgroup->nrefs > 0)
488 return cgroup;
1b41d3b1 489
e3ddb498
MT
490 pakfire_cgroup_free(cgroup);
491 return NULL;
1b41d3b1
MT
492}
493
e3ddb498
MT
494/*
495 Immediately kills all processes in this cgroup
496*/
497int pakfire_cgroup_killall(struct pakfire_cgroup* cgroup) {
498 int r = pakfire_cgroup_write(cgroup, "cgroup.kill", "1");
499 if (r)
500 ERROR(cgroup->pakfire, "Could not kill processes: %m\n");
1b41d3b1 501
e3ddb498
MT
502 return r;
503}
1b41d3b1 504
e3ddb498
MT
505/*
506 Immediately destroys this cgroup
507*/
508int pakfire_cgroup_destroy(struct pakfire_cgroup* cgroup) {
509 int r;
1b41d3b1 510
e3ddb498
MT
511 // Kill everything in this group
512 r = pakfire_cgroup_killall(cgroup);
513 if (r)
514 return r;
1b41d3b1 515
e3ddb498
MT
516 // Delete the directory
517 r = rmdir(cgroup->path);
518 if (r) {
519 ERROR(cgroup->pakfire, "Could not destroy cgroup: %m\n");
520 return r;
1b41d3b1
MT
521 }
522
e3ddb498
MT
523 // Close the file descriptor
524 if (cgroup->fd) {
525 close(cgroup->fd);
526 cgroup->fd = 0;
d5256224 527 }
d5256224
MT
528
529 return 0;
530}
820c32c7 531
e3ddb498
MT
532int pakfire_cgroup_fd(struct pakfire_cgroup* cgroup) {
533 return cgroup->fd;
820c32c7 534}
46dd01c6
MT
535
536// Memory
537
538int pakfire_cgroup_set_guaranteed_memory(struct pakfire_cgroup* cgroup, size_t mem) {
539 int r;
540
541 // Enable memory controller
542 r = pakfire_cgroup_enable_controllers(cgroup, PAKFIRE_CGROUP_CONTROLLER_MEMORY);
543 if (r)
544 return r;
545
546 DEBUG(cgroup->pakfire, "%s: Setting guaranteed memory to %zu byte(s)\n",
547 pakfire_cgroup_name(cgroup), mem);
548
549 // Set value
550 r = pakfire_cgroup_write(cgroup, "memory.min", "%zu\n", mem);
551 if (r)
552 ERROR(cgroup->pakfire, "%s: Could not set guaranteed memory: %m\n",
553 pakfire_cgroup_name(cgroup));
554
555 return r;
556}
557
558int pakfire_cgroup_set_memory_limit(struct pakfire_cgroup* cgroup, size_t mem) {
559 int r;
560
561 // Enable memory controller
562 r = pakfire_cgroup_enable_controllers(cgroup, PAKFIRE_CGROUP_CONTROLLER_MEMORY);
563 if (r)
564 return r;
565
566 DEBUG(cgroup->pakfire, "%s: Setting memory limit to %zu byte(s)\n",
567 pakfire_cgroup_name(cgroup), mem);
568
569 // Set value
570 r = pakfire_cgroup_write(cgroup, "memory.max", "%zu\n", mem);
571 if (r)
572 ERROR(cgroup->pakfire, "%s: Could not set memory limit: %m\n",
573 pakfire_cgroup_name(cgroup));
574
575 return r;
576}