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

drakenclimber / libcgroup / 14344784655

08 Apr 2025 10:26PM UTC coverage: 48.546% (-11.1%) from 59.654%
14344784655

push

github

drakenclimber
github: Delete cgroup v1 workflows

Github has deprecated Ubuntu 20.  Since this was the last runner still
running cgroup v1 by default, we no longer have a runner than can run
the automated tests in cgroup v1 mode.  Therefore, delete the cgroup v1
automated test runs.

Note that we'll continue to run them by hand periodically and before
releases, but upstream changes may end up breaking/affecting cgroup v1
support.  We strongly recommend migrating to cgroup v2.

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

4893 of 10079 relevant lines covered (48.55%)

252.97 hits per line

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

2.69
/src/systemd.c
1
/* SPDX-License-Identifier: LGPL-2.1-only */
2
/**
3
 * Copyright (c) 2022 Oracle and/or its affiliates.
4
 * Author: Tom Hromatka <tom.hromatka@oracle.com>
5
 * Author: Silvia Chapa <silvia.chapa@oracle.com>
6
 */
7

8
#include <libcgroup-internal.h>
9

10
#ifdef WITH_SYSTEMD
11
#include <systemd/sd-bus.h>
12
#include <libcgroup.h>
13
#include <unistd.h>
14
#include <assert.h>
15
#include <stdlib.h>
16
#include <libgen.h>
17
#include <errno.h>
18

19
#define USEC_PER_SEC 1000000
20

21
static const char * const modes[] = {
22
        "fail",                        /* CGROUP_SYSTEMD_MODE_FAIL */
23
        "replace",                /* CGROUP_SYSTEMD_MODE_REPLACE */
24
        "isolate",                /* CGROUP_SYSTEMD_MODE_ISOLATE */
25
        "ignore-dependencies",        /* CGROUP_SYSTEMD_MODE_IGNORE_DEPS */
26
        "ignore-requirements",        /* CGROUP_SYSTEMD_MODE_IGNORE_REQS */
27
};
28
static_assert(ARRAY_SIZE(modes) == CGROUP_SYSTEMD_MODE_CNT,
29
              "modes[] array must be same length as CGROUP_SYSTEMD_MODE_CNT");
30

31
/*
32
 * controller_name table borrowed from:
33
 * https://github.com/systemd/systemd/blob/main/src/basic/cgroup-util.c#L2260
34
 */
35
static const char * const controller_name[] = {
36
        /* subset of controllers (cgroup v1/v2), systemd supports */
37
        "cpu",
38
        "cpuacct",
39
        "cpuset",
40
        "io",
41
        "blkio",
42
        "memory",
43
        "devices",
44
        "pids",
45

46
        /* systemd pseudo BPF based controllers (cgroup v2 ) */
47
        "bpf-firewall",
48
        "bpf-devices",
49
        "bpf-foreign",
50
        "bpf-socket-bind",
51
        "bpf-restrict-network-interfaces",
52
};
53

54
static const char * const sender = "org.freedesktop.systemd1";
55
static const char * const path = "/org/freedesktop/systemd1";
56
static const char * const interface = "org.freedesktop.systemd1.Manager";
57

58
int cgroup_set_default_scope_opts(struct cgroup_systemd_scope_opts * const opts)
6✔
59
{
60
        if (!opts)
6✔
61
                return ECGINVAL;
×
62

63
        opts->delegated = 1;
6✔
64
        opts->mode = CGROUP_SYSTEMD_MODE_FAIL;
6✔
65
        opts->pid = -1;
6✔
66

67
        return 0;
6✔
68
}
69

70
/*
71
 * Returns time elapsed in usec
72
 *
73
 * Inspired-by: https://github.com/cockpit-project/cockpit/blob/main/src/tls/socket-io.c#L39
74
 */
75
static int64_t elapsed_time(const struct timespec * const start, const struct timespec * const end)
×
76
{
77
        int64_t elapsed = (end->tv_sec - start->tv_sec) * 1000000 +
×
78
                          (end->tv_nsec - start->tv_nsec) / 1000;
×
79

80
        assert(elapsed >= 0);
×
81

82
        return elapsed;
×
83
}
84

85
static int job_removed_callback(sd_bus_message *message, void *user_data, sd_bus_error *error)
×
86
{
87
        const char *result, *msg_path, *scope_name;
88
        const char **job_path = user_data;
×
89
        int ret;
90

91
        ret = sd_bus_message_read(message, "uoss", NULL, &msg_path, &scope_name, &result);
×
92
        if (ret < 0) {
×
93
                cgroup_err("callback message read failed: %d\n", errno);
×
94
                return 0;
×
95
        }
96

97
        if (*job_path == NULL || strcmp(msg_path, *job_path) != 0) {
×
98
                cgroup_dbg("Received a systemd signal, but it was not our message\n");
×
99
                return 0;
×
100
        }
101

102
        cgroup_dbg("Received JobRemoved signal for scope %s.  Result: %s\n", scope_name, result);
×
103

104
        /*
105
         * Use the job_path pointer as a way to inform the original thread that the job has
106
         * completed.
107
         */
108
        *job_path = NULL;
×
109
        return 0;
×
110
}
111

112
static int validate_scope_slice_name(const char * const scope_name, const char * const slice_name)
×
113
{
114
        char *_scope_name = NULL;
×
115
        int i, len, ret = 0;
×
116

117
        /* get the scope name without the suffix, min length is <n>.scope */
118
        len = strlen(scope_name) - 6;
×
119
        if (len < 1)
×
120
                cgroup_warn("Invalid scope name %s\n", scope_name);
×
121
        else if (strcmp(&scope_name[len], ".scope") != 0)
×
122
                cgroup_warn("scope doesn't have expected suffix\n");
×
123

124
        /* get the slice name without the suffix, min length is <n>.slice */
125
        len = strlen(slice_name) - 6;
×
126
        if (len < 1)
×
127
                cgroup_warn("Invalid slice name %s\n", slice_name);
×
128
        else if (strcmp(&slice_name[len], ".slice") != 0)
×
129
                cgroup_warn("slice doesn't have expected suffix\n");
×
130

131
        /*
132
         * systemd will silently prefix a '_' to the scope name and
133
         * create, and delegate it under the slice. If it matches with any
134
         * of the controller in the controller_name[]. i.e.,
135
         * oracle.slice/cpuset.scope, will be created as
136
         * oracle.slice/_cpuset.scope
137
         *
138
         * Disallow such scope names, that will cause confusion to the
139
         * users, where they might try to operate scope cgroup, but
140
         * the delegated scope is _scope cgroup.
141
         */
142
        _scope_name = strdup(scope_name);
×
143
        if (_scope_name == NULL) {
×
144
                cgroup_err("Failed to allocate memory for _scope_name\n");
×
145
                last_errno = errno;
×
146
                ret = 1;
×
147
                goto out;
×
148
        }
149

150
        len = strlen(_scope_name) - 6;        /* strlen(".scope") + '\0' */
×
151
        _scope_name[len] = '\0';
×
152

153
        for (i = 0; i < ARRAY_SIZE(controller_name); i++) {
×
154
                if (strcmp(_scope_name, controller_name[i]))
×
155
                        continue;
×
156

157
                cgroup_err("Invalid scope name, using controller name %s\n", controller_name[i]);
×
158
                ret = 1;
×
159
                break;
×
160
        }
161

162
        free(_scope_name);
×
163
out:
×
164
        return ret;
×
165
}
166

167
int cgroup_create_scope(const char * const scope_name, const char * const slice_name,
×
168
                        const struct cgroup_systemd_scope_opts * const opts)
169
{
170
        sd_bus_message *msg = NULL, *reply = NULL;
×
171
        int ret = 0, sdret = 0, cgret = ECGFAIL;
×
172
        sd_bus_error error = SD_BUS_ERROR_NULL;
×
173
        const char *job_path = NULL;
×
174
        struct timespec start, now;
175
        sd_bus *bus = NULL;
×
176
        pid_t child_pid;
177

178
        if (!scope_name || !slice_name || !opts)
×
179
                return ECGINVAL;
×
180

181
        if (validate_scope_slice_name(scope_name, slice_name))
×
182
                return ECGINVAL;
×
183

184
        if (opts->mode >= CGROUP_SYSTEMD_MODE_CNT) {
×
185
                cgroup_err("invalid systemd mode: %d\n", opts->mode);
×
186
                return ECGINVAL;
×
187
        }
188

189
        if (opts->mode == CGROUP_SYSTEMD_MODE_ISOLATE ||
×
190
            opts->mode == CGROUP_SYSTEMD_MODE_IGNORE_DEPS ||
×
191
            opts->mode == CGROUP_SYSTEMD_MODE_IGNORE_REQS) {
×
192
                cgroup_err("unsupported systemd mode: %d\n", opts->mode);
×
193
                return ECGINVAL;
×
194
        }
195

196
        if (opts->pid < 0) {
×
197
                child_pid = fork();
×
198
                if (child_pid < 0) {
×
199
                        last_errno = errno;
×
200
                        cgroup_err("fork failed: %d\n", errno);
×
201
                        return ECGOTHER;
×
202
                }
203

204
                if (child_pid == 0) {
×
205
                        static char * const args[] = {"libcgroup_systemd_idle_thread", NULL};
206

207
                        /*
208
                         * Have the child sleep forever.  Systemd will delete the scope if
209
                         * there isn't a running process in it.
210
                         */
211
                        execvp("libcgroup_systemd_idle_thread", args);
×
212

213
                        /* The child process should never get here */
214
                        last_errno = errno;
×
215
                        cgroup_err("failed to create system idle thread.\n");
×
216
                        return ECGOTHER;
×
217
                }
218

219
                cgroup_dbg("created libcgroup_system_idle thread pid %d\n", child_pid);
×
220
        } else {
221
                child_pid = opts->pid;
×
222
        }
223
        cgroup_dbg("pid %d will be placed in scope %s\n", child_pid, scope_name);
×
224

225
        sdret = sd_bus_default_system(&bus);
×
226
        if (sdret < 0) {
×
227
                cgroup_err("failed to open the system bus: %d\n", errno);
×
228
                goto out;
×
229
        }
230

231
        sdret = sd_bus_match_signal(bus, NULL, sender, path, interface,
×
232
                                    "JobRemoved", job_removed_callback, &job_path);
233
        if (sdret < 0) {
×
234
                cgroup_err("failed to install match callback: %d\n", errno);
×
235
                goto out;
×
236
        }
237

238
        sdret = sd_bus_message_new_method_call(bus, &msg, sender, path, interface,
×
239
                                               "StartTransientUnit");
240
        if (sdret < 0) {
×
241
                cgroup_err("failed to create the systemd msg: %d\n", errno);
×
242
                goto out;
×
243
        }
244

245
        sdret = sd_bus_message_append(msg, "ss", scope_name, modes[opts->mode]);
×
246
        if (sdret < 0) {
×
247
                cgroup_err("failed to append the scope name: %d\n", errno);
×
248
                goto out;
×
249
        }
250

251
        sdret = sd_bus_message_open_container(msg, 'a', "(sv)");
×
252
        if (sdret < 0) {
×
253
                cgroup_err("failed to open container: %d\n", errno);
×
254
                goto out;
×
255
        }
256

257
        sdret = sd_bus_message_append(msg, "(sv)", "Description", "s",
×
258
                                      "scope created by libcgroup");
259
        if (sdret < 0) {
×
260
                cgroup_err("failed to append the description: %d\n", errno);
×
261
                goto out;
×
262
        }
263

264
        sdret = sd_bus_message_append(msg, "(sv)", "PIDs", "au", 1, child_pid);
×
265
        if (sdret < 0) {
×
266
                cgroup_err("failed to append the PID: %d\n", errno);
×
267
                goto out;
×
268
        }
269

270
        sdret = sd_bus_message_append(msg, "(sv)", "Slice", "s", slice_name);
×
271
        if (sdret < 0) {
×
272
                cgroup_err("failed to append the slice: %d\n", errno);
×
273
                goto out;
×
274
        }
275

276
        if (opts->delegated == 1) {
×
277
                sdret = sd_bus_message_append(msg, "(sv)", "Delegate", "b", 1);
×
278
                if (sdret < 0) {
×
279
                        cgroup_err("failed to append delegate: %d\n", errno);
×
280
                        goto out;
×
281
                }
282
        }
283

284
        sdret = sd_bus_message_close_container(msg);
×
285
        if (sdret < 0) {
×
286
                cgroup_err("failed to close the container: %d\n", errno);
×
287
                goto out;
×
288
        }
289

290
        sdret = sd_bus_message_append(msg, "a(sa(sv))", 0);
×
291
        if (sdret < 0) {
×
292
                cgroup_err("failed to append aux structure: %d\n", errno);
×
293
                goto out;
×
294
        }
295

296
        sdret = sd_bus_call(bus, msg, 0, &error, &reply);
×
297
        if (sdret < 0) {
×
298
                cgroup_err("sd_bus_call() failed: %d\n",
×
299
                           sd_bus_message_get_errno(msg));
300
                cgroup_err("error message: %s\n", error.message);
×
301
                goto out;
×
302
        }
303

304
        /* Receive the job_path from systemd */
305
        sdret = sd_bus_message_read(reply, "o", &job_path);
×
306
        if (sdret < 0) {
×
307
                cgroup_err("failed to read reply: %d\n", errno);
×
308
                goto out;
×
309
        }
310

311
        cgroup_dbg("job_path = %s\n", job_path);
×
312

313
        ret = clock_gettime(CLOCK_MONOTONIC, &start);
×
314
        if (ret < 0) {
×
315
                last_errno = errno;
×
316
                cgroup_err("Failed to get time: %d\n", errno);
×
317
                cgret = ECGOTHER;
×
318
                goto out;
×
319
        }
320

321
        /* The callback will null out the job_path pointer on completion */
322
        while (job_path) {
×
323
                sdret = sd_bus_process(bus, NULL);
×
324
                if (sdret < 0) {
×
325
                        cgroup_err("failed to process the sd bus: %d\n", errno);
×
326
                        goto out;
×
327
                }
328

329
                if (sdret == 0) {
×
330
                        /*
331
                         * Per the sd_bus_wait() man page, call this function after sd_bus_process
332
                         * returns zero. The wait time (usec) was somewhat arbitrarily chosen
333
                         */
334
                        sdret = sd_bus_wait(bus, 10);
×
335
                        if (sdret < 0) {
×
336
                                cgroup_err("failed to wait for sd bus: %d\n", errno);
×
337
                                goto out;
×
338
                        }
339
                }
340

341
                ret = clock_gettime(CLOCK_MONOTONIC, &now);
×
342
                if (ret < 0) {
×
343
                        last_errno = errno;
×
344
                        cgroup_err("Failed to get time: %d\n", errno);
×
345
                        cgret = ECGOTHER;
×
346
                        goto out;
×
347
                }
348

349
                if (elapsed_time(&start, &now) > USEC_PER_SEC) {
×
350
                        cgroup_err("The create scope command timed out\n");
×
351
                        goto out;
×
352
                }
353
        }
354

355
        cgret = 0;
×
356

357
out:
×
358
        if (cgret && opts->pid < 0)
×
359
                kill(child_pid, SIGTERM);
×
360

361
        sd_bus_error_free(&error);
×
362
        sd_bus_message_unref(msg);
×
363
        sd_bus_message_unref(reply);
×
364
        sd_bus_unref(bus);
×
365

366
        return cgret;
×
367
}
368

369
int cgroup_create_scope2(struct cgroup *cgroup, int ignore_ownership,
×
370
                         const struct cgroup_systemd_scope_opts * const opts)
371
{
372
        char *copy1 = NULL, *copy2 = NULL, *slash, *slice_name, *scope_name;
×
373
        int ret = 0;
×
374

375
        if (!cgroup)
×
376
                return ECGROUPNOTALLOWED;
×
377

378
        slash = strstr(cgroup->name, "/");
×
379
        if (!slash) {
×
380
                cgroup_err("cgroup name does not contain a slash: %s\n", cgroup->name);
×
381
                return ECGINVAL;
×
382
        }
383

384
        slash = strstr(slash + 1, "/");
×
385
        if (slash) {
×
386
                cgroup_err("cgroup name contains more than one slash: %s\n", cgroup->name);
×
387
                return ECGINVAL;
×
388
        }
389

390
        copy1 = strdup(cgroup->name);
×
391
        if (!copy1) {
×
392
                last_errno = errno;
×
393
                ret = ECGOTHER;
×
394
                goto err;
×
395
        }
396

397
        scope_name = basename(copy1);
×
398

399
        copy2 = strdup(cgroup->name);
×
400
        if (!copy2) {
×
401
                last_errno = errno;
×
402
                ret = ECGOTHER;
×
403
                goto err;
×
404
        }
405

406
        slice_name = dirname(copy2);
×
407

408
        ret = cgroup_create_scope(scope_name, slice_name, opts);
×
409
        if (ret)
×
410
                goto err;
×
411

412
        /*
413
         * Utilize cgroup_create_cgroup() to assign the requested owner/group and permissions.
414
         * cgroup_create_cgroup() can gracefully handle EEXIST if the cgroup already exists, so
415
         * we can reuse its ownership logic without penalty.
416
         */
417
        ret = cgroup_create_cgroup(cgroup, ignore_ownership);
×
418

419
err:
×
420
        if (copy1)
×
421
                free(copy1);
×
422
        if (copy2)
×
423
                free(copy2);
×
424

425
        return ret;
×
426
}
427

428
bool cgroup_is_systemd_enabled(void)
×
429
{
430
        return true;
×
431
}
432
#else
433
int cgroup_set_default_scope_opts(struct cgroup_systemd_scope_opts * const opts)
×
434
{
435
        cgroup_err("Systemd support not compiled\n");
×
436
        return 1;
×
437
}
438

439
int cgroup_create_scope(const char * const scope_name, const char * const slice_name,
×
440
                        const struct cgroup_systemd_scope_opts * const opts)
441
{
442
        cgroup_err("Systemd support not compiled\n");
×
443
        return 1;
×
444
}
445

446
int cgroup_create_scope2(struct cgroup *cgroup, int ignore_ownership,
×
447
                         const struct cgroup_systemd_scope_opts * const opts)
448
{
449
        cgroup_err("Systemd support not compiled\n");
×
450
        return 1;
×
451
}
452

453
bool cgroup_is_systemd_enabled(void)
×
454
{
455
        return false;
×
456
}
457
#endif
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