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

drakenclimber / libcgroup / 15115411636

19 May 2025 02:16PM UTC coverage: 54.501% (-2.0%) from 56.506%
15115411636

push

github

drakenclimber
ftests: Add systemd set-property support to cgset

Add support for setting systemd properties, via the -p flag, to the
ftests' cgset method.

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

5606 of 10286 relevant lines covered (54.5%)

330.4 hits per line

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

51.4
/src/systemd.c
1
/* SPDX-License-Identifier: LGPL-2.1-only */
2
/**
3
 * Copyright (c) 2022-2025 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
const struct systemd_property_and_type systemd_property_table[] = {
59
        { "AllowedCPUs", SD_BUS_TYPE_ARRAY, SD_BUS_TYPE_BYTE },
60
        { "CPUAccounting", SD_BUS_TYPE_BOOLEAN, _SD_BUS_TYPE_INVALID },
61
        { "CPUQuotaPeriodUSec", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
62
        { "CPUQuotaPerSecUSec", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
63
        { "CPUWeight", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
64
        { "DefaultMemoryLow", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
65
        { "DefaultMemoryLowScale", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
66
        { "DefaultMemoryMin", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
67
        { "DefaultMemoryMinScale", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
68
        { "DefaultStartupMemoryLow", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
69
        { "IOAccounting", SD_BUS_TYPE_BOOLEAN, _SD_BUS_TYPE_INVALID },
70
        { "IOWeight", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
71
        { "MemoryAccounting", SD_BUS_TYPE_BOOLEAN, _SD_BUS_TYPE_INVALID },
72
        { "MemoryHigh", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
73
        { "MemoryHighScale", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
74
        { "MemoryLow", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
75
        { "MemoryLowScale", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
76
        { "MemoryMax", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
77
        { "MemoryMaxScale", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
78
        { "MemoryMin", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
79
        { "MemoryMinScale", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
80
        { "MemorySwapMax", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
81
        { "MemorySwapMaxScale", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
82
        { "MemoryZSwapMax", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
83
        { "MemoryZSwapMaxScale", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
84
        { "MemoryZSwapWriteback", SD_BUS_TYPE_BOOLEAN, _SD_BUS_TYPE_INVALID },
85
        { "StartupAllowedCPUs", SD_BUS_TYPE_ARRAY, SD_BUS_TYPE_BYTE },
86
        { "StartupIOWeight", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
87
        { "StartupMemoryLow", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
88
        { "StartupMemoryHigh", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
89
        { "StartupMemoryMax", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
90
        { "StartupMemorySwapMax", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
91
        { "StartupMemoryZSwapMax", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
92
        { "TasksAccounting", SD_BUS_TYPE_BOOLEAN, _SD_BUS_TYPE_INVALID },
93
        { "TasksMax", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
94
        { "TasksMaxScale", SD_BUS_TYPE_UINT64, _SD_BUS_TYPE_INVALID },
95
};
96
const int systemd_property_table_sz = ARRAY_SIZE(systemd_property_table);
97

98
int cgroup_set_default_scope_opts(struct cgroup_systemd_scope_opts * const opts)
37✔
99
{
100
        if (!opts)
37✔
101
                return ECGINVAL;
×
102

103
        opts->delegated = 1;
37✔
104
        opts->mode = CGROUP_SYSTEMD_MODE_FAIL;
37✔
105
        opts->pid = -1;
37✔
106

107
        return 0;
37✔
108
}
109

110
/*
111
 * Returns time elapsed in usec
112
 *
113
 * Inspired-by: https://github.com/cockpit-project/cockpit/blob/main/src/tls/socket-io.c#L39
114
 */
115
static int64_t elapsed_time(const struct timespec * const start, const struct timespec * const end)
2,100✔
116
{
117
        int64_t elapsed = (end->tv_sec - start->tv_sec) * 1000000 +
2,100✔
118
                          (end->tv_nsec - start->tv_nsec) / 1000;
2,100✔
119

120
        assert(elapsed >= 0);
2,100✔
121

122
        return elapsed;
2,100✔
123
}
124

125
static int job_removed_callback(sd_bus_message *message, void *user_data, sd_bus_error *error)
19✔
126
{
127
        const char *result, *msg_path, *scope_name;
128
        const char **job_path = user_data;
19✔
129
        int ret;
130

131
        ret = sd_bus_message_read(message, "uoss", NULL, &msg_path, &scope_name, &result);
19✔
132
        if (ret < 0) {
19✔
133
                cgroup_err("callback message read failed: %d\n", errno);
×
134
                return 0;
×
135
        }
136

137
        if (*job_path == NULL || strcmp(msg_path, *job_path) != 0) {
19✔
138
                cgroup_dbg("Received a systemd signal, but it was not our message\n");
2✔
139
                return 0;
2✔
140
        }
141

142
        cgroup_dbg("Received JobRemoved signal for scope %s.  Result: %s\n", scope_name, result);
17✔
143

144
        /*
145
         * Use the job_path pointer as a way to inform the original thread that the job has
146
         * completed.
147
         */
148
        *job_path = NULL;
17✔
149
        return 0;
17✔
150
}
151

152
static int validate_scope_slice_name(const char * const scope_name, const char * const slice_name)
29✔
153
{
154
        char *_scope_name = NULL;
29✔
155
        int i, len, ret = 0;
29✔
156

157
        /* get the scope name without the suffix, min length is <n>.scope */
158
        len = strlen(scope_name) - 6;
29✔
159
        if (len < 1)
29✔
160
                cgroup_warn("Invalid scope name %s\n", scope_name);
2✔
161
        else if (strcmp(&scope_name[len], ".scope") != 0)
27✔
162
                cgroup_warn("scope doesn't have expected suffix\n");
2✔
163

164
        /* get the slice name without the suffix, min length is <n>.slice */
165
        len = strlen(slice_name) - 6;
29✔
166
        if (len < 1)
29✔
167
                cgroup_warn("Invalid slice name %s\n", slice_name);
2✔
168
        else if (strcmp(&slice_name[len], ".slice") != 0)
27✔
169
                cgroup_warn("slice doesn't have expected suffix\n");
2✔
170

171
        /*
172
         * systemd will silently prefix a '_' to the scope name and
173
         * create, and delegate it under the slice. If it matches with any
174
         * of the controller in the controller_name[]. i.e.,
175
         * oracle.slice/cpuset.scope, will be created as
176
         * oracle.slice/_cpuset.scope
177
         *
178
         * Disallow such scope names, that will cause confusion to the
179
         * users, where they might try to operate scope cgroup, but
180
         * the delegated scope is _scope cgroup.
181
         */
182
        _scope_name = strdup(scope_name);
29✔
183
        if (_scope_name == NULL) {
29✔
184
                cgroup_err("Failed to allocate memory for _scope_name\n");
×
185
                last_errno = errno;
×
186
                ret = 1;
×
187
                goto out;
×
188
        }
189

190
        len = strlen(_scope_name) - 6;        /* strlen(".scope") + '\0' */
29✔
191
        _scope_name[len] = '\0';
29✔
192

193
        for (i = 0; i < ARRAY_SIZE(controller_name); i++) {
380✔
194
                if (strcmp(_scope_name, controller_name[i]))
353✔
195
                        continue;
351✔
196

197
                cgroup_err("Invalid scope name, using controller name %s\n", controller_name[i]);
2✔
198
                ret = 1;
2✔
199
                break;
2✔
200
        }
201

202
        free(_scope_name);
29✔
203
out:
29✔
204
        return ret;
29✔
205
}
206

207
int cgroup_create_scope(const char * const scope_name, const char * const slice_name,
29✔
208
                        const struct cgroup_systemd_scope_opts * const opts)
209
{
210
        sd_bus_message *msg = NULL, *reply = NULL;
29✔
211
        int ret = 0, sdret = 0, cgret = ECGFAIL;
29✔
212
        sd_bus_error error = SD_BUS_ERROR_NULL;
29✔
213
        const char *job_path = NULL;
29✔
214
        struct timespec start, now;
215
        sd_bus *bus = NULL;
29✔
216
        pid_t child_pid;
217

218
        if (!scope_name || !slice_name || !opts)
29✔
219
                return ECGINVAL;
×
220

221
        if (validate_scope_slice_name(scope_name, slice_name))
29✔
222
                return ECGINVAL;
2✔
223

224
        if (opts->mode >= CGROUP_SYSTEMD_MODE_CNT) {
27✔
225
                cgroup_err("invalid systemd mode: %d\n", opts->mode);
×
226
                return ECGINVAL;
×
227
        }
228

229
        if (opts->mode == CGROUP_SYSTEMD_MODE_ISOLATE ||
27✔
230
            opts->mode == CGROUP_SYSTEMD_MODE_IGNORE_DEPS ||
27✔
231
            opts->mode == CGROUP_SYSTEMD_MODE_IGNORE_REQS) {
27✔
232
                cgroup_err("unsupported systemd mode: %d\n", opts->mode);
×
233
                return ECGINVAL;
×
234
        }
235

236
        if (opts->pid < 0) {
27✔
237
                child_pid = fork();
14✔
238
                if (child_pid < 0) {
28✔
239
                        last_errno = errno;
×
240
                        cgroup_err("fork failed: %d\n", errno);
×
241
                        return ECGOTHER;
×
242
                }
243

244
                if (child_pid == 0) {
28✔
245
                        static char * const args[] = {"libcgroup_systemd_idle_thread", NULL};
246

247
                        /*
248
                         * Have the child sleep forever.  Systemd will delete the scope if
249
                         * there isn't a running process in it.
250
                         */
251
                        execvp("libcgroup_systemd_idle_thread", args);
14✔
252

253
                        /* The child process should never get here */
254
                        last_errno = errno;
14✔
255
                        cgroup_err("failed to create system idle thread.\n");
14✔
256
                        return ECGOTHER;
×
257
                }
258

259
                cgroup_dbg("created libcgroup_system_idle thread pid %d\n", child_pid);
14✔
260
        } else {
261
                child_pid = opts->pid;
13✔
262
        }
263
        cgroup_dbg("pid %d will be placed in scope %s\n", child_pid, scope_name);
27✔
264

265
        sdret = sd_bus_default_system(&bus);
27✔
266
        if (sdret < 0) {
27✔
267
                cgroup_err("failed to open the system bus: %d\n", errno);
×
268
                goto out;
×
269
        }
270

271
        sdret = sd_bus_match_signal(bus, NULL, sender, path, interface,
27✔
272
                                    "JobRemoved", job_removed_callback, &job_path);
273
        if (sdret < 0) {
27✔
274
                cgroup_err("failed to install match callback: %d\n", errno);
×
275
                goto out;
×
276
        }
277

278
        sdret = sd_bus_message_new_method_call(bus, &msg, sender, path, interface,
27✔
279
                                               "StartTransientUnit");
280
        if (sdret < 0) {
27✔
281
                cgroup_err("failed to create the systemd msg: %d\n", errno);
×
282
                goto out;
×
283
        }
284

285
        sdret = sd_bus_message_append(msg, "ss", scope_name, modes[opts->mode]);
27✔
286
        if (sdret < 0) {
27✔
287
                cgroup_err("failed to append the scope name: %d\n", errno);
×
288
                goto out;
×
289
        }
290

291
        sdret = sd_bus_message_open_container(msg, 'a', "(sv)");
27✔
292
        if (sdret < 0) {
27✔
293
                cgroup_err("failed to open container: %d\n", errno);
×
294
                goto out;
×
295
        }
296

297
        sdret = sd_bus_message_append(msg, "(sv)", "Description", "s",
27✔
298
                                      "scope created by libcgroup");
299
        if (sdret < 0) {
27✔
300
                cgroup_err("failed to append the description: %d\n", errno);
×
301
                goto out;
×
302
        }
303

304
        sdret = sd_bus_message_append(msg, "(sv)", "PIDs", "au", 1, child_pid);
27✔
305
        if (sdret < 0) {
27✔
306
                cgroup_err("failed to append the PID: %d\n", errno);
×
307
                goto out;
×
308
        }
309

310
        sdret = sd_bus_message_append(msg, "(sv)", "Slice", "s", slice_name);
27✔
311
        if (sdret < 0) {
27✔
312
                cgroup_err("failed to append the slice: %d\n", errno);
×
313
                goto out;
×
314
        }
315

316
        if (opts->delegated == 1) {
27✔
317
                sdret = sd_bus_message_append(msg, "(sv)", "Delegate", "b", 1);
27✔
318
                if (sdret < 0) {
27✔
319
                        cgroup_err("failed to append delegate: %d\n", errno);
×
320
                        goto out;
×
321
                }
322
        }
323

324
        sdret = sd_bus_message_close_container(msg);
27✔
325
        if (sdret < 0) {
27✔
326
                cgroup_err("failed to close the container: %d\n", errno);
×
327
                goto out;
×
328
        }
329

330
        sdret = sd_bus_message_append(msg, "a(sa(sv))", 0);
27✔
331
        if (sdret < 0) {
27✔
332
                cgroup_err("failed to append aux structure: %d\n", errno);
×
333
                goto out;
×
334
        }
335

336
        sdret = sd_bus_call(bus, msg, 0, &error, &reply);
27✔
337
        if (sdret < 0) {
27✔
338
                cgroup_err("sd_bus_call() failed: %d\n",
10✔
339
                           sd_bus_message_get_errno(msg));
340
                cgroup_err("error message: %s\n", error.message);
10✔
341
                goto out;
10✔
342
        }
343

344
        /* Receive the job_path from systemd */
345
        sdret = sd_bus_message_read(reply, "o", &job_path);
17✔
346
        if (sdret < 0) {
17✔
347
                cgroup_err("failed to read reply: %d\n", errno);
×
348
                goto out;
×
349
        }
350

351
        cgroup_dbg("job_path = %s\n", job_path);
17✔
352

353
        ret = clock_gettime(CLOCK_MONOTONIC, &start);
17✔
354
        if (ret < 0) {
17✔
355
                last_errno = errno;
×
356
                cgroup_err("Failed to get time: %d\n", errno);
×
357
                cgret = ECGOTHER;
×
358
                goto out;
×
359
        }
360

361
        /* The callback will null out the job_path pointer on completion */
362
        while (job_path) {
2,117✔
363
                sdret = sd_bus_process(bus, NULL);
2,100✔
364
                if (sdret < 0) {
2,100✔
365
                        cgroup_err("failed to process the sd bus: %d\n", errno);
×
366
                        goto out;
×
367
                }
368

369
                if (sdret == 0) {
2,100✔
370
                        /*
371
                         * Per the sd_bus_wait() man page, call this function after sd_bus_process
372
                         * returns zero. The wait time (usec) was somewhat arbitrarily chosen
373
                         */
374
                        sdret = sd_bus_wait(bus, 10);
2,064✔
375
                        if (sdret < 0) {
2,064✔
376
                                cgroup_err("failed to wait for sd bus: %d\n", errno);
×
377
                                goto out;
×
378
                        }
379
                }
380

381
                ret = clock_gettime(CLOCK_MONOTONIC, &now);
2,100✔
382
                if (ret < 0) {
2,100✔
383
                        last_errno = errno;
×
384
                        cgroup_err("Failed to get time: %d\n", errno);
×
385
                        cgret = ECGOTHER;
×
386
                        goto out;
×
387
                }
388

389
                if (elapsed_time(&start, &now) > USEC_PER_SEC) {
2,100✔
390
                        cgroup_err("The create scope command timed out\n");
×
391
                        goto out;
×
392
                }
393
        }
394

395
        cgret = 0;
17✔
396

397
out:
27✔
398
        if (cgret && opts->pid < 0)
27✔
399
                kill(child_pid, SIGTERM);
8✔
400

401
        sd_bus_error_free(&error);
27✔
402
        sd_bus_message_unref(msg);
27✔
403
        sd_bus_message_unref(reply);
27✔
404
        sd_bus_unref(bus);
27✔
405

406
        return cgret;
27✔
407
}
408

409
int cgroup_create_scope2(struct cgroup *cgroup, int ignore_ownership,
21✔
410
                         const struct cgroup_systemd_scope_opts * const opts)
411
{
412
        char *copy1 = NULL, *copy2 = NULL, *slash, *slice_name, *scope_name;
21✔
413
        int ret = 0;
21✔
414

415
        if (!cgroup)
21✔
416
                return ECGROUPNOTALLOWED;
×
417

418
        slash = strstr(cgroup->name, "/");
21✔
419
        if (!slash) {
21✔
420
                cgroup_err("cgroup name does not contain a slash: %s\n", cgroup->name);
1✔
421
                return ECGINVAL;
1✔
422
        }
423

424
        slash = strstr(slash + 1, "/");
20✔
425
        if (slash) {
20✔
426
                cgroup_err("cgroup name contains more than one slash: %s\n", cgroup->name);
1✔
427
                return ECGINVAL;
1✔
428
        }
429

430
        copy1 = strdup(cgroup->name);
19✔
431
        if (!copy1) {
19✔
432
                last_errno = errno;
×
433
                ret = ECGOTHER;
×
434
                goto err;
×
435
        }
436

437
        scope_name = basename(copy1);
19✔
438

439
        copy2 = strdup(cgroup->name);
19✔
440
        if (!copy2) {
19✔
441
                last_errno = errno;
×
442
                ret = ECGOTHER;
×
443
                goto err;
×
444
        }
445

446
        slice_name = dirname(copy2);
19✔
447

448
        ret = cgroup_create_scope(scope_name, slice_name, opts);
19✔
449
        if (ret)
19✔
450
                goto err;
11✔
451

452
        /*
453
         * Utilize cgroup_create_cgroup() to assign the requested owner/group and permissions.
454
         * cgroup_create_cgroup() can gracefully handle EEXIST if the cgroup already exists, so
455
         * we can reuse its ownership logic without penalty.
456
         */
457
        ret = cgroup_create_cgroup(cgroup, ignore_ownership);
8✔
458

459
err:
19✔
460
        if (copy1)
19✔
461
                free(copy1);
19✔
462
        if (copy2)
19✔
463
                free(copy2);
19✔
464

465
        return ret;
19✔
466
}
467

468
bool cgroup_is_systemd_enabled(void)
17✔
469
{
470
        return true;
17✔
471
}
472

473
static int append_byte_array(sd_bus_message * const msg, const char * const setting,
×
474
                             const struct cgroup_systemd_value * const value)
475
{
476
        int sdret;
477

478
        sdret = sd_bus_message_open_container(msg, SD_BUS_TYPE_STRUCT, "sv");
×
479
        if (sdret < 0) {
×
480
                cgroup_err("failed to open sv container: %d\n", errno);
×
481
                goto out;
×
482
        }
483

484
        sdret = sd_bus_message_append(msg, "s", setting);
×
485
        if (sdret < 0) {
×
486
                cgroup_err("failed to append the setting, %s: %d\n", setting, errno);
×
487
                goto out;
×
488
        }
489

490
        sdret = sd_bus_message_open_container(msg, 'v', "ay");
×
491
        if (sdret < 0) {
×
492
                cgroup_err("failed to open byte array container: %d\n", errno);
×
493
                goto out;
×
494
        }
495

496
        sdret = sd_bus_message_append_array(msg, 'y', value->byte_array, value->array_len);
×
497
        if (sdret < 0) {
×
498
                cgroup_err("failed to append byte array: %d\n", errno);
×
499
                goto out;
×
500
        }
501

502
        sdret = sd_bus_message_close_container(msg);
×
503
        if (sdret < 0) {
×
504
                cgroup_err("failed to close the byte array container: %d\n", errno);
×
505
                goto out;
×
506
        }
507

508
        sdret = sd_bus_message_close_container(msg);
×
509
        if (sdret < 0) {
×
510
                cgroup_err("failed to close the sv container: %d\n", errno);
×
511
                goto out;
×
512
        }
513

514
out:
×
515
        return sdret;
×
516
}
517

518
int cgroup_set_property(const char * const cgrp, const char * const setting,
×
519
                        const struct cgroup_systemd_value * const value,
520
                        const struct cgroup_systemd_property_opts * const opts)
521
{
522
        sd_bus_message *msg = NULL, *reply = NULL;
×
523
        sd_bus_error error = SD_BUS_ERROR_NULL;
×
524
        int sdret = 0, cgret = ECGFAIL;
×
525
        sd_bus *bus = NULL;
×
526
        char t[2];
527

528
        if (!cgrp || !setting || !value || !opts)
×
529
                return ECGINVAL;
×
530

531
        sdret = sd_bus_default_system(&bus);
×
532
        if (sdret < 0) {
×
533
                cgroup_err("failed to open the system bus: %d\n", errno);
×
534
                goto out;
×
535
        }
536

537
        sdret = sd_bus_message_new_method_call(bus, &msg, sender, path, interface,
×
538
                                               "SetUnitProperties");
539
        if (sdret < 0) {
×
540
                cgroup_err("failed to create the systemd msg: %d\n", errno);
×
541
                goto out;
×
542
        }
543

544
        sdret = sd_bus_message_append(msg, "s", cgrp);
×
545
        if (sdret < 0) {
×
546
                cgroup_err("failed to append the cgroup name: %d\n", errno);
×
547
                goto out;
×
548
        }
549

550
        sdret = sd_bus_message_append(msg, "b", opts->runtime);
×
551
        if (sdret < 0) {
×
552
                cgroup_err("failed to append the runtime value: %d\n", errno);
×
553
                goto out;
×
554
        }
555

556
        sdret = sd_bus_message_open_container(msg, 'a', "(sv)");
×
557
        if (sdret < 0) {
×
558
                cgroup_err("failed to open container: %d\n", errno);
×
559
                goto out;
×
560
        }
561

562
        t[0] = value->type;
×
563
        t[1] = '\0';
×
564

565
        switch (value->type) {
×
566
        case SD_BUS_TYPE_BYTE:
×
567
                sdret = sd_bus_message_append(msg, "(sv)", setting, t, value->byte_value);
×
568
                break;
×
569
        case SD_BUS_TYPE_ARRAY:
×
570
                switch (value->array_type) {
×
571
                        case SD_BUS_TYPE_BYTE:
×
572
                                sdret = append_byte_array(msg, setting, value);
×
573
                                break;
×
574
                        default:
×
575
                                cgroup_err("unsupported d-bus array type: %d\n",
×
576
                                           value->array_type);
577
                                break;
×
578
                }
579
                break;
×
580
        case SD_BUS_TYPE_BOOLEAN:
×
581
                sdret = sd_bus_message_append(msg, "(sv)", setting, t, value->bool_value);
×
582
                break;
×
583
        case SD_BUS_TYPE_STRING:
×
584
                sdret = sd_bus_message_append(msg, "(sv)", setting, t, value->str_value);
×
585
                break;
×
586
        case SD_BUS_TYPE_INT32:
×
587
                sdret = sd_bus_message_append(msg, "(sv)", setting, t, value->int_value);
×
588
                break;
×
589
        case SD_BUS_TYPE_UINT32:
×
590
                sdret = sd_bus_message_append(msg, "(sv)", setting, t, value->uint_value);
×
591
                break;
×
592
        case SD_BUS_TYPE_INT64:
×
593
                sdret = sd_bus_message_append(msg, "(sv)", setting, t, value->ll_value);
×
594
                break;
×
595
        case SD_BUS_TYPE_UINT64:
×
596
                sdret = sd_bus_message_append(msg, "(sv)", setting, t, value->ull_value);
×
597
                break;
×
598
        case _SD_BUS_TYPE_INVALID:
×
599
                cgroup_err("Invalid systemd type\n");
×
600
                sdret = -1;
×
601
                break;
×
602
        }
603
        if (sdret < 0) {
×
604
                cgroup_err("failed to append the description: %d\n", errno);
×
605
                goto out;
×
606
        }
607

608
        sdret = sd_bus_message_close_container(msg);
×
609
        if (sdret < 0) {
×
610
                cgroup_err("failed to close the container: %d\n", errno);
×
611
                goto out;
×
612
        }
613

614
        sdret = sd_bus_call(bus, msg, 0, &error, &reply);
×
615
        if (sdret < 0) {
×
616
                cgroup_err("sd_bus_call() failed: %d\n",
×
617
                           sd_bus_message_get_errno(msg));
618
                cgroup_err("error message: %s\n", error.message);
×
619
                goto out;
×
620
        }
621

622
        cgret = 0;
×
623

624
out:
×
625
        sd_bus_error_free(&error);
×
626
        sd_bus_message_unref(msg);
×
627
        sd_bus_message_unref(reply);
×
628
        sd_bus_unref(bus);
×
629

630
        return cgret;
×
631
}
632

633
static int insert_cpu_into_byte_array(unsigned int cpu, unsigned char **array,
50✔
634
                                      int * const array_len)
635
{
636
        unsigned int byte_num;
637
        void *tmp;
638
        int i;
639

640
        byte_num = cpu / 8;
50✔
641

642
        if (byte_num >= (*array_len)) {
50✔
643
                tmp = realloc((*array), sizeof(unsigned char) * (byte_num + 1));
10✔
644
                if (tmp == NULL)
10✔
645
                        return ECGOTHER;
×
646

647
                *array = tmp;
10✔
648

649
                for (i = (*array_len); i < byte_num + 1; i++) {
23✔
650
                        /* zero out the newly added memory */
651
                        (*array)[i] = 0;
13✔
652
                }
653

654
                *array_len = byte_num + 1;
10✔
655
        }
656

657
        (*array)[byte_num] |= 1u << (cpu % 8);
50✔
658

659
        return 0;
50✔
660
}
661

662
int cgroup_systemd_cpuset_str_to_byte_array(char * const in_str, unsigned char **byte_array,
9✔
663
                                            int * const array_len)
664
{
665
        char *comma_saveptr = NULL, *hyphen_saveptr = NULL;
9✔
666
        char *comma_token = NULL, *hyphen_token = NULL;
9✔
667
        unsigned char *tmp_array;
668
        unsigned int cpu;
669
        int start, end;
670
        int ret, i;
671

672
        if (!in_str || !byte_array || !array_len)
9✔
673
                return ECGINVAL;
3✔
674

675
        *array_len = 0;
6✔
676
        tmp_array = NULL;
6✔
677

678
        comma_token = strtok_r(in_str, ",", &comma_saveptr);
6✔
679

680
        do {
681
                if (strstr(comma_token, "-") != NULL) {
28✔
682
                        hyphen_token = strtok_r(comma_token, "-", &hyphen_saveptr);
6✔
683
                        start = atoi(hyphen_token);
6✔
684

685
                        hyphen_token = strtok_r(NULL, "-", &hyphen_saveptr);
6✔
686
                        end = atoi(hyphen_token);
6✔
687

688
                        for (i = start; i <= end; i++) {
34✔
689
                                ret = insert_cpu_into_byte_array(i, &tmp_array, array_len);
28✔
690
                                if (ret)
28✔
691
                                        return ret;
×
692
                        }
693
                } else {
694
                        /* a single cpu was provided */
695
                        cpu = atoi(comma_token);
22✔
696

697
                        ret = insert_cpu_into_byte_array(cpu, &tmp_array, array_len);
22✔
698
                        if (ret)
22✔
699
                                return ret;
×
700
                }
701
        } while ((comma_token = strtok_r(NULL, ",", &comma_saveptr)));
28✔
702

703
        *byte_array = tmp_array;
6✔
704

705
        return 0;
6✔
706
}
707
#else
708
int cgroup_set_default_scope_opts(struct cgroup_systemd_scope_opts * const opts)
709
{
710
        cgroup_err("Systemd support not compiled\n");
711
        return 1;
712
}
713

714
int cgroup_create_scope(const char * const scope_name, const char * const slice_name,
715
                        const struct cgroup_systemd_scope_opts * const opts)
716
{
717
        cgroup_err("Systemd support not compiled\n");
718
        return 1;
719
}
720

721
int cgroup_create_scope2(struct cgroup *cgroup, int ignore_ownership,
722
                         const struct cgroup_systemd_scope_opts * const opts)
723
{
724
        cgroup_err("Systemd support not compiled\n");
725
        return 1;
726
}
727

728
bool cgroup_is_systemd_enabled(void)
729
{
730
        return false;
731
}
732

733
int cgroup_set_property(const char * const cgrp, const char * const setting,
734
                        const struct cgroup_systemd_value * const value,
735
                        const struct cgroup_systemd_property_opts * const opts)
736
{
737
        cgroup_err("Systemd support not compiled\n");
738
        return 1;
739
}
740

741
int cgroup_systemd_cpuset_str_to_byte_array(char * const in_str, unsigned char **byte_array,
742
                                            int * const array_len)
743
{
744
        return ECGROUPUNSUPP;
745
}
746
#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