• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

drakenclimber / libcgroup / 14455586901

14 Apr 2025 08:54PM UTC coverage: 56.146% (-1.0%) from 57.123%
14455586901

push

github

drakenclimber
ftests: Add a test for the memory abstraction layer

Add a test for the memory abstraction layer abstractions.
Currently only supports:
            memory.max <-> memory.limit_in_bytes
            memory.high <-> memory.soft_limit_in_bytes

-----------------------------------------------------------------
Test Results:
        Run Date:                          Apr 14 14:52:47
        Passed:                                  1 test(s)
        Skipped:                                 0 test(s)
        Failed:                                  0 test(s)
-----------------------------------------------------------------
Timing Results:
        Test                              Time (sec)
        --------------------------------------------
        setup                                   0.00
        093-cgxget-memory_settings.py           3.34
        teardown                                0.00
        --------------------------------------------
        Total Run Time                          3.34

Signed-off-by: Tom Hromatka <tom.hromatka@oracle.com>

5600 of 9974 relevant lines covered (56.15%)

584.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

53.21
/src/tools/cgexec.c
1
// SPDX-License-Identifier: LGPL-2.1-only
2
/**
3
 * Copyright RedHat Inc. 2008
4
 *
5
 * Authors:        Vivek Goyal <vgoyal@redhat.com>
6
 *
7
 * Replace systemd idle_thread enhancements by Kamalesh Babulal
8
 * Copyright (c) 2023 Oracle and/or its affiliates.
9
 * Author: Kamalesh Babulal <kamalesh.babulal@oracle.com>
10
 */
11

12
#ifndef _GNU_SOURCE
13
#define _GNU_SOURCE
14
#endif
15

16
#include "tools-common.h"
17

18
#include <libcgroup.h>
19

20
#include <limits.h>
21
#include <search.h>
22
#include <stdlib.h>
23
#include <string.h>
24
#include <unistd.h>
25
#include <getopt.h>
26
#include <signal.h>
27
#include <stdio.h>
28
#include <errno.h>
29
#include <grp.h>
30
#include <pwd.h>
31

32
#include <sys/mount.h>
33
#include <sys/types.h>
34
#include <sys/stat.h>
35
#include <sys/wait.h>
36

37
#define SYSTEMD_IDLE_THREAD        "libcgroup_systemd_idle_thread"
38

39
static pid_t find_scope_pid(pid_t pid);
40
static int write_systemd_unified(const char * const scope_name);
41
static int is_scope_parsed(const char * const path);
42

43
static struct option longopts[] = {
44
        {"sticky",        no_argument, NULL, 's'},
45
        {"help",        no_argument, NULL, 'h'},
46
        {0, 0, 0, 0}
47
};
48

49
static void usage(int status, const char *program_name)
2✔
50
{
51
        if (status != 0) {
2✔
52
                err("Wrong input parameters, try %s --help for more information.\n", program_name);
×
53
                return;
×
54
        }
55

56
        info("Usage: %s [-h] [-g <controllers>:<path>] [--sticky] ", program_name);
2✔
57
        info("command [arguments] ...\n");
2✔
58
        info("Run the task in given control group(s)\n");
2✔
59
        info("  -g <controllers>:<path>        Control group which should be added\n");
2✔
60
        info("  -h, --help                        Display this help\n");
2✔
61
        info("  --sticky                        cgred daemon does not ");
2✔
62
        info("change pidlist and children tasks\n");
2✔
63
#ifdef WITH_SYSTEMD
64
        info("  -b                                Ignore default systemd delegate hierarchy\n");
1✔
65
        info("  -r                                Replace the default idle_thread spawned ");
1✔
66
        info("for the systemd scope\n");
1✔
67
#endif
68
}
69

70
int main(int argc, char *argv[])
10✔
71
{
72
        struct cgroup_group_spec *cgrp_list[CG_HIER_MAX];
73
#ifdef WITH_SYSTEMD
74
        int ignore_default_systemd_delegate_slice = 0;
8✔
75
#endif
76
        pid_t scope_pid = -1;
10✔
77
        int child_status = 0;
10✔
78
        int replace_idle = 0;
10✔
79
        int cg_specified = 0;
10✔
80
        int flag_child = 0;
10✔
81
        int i, ret = 0;
10✔
82
        uid_t uid;
83
        gid_t gid;
84
        pid_t pid;
85
        int c;
86

87
        memset(cgrp_list, 0, sizeof(cgrp_list));
10✔
88
#ifdef WITH_SYSTEMD
89
        while ((c = getopt_long(argc, argv, "+g:shbr", longopts, NULL)) > 0) {
21✔
90
                switch (c) {
14✔
91
                case 'b':
1✔
92
                        ignore_default_systemd_delegate_slice = 1;
1✔
93
                        break;
1✔
94
                case 'r':
4✔
95
                        replace_idle = 1;
4✔
96
                        break;
4✔
97
#else
98
        while ((c = getopt_long(argc, argv, "+g:sh", longopts, NULL)) > 0) {
4✔
99
                switch (c) {
3✔
100
#endif
101
                case 'g':
10✔
102
                        ret = parse_cgroup_spec(cgrp_list, optarg, CG_HIER_MAX);
10✔
103
                        if (ret) {
10✔
104
                                err("cgroup controller and path parsing failed\n");
×
105
                                exit(EXIT_BADARGS);
×
106
                        }
107
                        cg_specified = 1;
10✔
108
                        break;
10✔
109
                case 's':
×
110
                        flag_child |= CGROUP_DAEMON_UNCHANGE_CHILDREN;
×
111
                        break;
×
112
                case 'h':
2✔
113
                        usage(0, argv[0]);
2✔
114
                        exit(0);
2✔
115
                default:
×
116
                        usage(1, argv[0]);
×
117
                        exit(EXIT_BADARGS);
×
118
                }
119
        }
120

121
        /* Executable name */
122
        if (!argv[optind]) {
8✔
123
                usage(1, argv[0]);
×
124
                exit(EXIT_BADARGS);
×
125
        }
126

127
        /* Initialize libcg */
128
        ret = cgroup_init();
8✔
129
        if (ret) {
8✔
130
                err("libcgroup initialization failed: %s\n", cgroup_strerror(ret));
×
131
                return ret;
×
132
        }
133

134
#ifdef WITH_SYSTEMD
135
        if (!ignore_default_systemd_delegate_slice)
7✔
136
                cgroup_set_default_systemd_cgroup();
6✔
137
#endif
138

139
        /* Just for debugging purposes. */
140
        uid = geteuid();
8✔
141
        gid = getegid();
8✔
142
        cgroup_dbg("My euid and egid is: %d,%d\n", (int) uid, (int) gid);
8✔
143

144
        uid = getuid();
8✔
145
        gid = getgid();
8✔
146
        pid = getpid();
8✔
147

148
        ret = cgroup_register_unchanged_process(pid, flag_child);
8✔
149
        if (ret) {
8✔
150
                err("registration of process failed\n");
×
151
                return ret;
×
152
        }
153

154
        /*
155
         * 'cgexec' command file needs the root privilege for executing a
156
         * cgroup_register_unchanged_process() by using unix domain socket,
157
         * and an euid/egid should be changed to the executing user from a
158
         * root user.
159
         */
160
        if (setresuid(uid, uid, uid)) {
8✔
161
                err("%s", strerror(errno));
×
162
                return -1;
×
163
        }
164

165
        if (setresgid(gid, gid, gid)) {
8✔
166
                err("%s", strerror(errno));
×
167
                return -1;
×
168
        }
169

170
        if (cg_specified) {
8✔
171
                /*
172
                 * User has specified the list of control group
173
                 * and controllers
174
                 */
175
                for (i = 0; i < CG_HIER_MAX; i++) {
16✔
176
                        if (!cgrp_list[i])
16✔
177
                                break;
8✔
178

179
                        ret = cgroup_change_cgroup_path(cgrp_list[i]->path, pid,
8✔
180
                                (const char *const*) cgrp_list[i]->controllers);
8✔
181
                        if (ret) {
8✔
182
                                err("cgroup change of group failed\n");
×
183
                                return ret;
×
184
                        }
185
                }
186
        } else {
187

188
                /* Change the cgroup by determining the rules based on uid */
189
                ret = cgroup_change_cgroup_flags(uid, gid, argv[optind], pid, 0);
×
190
                if (ret) {
×
191
                        err("cgroup change of group failed\n");
×
192
                        return ret;
×
193
                }
194
        }
195

196
        if (!replace_idle) {
8✔
197
                /* Now exec the new process */
198
                execvp(argv[optind], &argv[optind]);
4✔
199
                err("exec failed:%s", strerror(errno));
4✔
200
                return -1;
×
201
        }
202

203
        scope_pid = find_scope_pid(pid);
4✔
204
        if (scope_pid == -1)
4✔
205
                return -1;
3✔
206

207
        pid = fork();
1✔
208
        if (pid == -1) {
2✔
209
                err("Fork failed for pid %u:%s\n", pid, strerror(errno));
×
210
                return -1;
×
211
        }
212

213
        /* child process kills the spawned idle_thread */
214
        if (pid == 0) {
2✔
215
                ret = kill(scope_pid, SIGTERM);
1✔
216
                if (ret) {
1✔
217
                        err("Failed to kill pid %u:%s\n", scope_pid, strerror(errno));
×
218
                        exit(1);
×
219
                }
220

221
                exit(0);
1✔
222
        }
223

224
        wait(&child_status);
1✔
225
        if (WEXITSTATUS(child_status))
1✔
226
                return -1;
×
227

228
        /* Now exec the new process */
229
        execvp(argv[optind], &argv[optind]);
1✔
230
        err("exec failed:%s", strerror(errno));
1✔
231

232
        return -1;
×
233
}
234

235
static pid_t search_systemd_idle_thread_task(pid_t pids[], size_t size)
2✔
236
{
237
        char task_cmd[FILENAME_MAX];
238
        char buffer[FILENAME_MAX];
239
        FILE *pid_cmd_fp = NULL;
2✔
240
        int scope_pid = -1;
2✔
241
        int i;
242

243
        for (i = 0; i < size; i++) {
4✔
244
                snprintf(buffer, FILENAME_MAX, "/proc/%u/cmdline", pids[i]);
3✔
245
                pid_cmd_fp = fopen(buffer, "re");
3✔
246
                /* task might have exited */
247
                if (!pid_cmd_fp)
3✔
248
                        continue;
×
249

250
                /* task might have exited, so consider only successful reads. */
251
                if (fgets(task_cmd, FILENAME_MAX, pid_cmd_fp)) {
3✔
252
                        if (!strcmp(task_cmd, SYSTEMD_IDLE_THREAD)) {
3✔
253
                                scope_pid = pids[i];
1✔
254
                                fclose(pid_cmd_fp);
1✔
255
                                break;
1✔
256
                        }
257
                }
258
                fclose(pid_cmd_fp);
2✔
259
        }
260
        return scope_pid;
2✔
261

262
}
263

264
static pid_t find_scope_pid(pid_t pid)
4✔
265
{
266
        pid_t _scope_pid = -1, scope_pid = -1;
4✔
267
        char ctrl_name[CONTROL_NAMELEN_MAX];
268
        char scope_name[FILENAME_MAX];
269
        char cgrp_name[FILENAME_MAX];
270
        int found_systemd_cgrp = 0;
4✔
271
        int found_unified_cgrp = 0;
4✔
272
        char buffer[FILENAME_MAX];
273
        FILE *pid_proc_fp = NULL;
4✔
274
        char *_ctrl_name = NULL;
4✔
275
        int idx, ret, size = 0;
4✔
276
        pid_t *pids;
277

278

279
        /* Let's parse the cgroup of the pid, to check if its in one or
280
         * more .scopes.
281
         */
282
        snprintf(buffer, FILENAME_MAX, "/proc/%u/cgroup", pid);
4✔
283
        pid_proc_fp = fopen(buffer, "re");
4✔
284
        if (!pid_proc_fp) {
4✔
285
                err("Failed to open: %s\n", buffer);
×
286
                return -1;
×
287
        }
288

289
        while (fgets(buffer, FILENAME_MAX, pid_proc_fp)) {
8✔
290
                memset(ctrl_name, '\0', sizeof(CONTROL_NAMELEN_MAX));
4✔
291

292
                /* read according to the cgroup mode */
293
                if (strstr(buffer, "::"))
4✔
294
                        ret = sscanf(buffer, "%d::%4096s\n", &idx, cgrp_name);
4✔
295
                else
296
                        ret = sscanf(buffer, "%d:%[^:]:%4096s\n", &idx, ctrl_name, cgrp_name);
×
297

298
                if (ret != 2 && ret != 3) {
4✔
299
                        err("Unrecognized cgroup file format: %s\n", buffer);
×
300
                        goto out;
×
301
                }
302

303
                if (!is_cgroup_mode_unified()) {
4✔
304
                        if (ret == 3 && !strncmp(ctrl_name, "name=systemd", 12)) {
×
305
                                found_systemd_cgrp = 1;
×
306
                                continue;
×
307
                        } else if (ret == 2) {
×
308
                                found_unified_cgrp = 1;
×
309
                                continue;
×
310
                        }
311
                }
312

313
                /* skip if the cgroup path doesn't have systemd scope format */
314
                if (strstr(cgrp_name, ".scope") == NULL ||
4✔
315
                    strstr(cgrp_name, ".slice") == NULL)
3✔
316
                        continue;
2✔
317

318
                /* skip if we have already searched cgroup for idle_thread */
319
                if (is_scope_parsed(cgrp_name))
2✔
320
                        continue;
×
321

322
                /* cgroup v1 might have shared mount points cpu,cpuacct */
323
                _ctrl_name = strchr(ctrl_name, ',');
2✔
324
                if (_ctrl_name) {
2✔
325
                        size = strlen(ctrl_name) - strlen(_ctrl_name);
×
326
                        ctrl_name[size] = '\0';
×
327
                }
328

329
                if (ret == 2)
2✔
330
                        ret = cgroup_get_procs(cgrp_name, NULL, &pids, &size);
2✔
331
                else
332
                        ret = cgroup_get_procs(cgrp_name, ctrl_name, &pids, &size);
×
333
                if (ret) {
2✔
334
                        err("Failed to read cgroup.procs of cgroup: %s\n", cgrp_name + 1);
×
335
                        goto out;
×
336
                }
337

338
                /*
339
                 * .scope created by the non-libcgroup process, will not
340
                 * have libcgroup_systemd_idle_thread
341
                 */
342
                _scope_pid = search_systemd_idle_thread_task(pids, size);
2✔
343
                free(pids);
2✔
344

345
                if (_scope_pid == -1)
2✔
346
                        continue;
1✔
347

348
                if (scope_pid == -1) {
1✔
349
                        /*
350
                         * cgexec pid needs to written into:
351
                         * ../systemd/<slice>/<scope>/cgroup.procs (legacy/hybrid)
352
                         * ../unified/<slice>/<scope>/cgroup.procs (hybrid)
353
                         */
354
                        snprintf(scope_name, FILENAME_MAX, "%s", cgrp_name);
1✔
355
                        scope_pid = _scope_pid;
1✔
356
                        continue;
1✔
357
                }
358

359
                if (_scope_pid != scope_pid) {
×
360
                        err("Failed to replace scope idle_thread, found two idle_thread %u %u\n",
×
361
                            scope_pid, _scope_pid);
362
                        goto out;
×
363
                }
364
        }
365

366
        if (scope_pid == -1) {
4✔
367
                err("Failed to find idle_thread task\n");
3✔
368
                goto out;
3✔
369
        }
370

371
        if (is_cgroup_mode_legacy() && (found_systemd_cgrp == 0 || found_unified_cgrp == 1)) {
1✔
372
                err("cgroup legacy setup incorrect\n");
×
373
                scope_pid = -1;
×
374
                goto out;
×
375
        }
376

377
        if (is_cgroup_mode_hybrid() && (found_systemd_cgrp == 0 || found_unified_cgrp == 0)) {
1✔
378
                err("cgroup hybrid setup incorrect\n");
×
379
                scope_pid = -1;
×
380
                goto out;
×
381
        }
382

383
        /* This is true for cgroup v1 (legacy/hybrid) */
384
        if (found_systemd_cgrp) {
1✔
385
                ret = write_systemd_unified(scope_name);
×
386
                if (ret)
×
387
                        scope_pid = -1;
×
388
        }
389

390
out:
1✔
391
        if (pid_proc_fp)
4✔
392
                fclose(pid_proc_fp);
4✔
393

394
        return scope_pid;
4✔
395
}
396

397
/*
398
 * Parse the /proc/mounts file and look for the controller string
399
 * in each line. If found copies the mount point into mnt_point,
400
 * else return NULL mnt_point.
401
 */
402
static void find_mnt_point(const char * const controller, char **mnt_point)
×
403
{
404
        char proc_mount[] = "/proc/mounts";
×
405
        char buffer[FILENAME_MAX * 2];
406
        char cgrp_path[FILENAME_MAX];
407
        FILE *proc_mount_f = NULL;
×
408
        int ret;
409

410
        *mnt_point = NULL;
×
411

412
        proc_mount_f = fopen(proc_mount, "re");
×
413
        if (proc_mount_f == NULL) {
×
414
                err("Failed to read %s:%s\n", proc_mount, strerror(errno));
×
415
                goto out;
×
416
        }
417

418
        while (fgets(buffer, (FILENAME_MAX * 2), proc_mount_f) != NULL) {
×
419
                /* skip line that doesn't have controller */
420
                if (!strstr(buffer, controller))
×
421
                        continue;
×
422

423
                ret = sscanf(buffer, "%*s %4096s\n", cgrp_path);
×
424
                if (ret != 1) {
×
425
                        err("Failed during read of %s:%s\n", proc_mount, strerror(errno));
×
426
                        goto out;
×
427
                }
428

429
                *mnt_point = strdup(cgrp_path);
×
430
                if (!*mnt_point)
×
431
                        err("strdup of %s failed\n", cgrp_path);
×
432
                break;
×
433
        }
434

435
out:
×
436
        if (proc_mount_f)
×
437
                fclose(proc_mount_f);
×
438
}
×
439

440
static int write_systemd_unified(const char * const scope_name)
×
441
{
442
        char cgrp_procs_path[FILENAME_MAX * 2 + 25];
443
        FILE *cgrp_systemd_path_f = NULL;
×
444
        FILE *cgrp_unified_path_f = NULL;
×
445
        char *cgrp_name = NULL;
×
446
        pid_t pid;
447

448
        /* construct the systemd cgroup path, by parsing /proc/mounts */
449
        find_mnt_point("name=systemd ", &cgrp_name);
×
450
        if (!cgrp_name) {
×
451
                err("Unable find name=systemd cgroup path\n");
×
452
                return -1;
×
453
        }
454

455
        snprintf(cgrp_procs_path, sizeof(cgrp_procs_path), "%s/%s/cgroup.procs",
×
456
                 cgrp_name, scope_name);
457
        free(cgrp_name);
×
458

459
        cgrp_systemd_path_f = fopen(cgrp_procs_path, "we");
×
460
        if (!cgrp_systemd_path_f) {
×
461
                err("Failed to open %s\n", cgrp_procs_path);
×
462
                return -1;
×
463
        }
464

465
        if (is_cgroup_mode_hybrid()) {
×
466
                /*
467
                 * construct the unified cgroup path, by parsing
468
                 * /proc/mounts
469
                 */
470
                find_mnt_point("unified cgroup2", &cgrp_name);
×
471
                if (!cgrp_name) {
×
472
                        err("Unable find unified cgroup path\n");
×
473
                        fclose(cgrp_systemd_path_f);
×
474
                        return -1;
×
475
                }
476

477
                snprintf(cgrp_procs_path, sizeof(cgrp_procs_path), "%s/%s/cgroup.procs",
×
478
                                cgrp_name, scope_name);
479
                free(cgrp_name);
×
480

481
                cgrp_unified_path_f = fopen(cgrp_procs_path, "we");
×
482
                if (!cgrp_unified_path_f) {
×
483
                        err("Failed to open %s\n", cgrp_procs_path);
×
484
                        fclose(cgrp_systemd_path_f);
×
485
                        return -1;
×
486
                }
487
        }
488

489
        pid = getpid();
×
490

491
        fprintf(cgrp_systemd_path_f, "%d", pid);
×
492
        fflush(cgrp_systemd_path_f);
×
493
        fclose(cgrp_systemd_path_f);
×
494

495
        if (!is_cgroup_mode_hybrid())
×
496
                return 0;
×
497

498
        fprintf(cgrp_unified_path_f, "%d", pid);
×
499
        fflush(cgrp_unified_path_f);
×
500
        fclose(cgrp_unified_path_f);
×
501

502
        return 0;
×
503
}
504

505
static int is_scope_parsed(const char * const path)
2✔
506
{
507
        /*
508
         * As per, <kernel sources>/kernel/cgroup/cgroup.c::cgroup_init()
509
         * At the max there can be only 16 controllers and we are
510
         * not accounting for named hierarchies, which can be more
511
         * than 16 themselves.
512
         */
513
        static char parsed_scope_path[MAX_MNT_ELEMENTS][FILENAME_MAX];
514
        int i;
515

516
        for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
2✔
517
                if (!strcmp(parsed_scope_path[i], path))
2✔
518
                        return 1;
×
519

520
                if (parsed_scope_path[i][0] == '\0') {
2✔
521
                        snprintf(parsed_scope_path[i], FILENAME_MAX, "%s", path);
2✔
522
                        break;
2✔
523
                }
524
        }
525

526
        return 0;
2✔
527
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc