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

drakenclimber / libcgroup / 23307563353

19 Mar 2026 05:18PM UTC coverage: 56.24% (+0.03%) from 56.21%
23307563353

push

github

drakenclimber
ftests/077: drop unused global and fix list sorting

flake8 reported:
tests/ftests/077-pybindings-cgroup_get_procs.py:52:5: F824  is unused:
	name is never assigned in scope

The test already appends to the module-level list, so we do not reassign
it inside setup(). Drop the unused global declaration and call
list.sort() without reassigning the result to keep the pid lists in order
before comparing them.

Signed-off-by: Kamalesh Babulal <kamalesh.babulal@oracle.com>
Signed-off-by: Tom Hromatka <tom.hromatka@oracle.com>

5624 of 10000 relevant lines covered (56.24%)

609.26 hits per line

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

61.74
/src/api.c
1
// SPDX-License-Identifier: LGPL-2.1-only
2
/**
3
 * Copyright IBM Corporation. 2007
4
 *
5
 * Author:        Dhaval Giani <dhaval@linux.vnet.ibm.com>
6
 * Author:        Balbir Singh <balbir@linux.vnet.ibm.com>
7
 *
8
 * TODOs:
9
 *        1. Add more APIs for the control groups.
10
 *        2. Handle the configuration related APIs.
11
 *
12
 * Code initiated and designed by Dhaval Giani. All faults are most likely
13
 * his mistake.
14
 *
15
 * Bharata B Rao <bharata@linux.vnet.ibm.com> is willing is take blame
16
 * for mistakes in APIs for reading statistics.
17
 */
18

19
#ifndef _GNU_SOURCE
20
#define _GNU_SOURCE
21
#endif
22

23
#include <libcgroup.h>
24
#include <libcgroup-internal.h>
25

26
#include <pthread.h>
27
#include <dirent.h>
28
#include <unistd.h>
29
#include <mntent.h>
30
#include <stdlib.h>
31
#include <string.h>
32
#include <libgen.h>
33
#include <assert.h>
34
#include <errno.h>
35
#include <stdio.h>
36
#include <fcntl.h>
37
#include <ctype.h>
38
#include <fts.h>
39
#include <pwd.h>
40
#include <grp.h>
41

42
#include <sys/syscall.h>
43
#include <sys/socket.h>
44
#include <sys/types.h>
45
#include <sys/stat.h>
46
#include <sys/vfs.h>
47

48
#include <linux/un.h>
49

50
const struct cgroup_library_version library_version = {
51
        .major = CGROUP_VER_MAJOR,
52
        .minor = CGROUP_VER_MINOR,
53
        .release = CGROUP_VER_RELEASE,
54
};
55

56
/*
57
 * The errno which happened the last time (have to be thread specific)
58
 */
59
__thread int last_errno;
60

61
#define MAXLEN 256
62

63
/* the value have to be thread specific */
64
static __thread char errtext[MAXLEN];
65

66
/* Task command name length */
67
#define TASK_COMM_LEN 16
68

69
/* Check if cgroup_init has been called or not. */
70
static int cgroup_initialized;
71

72
/* List of configuration rules */
73
static struct cgroup_rule_list rl;
74

75
/* Temporary list of configuration rules (for non-cache apps) */
76
static struct cgroup_rule_list trl;
77

78
/* Lock for the list of rules (rl) */
79
static pthread_rwlock_t rl_lock = PTHREAD_RWLOCK_INITIALIZER;
80

81
/* Cgroup v2 mount path.  Null if v2 isn't mounted */
82
char cg_cgroup_v2_mount_path[FILENAME_MAX];
83

84
/* Namespace */
85
__thread char *cg_namespace_table[CG_CONTROLLER_MAX];
86

87
pthread_rwlock_t cg_mount_table_lock = PTHREAD_RWLOCK_INITIALIZER;
88
struct cg_mount_table_s cg_mount_table[CG_CONTROLLER_MAX];
89

90
/* Cgroup v2 mount paths, with empty controllers */
91
struct cg_mount_point *cg_cgroup_v2_empty_mount_paths;
92

93
#ifdef WITH_SYSTEMD
94
/* Default systemd path name. Length: <name>.slice/<name>.scope */
95
char systemd_default_cgroup[FILENAME_MAX * 2 + 1];
96
#endif
97

98
const char * const cgroup_strerror_codes[] = {
99
        "Cgroup is not compiled in",
100
        "Cgroup is not mounted",
101
        "Cgroup does not exist",
102
        "Cgroup has not been created",
103
        "Cgroup one of the needed subsystems is not mounted",
104
        "Cgroup, request came in from non owner",
105
        "Cgroup controllers are bound to different mount points",
106
        "Cgroup, operation not allowed",
107
        "Cgroup value set exceeds maximum",
108
        "Cgroup controller already exists",
109
        "Cgroup value already exists",
110
        "Cgroup invalid operation",
111
        "Cgroup, creation of controller failed",
112
        "Cgroup operation failed",
113
        "Cgroup not initialized",
114
        "Cgroup, requested group parameter does not exist",
115
        "Cgroup generic error",
116
        "Cgroup values are not equal",
117
        "Cgroup controllers are different",
118
        "Cgroup parsing failed",
119
        "Cgroup, rules file does not exist",
120
        "Cgroup mounting failed",
121
        "",                                /* 50022 is reserved for future errors */
122
        "End of File or iterator",
123
        "Failed to parse config file",
124
        "Have multiple paths for the same namespace",
125
        "Controller in namespace does not exist",
126
        "Either mount or namespace keyword has to be specified in the configuration file",
127
        "This kernel does not support this feature",
128
        "Value setting does not succeed",
129
        "Failed to remove a non-empty group",
130
        "Failed to convert from cgroup v1 to/from cgroup v2",
131
};
132

133
static const char * const cgroup_ignored_tasks_files[] = { "tasks", NULL };
134

135
#ifndef UNIT_TEST
136
static int cg_get_cgroups_from_proc_cgroups(pid_t pid, char *cgrp_list[],
137
                                            char *controller_list[], int list_len);
138

139
static int cgroupv2_get_subtree_control(const char *path, const char *ctrl_name,
140
                                        bool * const enabled);
141
#endif
142

143
static int cg_chown(const char *filename, uid_t owner, gid_t group)
14,452✔
144
{
145
        if (owner == NO_UID_GID)
14,452✔
146
                owner = getuid();
13,741✔
147
        if (group == NO_UID_GID)
14,452✔
148
                group = getgid();
13,741✔
149

150
        return chown(filename, owner, group);
14,452✔
151
}
152
static int cg_chown_file(FTS *fts, FTSENT *ent, uid_t owner, gid_t group)
14,451✔
153
{
154
        const char *filename = fts->fts_path;
14,451✔
155
        int ret = 0;
14,451✔
156

157
        cgroup_dbg("chown: seeing file %s\n", filename);
14,451✔
158
        switch (ent->fts_info) {
14,451✔
159
        case FTS_ERR:
×
160
                errno = ent->fts_errno;
×
161
                break;
×
162
        case FTS_D:
14,451✔
163
        case FTS_DC:
164
        case FTS_NSOK:
165
        case FTS_NS:
166
        case FTS_DNR:
167
        case FTS_DP:
168
        case FTS_F:
169
        case FTS_DEFAULT:
170
                ret = cg_chown(filename, owner, group);
14,451✔
171
                break;
14,451✔
172
        }
173
        if (ret < 0) {
14,451✔
174
                cgroup_warn("cannot change owner of file %s: %s\n", filename, strerror(errno));
×
175
                last_errno = errno;
×
176
                ret = ECGOTHER;
×
177
        }
178
        return ret;
14,451✔
179
}
180

181
/* TODO: Need to decide a better place to put this function. */
182
static int cg_chown_recursive(char **path, uid_t owner, gid_t group)
237✔
183
{
184
        int ret = 0;
237✔
185
        FTS *fts;
186

187
        cgroup_dbg("chown: path is %s\n", *path);
237✔
188
        fts = fts_open(path, FTS_PHYSICAL | FTS_NOCHDIR | FTS_NOSTAT, NULL);
237✔
189
        if (fts == NULL) {
237✔
190
                cgroup_warn("cannot open directory %s: %s\n", path, strerror(errno));
×
191
                last_errno = errno;
×
192
                return ECGOTHER;
×
193
        }
194

195
        while (1) {
14,451✔
196
                FTSENT *ent;
197

198
                ent = fts_read(fts);
14,688✔
199
                if (!ent) {
14,688✔
200
                        cgroup_warn("fts_read failed\n");
237✔
201
                        break;
237✔
202
                }
203
                ret = cg_chown_file(fts, ent, owner, group);
14,451✔
204
        }
205
        fts_close(fts);
237✔
206

207
        return ret;
237✔
208
}
209

210
int cg_chmod_path(const char *path, mode_t mode, int owner_is_umask)
408✔
211
{
212
        mode_t mask = -1U;
408✔
213
        struct stat buf;
214
        int fd;
215

216
        fd = open(path, O_RDONLY);
408✔
217
        if (fd == -1)
408✔
218
                goto fail;
×
219

220
        if (owner_is_umask) {
408✔
221
                mode_t umask, gmask, omask;
222
                /*
223
                 * Use owner permissions as an umask for group and others
224
                 * permissions because we trust kernel to initialize owner
225
                 * permissions to something useful.  Keep SUID and SGID bits.
226
                 */
227
                if (fstat(fd, &buf) == -1)
408✔
228
                        goto fail;
×
229

230
                /* 0700 == S_IRWXU */
231
                umask = 0700 & buf.st_mode;
408✔
232
                gmask = umask >> 3;
408✔
233
                omask = gmask >> 3;
408✔
234

235
                mask = umask|gmask|omask|S_ISUID|S_ISGID|S_ISVTX;
408✔
236
        }
237

238
        if (fchmod(fd, mode & mask))
408✔
239
                goto fail;
×
240

241
        close(fd);
408✔
242

243
        return 0;
408✔
244

245
fail:
×
246
        cgroup_warn("cannot change permissions of file %s: %s\n", path, strerror(errno));
×
247
        last_errno = errno;
×
248

249
        if (fd > -1)
×
250
                close(fd);
×
251

252
        return ECGOTHER;
×
253
}
254

255
int cg_chmod_file(FTS *fts, FTSENT *ent, mode_t dir_mode, int dirm_change, mode_t file_mode,
14,451✔
256
                  int filem_change, int owner_is_umask)
257
{
258
        const char *filename = fts->fts_path;
14,451✔
259
        int ret = 0;
14,451✔
260

261
        cgroup_dbg("chmod: seeing file %s\n", filename);
14,451✔
262

263
        switch (ent->fts_info) {
14,451✔
264
        case FTS_ERR:
×
265
                errno = ent->fts_errno;
×
266
                break;
×
267
        case FTS_D:
474✔
268
        case FTS_DC:
269
        case FTS_DNR:
270
        case FTS_DP:
271
                if (dirm_change)
474✔
272
                        ret = cg_chmod_path(filename, dir_mode, owner_is_umask);
12✔
273
                break;
474✔
274
        case FTS_F:
13,977✔
275
        case FTS_NSOK:
276
        case FTS_NS:
277
        case FTS_DEFAULT:
278
                if (filem_change)
13,977✔
279
                        ret = cg_chmod_path(filename, file_mode, owner_is_umask);
395✔
280
                break;
13,977✔
281
        }
282

283
        return ret;
14,451✔
284
}
285

286
/**
287
 * Changes permissions of all directories and control files (i.e. all files
288
 * except files named in ignore_list. The list must be terminated with NULL.
289
 */
290
static int cg_chmod_recursive_controller(char *path, mode_t dir_mode, int dirm_change,
237✔
291
                                         mode_t file_mode, int filem_change, int owner_is_umask,
292
                                         const char * const *ignore_list)
293
{
294
        int final_ret = 0;
237✔
295
        char *fts_path[2];
296
        int i, ignored;
297
        int ret = 0;
237✔
298
        FTS *fts;
299

300
        fts_path[0] = path;
237✔
301
        fts_path[1] = NULL;
237✔
302
        cgroup_dbg("chmod: path is %s\n", path);
237✔
303

304
        fts = fts_open(fts_path, FTS_PHYSICAL | FTS_NOCHDIR | FTS_NOSTAT, NULL);
237✔
305
        if (fts == NULL) {
237✔
306
                cgroup_warn("cannot open directory %s: %s\n", fts_path, strerror(errno));
×
307
                last_errno = errno;
×
308
                return ECGOTHER;
×
309
        }
310

311
        while (1) {
14,451✔
312
                FTSENT *ent;
313

314
                ent = fts_read(fts);
14,688✔
315
                if (!ent) {
14,688✔
316
                        if (errno != 0) {
237✔
317
                                cgroup_dbg("fts_read failed\n");
×
318
                                last_errno = errno;
×
319
                                final_ret = ECGOTHER;
×
320
                        }
321
                        break;
237✔
322
                }
323

324
                ignored = 0;
14,451✔
325
                if (ignore_list != NULL)
14,451✔
326
                        for (i = 0; ignore_list[i] != NULL; i++)
28,902✔
327
                                if (!strcmp(ignore_list[i], ent->fts_name)) {
14,451✔
328
                                        ignored = 1;
×
329
                                        break;
×
330
                                }
331
                if (ignored)
14,451✔
332
                        continue;
×
333

334
                ret = cg_chmod_file(fts, ent, dir_mode, dirm_change, file_mode, filem_change,
14,451✔
335
                                    owner_is_umask);
336
                if (ret) {
14,451✔
337
                        cgroup_warn("cannot change file mode %s: %s\n", fts_path, strerror(errno));
×
338
                        last_errno = errno;
×
339
                        final_ret = ECGOTHER;
×
340
                }
341
        }
342
        fts_close(fts);
237✔
343

344
        return final_ret;
237✔
345
}
346

347
int cg_chmod_recursive(struct cgroup *cgrp, mode_t dir_mode, int dirm_change, mode_t file_mode,
×
348
                       int filem_change)
349
{
350
        int final_ret = 0;
×
351
        char *path;
352
        int i, ret;
353

354
        path = malloc(FILENAME_MAX);
×
355
        if (!path) {
×
356
                last_errno = errno;
×
357
                return ECGOTHER;
×
358
        }
359
        for (i = 0; i < cgrp->index; i++) {
×
360
                if (!cg_build_path(cgrp->name, path, cgrp->controller[i]->name)) {
×
361
                        final_ret = ECGFAIL;
×
362
                        break;
×
363
                }
364

365
                ret = cg_chmod_recursive_controller(path, dir_mode, dirm_change, file_mode,
×
366
                                                    filem_change, 0, NULL);
367
                if (ret)
×
368
                        final_ret = ret;
×
369
        }
370
        free(path);
×
371

372
        return final_ret;
×
373
}
374

375
void cgroup_set_permissions(struct cgroup *cgrp, mode_t control_dperm, mode_t control_fperm,
15✔
376
                            mode_t task_fperm)
377
{
378
        if (!cgrp) {
15✔
379
                /* ECGROUPNOTALLOWED */
380
                cgroup_err("Cgroup, operation not allowed\n");
1✔
381
                return;
1✔
382
        }
383

384
        cgrp->control_dperm = control_dperm;
14✔
385
        cgrp->control_fperm = control_fperm;
14✔
386
        cgrp->task_fperm = task_fperm;
14✔
387
}
388

389
static char *cgroup_basename(const char *path)
491✔
390
{
391
        char *tmp_string;
392
        char *base;
393

394
        tmp_string = strdup(path);
491✔
395

396
        if (!tmp_string)
491✔
397
                return NULL;
×
398

399
        base = strdup(basename(tmp_string));
491✔
400

401
        free(tmp_string);
491✔
402

403
        return base;
491✔
404
}
405

406
int cgroup_test_subsys_mounted(const char *name)
860✔
407
{
408
        int i;
409

410
        pthread_rwlock_rdlock(&cg_mount_table_lock);
860✔
411

412
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
2,236✔
413
                if (strncmp(cg_mount_table[i].name, name, sizeof(cg_mount_table[i].name)) == 0) {
2,236✔
414
                        pthread_rwlock_unlock(&cg_mount_table_lock);
789✔
415
                        return 1;
789✔
416
                }
417

418
                /*
419
                 * The user has likely requested a file like cgroup.type or
420
                 * cgroup.procs. Allow this request as long as there's a
421
                 * cgroup v2 controller mounted.
422
                 */
423
                if (strncmp(name, CGRP_FILE_PREFIX, strlen(CGRP_FILE_PREFIX)) == 0 &&
1,447✔
424
                    cg_mount_table[i].version == CGROUP_V2) {
71✔
425
                        pthread_rwlock_unlock(&cg_mount_table_lock);
71✔
426
                        return 1;
71✔
427
                }
428
        }
429

430
        pthread_rwlock_unlock(&cg_mount_table_lock);
×
431

432
        return 0;
×
433
}
434

435
/**
436
 * Free a single cgroup_rule struct.
437
 *        @param r The rule to free from memory
438
 */
439
static void cgroup_free_rule(struct cgroup_rule *r)
×
440
{
441
        /* Loop variable */
442
        int i = 0;
×
443

444
        /* Make sure our rule is not NULL, first. */
445
        if (!r) {
×
446
                cgroup_warn("attempted to free NULL rule\n");
×
447
                return;
×
448
        }
449
        if (r->procname) {
×
450
                free(r->procname);
×
451
                r->procname = NULL;
×
452
        }
453
        /* We must free any used controller strings, too. */
454
        for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
×
455
                if (r->controllers[i])
×
456
                        free(r->controllers[i]);
×
457
        }
458

459
        free(r);
×
460
}
461

462
/**
463
 * Free a list of cgroup_rule structs.  If rl is the main list of rules, the
464
 * lock must be taken for writing before calling this function!
465
 *        @param rl Pointer to the list of rules to free from memory
466
 */
467
static void cgroup_free_rule_list(struct cgroup_rule_list *cg_rl)
×
468
{
469
        /* Temporary pointer */
470
        struct cgroup_rule *tmp = NULL;
×
471

472
        /* Make sure we're not freeing NULL memory! */
473
        if (!(cg_rl->head)) {
×
474
                cgroup_warn("attempted to free NULL list\n");
×
475
                return;
×
476
        }
477

478
        while (cg_rl->head) {
×
479
                tmp = cg_rl->head;
×
480
                cg_rl->head = tmp->next;
×
481
                cgroup_free_rule(tmp);
×
482
        }
483

484
        /* Don't leave wild pointers around! */
485
        cg_rl->head = NULL;
×
486
        cg_rl->tail = NULL;
×
487
}
488

489
static char *cg_skip_unused_charactors_in_rule(char *rule)
2✔
490
{
491
        char *itr;
492

493
        /* We ignore anything after a # sign as comments. */
494
        itr = strchr(rule, '#');
2✔
495
        if (itr)
2✔
496
                *itr = '\0';
×
497

498
        /* We also need to remove the newline character. */
499
        itr = strchr(rule, '\n');
2✔
500
        if (itr)
2✔
501
                *itr = '\0';
2✔
502

503
        /* Now, skip any leading tabs and spaces. */
504
        itr = rule;
2✔
505
        while (itr && isblank(*itr))
2✔
506
                itr++;
×
507

508
        /* If there's nothing left, we can ignore this line. */
509
        if (!strlen(itr))
2✔
510
                return NULL;
×
511

512
        return itr;
2✔
513
}
514

515
/**
516
 * Parse the options field in the rule from the cgrules configuration file
517
 *
518
 *        @param options Comma-separated string of options
519
 *        @param rule The rule that will contain the parsed options
520
 *        @return 0 on success, -EINVAL if the options are invalid
521
 * TODO: Make this function thread safe!
522
 */
523
STATIC int cgroup_parse_rules_options(char *options, struct cgroup_rule * const rule)
6✔
524
{
525
        char *stok_buff = NULL;
6✔
526
        size_t cmp_len;
527
        int ret = 0;
6✔
528

529
        if (!options) {
6✔
530
                cgroup_err("failed to parse options: (NULL)\n");
1✔
531
                return -EINVAL;
1✔
532
        }
533

534
        stok_buff = strtok(options, ",");
5✔
535
        if (!stok_buff) {
5✔
536
                cgroup_err("failed to parse options: %s\n", options);
1✔
537
                return -EINVAL;
1✔
538
        }
539

540
        do {
541
                cmp_len = min(strlen(stok_buff), strlen(CGRULE_OPTION_IGNORE));
5✔
542
                if (strlen(stok_buff) == strlen(CGRULE_OPTION_IGNORE) &&
5✔
543
                    strncmp(stok_buff, CGRULE_OPTION_IGNORE, cmp_len) == 0) {
4✔
544
                        rule->is_ignore |= CGRULE_OPT_IGNORE;
3✔
545
                        continue;
3✔
546
                }
547

548
                cmp_len = min(strlen(stok_buff), strlen(CGRULE_OPTION_IGNORE_RT));
2✔
549
                if (strlen(stok_buff) == strlen(CGRULE_OPTION_IGNORE_RT) &&
2✔
550
                    strncmp(stok_buff, CGRULE_OPTION_IGNORE_RT, cmp_len) == 0) {
×
551
                        rule->is_ignore |= CGRULE_OPT_IGNORE_RT;
×
552
                        continue;
×
553
                }
554

555
                cgroup_err("Unsupported option: %s\n", stok_buff);
2✔
556
                ret = -EINVAL;
2✔
557
                break;
2✔
558
        } while ((stok_buff = strtok(NULL, ",")));
3✔
559

560
        return ret;
4✔
561
}
562

563
STATIC int get_next_rule_field(char *rule, char *field, size_t field_len, bool expect_quotes)
34✔
564
{
565
        char *quote_start = NULL, *quote_end = NULL;
34✔
566
        char *tmp, *_rule = rule;
34✔
567
        int len = 0;
34✔
568

569
        if (!rule || !field)
34✔
570
                return ECGINVAL;
2✔
571

572
        /* trim the leading whitespace */
573
        while (*rule == ' ' || *rule == '\t')
54✔
574
                rule++;
22✔
575

576
        tmp = rule;
32✔
577

578
        while (*rule != ' ' && *rule != '\t' && *rule != '\n' && *rule != '\0') {
275✔
579
                if (*rule == '"') {
252✔
580
                        quote_start = rule;
9✔
581
                        rule++;
9✔
582
                        break;
9✔
583
                }
584
                rule++;
243✔
585
        }
586

587
        if (quote_start) {
32✔
588
                if (!expect_quotes)
9✔
589
                        return ECGINVAL;
1✔
590

591
                while (*rule != '"' && *rule != '\n' && *rule != '\0')
144✔
592
                        rule++;
136✔
593

594
                /* there should be a ending quote */
595
                if (*rule != '"')
8✔
596
                        return ECGINVAL;
1✔
597

598
                quote_end = rule;
7✔
599
        }
600

601
        if (quote_end) {
30✔
602
                /* copy until the ending quotes */
603
                len = quote_end - quote_start - 1;
7✔
604
                if (len >= field_len)
7✔
605
                        return ECGINVAL;
2✔
606

607
                strncpy(field, quote_start + 1, len);
5✔
608
                field[len] = '\0';
5✔
609
        } else {
610
                len = rule - tmp;
23✔
611
                if (len >= field_len)
23✔
612
                        return ECGINVAL;
1✔
613

614
                strncpy(field, tmp, len);
22✔
615
                field[len] = '\0';
22✔
616
        }
617

618
        len = (rule - _rule);
27✔
619

620
        /* count until the ending quotes */
621
        if (quote_start)
27✔
622
                len += 1;
5✔
623

624
        return len;
27✔
625
}
626

627
/**
628
 * Parse the configuration file that maps UID/GIDs to cgroups.  If ever the
629
 * configuration file is modified, applications should call this function to
630
 * load the new configuration rules.
631
 *
632
 * The function caller is responsible for calling free() on each rule in the
633
 * list.
634
 *
635
 * The cache parameter alters the behavior of this function.  If true, this
636
 * function will read the entire configuration file and store the results in
637
 * rl (global rules list).  If false, this function will only parse until it
638
 * finds a rule matching the given UID or GID.  It will store this rule in
639
 * trl, as well as any children rules (rules that begin with a %) that it has.
640
 *
641
 * This function is NOT thread safe!
642
 *        @param filename configuration file to parse
643
 *        @param cache True to cache rules, else false
644
 *        @param muid If cache is false, the UID to match against
645
 *        @param mgid If cache is false, the GID to match against
646
 *        @return 0 on success, -1 if no cache and match found, > 0 on error.
647
 * TODO: Make this function thread safe!
648
 *
649
 */
650
static int cgroup_parse_rules_file(char *filename, bool cache, uid_t muid, gid_t mgid,
2✔
651
                                   const char *mprocname)
652
{
653
        /* File descriptor for the configuration file */
654
        FILE *fp = NULL;
2✔
655

656
        /* Buffer to store the line we're working on */
657
        char buff[CGRP_RULE_MAXLINE] = { '\0' };
2✔
658

659
        /* Iterator for the line we're working on */
660
        char *itr = NULL;
2✔
661

662
        /* Pointer to process name in a line of the configuration file */
663
        char *procname = NULL;
2✔
664

665
        /* Pointer to the list that we're using */
666
        struct cgroup_rule_list *lst = NULL;
2✔
667

668
        /* Rule to add to the list */
669
        struct cgroup_rule *newrule = NULL;
2✔
670

671
        /* Structure to get GID from group name */
672
        struct group *grp;
673

674
        /* Structure to get UID from user name */
675
        struct passwd *pwd;
676

677
        /* Temporary storage for a configuration rule */
678
        char key[CGRP_RULE_MAXKEY] = { '\0' };
2✔
679
        char user[LOGIN_NAME_MAX] = { '\0' };
2✔
680
        char controllers[CG_CONTROLLER_MAX] = { '\0' };
2✔
681
        char destination[FILENAME_MAX] = { '\0' };
2✔
682
        char options[CG_OPTIONS_MAX] = { '\0' };
2✔
683
        uid_t uid = CGRULE_INVALID;
2✔
684
        gid_t gid = CGRULE_INVALID;
2✔
685
        bool has_options = false;
2✔
686
        size_t len_username;
687
        int len_procname;
688

689
        /* The current line number */
690
        unsigned int linenum = 0;
2✔
691

692
        /* Did we skip the previous line? */
693
        bool skipped = false;
2✔
694

695
        /* Have we found a matching rule (non-cache mode)? */
696
        bool matched = false;
2✔
697

698
        /* Return codes */
699
        int ret = 0;
2✔
700

701
        /* Temporary buffer for strtok() */
702
        char *stok_buff = NULL;
2✔
703

704
        /* Loop variable. */
705
        int i = 0;
2✔
706

707
        /* Determine which list we're using. */
708
        if (cache)
2✔
709
                lst = &rl;
2✔
710
        else
711
                lst = &trl;
×
712

713
        /* Open the configuration file. */
714
        fp = fopen(filename, "re");
2✔
715
        if (!fp) {
2✔
716
                cgroup_warn("failed to open configuration file %s: %s\n",
×
717
                            filename, strerror(errno));
718
                /* originally ret = 0, but this is parse fail, not success */
719
                ret = ECGRULESPARSEFAIL;
×
720
                goto finish;
×
721
        }
722

723
        /* Now, parse the configuration file one line at a time. */
724
        cgroup_dbg("Parsing configuration file %s.\n", filename);
2✔
725
        while (fgets(buff, sizeof(buff), fp) != NULL) {
4✔
726
                linenum++;
2✔
727

728
                itr = cg_skip_unused_charactors_in_rule(buff);
2✔
729
                if (!itr)
2✔
730
                        continue;
×
731

732
                /*
733
                 * If we skipped the last rule and this rule is a continuation
734
                 * of it (begins with %), then we should skip this rule too.
735
                 */
736
                if (skipped && *itr == '%') {
2✔
737
                        cgroup_warn("skipped child of invalid rule, line %d.\n", linenum);
×
738
                        continue;
×
739
                }
740

741
                /* clear the buffer. */
742
                grp = NULL;
2✔
743
                pwd = NULL;
2✔
744

745
                /*
746
                 * If there is something left, it should be a rule.  Otherwise,
747
                 * there's an error in the configuration file.
748
                 */
749
                skipped = false;
2✔
750

751
                ret = get_next_rule_field(itr, key, CGRP_RULE_MAXKEY, true);
2✔
752
                if (!ret) {
2✔
753
                        cgroup_err("failed to parse configuration file on line %d\n", linenum);
×
754
                        goto parsefail;
×
755
                }
756

757
                itr += ret;
2✔
758
                ret = get_next_rule_field(itr, controllers, CG_CONTROLLER_MAX, false);
2✔
759
                if (!ret) {
2✔
760
                        cgroup_err("failed to parse configuration file on line %d\n", linenum);
×
761
                        goto parsefail;
×
762
                }
763

764
                itr += ret;
2✔
765
                ret = get_next_rule_field(itr, destination, FILENAME_MAX, true);
2✔
766
                if (!ret) {
2✔
767
                        cgroup_err("failed to parse configuration file on line %d\n", linenum);
×
768
                        goto parsefail;
×
769
                }
770

771
                itr += ret;
2✔
772
                ret = get_next_rule_field(itr, options, CG_OPTIONS_MAX, false);
2✔
773
                if (!ret)
2✔
774
                        has_options = false;
2✔
775
                else
776
                        has_options = true;
×
777

778
                procname = strchr(key, ':');
2✔
779
                if (procname) {
2✔
780
                        /* <user>:<procname>  <subsystem>  <destination> */
781
                        procname++;        /* skip ':' */
2✔
782
                        len_username = procname - key - 1;
2✔
783
                        len_procname = strlen(procname);
2✔
784
                        if (len_procname < 0) {
2✔
785
                                cgroup_err("failed to parse configuration file on line %d\n",
×
786
                                           linenum);
787
                                goto parsefail;
×
788
                        }
789
                } else {
790
                        len_username = strlen(key);
×
791
                        len_procname = 0;
×
792
                }
793
                len_username = min(len_username, sizeof(user) - 1);
2✔
794
                memset(user, '\0', sizeof(user));
2✔
795
                strncpy(user, key, len_username);
2✔
796
                user[sizeof(user) - 1] = '\0';
2✔
797

798
                /*
799
                 * Next, check the user/group.  If it's a % sign, then we are
800
                 * continuing another rule and UID/GID should not be reset.
801
                 * If it's a @, we're dealing with a GID rule.  If it's a *,
802
                 * then we do not need to do a lookup because the rule always
803
                 * applies (it's a wildcard).  If we're using non-cache mode
804
                 * and we've found a matching rule, we only continue to parse
805
                 * if we're looking at a child rule.
806
                 */
807
                if ((!cache) && matched && (strncmp(user, "%", 1) != 0)) {
2✔
808
                        /* If we make it here, we finished (non-cache). */
809
                        cgroup_dbg("Parsing of configuration file complete.\n\n");
×
810
                        ret = -1;
×
811
                        goto close;
×
812
                }
813
                if (strncmp(user, "@", 1) == 0) {
2✔
814
                        /* New GID rule. */
815
                        itr = &(user[1]);
×
816
                        grp = getgrnam(itr);
×
817
                        if (grp) {
×
818
                                uid = CGRULE_INVALID;
×
819
                                gid = grp->gr_gid;
×
820
                        } else {
821
                                cgroup_warn("Entry for %s not found. Skipping rule on line %d.\n",
×
822
                                            itr, linenum);
823
                                skipped = true;
×
824
                                continue;
×
825
                        }
826
                } else if (strncmp(user, "*", 1) == 0) {
2✔
827
                        /* Special wildcard rule. */
828
                        uid = CGRULE_WILD;
2✔
829
                        gid = CGRULE_WILD;
2✔
830
                } else if (*itr != '%') {
×
831
                        /* New UID rule. */
832
                        pwd = getpwnam(user);
×
833
                        if (pwd) {
×
834
                                uid = pwd->pw_uid;
×
835
                                gid = CGRULE_INVALID;
×
836
                        } else {
837
                                cgroup_warn("Entry for %s not found. Skipping rule on line %d.\n",
×
838
                                            user, linenum);
839
                                skipped = true;
×
840
                                continue;
×
841
                        }
842
                } /* Else, we're continuing another rule (UID/GID are okay). */
843

844
                /*
845
                 * If we are not caching rules, then we need to check for a
846
                 * match before doing anything else.  We consider four cases:
847
                 * 1. The UID matches
848
                 * 2. The GID matches
849
                 * 3. The UID is a member of the GID, or
850
                 * 4. We're looking at the wildcard rule, which always matches.
851
                 * If none of these are true, we simply continue to the next
852
                 * line in the file.
853
                 */
854
                if (grp && muid != CGRULE_INVALID) {
2✔
855
                        pwd = getpwuid(muid);
×
856
                        if (!pwd)
×
857
                                continue;
×
858

859
                        for (i = 0; grp->gr_mem[i]; i++) {
×
860
                                if (!(strcmp(pwd->pw_name, grp->gr_mem[i])))
×
861
                                        matched = true;
×
862
                        }
863
                }
864

865
                if (uid == muid || gid == mgid || uid == CGRULE_WILD)
2✔
866
                        matched = true;
2✔
867

868
                if (!cache) {
2✔
869
                        if (!matched)
×
870
                                continue;
×
871
                        if (len_procname) {
×
872
                                char *mproc_base;
873
                                /*
874
                                 * If there is a rule based on process name,
875
                                 * it should be matched with mprocname.
876
                                 */
877
                                if (!mprocname) {
×
878
                                        uid = CGRULE_INVALID;
×
879
                                        gid = CGRULE_INVALID;
×
880
                                        matched = false;
×
881
                                        continue;
×
882
                                }
883

884
                                mproc_base = cgroup_basename(mprocname);
×
885
                                if (strcmp(mprocname, procname) && strcmp(mproc_base, procname)) {
×
886
                                        uid = CGRULE_INVALID;
×
887
                                        gid = CGRULE_INVALID;
×
888
                                        matched = false;
×
889
                                        free(mproc_base);
×
890
                                        continue;
×
891
                                }
892
                                free(mproc_base);
×
893
                        }
894
                }
895

896
                /*
897
                 * Now, we're either caching rules or we found a match.
898
                 * Either way, copy everything into a new rule and push it
899
                 * into the list.
900
                 */
901
                newrule = calloc(1, sizeof(struct cgroup_rule));
2✔
902
                if (!newrule) {
2✔
903
                        cgroup_err("out of memory? Error was: %s\n", strerror(errno));
×
904
                        last_errno = errno;
×
905
                        ret = ECGOTHER;
×
906
                        goto close;
×
907
                }
908

909
                newrule->uid = uid;
2✔
910
                newrule->gid = gid;
2✔
911
                newrule->is_ignore = 0;
2✔
912

913
                len_username = min(len_username, sizeof(newrule->username) - 1);
2✔
914
                strncpy(newrule->username, user, len_username);
2✔
915
                newrule->username[sizeof(newrule->username) - 1] = '\0';
2✔
916

917
                if (len_procname) {
2✔
918
                        newrule->procname = strdup(procname);
2✔
919
                        if (!newrule->procname) {
2✔
920
                                cgroup_err("strdup failed to allocate memory %s\n",
×
921
                                           strerror(errno));
922
                                free(newrule);
×
923
                                last_errno = errno;
×
924
                                ret = ECGOTHER;
×
925
                                goto close;
×
926
                        }
927
                } else {
928
                        newrule->procname = NULL;
×
929
                }
930
                strncpy(newrule->destination, destination, sizeof(newrule->destination) - 1);
2✔
931
                newrule->destination[sizeof(newrule->destination) - 1] = '\0';
2✔
932

933
                if (has_options) {
2✔
934
                        ret = cgroup_parse_rules_options(options, newrule);
×
935
                        if (ret < 0)
×
936
                                goto destroyrule;
×
937
                }
938

939
                newrule->next = NULL;
2✔
940

941
                /* Parse the controller list, and add that to newrule too. */
942
                stok_buff = strtok(controllers, ",");
2✔
943
                if (!stok_buff) {
2✔
944
                        cgroup_err("failed to parse controllers on line %d\n", linenum);
×
945
                        goto destroyrule;
×
946
                }
947

948
                i = 0;
2✔
949
                do {
950
                        if (i >= MAX_MNT_ELEMENTS) {
2✔
951
                                cgroup_err("too many controllers listed on line %d\n", linenum);
×
952
                                goto destroyrule;
×
953
                        }
954

955
                        newrule->controllers[i] =
2✔
956
                                strndup(stok_buff, strlen(stok_buff) + 1);
2✔
957
                        if (!(newrule->controllers[i])) {
2✔
958
                                cgroup_err("out of memory? Error was: %s\n", strerror(errno));
×
959
                                goto destroyrule;
×
960
                        }
961
                        i++;
2✔
962
                } while ((stok_buff = strtok(NULL, ",")));
2✔
963

964
                /* Now, push the rule. */
965
                if (lst->head == NULL) {
2✔
966
                        lst->head = newrule;
2✔
967
                        lst->tail = newrule;
2✔
968
                } else {
969
                        lst->tail->next = newrule;
×
970
                        lst->tail = newrule;
×
971
                }
972

973
                cgroup_dbg("Added rule %s (UID: %d, GID: %d) -> %s for controllers:",
2✔
974
                           lst->tail->username, lst->tail->uid, lst->tail->gid,
975
                           lst->tail->destination);
976

977
                for (i = 0; i < MAX_MNT_ELEMENTS && lst->tail->controllers[i]; i++)
4✔
978
                        cgroup_dbg(" %s", lst->tail->controllers[i]);
2✔
979
                cgroup_dbg("\n");
2✔
980
        }
981

982
        /* If we make it here, there were no errors. */
983
        cgroup_dbg("Parsing of configuration file complete.\n\n");
2✔
984
        ret = (matched && !cache) ? -1 : 0;
2✔
985
        goto close;
2✔
986

987
destroyrule:
×
988
        cgroup_free_rule(newrule);
×
989

990
parsefail:
×
991
        ret = ECGRULESPARSEFAIL;
×
992

993
close:
2✔
994
        fclose(fp);
2✔
995
finish:
2✔
996
        return ret;
2✔
997
}
998

999
/**
1000
 * Parse CGRULES_CONF_FILE and all files in CGRULES_CONF_FILE_DIR.
1001
 * If CGRULES_CONF_FILE_DIR does not exists or can not be read, parse only
1002
 * CGRULES_CONF_FILE. This way we keep the back compatibility.
1003
 *
1004
 * Original description of this function moved to cgroup_parse_rules_file.
1005
 * Also cloned and all occurrences of file changed to files.
1006
 *
1007
 * Parse the configuration files that maps UID/GIDs to cgroups. If ever the
1008
 * configuration files are modified, applications should call this function to
1009
 * load the new configuration rules. The function caller is responsible for
1010
 * calling free() on each rule in the list.
1011
 *
1012
 * The cache parameter alters the behavior of this function.  If true, this
1013
 * function will read the entire content of all configuration files and store
1014
 * the results in rl (global rules list). If false, this function will only
1015
 * parse until it finds a file and a rule matching the given UID or GID.
1016
 * The remaining files are skipped. It will store this rule in trl, as well as
1017
 * any children rules (rules that begin with a %) that it has.
1018
 *
1019
 * Files can be read in an random order so the first match must not be
1020
 * dependent on it. Thus construct the rules the way not to break this
1021
 * assumption.
1022
 *
1023
 * This function is NOT thread safe!
1024
 *        @param cache True to cache rules, else false
1025
 *        @param muid If cache is false, the UID to match against
1026
 *        @param mgid If cache is false, the GID to match against
1027
 *        @return 0 on success, -1 if no cache and match found, > 0 on error.
1028
 * TODO: Make this function thread safe!
1029
 */
1030
static int cgroup_parse_rules(bool cache, uid_t muid, gid_t mgid, const char *mprocname)
2✔
1031
{
1032
        /* Pointer to the list that we're using */
1033
        struct cgroup_rule_list *lst = NULL;
2✔
1034

1035
        /* Directory variables */
1036
        const char *dirname = CGRULES_CONF_DIR;
2✔
1037
        struct dirent *item;
1038
        char *tmp;
1039
        int sret;
1040
        DIR *d;
1041

1042
        int ret;
1043

1044
        /* Determine which list we're using. */
1045
        if (cache)
2✔
1046
                lst = &rl;
2✔
1047
        else
1048
                lst = &trl;
×
1049

1050
        /* If our list already exists, clean it. */
1051
        if (lst->head)
2✔
1052
                cgroup_free_rule_list(lst);
×
1053

1054
        pthread_rwlock_wrlock(&rl_lock);
2✔
1055

1056
        /* Parse CGRULES_CONF_FILE configuration file (back compatibility). */
1057
        ret = cgroup_parse_rules_file(CGRULES_CONF_FILE, cache, muid, mgid, mprocname);
2✔
1058

1059
        /*
1060
         * if match (ret = -1), stop parsing other files,
1061
         * just return or ret > 0 => error
1062
         */
1063
        if (ret != 0) {
2✔
1064
                pthread_rwlock_unlock(&rl_lock);
×
1065
                return ret;
×
1066
        }
1067

1068
        /* Continue parsing */
1069
        d = opendir(dirname);
2✔
1070
        if (!d) {
2✔
1071
                cgroup_warn("Failed to open directory %s: %s\n", dirname, strerror(errno));
2✔
1072
                /*
1073
                 * Cannot read directory. However, CGRULES_CONF_FILE is
1074
                 * successfully parsed. Thus return as a success for back
1075
                 * compatibility.
1076
                 */
1077
                pthread_rwlock_unlock(&rl_lock);
2✔
1078

1079
                return 0;
2✔
1080
        }
1081

1082
        /* Read all files from CGRULES_CONF_FILE_DIR */
1083
        do {
1084
                item = readdir(d);
×
1085
                if (item && (item->d_type == DT_REG || item->d_type == DT_LNK)) {
×
1086

1087
                        sret = asprintf(&tmp, "%s/%s", dirname, item->d_name);
×
1088
                        if (sret < 0) {
×
1089
                                cgroup_err("Out of memory\n");
×
1090

1091
                                /*
1092
                                 * Cannot read directory.
1093
                                 * However, CGRULES_CONF_FILE is successfully
1094
                                 * parsed. Thus return as a success for back
1095
                                 * compatibility.
1096
                                 */
1097
                                ret = 0;
×
1098
                                goto unlock_list;
×
1099
                        }
1100

1101
                        cgroup_dbg("Parsing cgrules file: %s\n", tmp);
×
1102
                        ret = cgroup_parse_rules_file(tmp, cache, muid, mgid, mprocname);
×
1103

1104
                        free(tmp);
×
1105

1106
                        /* Match with cache disabled? */
1107
                        if (ret != 0)
×
1108
                                goto unlock_list;
×
1109
                }
1110
                if (!item && errno) {
×
1111
                        cgroup_warn("cannot read %s: %s\n", dirname, strerror(errno));
×
1112
                        /*
1113
                         * Cannot read an item.
1114
                         * But continue for back compatibility as a success.
1115
                         */
1116
                        ret = 0;
×
1117
                        goto unlock_list;
×
1118
                }
1119
        } while (item != NULL);
×
1120

1121
unlock_list:
×
1122
        closedir(d);
×
1123
        pthread_rwlock_unlock(&rl_lock);
×
1124

1125
        return ret;
×
1126
}
1127

1128
int cg_add_duplicate_mount(struct cg_mount_table_s *item, const char *path)
8✔
1129
{
1130
        struct cg_mount_point *mount, *it;
1131

1132
        mount = malloc(sizeof(struct cg_mount_point));
8✔
1133
        if (!mount) {
8✔
1134
                last_errno = errno;
×
1135
                return ECGOTHER;
×
1136
        }
1137
        mount->next = NULL;
8✔
1138

1139
        strncpy(mount->path, path, sizeof(mount->path));
8✔
1140
        mount->path[sizeof(mount->path)-1] = '\0';
8✔
1141

1142
        /*
1143
         * Add the mount point to the end of the list. Assuming the list is
1144
         * short, no optimization is done.
1145
         */
1146
        it = &item->mount;
8✔
1147
        while (it->next)
8✔
1148
                it = it->next;
×
1149

1150
        it->next = mount;
8✔
1151

1152
        return 0;
8✔
1153
}
1154

1155
/*
1156
 * Tries to find if any controller in cg_mount_table have already mounted on
1157
 * the mount_path and if mounted sets the matching controller idx share_mnt
1158
 * flag and Return 1 or 0 otherwise.
1159
 */
1160
static int cgroup_set_cg_mnt_tbl_shared_mnt(char *mount_path, int *mnt_tbl_idx)
14,071✔
1161
{
1162
        int i, shared_mnt = 0;
14,071✔
1163

1164
        /* Check if controllers share mount points */
1165
        for  (i = 0; i < *mnt_tbl_idx; i++) {
33,768✔
1166
                if (strncmp(mount_path, cg_mount_table[i].mount.path, FILENAME_MAX) == 0) {
31,945✔
1167
                        cg_mount_table[i].shared_mnt = 1;
12,248✔
1168
                        shared_mnt = 1;
12,248✔
1169
                        break;
12,248✔
1170
                }
1171
        }
1172

1173
        return shared_mnt;
14,071✔
1174
}
1175

1176
static void cgroup_cg_mount_table_append(const char *name, const char *mount_path,
14,063✔
1177
                                         enum cg_version_t version, int *mnt_tbl_idx,
1178
                                         const char *mnt_opts, int shared_mnt)
1179
{
1180
        int i = *mnt_tbl_idx;
14,063✔
1181

1182
        strncpy(cg_mount_table[i].name,        name, CONTROL_NAMELEN_MAX);
14,063✔
1183
        cg_mount_table[i].name[CONTROL_NAMELEN_MAX-1] = '\0';
14,063✔
1184

1185
        strncpy(cg_mount_table[i].mount.path, mount_path, FILENAME_MAX);
14,063✔
1186
        cg_mount_table[i].mount.path[FILENAME_MAX-1] = '\0';
14,063✔
1187

1188
        cg_mount_table[i].shared_mnt = shared_mnt;
14,063✔
1189
        cg_mount_table[i].version = version;
14,063✔
1190
        cg_mount_table[i].mount.next = NULL;
14,063✔
1191

1192
        cgroup_dbg("Found cgroup option %s, count %d\n", mnt_opts, i);
14,063✔
1193

1194
        (*mnt_tbl_idx)++;
14,063✔
1195
}
14,063✔
1196

1197
/**
1198
 * Process a cgroup v1 mount and add it to cg_mount_table if it's not a
1199
 * duplicate.
1200
 *
1201
 *        @param controllers List of controllers from /proc/cgroups
1202
 *        @param ent File system description of cgroup mount being processed
1203
 *        @param mnt_tbl_idx cg_mount_table index
1204
 */
1205
STATIC int cgroup_process_v1_mnt(char *controllers[], struct mntent *ent, int *mnt_tbl_idx)
366✔
1206
{
1207
        char *strtok_buffer = NULL, *mntopt = NULL;
366✔
1208
        int shared_mnt, duplicate;
1209
        int i, j, ret = 0;
366✔
1210
        char c = 0;
366✔
1211

1212
        for (i = 0; controllers[i] != NULL; i++) {
5,366✔
1213
                mntopt = hasmntopt(ent, controllers[i]);
5,000✔
1214

1215
                if (!mntopt)
5,000✔
1216
                        continue;
4,998✔
1217

1218
                c = mntopt[strlen(controllers[i])];
2✔
1219

1220
                if (c != '\0' && c != ',')
2✔
1221
                        continue;
×
1222

1223
                cgroup_dbg("found %s in %s\n", controllers[i], ent->mnt_opts);
2✔
1224

1225
                /* Check if controllers share mount points */
1226
                shared_mnt = cgroup_set_cg_mnt_tbl_shared_mnt(ent->mnt_dir, mnt_tbl_idx);
2✔
1227

1228
                /* Do not have duplicates in mount table */
1229
                duplicate = 0;
2✔
1230
                for  (j = 0; j < *mnt_tbl_idx; j++) {
2✔
1231
                        if (strncmp(controllers[i], cg_mount_table[j].name, FILENAME_MAX) == 0) {
1✔
1232
                                duplicate = 1;
1✔
1233
                                break;
1✔
1234
                        }
1235
                }
1236

1237
                if (duplicate) {
2✔
1238
                        cgroup_dbg("controller %s is already mounted on %s\n", mntopt,
1✔
1239
                                   cg_mount_table[j].mount.path);
1240
                        ret = cg_add_duplicate_mount(&cg_mount_table[j], ent->mnt_dir);
1✔
1241
                        if (ret)
1✔
1242
                                goto out;
×
1243
                        /* Continue with next controller */
1244
                        continue;
1✔
1245
                }
1246

1247
                cgroup_cg_mount_table_append(controllers[i], ent->mnt_dir, CGROUP_V1, mnt_tbl_idx,
1✔
1248
                                             ent->mnt_opts, shared_mnt);
1✔
1249

1250
                if ((*mnt_tbl_idx) >= CG_CONTROLLER_MAX)
1✔
1251
                        goto out;
×
1252
        }
1253

1254
        /* Doesn't match the controller. Check if it is a named hierarchy. */
1255
        mntopt = hasmntopt(ent, "name");
366✔
1256

1257
        if (mntopt) {
366✔
1258
                mntopt = strtok_r(mntopt, ",", &strtok_buffer);
364✔
1259
                if (!mntopt)
364✔
1260
                        goto out;
×
1261

1262
#ifdef OPAQUE_HIERARCHY
1263
                /* Ignore the opaque hierarchy. */
1264
                if (strcmp(mntopt, OPAQUE_HIERARCHY) == 0)
364✔
1265
                        goto out;
1✔
1266
#endif
1267
                /* Check if controllers share mount points */
1268
                shared_mnt = cgroup_set_cg_mnt_tbl_shared_mnt(ent->mnt_dir, mnt_tbl_idx);
363✔
1269

1270
                /* Check if it is a duplicate */
1271
                duplicate = 0;
363✔
1272
                for (j = 0; j < *mnt_tbl_idx; j++) {
20,010✔
1273
                        if (strncmp(mntopt, cg_mount_table[j].name, FILENAME_MAX) == 0) {
19,647✔
1274
                                duplicate = 1;
×
1275
                                break;
×
1276
                        }
1277
                }
1278

1279
                if (duplicate) {
363✔
1280
                        cgroup_dbg("controller %s is already mounted on %s\n", mntopt,
×
1281
                                   cg_mount_table[j].mount.path);
1282
                        ret = cg_add_duplicate_mount(&cg_mount_table[j], ent->mnt_dir);
×
1283
                        goto out;
×
1284
                }
1285

1286
                cgroup_cg_mount_table_append(mntopt, ent->mnt_dir, CGROUP_V1, mnt_tbl_idx,
363✔
1287
                                             ent->mnt_opts, shared_mnt);
363✔
1288
        }
1289

1290
out:
2✔
1291
        return ret;
366✔
1292
}
1293

1294
/**
1295
 * Process a cgroup v2 mount and add it to cg_mount_table if it's not a
1296
 * duplicate.
1297
 *
1298
 *        @param ent File system description of cgroup mount being processed
1299
 *        @param mnt_tbl_idx cg_mount_table index
1300
 */
1301
STATIC int cgroup_process_v2_mnt(struct mntent *ent, int *mnt_tbl_idx)
1,453✔
1302
{
1303
        char *ret_c = NULL, line[CGV2_CONTROLLERS_LL_MAX], *stok_buff = NULL;
1,453✔
1304
        char *controller = NULL, *controllers = NULL;
1,453✔
1305
        char cgrp_controllers_path[FILENAME_MAX];
1306
        int ret = 0, i, duplicate, shared_mnt;
1,453✔
1307
        FILE *fp = NULL;
1,453✔
1308

1309
        /*
1310
         * Save off this mount point.  This may be used later to
1311
         * build the cg_path.
1312
         */
1313
        strncpy(cg_cgroup_v2_mount_path, ent->mnt_dir, FILENAME_MAX-1);
1,453✔
1314
        cg_cgroup_v2_mount_path[FILENAME_MAX-1] = '\0';
1,453✔
1315

1316
        /* determine what v2 controllers are available on this mount */
1317
        snprintf(cgrp_controllers_path, FILENAME_MAX, "%s/%s", ent->mnt_dir, CGV2_CONTROLLERS_FILE);
1,453✔
1318
        fp = fopen(cgrp_controllers_path, "re");
1,453✔
1319
        if (!fp) {
1,453✔
1320
                ret = ECGOTHER;
×
1321
                goto out;
×
1322
        }
1323

1324
        ret_c = fgets(line, CGV2_CONTROLLERS_LL_MAX, fp);
1,453✔
1325
        if (ret_c == NULL) {
1,453✔
1326
                struct cg_mount_point *tmp, *t;
1327

1328
                ret = ECGEOF;
1✔
1329

1330
                tmp = malloc(sizeof(struct cg_mount_point));
1✔
1331
                if (tmp == NULL) {
1✔
1332
                        last_errno = errno;
×
1333
                        ret = ECGOTHER;
×
1334
                        goto out;
×
1335
                }
1336

1337
                strncpy(tmp->path, cg_cgroup_v2_mount_path, sizeof(tmp->path) - 1);
1✔
1338
                tmp->path[sizeof(tmp->path)-1] = '\0';
1✔
1339
                tmp->next = NULL;
1✔
1340

1341
                t = cg_cgroup_v2_empty_mount_paths;
1✔
1342
                if (t == NULL) {
1✔
1343
                        cg_cgroup_v2_empty_mount_paths = tmp;
1✔
1344
                        goto out;
1✔
1345
                }
1346

1347
                while (t->next != NULL)
×
1348
                        t = t->next;
×
1349
                t->next = tmp;
×
1350

1351
                goto out;
×
1352
        }
1353

1354
        /* Remove the trailing newline */
1355
        ret_c[strlen(ret_c) - 1] = '\0';
1,452✔
1356

1357
        /*
1358
         * The "cgroup" controller is a pseudo-controller that has settings that a user may
1359
         * wish to read/modify.  Add it to our cg_mount_table so that it can be manipulated
1360
         * like other "normal" controllers
1361
         */
1362
        controllers = malloc(strlen(ret_c) + strlen(CGRP_FILE_PREFIX) + 2);
1,452✔
1363
        if (!controllers) {
1,452✔
1364
                ret = ECGOTHER;
×
1365
                goto out;
×
1366
        }
1367

1368
        sprintf(controllers, "%s %s", ret_c, CGRP_FILE_PREFIX);
1,452✔
1369

1370
        /*
1371
         * cgroup.controllers returns a list of available controllers in
1372
         * the following format:
1373
         *        cpuset cpu io memory pids rdma
1374
         */
1375
        controller = strtok_r(controllers, " ", &stok_buff);
1,452✔
1376
        do {
1377
                /* Check if controllers share mount points */
1378
                shared_mnt = cgroup_set_cg_mnt_tbl_shared_mnt(ent->mnt_dir, mnt_tbl_idx);
13,706✔
1379

1380
                /* Do not have duplicates in mount table */
1381
                duplicate = 0;
13,706✔
1382
                for  (i = 0; i < *mnt_tbl_idx; i++) {
71,729✔
1383
                        if (strncmp(cg_mount_table[i].name, controller, FILENAME_MAX) == 0) {
58,030✔
1384
                                duplicate = 1;
7✔
1385
                                break;
7✔
1386
                        }
1387
                }
1388

1389
                if (duplicate) {
13,706✔
1390
                        cgroup_dbg("controller %s is already mounted on %s\n", controller,
7✔
1391
                                   cg_mount_table[i].mount.path);
1392

1393
                        ret = cg_add_duplicate_mount(&cg_mount_table[i], ent->mnt_dir);
7✔
1394
                        if (ret)
7✔
1395
                                break;
×
1396

1397
                        continue;
7✔
1398
                }
1399

1400
                /* This controller is not in the mount table.  Add it */
1401
                cgroup_cg_mount_table_append(controller, ent->mnt_dir, CGROUP_V2, mnt_tbl_idx,
13,699✔
1402
                                             controller, shared_mnt);
1403

1404
                if ((*mnt_tbl_idx) >= CG_CONTROLLER_MAX)
13,699✔
1405
                        goto out;
×
1406
        } while ((controller = strtok_r(NULL, " ", &stok_buff)));
13,706✔
1407

1408
out:
1,452✔
1409
        if (fp)
1,453✔
1410
                fclose(fp);
1,453✔
1411

1412
        if (controllers)
1,453✔
1413
                free(controllers);
1,452✔
1414

1415
        return ret;
1,453✔
1416
}
1417

1418
/*
1419
 * Free global variables filled by previous cgroup_init(). This function
1420
 * should be called with cg_mount_table_lock taken.
1421
 */
1422
static void cgroup_free_cg_mount_table(void)
1,449✔
1423
{
1424
        struct cg_mount_point *mount, *tmp;
1425
        int i;
1426

1427
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
6,299✔
1428
                mount = cg_mount_table[i].mount.next;
4,850✔
1429

1430
                while (mount) {
4,850✔
1431
                        tmp = mount;
×
1432
                        mount = mount->next;
×
1433
                        free(tmp);
×
1434
                }
1435
        }
1436

1437
        memset(&cg_mount_table, 0, sizeof(cg_mount_table));
1,449✔
1438
        memset(&cg_cgroup_v2_mount_path, 0, sizeof(cg_cgroup_v2_mount_path));
1,449✔
1439
        memset(&cg_cgroup_v2_empty_mount_paths, 0, sizeof(cg_cgroup_v2_empty_mount_paths));
644✔
1440
}
1,449✔
1441

1442
/*
1443
 * Parses the mount options of the given mount point and checks for the
1444
 * option in the list of mount options and sets is_set accordingly.
1445
 * @mnt: Mount point name to search for mount points.
1446
 * @mnt_opt: Mount option to be searched.
1447
 * @is_set: Set to 1, when mount option is found, 0 otherwise.
1448
 *
1449
 * Returns 0, in case of success and ECGOTHER on failure.
1450
 */
1451
static int check_mount_point_opt(const char *mnt, const char *mnt_opt, int * const is_set)
×
1452
{
1453
        struct mntent *ent, *temp_ent = NULL;
×
1454
        char mntent_buffer[4 * FILENAME_MAX];
1455
        char *mntopt = NULL;
×
1456
        char mnt_opt_delim;
1457
        FILE *proc_mount;
1458
        int ret = 0;
×
1459

1460
        if (!mnt || !mnt_opt || !is_set)
×
1461
                return ECGINVAL;
×
1462

1463
        proc_mount = setmntent(mnt, "r");
×
1464
        if (!proc_mount) {
×
1465
                cgroup_err("cannot open %s: %s\n", mnt, strerror(errno));
×
1466
                last_errno = errno;
×
1467
                ret = ECGOTHER;
×
1468
                goto err;
×
1469
        }
1470

1471
        temp_ent = (struct mntent *) malloc(sizeof(struct mntent));
×
1472
        if (!temp_ent) {
×
1473
                last_errno = errno;
×
1474
                ret = ECGOTHER;
×
1475
                goto err;
×
1476
        }
1477

1478
        ent = getmntent_r(proc_mount, temp_ent,        mntent_buffer, sizeof(mntent_buffer));
×
1479
        if (!ent) {
×
1480
                last_errno = errno;
×
1481
                ret = ECGOTHER;
×
1482
                goto err;
×
1483
        }
1484

1485
        *is_set = 0;
×
1486
        while ((mntopt = hasmntopt(ent, mnt_opt))) {
×
1487
                mnt_opt_delim = mntopt[strlen(mnt_opt)];
×
1488
                if (mnt_opt_delim == '\0' || mnt_opt_delim == ',') {
×
1489
                        *is_set  = 1;
×
1490
                        break;
×
1491
                }
1492
        }
1493

1494
        if (*is_set == 0)
×
1495
                cgroup_dbg("%s, not found in the list of mount options of %s\n", mnt_opt, mnt);
×
1496

1497
err:
×
1498
        if (proc_mount)
×
1499
                endmntent(proc_mount);
×
1500

1501
        if (temp_ent)
×
1502
                free(temp_ent);
×
1503

1504
        return ret;
×
1505
}
1506

1507
/*
1508
 * Reads /proc/cgroups and populates the controllers/subsys_name. This
1509
 * function should be called with cg_mount_table_lock taken.
1510
 */
1511
static int cgroup_populate_controllers(char *controllers[CG_CONTROLLER_MAX])
1,449✔
1512
{
1513
        int hierarchy, num_cgrps, enabled;
1514
        char subsys_name[FILENAME_MAX];
1515
        char mnt_opt[] = "subset=pid";
1,449✔
1516
        char *buf = NULL;
1,449✔
1517
        FILE *proc_cgrp;
1518
        int mnt_opt_set;
1519
        int ret = 0;
1,449✔
1520
        int err, i = 0;
1,449✔
1521

1522
        proc_cgrp = fopen("/proc/cgroups", "re");
1,449✔
1523
        if (!proc_cgrp) {
1,449✔
1524
                cgroup_warn("cannot open /proc/cgroups: %s\n", strerror(errno));
×
1525
                ret = check_mount_point_opt("/proc/self/mounts", mnt_opt, &mnt_opt_set);
×
1526
                if (ret)
×
1527
                        goto err;
×
1528

1529
                if (!mnt_opt_set)
×
1530
                        ret = ECGINVAL;
×
1531
                /*
1532
                 * /proc, mounted with subset=pid is valid. cgroup v2 doesn't
1533
                 * depend on /proc/cgroups to parse the available controllers.
1534
                 */
1535
                goto err;
×
1536
        }
1537

1538
        /*
1539
         * The first line of the file has stuff we are not interested in.
1540
         * So just read it and discard the information.
1541
         */
1542
        buf = malloc(CGV2_CONTROLLERS_LL_MAX);
1,449✔
1543
        if (!buf) {
1,449✔
1544
                last_errno = errno;
×
1545
                ret = ECGOTHER;
×
1546
                goto err;
×
1547
        }
1548

1549
        if (!fgets(buf, CGV2_CONTROLLERS_LL_MAX, proc_cgrp)) {
1,449✔
1550
                cgroup_err("cannot read /proc/cgroups: %s\n", strerror(errno));
×
1551
                last_errno = errno;
×
1552
                ret = ECGOTHER;
×
1553
                goto err;
×
1554
        }
1555

1556
        while (!feof(proc_cgrp)) {
21,091✔
1557
                /*
1558
                 * check Linux Kernel sources/kernel/cgroup/cgroup.c cgroup_init_early(),
1559
                 * MAX_CGROUP_TYPE_NAMELEN check for details on why 32 is used.
1560
                 */
1561
                err = fscanf(proc_cgrp, "%32s %d %d %d", subsys_name, &hierarchy, &num_cgrps,
21,091✔
1562
                             &enabled);
1563
                if (err < 0)
21,091✔
1564
                        break;
1,449✔
1565

1566
                controllers[i] = strdup(subsys_name);
19,642✔
1567
                if (controllers[i] == NULL) {
19,642✔
1568
                        last_errno = errno;
×
1569
                        ret = ECGOTHER;
×
1570
                        break;
×
1571
                }
1572
                i++;
19,642✔
1573
        }
1574

1575
err:
×
1576
        if (proc_cgrp)
1,449✔
1577
                fclose(proc_cgrp);
1,449✔
1578

1579
        if (buf)
1,449✔
1580
                free(buf);
1,449✔
1581

1582
        if (ret != 0) {
1,449✔
1583
                for (i = 0; controllers[i]; i++) {
×
1584
                        free(controllers[i]);
×
1585
                        controllers[i] = NULL;
×
1586
                }
1587
        }
1588

1589
        return ret;
1,449✔
1590
}
1591

1592
/*
1593
 * Reads /proc/self/mounts and populates the cgroup v1/v2 mount points into the
1594
 * global cg_mount_table.
1595
 * This function should be called with cg_mount_table_lock taken.
1596
 */
1597
static int cgroup_populate_mount_points(char *controllers[CG_CONTROLLER_MAX])
1,449✔
1598
{
1599
        char mntent_buffer[4 * FILENAME_MAX];
1600
        struct mntent *ent, *temp_ent = NULL;
1,449✔
1601
        int found_mnt = 0;
1,449✔
1602
        FILE *proc_mount;
1603
        int ret = 0;
1,449✔
1604

1605
        proc_mount = fopen("/proc/self/mounts", "re");
1,449✔
1606
        if (proc_mount == NULL) {
1,449✔
1607
                cgroup_err("cannot open /proc/self/mounts: %s\n", strerror(errno));
×
1608
                last_errno = errno;
×
1609
                ret = ECGOTHER;
×
1610
                goto err;
×
1611
        }
1612

1613
        temp_ent = (struct mntent *) malloc(sizeof(struct mntent));
1,449✔
1614
        if (!temp_ent) {
1,449✔
1615
                last_errno = errno;
×
1616
                ret = ECGOTHER;
×
1617
                goto err;
×
1618
        }
1619

1620
        while ((ent = getmntent_r(proc_mount, temp_ent,        mntent_buffer,
52,344✔
1621
                                  sizeof(mntent_buffer))) != NULL) {
52,344✔
1622

1623
                if (strcmp(ent->mnt_type, "cgroup") == 0) {
50,899✔
1624
                        if (controllers[0] == NULL) {
363✔
1625
                                cgroup_err("cgroup v1 requires /proc/cgroups, check if /proc ");
×
1626
                                cgroup_cont("is mounted with subset=pid option.\n");
×
1627
                                ret = ECGINVAL;
×
1628
                                goto err;
×
1629
                        }
1630

1631
                        ret = cgroup_process_v1_mnt(controllers, ent, &found_mnt);
363✔
1632
                        if (ret)
363✔
1633
                                goto err;
×
1634

1635
                        if (found_mnt >= CG_CONTROLLER_MAX)
363✔
1636
                                break;
4✔
1637

1638
                        continue;
359✔
1639
                }
1640

1641
                if (strcmp(ent->mnt_type, "cgroup2") == 0) {
50,536✔
1642
                        ret = cgroup_process_v2_mnt(ent, &found_mnt);
1,449✔
1643
                        if (ret == ECGEOF) {
1,449✔
1644
                        /* The controllers file was empty.  Ignore and move on. */
1645
                                ret = 0;
×
1646
                                continue;
×
1647
                        }
1648

1649
                        if (ret)
1,449✔
1650
                                goto err;
×
1651

1652
                        if (found_mnt >= CG_CONTROLLER_MAX)
1,449✔
1653
                                break;
×
1654
                }
1655
        }
1656

1657
        if (!found_mnt)
1,449✔
1658
                ret = ECGROUPNOTMOUNTED;
×
1659

1660
        if (found_mnt >= CG_CONTROLLER_MAX) {
1,449✔
1661
                cgroup_err("Mount points exceeds CG_CONTROLLER_MAX");
4✔
1662
                ret = ECGMAXVALUESEXCEEDED;
4✔
1663
                /*
1664
                 * There are loops in the libcgroup codebase that expect
1665
                 * there to be a null name entry at the end of the
1666
                 * cg_mount_table[].
1667
                 */
1668
                cg_mount_table[CG_CONTROLLER_MAX - 1].name[0] = '\0';
4✔
1669
        }
1670

1671
err:
1,445✔
1672
        if (proc_mount)
1,449✔
1673
                fclose(proc_mount);
1,449✔
1674

1675
        if (temp_ent)
1,449✔
1676
                free(temp_ent);
1,449✔
1677

1678
        return ret;
1,449✔
1679
}
1680

1681
/**
1682
 * cgroup_init(), initializes the MOUNT_POINT.
1683
 *
1684
 * This code is theoretically thread safe now. Its not really tested so it can
1685
 * blow up. If does for you, please let us know with your test case and we can
1686
 * really make it thread safe.
1687
 */
1688
int cgroup_init(void)
1,449✔
1689
{
1690
        static char *controllers[CG_CONTROLLER_MAX];
1691
        int ret = 0;
1,449✔
1692
        int i;
1693

1694
        cgroup_set_default_logger(-1);
1,449✔
1695

1696
        pthread_rwlock_wrlock(&cg_mount_table_lock);
1,449✔
1697

1698
        /* Free global variables filled by previous cgroup_init() */
1699
        cgroup_free_cg_mount_table();
1,449✔
1700

1701
        ret = cgroup_populate_controllers(controllers);
1,449✔
1702
        if (ret)
1,449✔
1703
                goto unlock_exit;
×
1704

1705
        ret = cgroup_populate_mount_points(controllers);
1,449✔
1706
        if (ret)
1,449✔
1707
                goto unlock_exit;
4✔
1708

1709
        cgroup_initialized = 1;
1,445✔
1710

1711
unlock_exit:
1,449✔
1712
        for (i = 0; controllers[i]; i++) {
21,091✔
1713
                free(controllers[i]);
19,642✔
1714
                controllers[i] = NULL;
19,642✔
1715
        }
1716

1717
        pthread_rwlock_unlock(&cg_mount_table_lock);
1,449✔
1718

1719
        return ret;
1,449✔
1720
}
1721

1722
static int cg_test_mounted_fs(void)
990✔
1723
{
1724
        char mntent_buff[4 * FILENAME_MAX];
1725
        struct mntent *temp_ent = NULL;
990✔
1726
        struct mntent *ent = NULL;
990✔
1727
        FILE *proc_mount = NULL;
990✔
1728
        int ret = 1;
990✔
1729

1730
        proc_mount = fopen("/proc/self/mounts", "re");
990✔
1731
        if (proc_mount == NULL)
990✔
1732
                return 0;
×
1733

1734
        temp_ent = (struct mntent *) malloc(sizeof(struct mntent));
990✔
1735
        if (!temp_ent) {
990✔
1736
                /* We just fail at the moment. */
1737
                fclose(proc_mount);
×
1738
                return 0;
×
1739
        }
1740

1741
        ent = getmntent_r(proc_mount, temp_ent, mntent_buff, sizeof(mntent_buff));
990✔
1742
        if (!ent) {
990✔
1743
                ret = 0;
×
1744
                goto done;
×
1745
        }
1746

1747
        while (strcmp(ent->mnt_type, "cgroup") != 0 &&
13,250✔
1748
               strcmp(ent->mnt_type, "cgroup2") != 0) {
13,250✔
1749
                ent = getmntent_r(proc_mount, temp_ent, mntent_buff, sizeof(mntent_buff));
12,260✔
1750
                if (ent == NULL) {
12,260✔
1751
                        ret = 0;
×
1752
                        goto done;
×
1753
                }
1754
        }
1755
done:
990✔
1756
        fclose(proc_mount);
990✔
1757
        free(temp_ent);
990✔
1758

1759
        return ret;
990✔
1760
}
1761

1762
static inline pid_t cg_gettid(void)
2✔
1763
{
1764
        return syscall(__NR_gettid);
2✔
1765
}
1766

1767
static char *cg_concat_path(const char *pref, const char *suf, char *path)
30,518✔
1768
{
1769
        if ((suf[strlen(suf)-1] == '/') || ((strlen(suf) == 0) && (pref[strlen(pref)-1] == '/')))
30,518✔
1770
                snprintf(path, FILENAME_MAX, "%s%s", pref, suf+((suf[0] == '/') ? 1 : 0));
118✔
1771
        else
1772
                snprintf(path, FILENAME_MAX, "%s%s/", pref, suf+((suf[0] == '/') ? 1 : 0));
30,400✔
1773

1774
        path[FILENAME_MAX-1] = '\0';
30,518✔
1775

1776
        return path;
30,518✔
1777
}
1778

1779
/* Call with cg_mount_table_lock taken */
1780
/* path value have to have size at least FILENAME_MAX */
1781
char *cg_build_path_locked(const char *name, char *path, const char *type)
31,204✔
1782
{
1783
        char *tmp_systemd_default_cgrp, *_path = NULL;
31,204✔
1784
        /*
1785
         * len is the allocation size for path, that stores:
1786
         * cg_mount_table[i].mount.path + '/' + cg_namespace_table[i] + '/'
1787
         */
1788
        int i, ret, len = (FILENAME_MAX * 2) + 2;
31,204✔
1789

1790
        /*
1791
         * systemd_default_cgroup can't be clobbered.   The user may pass
1792
         * multiple cgroups, hence use temporary variable for manipulations
1793
         * for example:
1794
         * cgget -g cpu:/ -g cpu:cgrp1 -g cpu:/cgrp2
1795
         */
1796
        tmp_systemd_default_cgrp = calloc(1, (sizeof(char) * len));
31,204✔
1797
        if (!tmp_systemd_default_cgrp) {
31,204✔
1798
                cgroup_err("Failed to allocate memory for tmp_systemd_default_cgroup\n");
×
1799
                goto out;
×
1800
        }
1801

1802
#ifdef WITH_SYSTEMD
1803
        /*
1804
         * If the user specifies the name as /<cgroup-name>, they are
1805
         * effectively overriding the systemd_default_cgroup but if the name
1806
         * is "/", the cgroup root path is systemd_default_cgroup
1807
         */
1808
        if (strlen(systemd_default_cgroup) && name && name[0] == '/' && name[1] != '\0')
16,276✔
1809
                tmp_systemd_default_cgrp[0] = '\0';
1810
        else
1811
                snprintf(tmp_systemd_default_cgrp, len, "%s/", systemd_default_cgroup);
16,276✔
1812

1813
        /* allocate more space for systemd_default_cgroup + '/' */
1814
        len += (FILENAME_MAX + 1);
16,276✔
1815
#endif
1816
        /*
1817
         * Recent gcc are unhappy when sizeof(dest) <= sizeof(src) with
1818
         * snprintf()'s.  Alternative is to use multiple strncpy()/strcat(),
1819
         * work around it by allocating large temporary buffer _path and
1820
         * copying the constructed _path into path.
1821
         */
1822
        _path = malloc(len);
31,204✔
1823
        if (!_path) {
31,204✔
1824
                cgroup_err("Failed to allocate memory for _path\n");
×
1825
                goto out;
×
1826
        }
1827

1828
        /*
1829
         * If no type is specified, and there's a valid cgroup v2 mount, then
1830
         * build up a path to this mount (and cgroup name if supplied).
1831
         * This can be used to create a cgroup v2 cgroup that's not attached to
1832
         * any controller.
1833
         */
1834
        if (!type && strlen(cg_cgroup_v2_mount_path) > 0) {
31,204✔
1835
                ret = snprintf(_path, len, "%s/%s", cg_cgroup_v2_mount_path,
125✔
1836
                               tmp_systemd_default_cgrp);
1837
                if (ret >= FILENAME_MAX)
125✔
1838
                        cgroup_dbg("filename too long: %s", _path);
×
1839

1840
                strncpy(path, _path, FILENAME_MAX - 1);
125✔
1841
                path[FILENAME_MAX - 1] = '\0';
125✔
1842

1843
                if (name) {
125✔
1844
                        char *tmp;
1845

1846
                        tmp = strdup(path);
125✔
1847
                        if (tmp == NULL) {
125✔
1848
                                path = NULL;
×
1849
                                goto out;
×
1850
                        }
1851

1852
                        cg_concat_path(tmp, name, path);
125✔
1853
                        free(tmp);
125✔
1854
                }
1855
                goto out;
125✔
1856
        }
1857

1858
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
107,818✔
1859
                /* Two ways to successfully move forward here:
1860
                 * 1. The "type" controller matches the name of a mounted
1861
                 *    controller
1862
                 * 2. The "type" controller requested is "cgroup" and there's
1863
                 *    a "real" controller mounted as cgroup v2
1864
                 */
1865
                if ((type && strcmp(cg_mount_table[i].name, type) == 0) ||
107,816✔
1866
                    (type && strcmp(type, CGRP_FILE_PREFIX) == 0 &&
77,103✔
1867
                     cg_mount_table[i].version == CGROUP_V2)) {
364✔
1868

1869
                        if (cg_namespace_table[i])
31,077✔
1870
                                ret = snprintf(_path, len, "%s/%s%s/", cg_mount_table[i].mount.path,
4✔
1871
                                               tmp_systemd_default_cgrp, cg_namespace_table[i]);
1872
                        else
1873
                                ret = snprintf(_path, len, "%s/%s", cg_mount_table[i].mount.path,
31,073✔
1874
                                               tmp_systemd_default_cgrp);
1875

1876
                        if (ret >= FILENAME_MAX)
31,077✔
1877
                                cgroup_dbg("filename too long: %s", _path);
×
1878

1879
                        strncpy(path, _path, FILENAME_MAX - 1);
31,077✔
1880
                        path[FILENAME_MAX - 1] = '\0';
31,077✔
1881

1882
                        if (name) {
31,077✔
1883
                                char *tmp;
1884

1885
                                tmp = strdup(path);
30,393✔
1886
                                if (tmp == NULL)
30,393✔
1887
                                        break;
×
1888

1889
                                cg_concat_path(tmp, name, path);
30,393✔
1890
                                free(tmp);
30,393✔
1891
                        }
1892
                        goto out;
31,077✔
1893
                }
1894
        }
1895
        path = NULL;
2✔
1896

1897
out:
31,204✔
1898
        if (_path)
31,204✔
1899
                free(_path);
31,204✔
1900

1901
        if (tmp_systemd_default_cgrp)
31,204✔
1902
                free(tmp_systemd_default_cgrp);
31,204✔
1903

1904
        return path;
31,204✔
1905
}
1906

1907
char *cg_build_path(const char *name, char *path, const char *type)
1,856✔
1908
{
1909
        pthread_rwlock_rdlock(&cg_mount_table_lock);
1,856✔
1910
        path = cg_build_path_locked(name, path, type);
1,856✔
1911
        pthread_rwlock_unlock(&cg_mount_table_lock);
1,856✔
1912

1913
        return path;
1,856✔
1914
}
1915

1916
static int cgroup_get_cg_type(const char * const path, char * const type,
566✔
1917
                              size_t type_sz, bool is_tid)
1918
{
1919
        char cg_type_path[FILENAME_MAX];
1920
        char cg_type[CGV2_CONTROLLERS_LL_MAX];
1921
        int len, err = 0;
566✔
1922
        FILE *fp = NULL;
566✔
1923

1924
        snprintf(cg_type_path, FILENAME_MAX, "%scgroup.type", path);
566✔
1925
        fp = fopen(cg_type_path, "re");
566✔
1926
        if (!fp) {
566✔
1927
                if (errno == ENOENT) {
130✔
1928
                        /* file cgroup.type, doesn't exist for root cgroup. */
1929
                        snprintf(type, type_sz, "cgroup.procs");
130✔
1930
                        goto out;
130✔
1931
                } else {
1932
                        cgroup_warn("failed to open file %s: %s\n", cg_type_path, strerror(errno));
×
1933
                        err = ECGOTHER;
×
1934
                        goto out;
×
1935
                }
1936
        }
1937

1938
        if (fgets(cg_type, CGV2_CONTROLLERS_LL_MAX, fp) == NULL) {
436✔
1939
                cgroup_warn("failed to read file %s: %s\n", cg_type_path, strerror(errno));
×
1940
                err = ECGOTHER;
×
1941
                goto out;
×
1942
        }
1943

1944
        len = strlen(cg_type) - 1;
436✔
1945
        /*
1946
         * Append cgroup.threads to the path, if the cgroup.type is 'threaded'
1947
         * or 'domain threaded', with is_tid set. For cgroup.type 'domain' or
1948
         * 'domain invalid' or 'domain threaded', with is_tid is unset, append
1949
         * cgroup.procs to the path.
1950
         *
1951
         * domain type is used for regular cgroup and domain threaded for root
1952
         * of threaded cgroup v2 subtree. Another possible type is domain invalid,
1953
         * it's an invalid state, under the threaded subtree. is_tid is set when
1954
         * called from cgroup_attach_thread_tid() or unset other wise.
1955
         * Refer to Kernel's cgroup v2 documentation for more detailed explanation
1956
         * on domains types.
1957
         */
1958
        if (strncmp(cg_type, "domain", len) == 0         ||
436✔
1959
            strncmp(cg_type, "domain invalid", len) == 0 ||
20✔
1960
            (!is_tid && strncmp(cg_type, "domain threaded", len) == 0)) {
13✔
1961
                snprintf(type, type_sz, "cgroup.procs");
432✔
1962
        } else if (strncmp(cg_type, "threaded", len) == 0 ||
4✔
1963
                   (is_tid && strncmp(cg_type, "domain threaded", len) == 0)) {
×
1964
                snprintf(type, type_sz, "cgroup.threads");
4✔
1965
        } else {
1966
                cgroup_warn("invalid %scgroup.type: %s\n", path, cg_type);
×
1967
                err = ECGOTHER;
×
1968
        }
1969

1970
out:
566✔
1971
        if (fp)
566✔
1972
                fclose(fp);
436✔
1973

1974
        return err;
566✔
1975
}
1976

1977
int cgroup_build_tasks_procs_path(char * const path, size_t path_sz, const char * const cg_name,
570✔
1978
                                  const char * const ctrl_name)
1979
{
1980
        enum cg_version_t version;
1981
        char cg_type[CGV2_CONTROLLERS_LL_MAX];
1982
        int err = ECGOTHER;
570✔
1983

1984
        if (!cg_build_path(cg_name, path, ctrl_name))
570✔
1985
                goto error;
1✔
1986

1987
        err = cgroup_get_controller_version(ctrl_name, &version);
569✔
1988
        if (err)
569✔
1989
                goto error;
×
1990

1991
        switch (version) {
569✔
1992
        case CGROUP_V1:
2✔
1993
                strncat(path, "tasks", path_sz - strlen(path));
2✔
1994
                err = 0;
2✔
1995
                break;
2✔
1996
        case CGROUP_V2:
566✔
1997
                err = cgroup_get_cg_type(path, cg_type, sizeof(cg_type), 0);
566✔
1998
                if (err)
566✔
1999
                        goto error;
×
2000

2001
                strncat(path, cg_type, path_sz - strlen(path));
566✔
2002
                break;
566✔
2003
        default:
1✔
2004
                err = ECGOTHER;
1✔
2005
                break;
1✔
2006
        }
2007

2008
error:
570✔
2009
        if (err)
570✔
2010
                path[0] = '\0';
2✔
2011

2012
        cgroup_dbg("cgroup build procs path: %s\n", path);
570✔
2013

2014
        return err;
570✔
2015
}
2016

2017
STATIC int cgroupv2_controller_enabled(const char * const cg_name, const char * const ctrl_name)
83✔
2018
{
2019
        char path[FILENAME_MAX] = {0};
83✔
2020
        char *parent = NULL, *dname;
83✔
2021
        enum cg_version_t version;
2022
        bool enabled;
2023
        int error;
2024

2025
        error = cgroup_get_controller_version(ctrl_name, &version);
83✔
2026
        if (error)
83✔
2027
                return error;
×
2028

2029
        if (version != CGROUP_V2)
83✔
2030
                return 0;
1✔
2031

2032
        if (ctrl_name == NULL)
82✔
2033
                /* cgroup v2 supports cgroups with no controllers. */
2034
                return 0;
6✔
2035

2036
        if (strncmp(cg_name, "/", strlen(cg_name)) == 0)
76✔
2037
                /*
2038
                 * The root cgroup has been requested.  All version 2
2039
                 * controllers are enabled on the root cgroup.
2040
                 */
2041
                return 0;
1✔
2042

2043
        if (!cg_build_path(cg_name, path, ctrl_name))
75✔
2044
                goto err;
×
2045

2046
        parent = strdup(path);
75✔
2047
        if (!parent) {
75✔
2048
                error = ECGOTHER;
×
2049
                goto err;
×
2050
        }
2051

2052
        dname = dirname(parent);
75✔
2053

2054
        error = cgroupv2_get_subtree_control(dname, ctrl_name, &enabled);
75✔
2055
        if (error)
75✔
2056
                goto err;
2✔
2057

2058
        if (enabled)
73✔
2059
                error = 0;
73✔
2060
err:
×
2061
        if (parent)
75✔
2062
                free(parent);
75✔
2063

2064
        return error;
75✔
2065
}
2066

2067
static int __cgroup_attach_task_pid(char *path, pid_t tid)
97✔
2068
{
2069
        FILE *tasks = NULL;
97✔
2070
        int ret = 0;
97✔
2071

2072
        tasks = fopen(path, "we");
97✔
2073
        if (!tasks) {
97✔
2074
                switch (errno) {
2✔
2075
                case EPERM:
×
2076
                        ret = ECGROUPNOTOWNER;
×
2077
                        break;
×
2078
                case ENOENT:
2✔
2079
                        ret = ECGROUPNOTEXIST;
2✔
2080
                        break;
2✔
2081
                default:
×
2082
                        ret = ECGROUPNOTALLOWED;
×
2083
                }
2084
                goto err;
2✔
2085
        }
2086
        ret = fprintf(tasks, "%d", tid);
95✔
2087
        if (ret < 0) {
95✔
2088
                last_errno = errno;
×
2089
                ret = ECGOTHER;
×
2090
                goto err;
×
2091
        }
2092
        ret = fflush(tasks);
95✔
2093
        if (ret) {
95✔
2094
                last_errno = errno;
2✔
2095
                ret = ECGOTHER;
2✔
2096
                goto err;
2✔
2097
        }
2098
        fclose(tasks);
93✔
2099
        return 0;
93✔
2100
err:
4✔
2101
        cgroup_warn("cannot write tid %d to %s:%s\n", tid, path, strerror(errno));
4✔
2102
        if (tasks)
4✔
2103
                fclose(tasks);
2✔
2104
        return ret;
4✔
2105
}
2106

2107
static int cgroup_build_tid_path(const char * const ctrl_name, char *path)
×
2108
{
2109
        char cg_type[CGV2_CONTROLLERS_LL_MAX];
2110
        enum cg_version_t version;
2111
        size_t len;
2112
        int ret;
2113

2114
        ret = cgroup_get_controller_version(ctrl_name, &version);
×
2115
        if (ret)
×
2116
                return ret;
×
2117

2118
        if (version == CGROUP_V2) {
×
2119

2120
                len = strlen(path) - 12;
×
2121
                if (strncmp(path + len, "cgroup.procs", 12) != 0) {
×
2122
                        ret = ECGOTHER;
×
2123
                        return ret;
×
2124
                }
2125

2126
                /* right trim cgroup.procs file name in the path */
2127
                path[len] = '\0';
×
2128

2129
                ret = cgroup_get_cg_type(path, cg_type, sizeof(cg_type), 1);
×
2130
                if (ret)
×
2131
                        return ret;
×
2132

2133
                strncat(path, cg_type, FILENAME_MAX - (len + 1));
×
2134
                path[FILENAME_MAX - 1] = '\0';
×
2135
        }
2136

2137
        if (version != CGROUP_V1)
×
2138
                return ret;
×
2139

2140
        /* replace tasks with cgroup.procs file name in the path */
2141
        len = strlen(path) - 5;
×
2142
        path[len] = '\0';
×
2143

2144
        strncat(path, "cgroup.procs", FILENAME_MAX - (len + 1));
×
2145
        path[FILENAME_MAX - 1] = '\0';
×
2146

2147
        return ret;
×
2148
}
2149

2150
static int cgroup_attach_task_tid(struct cgroup *cgrp, pid_t tid, bool move_tids)
59✔
2151
{
2152
        char path[FILENAME_MAX] = {0};
59✔
2153
        char *controller_name = NULL;
59✔
2154
        int empty_cgrp = 0;
59✔
2155
        int i, ret = 0;
59✔
2156

2157
        if (!cgroup_initialized) {
59✔
2158
                cgroup_warn("libcgroup is not initialized\n");
×
2159
                return ECGROUPNOTINITIALIZED;
×
2160
        }
2161

2162
        /* if the cgroup is NULL, attach the task to the root cgroup. */
2163
        if (!cgrp) {
59✔
2164
                pthread_rwlock_rdlock(&cg_mount_table_lock);
2✔
2165
                for (i = 0; i < CG_CONTROLLER_MAX && cg_mount_table[i].name[0] != '\0'; i++) {
22✔
2166
                        ret = cgroup_build_tasks_procs_path(path, sizeof(path), NULL,
20✔
2167
                                                            cg_mount_table[i].name);
20✔
2168
                        if (ret)
20✔
2169
                                return ret;
×
2170

2171
                        if (move_tids) {
20✔
2172
                                ret = cgroup_build_tid_path(controller_name, path);
×
2173
                                if (ret)
×
2174
                                        return ret;
×
2175
                        }
2176

2177
                        ret = __cgroup_attach_task_pid(path, tid);
20✔
2178
                        if (ret) {
20✔
2179
                                pthread_rwlock_unlock(&cg_mount_table_lock);
×
2180
                                return ret;
×
2181
                        }
2182
                }
2183
                pthread_rwlock_unlock(&cg_mount_table_lock);
2✔
2184
        } else {
2185
                for (i = 0; i < cgrp->index; i++) {
128✔
2186
                        if (!cgroup_test_subsys_mounted(cgrp->controller[i]->name)) {
71✔
2187
                                cgroup_warn("subsystem %s is not mounted\n",
×
2188
                                            cgrp->controller[i]->name);
2189
                                return ECGROUPSUBSYSNOTMOUNTED;
×
2190
                        }
2191
                }
2192

2193
                if (cgrp->index == 0)
57✔
2194
                        /* Valid empty cgroup v2 with no controllers added. */
2195
                        empty_cgrp = 1;
6✔
2196

2197
                for (i = 0, controller_name = NULL;
57✔
2198
                     empty_cgrp > 0 || i < cgrp->index;
130✔
2199
                     i++, empty_cgrp--) {
73✔
2200

2201
                        if (cgrp->controller[i])
77✔
2202
                                controller_name = cgrp->controller[i]->name;
71✔
2203

2204
                        ret = cgroupv2_controller_enabled(cgrp->name, controller_name);
77✔
2205
                        if (ret)
77✔
2206
                                return ret;
×
2207

2208
                        ret = cgroup_build_tasks_procs_path(path, sizeof(path), cgrp->name,
77✔
2209
                                                            controller_name);
2210
                        if (ret)
77✔
2211
                                return ret;
×
2212

2213
                        if (move_tids) {
77✔
2214
                                ret = cgroup_build_tid_path(controller_name, path);
×
2215
                                if (ret)
×
2216
                                        return ret;
×
2217
                        }
2218

2219
                        ret = __cgroup_attach_task_pid(path, tid);
77✔
2220
                        if (ret)
77✔
2221
                                return ret;
4✔
2222
                }
2223
        }
2224
        return 0;
55✔
2225
}
2226

2227
/**
2228
 *  cgroup_attach_task_pid is used to assign tasks to a cgroup.
2229
 *  struct cgroup *cgroup: The cgroup to assign the thread to.
2230
 *  pid_t tid: The thread to be assigned to the cgroup.
2231
 *
2232
 *  returns 0 on success.
2233
 *  returns ECGROUPNOTOWNER if the caller does not have access to the cgroup.
2234
 *  returns ECGROUPNOTALLOWED for other causes of failure.
2235
 */
2236
int cgroup_attach_task_pid(struct cgroup *cgroup, pid_t tid)
57✔
2237
{
2238
        return cgroup_attach_task_tid(cgroup, tid, 0);
57✔
2239
}
2240

2241
/**
2242
 * cgroup_attach_task is used to attach the current thread to a cgroup.
2243
 * struct cgroup *cgroup: The cgroup to assign the current thread to.
2244
 *
2245
 * See cg_attach_task_pid for return values.
2246
 */
2247
int cgroup_attach_task(struct cgroup *cgroup)
2✔
2248
{
2249
        pid_t tid = cg_gettid();
2✔
2250

2251
        return cgroup_attach_task_tid(cgroup, tid, 0);
2✔
2252
}
2253

2254
/**
2255
 *  cgroup_attach_thread_tid is used to assign threads to a cgroup.
2256
 *  struct cgroup *cgroup: The cgroup to assign the thread to.
2257
 *  pid_t tid: The thread to be assigned to the cgroup.
2258
 *
2259
 *  returns 0 on success.
2260
 *  returns ECGROUPNOTOWNER if the caller does not have access to the cgroup.
2261
 *  returns ECGROUPNOTALLOWED for other causes of failure.
2262
 */
2263
int cgroup_attach_thread_tid(struct cgroup *cgroup, pid_t tid)
×
2264
{
2265
        return cgroup_attach_task_tid(cgroup, tid, 1);
×
2266
}
2267

2268
/**
2269
 * cg_mkdir_p, emulate the mkdir -p command (recursively creating paths)
2270
 * @path: path to create
2271
 */
2272
int cg_mkdir_p(const char *path)
496✔
2273
{
2274
        char *real_path = NULL;
496✔
2275
        char *tmp_path = NULL;
496✔
2276
        struct stat st;
2277
        int ret = 0;
496✔
2278
        int i = 0;
496✔
2279
        char pos;
2280

2281
        real_path = strdup(path);
496✔
2282
        if (!real_path) {
496✔
2283
                last_errno = errno;
×
2284
                return ECGOTHER;
×
2285
        }
2286

2287
        do {
2288
                while (real_path[i] != '\0' && real_path[i] == '/')
4,552✔
2289
                        i++;
2,344✔
2290

2291
                if (real_path[i] == '\0')
2,208✔
2292
                        break; /* The path ends with '/', ignore it. */
247✔
2293

2294
                while (real_path[i] != '\0' && real_path[i] != '/')
12,922✔
2295
                        i++;
10,961✔
2296

2297
                pos = real_path[i];
1,961✔
2298
                real_path[i] = '\0';                /* Temporarily overwrite "/" */
1,961✔
2299

2300
                /* 0775 == S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH */
2301
                ret = mkdir(real_path, 0775);
1,961✔
2302
                if (ret) {
1,961✔
2303
                        switch (errno) {
1,732✔
2304
                        case EEXIST:
1,732✔
2305
                                ret = 0;        /* Not fatal really */
1,732✔
2306
                                break;
1,732✔
2307
                        case EPERM:
×
2308
                                ret = ECGROUPNOTOWNER;
×
2309
                                goto done;
×
2310
                        case EROFS:
×
2311
                                /*
2312
                                 * Check if path exists, use tmp_path to
2313
                                 * keep Coverity happy
2314
                                 */
2315
                                tmp_path = real_path;
×
2316
                                ret = stat(tmp_path, &st);
×
2317
                                if (ret == 0)
×
2318
                                        break;        /* Path exists */
×
2319
                        default: /* fallthrough */
2320
                                ret = ECGROUPNOTALLOWED;
×
2321
                                goto done;
×
2322
                        }
2323
                }
2324
                real_path[i] = pos;
1,961✔
2325
        } while (real_path[i]);
1,961✔
2326

2327
done:
249✔
2328
        free(real_path);
496✔
2329

2330
        return ret;
496✔
2331
}
2332

2333
/*
2334
 * create_control_group()
2335
 * This is the basic function used to create the control group. This function
2336
 * just makes the group. It does not set any permissions, or any control values.
2337
 * The argument path is the fully qualified path name to make it generic.
2338
 */
2339
static int cg_create_control_group(const char *path)
496✔
2340
{
2341
        int error;
2342

2343
        if (!cg_test_mounted_fs())
496✔
2344
                return ECGROUPNOTMOUNTED;
×
2345

2346
        error = cg_mkdir_p(path);
496✔
2347
        return error;
496✔
2348
}
2349

2350
/*
2351
 * set_control_value()
2352
 * This is the low level function for putting in a value in a control file.
2353
 * This function takes in the complete path and sets the value in val in that file.
2354
 */
2355
static int cg_set_control_value(char *path, const char *val)
494✔
2356
{
2357
        char *str_val_start;
2358
        char *str_val;
2359
        int ctl_file;
2360
        size_t len;
2361
        char *pos;
2362

2363
        if (!cg_test_mounted_fs())
494✔
2364
                return ECGROUPNOTMOUNTED;
×
2365

2366
        ctl_file = open(path, O_RDWR | O_CLOEXEC);
494✔
2367

2368
        if (ctl_file == -1) {
494✔
2369
                if (errno == EPERM) {
×
2370
                        /*
2371
                         * We need to set the correct error value, does the
2372
                         * group exist but we don't have the subsystem mounted
2373
                         * at that point, or is it that the group does not exist.
2374
                         * So we check if the tasks file exist. Before that, we
2375
                         * need to extract the path.
2376
                         */
2377
                        char *path_dir_end;
2378
                        FILE *control_file;
2379
                        char *tasks_path;
2380

2381
                        path_dir_end = strrchr(path, '/');
×
2382
                        if (path_dir_end == NULL)
×
2383
                                return ECGROUPVALUENOTEXIST;
×
2384

2385
                        /* task_path contain: $path/tasks */
2386
                        tasks_path = (char *)malloc(strlen(path) + 6 + 1);
×
2387
                        if (tasks_path == NULL) {
×
2388
                                last_errno = errno;
×
2389
                                return ECGOTHER;
×
2390
                        }
2391
                        strcpy(tasks_path, path);
×
2392
                        strcat(tasks_path, "/tasks");
×
2393

2394
                        /* Test tasks file for read flag */
2395
                        control_file = fopen(tasks_path, "re");
×
2396
                        if (!control_file) {
×
2397
                                if (errno == ENOENT) {
×
2398
                                        free(tasks_path);
×
2399
                                        return ECGROUPSUBSYSNOTMOUNTED;
×
2400
                                }
2401
                        } else {
2402
                                fclose(control_file);
×
2403
                        }
2404

2405
                        free(tasks_path);
×
2406
                        return ECGROUPNOTALLOWED;
×
2407
                }
2408
                return ECGROUPVALUENOTEXIST;
×
2409
        }
2410

2411
        /*
2412
         * Split the multiline value into lines.  One line is a special
2413
         * case of multiline value.
2414
         */
2415
        str_val = strdup(val);
494✔
2416
        if (str_val == NULL) {
494✔
2417
                last_errno = errno;
×
2418
                close(ctl_file);
×
2419
                return ECGOTHER;
×
2420
        }
2421

2422
        str_val_start = str_val;
494✔
2423
        pos = str_val;
494✔
2424

2425
        do {
2426
                str_val = pos;
506✔
2427
                pos = strchr(str_val, '\n');
506✔
2428

2429
                if (pos) {
506✔
2430
                        *pos = '\0';
12✔
2431
                        ++pos;
12✔
2432
                }
2433

2434
                len = strlen(str_val);
506✔
2435
                if (len > 0) {
506✔
2436
                        if (write(ctl_file, str_val, len) == -1) {
494✔
2437
                                last_errno = errno;
23✔
2438
                                free(str_val_start);
23✔
2439
                                close(ctl_file);
23✔
2440
                                return ECGOTHER;
23✔
2441
                        }
2442
                } else
2443
                        cgroup_warn("skipping empty line for %s\n", path);
12✔
2444
        } while (pos);
483✔
2445

2446
        if (close(ctl_file)) {
471✔
2447
                last_errno = errno;
×
2448
                free(str_val_start);
×
2449
                return ECGOTHER;
×
2450
        }
2451

2452
        free(str_val_start);
471✔
2453
        return 0;
471✔
2454
}
2455

2456
/**
2457
 * Walk the settings in controller and write their values to disk
2458
 *
2459
 * @param base The full path to the base of this cgroup
2460
 * @param controller The controller whose values are being updated
2461
 * @param ignore_non_dirty_values If set skips writing non-dirty controller settings
2462
 */
2463
STATIC int cgroup_set_values_recursive(const char * const base,
615✔
2464
                                       const struct cgroup_controller * const controller,
2465
                                       bool ignore_non_dirty_values)
2466
{
2467
        struct control_value *cv;
2468
        struct stat path_stat;
2469
        int ret, j, error = 0;
615✔
2470
        char *path = NULL;
615✔
2471

2472
        for (j = 0; j < controller->index; j++) {
1,013✔
2473
                cv = controller->values[j];
430✔
2474

2475
                /*
2476
                 * ignore_non_dirty_values is set while writing into
2477
                 * existing cgroup to modify controller settings and
2478
                 * unset during cgroup creation.  The subtle difference
2479
                 * is that dirty flag is unset for all the controller
2480
                 * settings during cgroup creation, whereas some or all
2481
                 * controller settings have the dirty flag set during
2482
                 * modification.
2483
                 *
2484
                 * Be careful with ignore_non_dirty_values flag, setting
2485
                 * it writing only the controller settings that has it
2486
                 * dirty value set.
2487
                 */
2488
                if (ignore_non_dirty_values && cv->dirty == false)
430✔
2489
                        continue;
×
2490

2491
                /* We don't support, writing multiline settings */
2492
                if (strcspn(cv->value, "\n")  < (strlen(cv->value) - 1))
430✔
2493
                        continue;
×
2494

2495
                ret = asprintf(&path, "%s%s", base, cv->name);
430✔
2496
                if (ret < 0) {
430✔
2497
                        last_errno = errno;
×
2498
                        error = ECGOTHER;
×
2499
                        goto err;
×
2500
                }
2501

2502
                /* skip read-only settings */
2503
                ret = (stat(path, &path_stat));
430✔
2504
                if (ret < 0) {
430✔
2505
                        last_errno = errno;
10✔
2506
                        error = ECGROUPVALUENOTEXIST;
10✔
2507
                        goto err;
10✔
2508
                }
2509

2510
                /* 0200 == S_IWUSR */
2511
                if (!(path_stat.st_mode & 0200)) {
420✔
2512
                        free(path);
×
2513
                        path = NULL;
×
2514
                        continue;
×
2515
                }
2516

2517
                cgroup_dbg("setting %s to \"%s\", pathlen %d\n", path, cv->value, ret);
420✔
2518

2519
                error = cg_set_control_value(path, cv->value);
420✔
2520
                free(path);
420✔
2521
                path = NULL;
420✔
2522

2523
                if (error) {
420✔
2524
                        /* Ignore the errors on deprecated settings */
2525
                        if (last_errno == EOPNOTSUPP) {
22✔
2526
                                error = 0;
×
2527
                                continue;
×
2528
                        }
2529
                        goto err;
22✔
2530
                }
2531
                cv->dirty = false;
398✔
2532
        }
2533

2534
err:
583✔
2535
        /*
2536
         * As currently written, path should always be null as we are\
2537
         * exiting this function, but let's check just in case, and free it
2538
         * if it's non-null
2539
         */
2540
        if (path)
615✔
2541
                free(path);
10✔
2542

2543
        return error;
615✔
2544
}
2545

2546
/**
2547
 * Check if the requested cgroup controller is enabled in the specified file
2548
 *
2549
 * @param path Cgroup directory
2550
 * @param ctrl_name Name of the controller to check
2551
 * @param output parameter that indicates whether the controller is enabled
2552
 * @param file to open and parse
2553
 *        0 = cgroup.subtree_control
2554
 *        1 = cgroup.controllers
2555
 */
2556
STATIC int __cgroupv2_get_enabled(const char *path, const char *ctrl_name,
1,169✔
2557
                                  bool * const enabled, int file_enum)
2558
{
2559
        char *path_copy = NULL, *saveptr = NULL, *token, *ret_c, *filename;
1,169✔
2560
        int ret, error = ECGROUPNOTMOUNTED;
1,169✔
2561
        char buffer[FILENAME_MAX];
2562
        FILE *fp = NULL;
1,169✔
2563

2564
        if (!path || !ctrl_name || !enabled)
1,169✔
2565
                return ECGOTHER;
×
2566

2567
        *enabled = false;
1,169✔
2568

2569
        switch (file_enum) {
1,169✔
2570
        case 0: /* cgroup.subtree_control */
553✔
2571
                filename = CGV2_SUBTREE_CTRL_FILE;
553✔
2572
                break;
553✔
2573
        case 1: /* cgroup.controllers */
616✔
2574
                filename = CGV2_CONTROLLERS_FILE;
616✔
2575
                break;
616✔
2576
        default:
×
2577
                return ECGINVAL;
×
2578
        }
2579

2580
        path_copy = (char *)malloc(FILENAME_MAX);
1,169✔
2581
        if (!path_copy) {
1,169✔
2582
                error = ECGOTHER;
×
2583
                goto out;
×
2584
        }
2585

2586
        ret = snprintf(path_copy, FILENAME_MAX, "%s/%s", path, filename);
1,169✔
2587
        if (ret < 0) {
1,169✔
2588
                error = ECGOTHER;
×
2589
                goto out;
×
2590
        }
2591

2592
        fp = fopen(path_copy, "re");
1,169✔
2593
        if (!fp) {
1,169✔
2594
                cgroup_warn("fopen failed\n");
×
2595
                last_errno = errno;
×
2596
                error = ECGOTHER;
×
2597
                goto out;
×
2598
        }
2599

2600
        ret_c = fgets(buffer, sizeof(buffer), fp);
1,169✔
2601
        if (ret_c == NULL)
1,169✔
2602
                /* The subtree control file is empty */
2603
                goto out;
51✔
2604

2605
        /* Remove the trailing newline */
2606
        ret_c[strlen(ret_c) - 1] = '\0';
1,118✔
2607

2608
        /*
2609
         * Split the enabled controllers by " " and evaluate if the
2610
         * requested controller is enabled.
2611
         */
2612
        token = strtok_r(buffer, " ", &saveptr);
1,118✔
2613
        do {
2614
                if (strncmp(ctrl_name, token, FILENAME_MAX) == 0) {
3,516✔
2615
                        error = 0;
829✔
2616
                        *enabled = true;
829✔
2617
                        break;
829✔
2618
                }
2619
        } while ((token = strtok_r(NULL, " ", &saveptr)));
2,687✔
2620

2621
out:
289✔
2622
        if (path_copy)
1,169✔
2623
                free(path_copy);
1,169✔
2624
        if (fp)
1,169✔
2625
                fclose(fp);
1,169✔
2626

2627
        return error;
1,169✔
2628
}
2629

2630
/**
2631
 * Check if the requested cgroup controller is enabled on this subtree
2632
 *
2633
 * @param path Cgroup directory
2634
 * @param ctrl_name Name of the controller to check
2635
 * @param output parameter that indicates whether the controller is enabled
2636
 */
2637
STATIC int cgroupv2_get_subtree_control(const char *path, const char *ctrl_name,
553✔
2638
                                        bool * const enabled)
2639
{
2640
        return __cgroupv2_get_enabled(path, ctrl_name, enabled, 0);
553✔
2641
}
2642
/**
2643
 * Check if the requested cgroup controller is enabled in this cgroup's cgroup.controllers file
2644
 *
2645
 * @param path Cgroup directory
2646
 * @param ctrl_name Name of the controller to check
2647
 * @param output parameter that indicates whether the controller is enabled
2648
 */
2649
static int cgroupv2_get_controllers(const char *path, const char *ctrl_name,
616✔
2650
                                    bool * const enabled)
2651
{
2652
        return __cgroupv2_get_enabled(path, ctrl_name, enabled, 1);
616✔
2653
}
2654

2655
/**
2656
 * Enable/Disable a controller in the cgroup v2 subtree_control file
2657
 *
2658
 * @param path Directory that contains the subtree_control file
2659
 * @param ctrl_name Name of the controller to be enabled/disabled
2660
 * @param enable Enable/Disable the given controller
2661
 */
2662
STATIC int cgroupv2_subtree_control(const char *path, const char *ctrl_name, bool enable)
74✔
2663
{
2664
        int ret, error = ECGOTHER;
74✔
2665
        char *path_copy = NULL;
74✔
2666
        char *value = NULL;
74✔
2667

2668
        if (!path || !ctrl_name)
74✔
2669
                return ECGOTHER;
×
2670

2671
        value = (char *)malloc(FILENAME_MAX);
74✔
2672
        if (!value)
74✔
2673
                goto out;
×
2674

2675
        path_copy = (char *)malloc(FILENAME_MAX);
74✔
2676
        if (!path_copy)
74✔
2677
                goto out;
×
2678

2679
        ret = snprintf(path_copy, FILENAME_MAX, "%s/%s", path, CGV2_SUBTREE_CTRL_FILE);
74✔
2680
        if (ret < 0)
74✔
2681
                goto out;
×
2682

2683
        if (enable)
74✔
2684
                ret = snprintf(value, FILENAME_MAX, "+%s", ctrl_name);
73✔
2685
        else
2686
                ret = snprintf(value, FILENAME_MAX, "-%s", ctrl_name);
1✔
2687
        if (ret < 0)
74✔
2688
                goto out;
×
2689

2690
        error = cg_set_control_value(path_copy, value);
74✔
2691
        if (error)
74✔
2692
                goto out;
1✔
2693

2694
out:
73✔
2695
        if (value)
74✔
2696
                free(value);
74✔
2697
        if (path_copy)
74✔
2698
                free(path_copy);
74✔
2699
        return error;
74✔
2700
}
2701

2702
/*
2703
 * Test if the controller is enabled in the root_cgrp.subtree_control file
2704
 * and enable the controller, if not enabled.
2705
 */
2706
static int test_and_set_ctrl_mnt_path(const char * const mount_path, const char * const ctrl_name)
223✔
2707
{
2708
        bool enabled;
2709
        int ret;
2710

2711
        /* return if the controller is enabled */
2712
        ret = cgroupv2_get_subtree_control(mount_path, ctrl_name, &enabled);
223✔
2713
        if (ret == ECGOTHER)
223✔
2714
                return ret;
×
2715

2716
        if (enabled == true)
223✔
2717
                return 0;
213✔
2718

2719
        /* check if the controller is available is controllers file */
2720
        ret = cgroupv2_get_controllers(mount_path, ctrl_name, &enabled);
10✔
2721
        if (ret == ECGOTHER || ret == ECGROUPNOTMOUNTED)
10✔
2722
                return ret;
×
2723

2724
        /* enable the controller in the subtree_control file */
2725
        ret = cgroupv2_subtree_control(mount_path, ctrl_name, 1);
10✔
2726
        if (ret)
10✔
2727
                return ret;
×
2728

2729
        return 0;
10✔
2730
}
2731

2732
/**
2733
 * Recursively enable/disable a controller in the cgv2 subtree_control file
2734
 *
2735
 * @param path Directory that contains the subtree_control file
2736
 * @param ctrl_name Name of the controller to be enabled/disabled
2737
 * @param enable Enable/Disable the given controller
2738
 */
2739
STATIC int cgroupv2_subtree_control_recursive(char *path, const char *ctrl_name, bool enable)
223✔
2740
{
2741
        char *path_copy, *tmp_path, *stok_buff = NULL;
223✔
2742
        bool found_mount = false, controller_enabled = false;
223✔
2743
        size_t mount_len;
2744
        int i, error = 0;
223✔
2745

2746
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
583✔
2747
                if (strncmp(cg_mount_table[i].name, ctrl_name,
583✔
2748
                            sizeof(cg_mount_table[i].name)) == 0) {
2749
                        found_mount = true;
223✔
2750
                        break;
223✔
2751
                }
2752
        }
2753

2754
        if (!found_mount)
223✔
2755
                return ECGROUPSUBSYSNOTMOUNTED;
×
2756

2757
        path_copy = strdup(path);
223✔
2758
        if (!path_copy)
223✔
2759
                return ECGOTHER;
×
2760

2761
        /*
2762
         * Null terminate the path_copy to match the string length of the
2763
         * controller mount.  We'll incrementally build up the string, subdir
2764
         * by subdir, and enable the subtree control file each step of the way
2765
         */
2766
        mount_len = strlen(cg_mount_table[i].mount.path);
223✔
2767
        path_copy[mount_len] = '\0';
223✔
2768

2769
        /*
2770
         * systemd by default only enables cpu, cpuset, io, memory, and pids
2771
         * controller in the root_cgroup.subtree_control. Trying to enable
2772
         * hugetlb and misc controller in a nested cgroups, will fail
2773
         * because they are not, enabled the controller in the root_cgroup.
2774
         * i.e. # cgcreate -ghugetlb:foo/bar
2775
         *
2776
         * check if the controller is enabled in the
2777
         * root_cgroup.subtree_control file and if not enable it. Let's
2778
         * check for all controllers, so that it accommodates other
2779
         * controllers systemd decides to disable by default in the future.
2780
         */
2781
        error = test_and_set_ctrl_mnt_path(path_copy, ctrl_name);
223✔
2782
        if (error)
223✔
2783
                goto out;
×
2784

2785
        tmp_path = strtok_r(&path[mount_len], "/", &stok_buff);
223✔
2786
        do {
2787
                if (tmp_path) {
249✔
2788
                        strcat(path_copy, "/");
94✔
2789
                        strcat(path_copy, tmp_path);
94✔
2790
                }
2791

2792
                error = cg_create_control_group(path_copy);
249✔
2793
                if (error)
249✔
2794
                        goto out;
×
2795

2796
                error = cgroupv2_get_subtree_control(path_copy, ctrl_name, &controller_enabled);
249✔
2797
                if (controller_enabled)
249✔
2798
                        continue;
187✔
2799
                if (error != ECGROUPNOTMOUNTED)
62✔
2800
                        goto out;
×
2801

2802
                error = cgroupv2_subtree_control(path_copy, ctrl_name, enable);
62✔
2803
                if (error)
62✔
2804
                        goto out;
1✔
2805
        } while ((tmp_path = strtok_r(NULL, "/", &stok_buff)));
248✔
2806

2807
out:
222✔
2808
        free(path_copy);
223✔
2809
        return error;
223✔
2810
}
2811

2812
/**
2813
 * cgroup_modify_cgroup modifies the cgroup control files.
2814
 * struct cgroup *cgrp: The name will be the cgroup to be modified.
2815
 * The values will be the values to be modified, those not mentioned in the
2816
 * structure will not be modified.
2817
 *
2818
 * The uids cannot be modified yet.
2819
 *
2820
 * returns 0 on success.
2821
 */
2822

2823
int cgroup_modify_cgroup(struct cgroup *cgrp)
411✔
2824
{
2825
        char base[FILENAME_MAX];
2826
        int error = 0;
411✔
2827
        int i;
2828

2829
        if (!cgroup_initialized)
411✔
2830
                return ECGROUPNOTINITIALIZED;
×
2831

2832
        if (!cgrp)
411✔
2833
                return ECGROUPNOTALLOWED;
×
2834

2835
        for (i = 0; i < cgrp->index; i++) {
801✔
2836
                if (!cgroup_test_subsys_mounted(cgrp->controller[i]->name)) {
390✔
2837
                        cgroup_warn("subsystem %s is not mounted\n", cgrp->controller[i]->name);
×
2838
                        return ECGROUPSUBSYSNOTMOUNTED;
×
2839
                }
2840
        }
2841

2842
        for (i = 0; i < cgrp->index; i++) {
789✔
2843
                if (!cg_build_path(cgrp->name, base, cgrp->controller[i]->name))
390✔
2844
                        continue;
×
2845

2846
                error = cgroup_set_values_recursive(base, cgrp->controller[i], true);
390✔
2847
                if (error)
390✔
2848
                        goto err;
12✔
2849
        }
2850
err:
399✔
2851
        return error;
411✔
2852
}
2853

2854
int cgroup_copy_controller_values(struct cgroup_controller * const dst,
656✔
2855
                                  const struct cgroup_controller * const src)
2856
{
2857
        int i, ret = 0;
656✔
2858

2859
        if (!dst || !src)
656✔
2860
                return ECGFAIL;
×
2861

2862
        strncpy(dst->name, src->name, CONTROL_NAMELEN_MAX);
656✔
2863
        dst->name[CONTROL_NAMELEN_MAX - 1] = '\0';
656✔
2864

2865
        for (i = 0; i < src->index; i++, dst->index++) {
1,314✔
2866
                struct control_value *src_val = src->values[i];
658✔
2867
                struct control_value *dst_val;
2868

2869
                dst->values[i] = calloc(1, sizeof(struct control_value));
658✔
2870
                if (!dst->values[i]) {
658✔
2871
                        last_errno = errno;
×
2872
                        ret = ECGOTHER;
×
2873
                        goto err;
×
2874
                }
2875

2876
                dst_val = dst->values[i];
658✔
2877
                strncpy(dst_val->value, src_val->value, CG_CONTROL_VALUE_MAX);
658✔
2878
                dst_val->value[CG_CONTROL_VALUE_MAX - 1] = '\0';
658✔
2879

2880
                strncpy(dst_val->name, src_val->name, FILENAME_MAX);
658✔
2881
                dst_val->name[FILENAME_MAX - 1] = '\0';
658✔
2882

2883
                if (src_val->multiline_value) {
658✔
2884
                        dst_val->multiline_value = strdup(src_val->multiline_value);
×
2885
                        if (!dst_val->multiline_value) {
×
2886
                                last_errno = errno;
×
2887
                                ret = ECGOTHER;
×
2888
                                goto err;
×
2889
                        }
2890
                } else {
2891
                        dst_val->multiline_value = NULL;
658✔
2892
                }
2893

2894
                if (src_val->prev_name) {
658✔
2895
                        dst_val->prev_name = strdup(src_val->prev_name);
×
2896
                        if (!dst_val->prev_name) {
×
2897
                                last_errno = errno;
×
2898
                                ret = ECGOTHER;
×
2899
                                goto err;
×
2900
                        }
2901
                } else {
2902
                        dst_val->prev_name = NULL;
658✔
2903
                }
2904
                /*
2905
                 * set dirty flag unconditionally, as we overwrite
2906
                 * destination controller values.
2907
                 */
2908
                dst_val->dirty = true;
658✔
2909
        }
2910

2911
        return ret;
656✔
2912

2913
err:
×
2914
        dst->index = 0;
×
2915
        for (i = 0; i < src->index; i++) {
×
2916
                if (dst->values[i]) {
×
2917
                        if (dst->values[i]->multiline_value)
×
2918
                                free(dst->values[i]->multiline_value);
×
2919

2920
                        if (dst->values[i]->prev_name)
×
2921
                                free(dst->values[i]->prev_name);
×
2922

2923
                        free(dst->values[i]);
×
2924
                }
2925
        }
2926

2927
        return ret;
×
2928
}
2929

2930
/**
2931
 * @dst: Destination control group
2932
 * @src: Source from which values will be copied to dst
2933
 *
2934
 * Create a duplicate copy of src in dst. This will be useful for those who
2935
 * that intend to create new instances based on an existing control group
2936
 */
2937
int cgroup_copy_cgroup(struct cgroup *dst, struct cgroup *src)
417✔
2938
{
2939
        int ret = 0, i;
417✔
2940

2941
        if (!dst || !src)
417✔
2942
                return ECGROUPNOTEXIST;
×
2943

2944
        /* Should we just use the restrict keyword instead? */
2945
        if (dst == src)
417✔
2946
                return ECGFAIL;
×
2947

2948
        cgroup_free_controllers(dst);
417✔
2949

2950
        for (i = 0; i < src->index; i++, dst->index++) {
813✔
2951
                struct cgroup_controller *src_ctlr = src->controller[i];
396✔
2952
                struct cgroup_controller *dst_ctlr;
2953

2954
                dst->controller[i] = calloc(1, sizeof(struct cgroup_controller));
396✔
2955
                if (!dst->controller[i]) {
396✔
2956
                        last_errno = errno;
×
2957
                        ret = ECGOTHER;
×
2958
                        goto err;
×
2959
                }
2960

2961
                dst_ctlr = dst->controller[i];
396✔
2962
                ret = cgroup_copy_controller_values(dst_ctlr, src_ctlr);
396✔
2963
                if (ret)
396✔
2964
                        goto err;
×
2965
        }
2966
err:
417✔
2967
        return ret;
417✔
2968
}
2969

2970
/**
2971
 * Chown and chmod the tasks file in cg_path
2972
 *
2973
 * @param uid The UID that will own the tasks file
2974
 * @param gid The GID that will own the tasks file
2975
 * @param fperm The permissions to place on the tasks file
2976
 */
2977
STATIC int cgroup_chown_chmod_tasks(const char * const cg_path, uid_t uid, gid_t gid, mode_t fperm)
1✔
2978
{
2979
        int ret, error;
2980
        char *tasks_path = NULL;
1✔
2981

2982
        tasks_path = (char *)malloc(FILENAME_MAX);
1✔
2983
        if (tasks_path == NULL)
1✔
2984
                return ECGOTHER;
×
2985

2986
        ret = snprintf(tasks_path, FILENAME_MAX, "%s/tasks", cg_path);
1✔
2987
        if (ret < 0 || ret >= FILENAME_MAX) {
1✔
2988
                last_errno = errno;
×
2989
                error = ECGOTHER;
×
2990
                goto err;
×
2991
        }
2992

2993
        error = cg_chown(tasks_path, uid, gid);
1✔
2994
        if (!error && fperm != NO_PERMS)
1✔
2995
                error = cg_chmod_path(tasks_path, fperm, 1);
1✔
2996

2997
        if (error) {
1✔
2998
                last_errno = errno;
×
2999
                error = ECGOTHER;
×
3000
        }
3001

3002
err:
1✔
3003
        if (tasks_path)
1✔
3004
                free(tasks_path);
1✔
3005

3006
        return error;
1✔
3007
}
3008

3009
static int _cgroup_create_cgroup(const struct cgroup * const cgrp,
248✔
3010
                                 const struct cgroup_controller * const controller,
3011
                                 int ignore_ownership)
3012
{
3013
        enum cg_version_t version = CGROUP_UNK;
248✔
3014
        char *fts_path[2];
3015
        char *base = NULL;
248✔
3016
        char *path = NULL;
248✔
3017
        int error;
3018

3019
        fts_path[0] = (char *)malloc(FILENAME_MAX);
248✔
3020
        if (!fts_path[0]) {
248✔
3021
                last_errno = errno;
×
3022
                return ECGOTHER;
×
3023
        }
3024
        fts_path[1] = NULL;
248✔
3025
        path = fts_path[0];
248✔
3026

3027
        if (controller) {
248✔
3028
                if (!cg_build_path(cgrp->name, path, controller->name)) {
225✔
3029
                        error = ECGOTHER;
×
3030
                        goto err;
×
3031
                }
3032

3033
                error = cgroup_get_controller_version(controller->name, &version);
225✔
3034
                if (error)
225✔
3035
                        goto err;
×
3036

3037
                if (version == CGROUP_V2) {
225✔
3038
                        char *parent, *dname;
3039

3040
                        parent = strdup(path);
223✔
3041
                        if (!parent) {
223✔
3042
                                error = ECGOTHER;
×
3043
                                goto err;
×
3044
                        }
3045

3046
                        dname = dirname(parent);
223✔
3047

3048
                        error = cgroupv2_subtree_control_recursive(dname, controller->name, true);
223✔
3049
                        free(parent);
223✔
3050
                        if (error)
223✔
3051
                                goto err;
1✔
3052
                }
3053
        } else {
3054
                if (!cg_build_path(cgrp->name, path, NULL)) {
23✔
3055
                        error = ECGOTHER;
×
3056
                        goto err;
×
3057
                }
3058
        }
3059

3060
        error = cg_create_control_group(path);
247✔
3061
        if (error)
247✔
3062
                goto err;
×
3063

3064
        base = strdup(path);
247✔
3065

3066
        if (!base) {
247✔
3067
                last_errno = errno;
×
3068
                error = ECGOTHER;
×
3069
                goto err;
×
3070
        }
3071

3072
        if (!ignore_ownership) {
247✔
3073
                cgroup_dbg("Changing ownership of %s\n", fts_path[0]);
237✔
3074
                error = cg_chown_recursive(fts_path, cgrp->control_uid, cgrp->control_gid);
237✔
3075
                if (!error)
237✔
3076
                        error = cg_chmod_recursive_controller(fts_path[0],
237✔
3077
                                                              cgrp->control_dperm,
237✔
3078
                                                              cgrp->control_dperm != NO_PERMS,
237✔
3079
                                                              cgrp->control_fperm,
237✔
3080
                                                              cgrp->control_fperm != NO_PERMS,
237✔
3081
                                                              1, cgroup_ignored_tasks_files);
3082
        }
3083

3084
        if (error)
247✔
3085
                goto err;
×
3086

3087
        if (controller) {
247✔
3088
                error = cgroup_set_values_recursive(base, controller, false);
224✔
3089
                if (error)
224✔
3090
                        goto err;
20✔
3091
        }
3092

3093
        if (!ignore_ownership && version == CGROUP_V1) {
227✔
3094
                error = cgroup_chown_chmod_tasks(base, cgrp->tasks_uid, cgrp->tasks_gid,
×
3095
                                                 cgrp->task_fperm);
×
3096
                if (error)
×
3097
                        goto err;
×
3098
        }
3099
err:
227✔
3100
        if (path)
248✔
3101
                free(path);
248✔
3102
        if (base)
248✔
3103
                free(base);
247✔
3104

3105
        return error;
248✔
3106
}
3107

3108
/**
3109
 * cgroup_create_cgroup creates a new control group.
3110
 * struct cgroup *cgrp: The control group to be created
3111
 *
3112
 * returns 0 on success. We recommend calling cg_delete_cgroup if this
3113
 * routine fails. That should do the cleanup operation. If ECGCANTSETVALUE
3114
 * is returned, the group was created successfully but not all controller
3115
 * parameters were successfully set.
3116
 */
3117
int cgroup_create_cgroup(struct cgroup *cgrp, int ignore_ownership)
215✔
3118
{
3119
        int error = 0;
215✔
3120
        int i;
3121

3122
        if (!cgroup_initialized)
215✔
3123
                return ECGROUPNOTINITIALIZED;
×
3124

3125
        if (!cgrp)
215✔
3126
                return ECGROUPNOTALLOWED;
×
3127

3128
        for (i = 0; i < cgrp->index; i++) {
440✔
3129
                if (!cgroup_test_subsys_mounted(cgrp->controller[i]->name))
225✔
3130
                        return ECGROUPSUBSYSNOTMOUNTED;
×
3131
        }
3132

3133
        if (cgrp->index == 0) {
215✔
3134
                /* Create an empty cgroup v2 cgroup */
3135
                error = _cgroup_create_cgroup(cgrp, NULL, ignore_ownership);
23✔
3136
                if (error)
23✔
3137
                        /*
3138
                         * Since we only attempted to create a single cgroup,
3139
                         * _cgroup_create_cgroup() can capably undo the failure and no
3140
                         * interventions are required here.
3141
                         */
3142
                        return error;
×
3143
        }
3144

3145
        /*
3146
         * XX: One important test to be done is to check, if you have
3147
         * multiple subsystems mounted at one point, all of them *have* be
3148
         * on the cgroup data structure. If not, we fail.
3149
         */
3150
        for (i = 0; i < cgrp->index; i++) {
419✔
3151
                error = _cgroup_create_cgroup(cgrp, cgrp->controller[i], ignore_ownership);
225✔
3152
                if (error) {
225✔
3153
                        int del_error;
3154

3155
                        /*
3156
                         * This will remove any cgroup directories that were made, but it won't
3157
                         * undo changes that have been written to the parent cgroup's
3158
                         * subtree_control file.  To safely undo changes there, we would need to
3159
                         * save the subtree_control file's previous value and restore it.
3160
                         */
3161
                        del_error = cgroup_delete_cgroup(cgrp, 1);
21✔
3162
                        if (del_error)
21✔
3163
                                cgroup_err("Failed to delete %s: %s\n", cgrp->name,
×
3164
                                           cgroup_strerror(del_error));
3165
                        return error;
21✔
3166
                }
3167
        }
3168

3169
        return 0;
194✔
3170
}
3171

3172
/**
3173
 * Obtain the calculated parent name of specified cgroup; no validation of
3174
 * the existence of the child or parent group is performed.
3175
 *
3176
 * Given the path-like hierarchy of cgroup names, this function returns the
3177
 * dirname() of the cgroup name as the likely parent name; the caller is
3178
 * responsible for validating parent as appropriate.
3179
 *
3180
 * @param cgrp The cgroup to query for parent's name
3181
 * @param parent Output, name of parent's group, or NULL if the
3182
 *        provided cgroup is the root group.
3183
 *        Caller is responsible to free the returned string.
3184
 * @return 0 on success, > 0 on error
3185
 */
3186
static int cgroup_get_parent_name(struct cgroup *cgrp, char **parent)
188✔
3187
{
3188
        char *pdir = NULL;
188✔
3189
        char *dir = NULL;
188✔
3190
        int ret = 0;
188✔
3191

3192
        dir = strdup(cgrp->name);
188✔
3193
        if (!dir) {
188✔
3194
                last_errno = errno;
×
3195
                return ECGOTHER;
×
3196
        }
3197
        cgroup_dbg("group name is %s\n", dir);
188✔
3198

3199
        pdir = dirname(dir);
188✔
3200
        cgroup_dbg("parent's group name is %s\n", pdir);
188✔
3201

3202
        /* Check for root group */
3203
        if (strlen(cgrp->name) == 0 || !strcmp(cgrp->name, pdir)) {
188✔
3204
                cgroup_dbg("specified cgroup \"%s\" is root group\n", cgrp->name);
×
3205
                *parent = NULL;
×
3206
        } else {
3207
                *parent = strdup(pdir);
188✔
3208
                if (*parent == NULL) {
188✔
3209
                        last_errno = errno;
×
3210
                        ret = ECGOTHER;
×
3211
                }
3212
        }
3213
        free(dir);
188✔
3214

3215
        return ret;
188✔
3216
}
3217

3218
/*
3219
 * Checks if the cgroup's controller shares the mount point with any other
3220
 * controller in cg_mount_table.
3221
 * Returns 1 if shared or 0.
3222
 */
3223
static int is_cgrp_ctrl_shared_mnt(char *controller)
193✔
3224
{
3225
        int ret = 0;
193✔
3226
        int i;
3227

3228
        if (!controller)
193✔
3229
                return ret;
22✔
3230

3231
        pthread_rwlock_rdlock(&cg_mount_table_lock);
171✔
3232

3233
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
395✔
3234

3235
                if (strncmp(cg_mount_table[i].name, controller, CONTROL_NAMELEN_MAX) == 0 &&
395✔
3236
                    cg_mount_table[i].shared_mnt) {
171✔
3237
                        ret = 1;
171✔
3238
                        break;
171✔
3239
                }
3240
        }
3241

3242
        pthread_rwlock_unlock(&cg_mount_table_lock);
171✔
3243

3244
        return ret;
171✔
3245
}
3246
/**
3247
 * Find the parent of the specified directory. It returns the parent in
3248
 * hierarchy of given controller (the parent is usually name/.. unless name
3249
 * is a mount point.  It is assumed both the cgroup (and, therefore, parent)
3250
 * already exist, and will fail otherwise.
3251
 *
3252
 * When namespaces are used, a group can have different parents for
3253
 * different controllers.
3254
 *
3255
 * @param cgrp The cgroup
3256
 * @param controller The controller
3257
 * @param parent Output, name of parent's group (if the group has parent) or
3258
 *        NULL, if the provided cgroup is the root group and has no parent.
3259
 *        Caller is responsible to free the returned string!
3260
 * @return 0 on success, >0 on error.
3261
 */
3262
static int cgroup_find_parent(struct cgroup *cgrp, char *controller, char **parent)
193✔
3263
{
3264
        struct stat stat_child, stat_parent;
3265
        char child_path[FILENAME_MAX];
3266
        char *parent_path = NULL;
193✔
3267
        int ret = 0;
193✔
3268

3269
        *parent = NULL;
193✔
3270

3271
        pthread_rwlock_rdlock(&cg_mount_table_lock);
193✔
3272
        if (!cg_build_path_locked(cgrp->name, child_path, controller)) {
193✔
3273
                pthread_rwlock_unlock(&cg_mount_table_lock);
×
3274
                return ECGFAIL;
×
3275
        }
3276
        pthread_rwlock_unlock(&cg_mount_table_lock);
193✔
3277

3278
        cgroup_dbg("path is %s\n", child_path);
193✔
3279

3280
        if (asprintf(&parent_path, "%s/..", child_path) < 0)
193✔
3281
                return ECGFAIL;
×
3282

3283
        cgroup_dbg("parent's name is %s\n", parent_path);
193✔
3284

3285
        if (stat(child_path, &stat_child) < 0) {
193✔
3286
                if (is_cgrp_ctrl_shared_mnt(controller)) {
5✔
3287
                        last_errno = errno;
5✔
3288
                        ret = ECGROUPNOTEXIST;
5✔
3289
                        goto free_parent;
5✔
3290
                }
3291
                last_errno = errno;
×
3292
                ret = ECGOTHER;
×
3293
                goto free_parent;
×
3294
        }
3295

3296
        if (stat(parent_path, &stat_parent) < 0) {
188✔
3297
                last_errno = errno;
×
3298
                ret = ECGOTHER;
×
3299
                goto free_parent;
×
3300
        }
3301

3302
        /* Is the specified "name" a mount point? */
3303
        if (stat_parent.st_dev != stat_child.st_dev) {
188✔
3304
                *parent = NULL;
×
3305
                ret = 0;
×
3306
                cgroup_dbg("Parent is on different device\n");
×
3307
        } else {
3308
                ret = cgroup_get_parent_name(cgrp, parent);
188✔
3309
        }
3310

3311
free_parent:
193✔
3312
        free(parent_path);
193✔
3313
        return ret;
193✔
3314
}
3315

3316
/**
3317
 * @cgrp: cgroup data structure to be filled with parent values and then
3318
 *        passed down for creation
3319
 * @ignore_ownership: Ignore doing a chown on the newly created cgroup
3320
 * @return 0 on success, > 0 on failure.  If ECGCANTSETVALUE is returned,
3321
 *        the group was created
3322
 * successfully, but not all controller parameters were copied from the
3323
 * parent successfully; unfortunately, this is expected...
3324
 */
3325
int cgroup_create_cgroup_from_parent(struct cgroup *cgrp, int ignore_ownership)
×
3326
{
3327
        struct cgroup *parent_cgrp = NULL;
×
3328
        char *parent = NULL;
×
3329
        int ret = ECGFAIL;
×
3330

3331
        if (!cgroup_initialized)
×
3332
                return ECGROUPNOTINITIALIZED;
×
3333

3334
        ret = cgroup_get_parent_name(cgrp, &parent);
×
3335
        if (ret)
×
3336
                return ret;
×
3337

3338
        if (parent == NULL) {
×
3339
                /*
3340
                 * The group to create is root group!
3341
                 * TODO: find better error code?
3342
                 */
3343
                return ECGFAIL;
×
3344
        }
3345

3346
        cgroup_dbg("parent is %s\n", parent);
×
3347
        parent_cgrp = cgroup_new_cgroup(parent);
×
3348
        if (!parent_cgrp) {
×
3349
                ret = ECGFAIL;
×
3350
                goto err_nomem;
×
3351
        }
3352

3353
        if (cgroup_get_cgroup(parent_cgrp)) {
×
3354
                ret = ECGFAIL;
×
3355
                goto err_parent;
×
3356
        }
3357

3358
        cgroup_dbg("got parent group for %s\n", parent_cgrp->name);
×
3359
        ret = cgroup_copy_cgroup(cgrp, parent_cgrp);
×
3360
        if (ret)
×
3361
                goto err_parent;
×
3362

3363
        cgroup_dbg("copied parent group %s to %s\n", parent_cgrp->name, cgrp->name);
×
3364
        ret = cgroup_create_cgroup(cgrp, ignore_ownership);
×
3365

3366
err_parent:
×
3367
        cgroup_free(&parent_cgrp);
×
3368
err_nomem:
×
3369
        free(parent);
×
3370
        return ret;
×
3371
}
3372

3373
/**
3374
 * Move all processes from one task file to another.
3375
 * @param input_tasks Pre-opened file to read tasks from.
3376
 * @param output_tasks Pre-opened file to write tasks to.
3377
 * @return 0 on success, >0 on error.
3378
 */
3379
static int cg_move_task_files(FILE *input_tasks, FILE *output_tasks)
220✔
3380
{
3381
        int ret = 0;
220✔
3382
        int tids;
3383

3384
        while (!feof(input_tasks)) {
220✔
3385
                ret = fscanf(input_tasks, "%d", &tids);
220✔
3386
                if (ret == EOF || ret == 0) {
220✔
3387
                        ret = 0;
220✔
3388
                        break;
220✔
3389
                }
3390
                if (ret < 0)
×
3391
                        break;
×
3392

3393
                ret = fprintf(output_tasks, "%d", tids);
×
3394
                if (ret < 0 && errno != ESRCH)
×
3395
                        break;
×
3396

3397
                /* Flush the file, we need only one process per write() call. */
3398
                ret = fflush(output_tasks);
×
3399
                if (ret < 0) {
×
3400
                        if (errno == ESRCH)
×
3401
                                ret = 0;
×
3402
                        else
3403
                                break;
×
3404
                }
3405
        }
3406

3407
        if (ret < 0) {
220✔
3408
                last_errno = errno;
×
3409
                return ECGOTHER;
×
3410
        }
3411
        return 0;
220✔
3412
}
3413

3414
/**
3415
 * Remove one cgroup from specific controller. The function moves all
3416
 * processes from it to given target group.
3417
 *
3418
 * The function succeeds if the group to remove is already removed - when
3419
 * cgroup_delete_cgroup is called with group with two controllers mounted
3420
 * to the same hierarchy, this function is called once for each of these
3421
 * controllers. And during the second call the group is already removed...
3422
 *
3423
 * @param cgrp_name Name of the group to remove.
3424
 * @param controller  Name of the controller.
3425
 * @param target_tasks Opened tasks file of the target group, where all
3426
 *        processes should be moved.
3427
 * @param flags Flag indicating whether the errors from task
3428
 *        migration should be ignored (CGROUP_DELETE_IGNORE_MIGRATION) or not (0).
3429
 * @returns 0 on success, >0 on error.
3430
 */
3431
static int cg_delete_cgrp_controller(char *cgrp_name, char *controller, FILE *target_tasks,
220✔
3432
                                     int flags)
3433
{
3434
        char path[FILENAME_MAX];
3435
        FILE *delete_tasks;
3436
        int ret = 0;
220✔
3437

3438
        cgroup_dbg("Removing group %s:%s\n", controller, cgrp_name);
220✔
3439

3440
        if (!(flags & CGFLAG_DELETE_EMPTY_ONLY)) {
220✔
3441
                /* Open tasks file of the group to delete. */
3442
                ret = cgroup_build_tasks_procs_path(path, sizeof(path), cgrp_name, controller);
220✔
3443
                if (ret != 0)
220✔
3444
                        return ECGROUPSUBSYSNOTMOUNTED;
×
3445

3446
                delete_tasks = fopen(path, "re");
220✔
3447
                if (delete_tasks) {
220✔
3448
                        ret = cg_move_task_files(delete_tasks, target_tasks);
220✔
3449
                        if (ret != 0) {
220✔
3450
                                cgroup_warn("removing tasks from %s failed: %s\n", path,
×
3451
                                            cgroup_strerror(ret));
3452
                        }
3453
                        fclose(delete_tasks);
220✔
3454
                } else {
3455
                        /*
3456
                         * Can't open the tasks file. If the file does not exist,
3457
                         * ignore it - the group has been already removed.
3458
                         */
3459
                        if (errno != ENOENT) {
×
3460
                                cgroup_err("cannot open %s: %s\n", path, strerror(errno));
×
3461
                                last_errno = errno;
×
3462
                                ret = ECGOTHER;
×
3463
                        }
3464
                }
3465

3466
                if (ret != 0 && !(flags & CGFLAG_DELETE_IGNORE_MIGRATION))
220✔
3467
                        return ret;
×
3468
        }
3469

3470
        /* Remove the group. */
3471
        if (!cg_build_path(cgrp_name, path, controller))
220✔
3472
                return ECGROUPSUBSYSNOTMOUNTED;
×
3473

3474
        ret = rmdir(path);
220✔
3475
        if (ret == 0 || errno == ENOENT)
220✔
3476
                return 0;
220✔
3477

3478
        if ((flags & CGFLAG_DELETE_EMPTY_ONLY) && (errno == EBUSY))
×
3479
                return ECGNONEMPTY;
×
3480

3481
        cgroup_warn("cannot remove directory %s: %s\n", path, strerror(errno));
×
3482
        last_errno = errno;
×
3483

3484
        return ECGOTHER;
×
3485
}
3486

3487
/**
3488
 * Recursively delete one control group. Moves all tasks from the group and
3489
 * its subgroups to given task file.
3490
 *
3491
 * @param cgrp_name The group to delete.
3492
 * @param controller The controller, where to delete.
3493
 * @param target_tasks Opened file, where all tasks should be moved.
3494
 * @param flags Combination of CGFLAG_DELETE_* flags. The function assumes
3495
 *        that CGFLAG_DELETE_RECURSIVE is set.
3496
 * @param delete_root Whether the group itself should be removed(1) or not(0).
3497
 */
3498
static int cg_delete_cgrp_controller_recursive(char *cgrp_name, char *controller,
21✔
3499
                                               FILE *target_tasks, int flags, int delete_root)
3500
{
3501
        char child_name[FILENAME_MAX + 1];
3502
        struct cgroup_file_info info;
3503
        int level, group_len;
3504
        void *handle;
3505
        int ret;
3506

3507
        cgroup_dbg("Recursively removing %s:%s\n", controller, cgrp_name);
21✔
3508

3509
        ret = cgroup_walk_tree_begin(controller, cgrp_name, 0, &handle, &info, &level);
21✔
3510
        if (ret == 0)
21✔
3511
                ret = cgroup_walk_tree_set_flags(&handle, CGROUP_WALK_TYPE_POST_DIR);
21✔
3512

3513
        if (ret != 0) {
21✔
3514
                cgroup_walk_tree_end(&handle);
×
3515
                return ret;
×
3516
        }
3517

3518
        group_len = strlen(info.full_path);
21✔
3519

3520
        /* Skip the root group, it will be handled explicitly at the end. */
3521
        ret = cgroup_walk_tree_next(0, &handle, &info, level);
21✔
3522

3523
        while (ret == 0) {
2,462✔
3524
                if (info.type == CGROUP_FILE_TYPE_DIR && info.depth > 0) {
2,441✔
3525
                        snprintf(child_name, sizeof(child_name), "%s/%s", cgrp_name,
32✔
3526
                                 info.full_path + group_len);
32✔
3527

3528
                        ret = cg_delete_cgrp_controller(child_name, controller, target_tasks,
32✔
3529
                                                          flags);
3530
                        if (ret != 0)
32✔
3531
                                break;
×
3532
                }
3533

3534
                ret = cgroup_walk_tree_next(0, &handle, &info, level);
2,441✔
3535
        }
3536
        if (ret == ECGEOF) {
21✔
3537
                /* Iteration finished successfully, remove the root group. */
3538
                ret = 0;
21✔
3539
                if (delete_root)
21✔
3540
                        ret = cg_delete_cgrp_controller(cgrp_name, controller, target_tasks, flags);
21✔
3541
        }
3542

3543
        cgroup_walk_tree_end(&handle);
21✔
3544

3545
        return ret;
21✔
3546
}
3547

3548
/**
3549
 * cgroup_delete cgroup deletes a control group.
3550
 * struct cgroup *cgrp takes the group which is to be deleted.
3551
 *
3552
 * returns 0 on success.
3553
 */
3554
int cgroup_delete_cgroup(struct cgroup *cgrp, int ignore_migration)
27✔
3555
{
3556
        int flags = ignore_migration ? CGFLAG_DELETE_IGNORE_MIGRATION : 0;
27✔
3557

3558
        return cgroup_delete_cgroup_ext(cgrp, flags);
27✔
3559
}
3560

3561
int cgroup_delete_cgroup_ext(struct cgroup *cgrp, int flags)
192✔
3562
{
3563
        int first_error = 0, first_errno = 0;
192✔
3564
        int cgrp_del_on_shared_mnt = 0;
192✔
3565
        char parent_path[FILENAME_MAX];
3566
        char *controller_name = NULL;
192✔
3567
        FILE *parent_tasks = NULL;
192✔
3568
        char *parent_name = NULL;
192✔
3569
        int delete_group = 1;
192✔
3570
        int empty_cgrp = 0;
192✔
3571
        int i, ret;
3572

3573
        if (!cgroup_initialized)
192✔
3574
                return ECGROUPNOTINITIALIZED;
×
3575

3576
        if (!cgrp)
192✔
3577
                return ECGROUPNOTALLOWED;
×
3578

3579
        if ((flags & CGFLAG_DELETE_RECURSIVE)
192✔
3580
            && (flags & CGFLAG_DELETE_EMPTY_ONLY))
25✔
3581
                return ECGINVAL;
×
3582

3583
        if (cgrp->index == 0)
192✔
3584
                /* Valid empty cgroup v2 with not controllers added. */
3585
                empty_cgrp = 1;
22✔
3586

3587
        for (i = 0; i < cgrp->index; i++) {
363✔
3588
                if (!cgroup_test_subsys_mounted(cgrp->controller[i]->name))
171✔
3589
                        return ECGROUPSUBSYSNOTMOUNTED;
×
3590
        }
3591

3592
        /*
3593
         * Remove the group from all controllers and in the case of cgroup
3594
         * with no controllers, perform all actions of a single controller.
3595
         */
3596
        for (i = 0; empty_cgrp > 0 || i < cgrp->index; i++, empty_cgrp--) {
385✔
3597

3598
                ret = 0;
193✔
3599
                controller_name = NULL;
193✔
3600

3601
                if (cgrp->controller[i])
193✔
3602
                        controller_name = cgrp->controller[i]->name;
171✔
3603

3604
                /* Find parent, it can be different for each controller */
3605
                if (!(flags & CGFLAG_DELETE_EMPTY_ONLY)) {
193✔
3606
                        ret = cgroup_find_parent(cgrp, controller_name, &parent_name);
193✔
3607
                        if (ret) {
193✔
3608
                                /*
3609
                                 * ECGROPNOTEXIST is returned on cgroup v1, where
3610
                                 * controllers share the mount points. When cgroup
3611
                                 * is deleted on one of symbolic link (controller)
3612
                                 * and they should pass on other controllers sharing
3613
                                 * the mount point.
3614
                                 *
3615
                                 * cgrp_del_shared_mnt serves as an extra check
3616
                                 * flag that gets set, when the cgroup exists and
3617
                                 * is deleted or else sets an error.
3618
                                 */
3619
                                if (first_error == 0 &&
5✔
3620
                                    (ret != ECGROUPNOTEXIST ||
5✔
3621
                                    (ret == ECGROUPNOTEXIST && cgrp_del_on_shared_mnt == 0))) {
5✔
3622
                                        first_errno = last_errno;
4✔
3623
                                        first_error = ECGOTHER;
4✔
3624
                                }
3625
                                continue;
5✔
3626
                        }
3627

3628
                        if (is_cgrp_ctrl_shared_mnt(controller_name))
188✔
3629
                                cgrp_del_on_shared_mnt = 1;
166✔
3630

3631
                        if (parent_name == NULL) {
188✔
3632
                                /* Root group is being deleted. */
3633
                                if (!(flags & CGFLAG_DELETE_RECURSIVE))
×
3634
                                        /* root group is being deleted in non-recursive mode */
3635
                                        continue;
×
3636
                                /*
3637
                                 * Move all tasks to the root group and
3638
                                 * do not delete it afterwards.
3639
                                 */
3640
                                parent_name = strdup(".");
×
3641
                                if (parent_name == NULL) {
×
3642
                                        if (first_error == 0) {
×
3643
                                                first_errno = errno;
×
3644
                                                first_error = ECGOTHER;
×
3645
                                        }
3646
                                        continue;
×
3647
                                }
3648
                                delete_group = 0;
×
3649
                        }
3650
                }
3651

3652
                if (parent_name) {
188✔
3653
                        /* Tasks need to be moved, pre-open target tasks file */
3654
                        ret = cgroup_build_tasks_procs_path(parent_path, sizeof(parent_path),
188✔
3655
                                                            parent_name, controller_name);
3656
                        if (ret != 0) {
188✔
3657
                                if (first_error == 0)
×
3658
                                        first_error = ECGFAIL;
×
3659
                                free(parent_name);
×
3660
                                continue;
×
3661
                        }
3662

3663
                        parent_tasks = fopen(parent_path, "we");
188✔
3664
                        if (!parent_tasks) {
188✔
3665
                                if (first_error == 0) {
×
3666
                                        cgroup_warn("cannot open tasks file %s: %s\n", parent_path,
×
3667
                                                    strerror(errno));
3668
                                        first_errno = errno;
×
3669
                                        first_error = ECGOTHER;
×
3670
                                }
3671
                                free(parent_name);
×
3672
                                continue;
×
3673
                        }
3674
                }
3675
                if (flags & CGFLAG_DELETE_RECURSIVE) {
188✔
3676
                        ret = cg_delete_cgrp_controller_recursive(cgrp->name, controller_name,
21✔
3677
                                                                    parent_tasks, flags,
3678
                                                                    delete_group);
3679
                } else {
3680
                        ret = cg_delete_cgrp_controller(cgrp->name, controller_name, parent_tasks,
167✔
3681
                                                        flags);
3682
                }
3683

3684
                if (parent_tasks) {
188✔
3685
                        fclose(parent_tasks);
188✔
3686
                        parent_tasks = NULL;
188✔
3687
                }
3688
                free(parent_name);
188✔
3689
                parent_name = NULL;
188✔
3690
                /*
3691
                 * If any of the controller delete fails, remember the first
3692
                 * error code, but continue with next controller and try remove
3693
                 * the group from all of them.
3694
                 */
3695
                if (ret) {
188✔
3696
                        /*
3697
                         * ECGNONEMPTY is more or less not an error, but an
3698
                         * indication that something was not removed.
3699
                         * Therefore it should be replaced by any other error.
3700
                         */
3701
                        if (ret != ECGNONEMPTY &&
×
3702
                            (first_error == 0 || first_errno == ECGNONEMPTY)) {
×
3703
                                first_errno = last_errno;
×
3704
                                first_error = ret;
×
3705
                        }
3706
                }
3707
        }
3708

3709
        /*
3710
         * Restore the last_errno to the first errno from
3711
         * cg_delete_cgroup_controller[_ext].
3712
         */
3713
        if (first_errno != 0)
192✔
3714
                last_errno = first_errno;
4✔
3715

3716
        return first_error;
192✔
3717
}
3718

3719
/*
3720
 * This function should really have more checks, but this version will assume
3721
 * that the callers have taken care of everything. Including the locking.
3722
 */
3723
static int cg_rd_ctrl_file(const char *subsys, const char *cgrp, const char *file, char **value)
3,606✔
3724
{
3725
        char path[FILENAME_MAX];
3726
        FILE *ctrl_file = NULL;
3,606✔
3727
        int ret;
3728

3729
        if (!cg_build_path_locked(cgrp, path, subsys))
3,606✔
3730
                return ECGFAIL;
×
3731

3732
        strncat(path, file, sizeof(path) - strlen(path) - 1);
3,606✔
3733
        ctrl_file = fopen(path, "re");
3,606✔
3734
        if (!ctrl_file)
3,606✔
3735
                return ECGROUPVALUENOTEXIST;
10✔
3736

3737
        *value = calloc(CG_CONTROL_VALUE_MAX, 1);
3,596✔
3738
        if (!*value) {
3,596✔
3739
                fclose(ctrl_file);
×
3740
                last_errno = errno;
×
3741
                return ECGOTHER;
×
3742
        }
3743

3744
        /* Using %as crashes when we try to read from files like memory.stat */
3745
        ret = fread(*value, 1, CG_CONTROL_VALUE_MAX-1, ctrl_file);
3,596✔
3746
        if (ret < 0) {
3,596✔
3747
                free(*value);
×
3748
                *value = NULL;
×
3749
        } else {
3750
                /* Remove trailing \n */
3751
                if (ret > 0 && (*value)[ret-1] == '\n')
3,596✔
3752
                        (*value)[ret-1] = '\0';
3,340✔
3753
        }
3754

3755
        fclose(ctrl_file);
3,596✔
3756

3757
        return 0;
3,596✔
3758
}
3759

3760
/*
3761
 * Call this function with required locks taken.
3762
 */
3763
int cgroup_fill_cgc(struct dirent *ctrl_dir, struct cgroup *cgrp, struct cgroup_controller *cgc,
24,226✔
3764
                    int cg_index)
3765
{
3766
        char path[FILENAME_MAX+1];
3767
        struct stat stat_buffer;
3768
        char *ctrl_value = NULL;
24,226✔
3769
        char *ctrl_name = NULL;
24,226✔
3770
        char *ctrl_file = NULL;
24,226✔
3771
        char *tmp_path = NULL;
24,226✔
3772
        char *d_name = NULL;
24,226✔
3773
        char *buffer = NULL;
24,226✔
3774
        int tmp_len = 0;
24,226✔
3775
        int error = 0;
24,226✔
3776

3777
        d_name = strdup(ctrl_dir->d_name);
24,226✔
3778

3779
        if (!d_name) {
24,226✔
3780
                error = ECGOTHER;
×
3781
                cgroup_err("strdup failed to allocate memory: %s\n", strerror(errno));
×
3782
                goto fill_error;
×
3783
        }
3784

3785
        if (!strcmp(d_name, ".") || !strcmp(d_name, "..")) {
24,226✔
3786
                error = ECGINVAL;
×
3787
                goto fill_error;
×
3788
        }
3789

3790
        /*
3791
         * This part really needs to be optimized out. Probably use some
3792
         * sort of a flag, but this is fine for now.
3793
         */
3794
        cg_build_path_locked(cgrp->name, path, cg_mount_table[cg_index].name);
24,226✔
3795
        strncat(path, d_name, sizeof(path) - strlen(path) - 1);
24,226✔
3796

3797
        error = stat(path, &stat_buffer);
24,226✔
3798
        if (error) {
24,226✔
3799
                error = ECGFAIL;
×
3800
                goto fill_error;
×
3801
        }
3802

3803
        /*
3804
         * We have already stored the tasks_uid & tasks_gid. This check is
3805
         * to avoid the overwriting of the values stored in
3806
         * control_uid & cotrol_gid. tasks file will have the uid and gid of
3807
         * the user who is capable of putting a task to this cgroup.
3808
         * control_uid and control_gid is meant for the users who are capable
3809
         * of managing the cgroup shares.
3810
         *
3811
         * The strstr() function will return the pointer to the
3812
         * beginning of the sub string "/tasks".
3813
         */
3814
        tmp_len = strlen(path) - strlen("/tasks");
24,226✔
3815

3816
        /* tmp_path would be pointing to the last six characters */
3817
        tmp_path = (char *)path + tmp_len;
24,226✔
3818

3819
        /*
3820
         * Checking to see, if this is actually a 'tasks' file We need to
3821
         * compare the last 6 bytes
3822
         */
3823
        if (strcmp(tmp_path, "/tasks")) {
24,226✔
3824
                cgrp->control_uid = stat_buffer.st_uid;
24,217✔
3825
                cgrp->control_gid = stat_buffer.st_gid;
24,217✔
3826
        }
3827

3828
        ctrl_name = strtok_r(d_name, ".", &buffer);
24,226✔
3829
        if (!ctrl_name) {
24,226✔
3830
                error = ECGFAIL;
×
3831
                goto fill_error;
×
3832
        }
3833

3834
        ctrl_file = strtok_r(NULL, ".", &buffer);
24,226✔
3835
        if (!ctrl_file) {
24,226✔
3836
                error = ECGINVAL;
9✔
3837
                goto fill_error;
9✔
3838
        }
3839

3840
        if (strcmp(ctrl_name, cg_mount_table[cg_index].name) == 0) {
24,217✔
3841
                error = cg_rd_ctrl_file(cg_mount_table[cg_index].name, cgrp->name,
3,606✔
3842
                                        ctrl_dir->d_name, &ctrl_value);
3,606✔
3843
                if (error || !ctrl_value)
3,606✔
3844
                        goto fill_error;
10✔
3845

3846
                if (cgroup_add_value_string(cgc, ctrl_dir->d_name, ctrl_value)) {
3,596✔
3847
                        error = ECGFAIL;
×
3848
                        goto fill_error;
×
3849
                }
3850
        }
3851
fill_error:
24,207✔
3852
        if (ctrl_value)
24,226✔
3853
                free(ctrl_value);
3,596✔
3854
        if (d_name)
24,226✔
3855
                free(d_name);
24,226✔
3856

3857
        return error;
24,226✔
3858
}
3859

3860
/*
3861
 * cgroup_get_cgroup reads the cgroup data from the filesystem.
3862
 * struct cgroup has the name of the group to be populated
3863
 *
3864
 * return 0 on success.
3865
 */
3866
int cgroup_get_cgroup(struct cgroup *cgrp)
73✔
3867
{
3868
        char cgrp_ctrl_path[FILENAME_MAX];
3869
        struct dirent *ctrl_dir = NULL;
73✔
3870
        char mnt_path[FILENAME_MAX];
3871
        int initial_controller_cnt;
3872
        char *control_path = NULL;
73✔
3873
        int controller_cnt = 0;
73✔
3874
        DIR *dir = NULL;
73✔
3875
        int error;
3876
        int i, j;
3877
        int ret;
3878

3879
        if (!cgroup_initialized) {
73✔
3880
                /* ECGROUPNOTINITIALIZED */
3881
                return ECGROUPNOTINITIALIZED;
×
3882
        }
3883

3884
        if (!cgrp) {
73✔
3885
                /* ECGROUPNOTALLOWED */
3886
                return ECGROUPNOTALLOWED;
×
3887
        }
3888

3889
        initial_controller_cnt = cgrp->index;
73✔
3890

3891
        pthread_rwlock_rdlock(&cg_mount_table_lock);
73✔
3892
        for (i = 0; i < CG_CONTROLLER_MAX && cg_mount_table[i].name[0] != '\0'; i++) {
733✔
3893
                struct cgroup_controller *cgc;
3894
                struct stat stat_buffer;
3895
                int mnt_path_len;
3896

3897
                if (initial_controller_cnt > 0) {
661✔
3898
                        bool skip_this_controller = true;
59✔
3899

3900
                        /*
3901
                         * The user has specified a list of controllers they are interested
3902
                         * in.  Only operate on the specified controllers
3903
                         */
3904
                        for (j = 0; j < cgrp->index; j++) {
215✔
3905
                                if (strncmp(cg_mount_table[i].name, cgrp->controller[j]->name,
156✔
3906
                                            CONTROL_NAMELEN_MAX) == 0)
3907
                                        skip_this_controller = false;
16✔
3908
                        }
3909

3910
                        if (skip_this_controller)
59✔
3911
                                continue;
304✔
3912
                }
3913

3914
                if (!cg_build_path_locked(NULL, mnt_path, cg_mount_table[i].name))
618✔
3915
                        continue;
×
3916

3917
                mnt_path_len = strlen(mnt_path);
618✔
3918
                strncat(mnt_path, cgrp->name, FILENAME_MAX - mnt_path_len - 1);
618✔
3919
                mnt_path[sizeof(mnt_path) - 1] = '\0';
618✔
3920

3921
                if (access(mnt_path, F_OK))
618✔
3922
                        continue;
2✔
3923

3924
                if (!cg_build_path_locked(cgrp->name, cgrp_ctrl_path, cg_mount_table[i].name)) {
616✔
3925
                        /* This fails when the cgroup does not exist for that controller. */
3926
                        continue;
×
3927
                }
3928

3929
                /* Get the uid and gid information. */
3930
                if (cg_mount_table[i].version == CGROUP_V1) {
616✔
3931
                        ret = asprintf(&control_path, "%s/tasks", cgrp_ctrl_path);
10✔
3932

3933
                        if (ret < 0) {
10✔
3934
                                last_errno = errno;
×
3935
                                error = ECGOTHER;
×
3936
                                goto unlock_error;
1✔
3937
                        }
3938

3939
                        if (stat(control_path, &stat_buffer)) {
10✔
3940
                                last_errno = errno;
1✔
3941
                                free(control_path);
1✔
3942
                                error = ECGOTHER;
1✔
3943
                                goto unlock_error;
1✔
3944
                        }
3945

3946
                        cgrp->tasks_uid = stat_buffer.st_uid;
9✔
3947
                        cgrp->tasks_gid = stat_buffer.st_gid;
9✔
3948

3949
                        free(control_path);
9✔
3950
                } else { /* cgroup v2 */
3951
                        bool enabled;
3952

3953
                        error = cgroupv2_get_controllers(cgrp_ctrl_path, cg_mount_table[i].name,
606✔
3954
                                                         &enabled);
3955
                        if (error == ECGROUPNOTMOUNTED) {
606✔
3956
                                /*
3957
                                 * This controller isn't enabled.  Only hide it from the
3958
                                 * user if they've chosen to view all enabled controllers.
3959
                                 *
3960
                                 * If they've specified the controllers they're interested in
3961
                                 * and we've made it this far, then they are explicitly
3962
                                 * interested in this controller and we should not remove it.
3963
                                 */
3964
                                if (initial_controller_cnt == 0) {
263✔
3965
                                        controller_cnt++;
259✔
3966
                                        continue;
259✔
3967
                                }
3968
                        } else if (error) {
343✔
3969
                                goto unlock_error;
×
3970
                        }
3971
                }
3972

3973
                if (initial_controller_cnt)
356✔
3974
                        cgc = cgroup_get_controller(cgrp, cg_mount_table[i].name);
16✔
3975
                else
3976
                        cgc = cgroup_add_controller(cgrp, cg_mount_table[i].name);
340✔
3977
                if (!cgc) {
356✔
3978
                        error = ECGINVAL;
×
3979
                        goto unlock_error;
×
3980
                }
3981

3982
                dir = opendir(cgrp_ctrl_path);
356✔
3983
                if (!dir) {
356✔
3984
                        last_errno = errno;
×
3985
                        error = ECGOTHER;
×
3986
                        goto unlock_error;
×
3987
                }
3988

3989
                controller_cnt++;
356✔
3990

3991
                while ((ctrl_dir = readdir(dir)) != NULL) {
22,853✔
3992
                        /* Skip over non regular files */
3993
                        if (ctrl_dir->d_type != DT_REG)
22,497✔
3994
                                continue;
1,055✔
3995

3996
                        error = cgroup_fill_cgc(ctrl_dir, cgrp, cgc, i);
21,442✔
3997
                        for (j = 0; j < cgc->index; j++)
124,424✔
3998
                                cgc->values[j]->dirty = false;
102,982✔
3999

4000
                        if (error == ECGFAIL) {
21,442✔
4001
                                closedir(dir);
×
4002
                                goto unlock_error;
×
4003
                        }
4004
                }
4005
                closedir(dir);
356✔
4006

4007
                if (!strcmp(cgc->name, "memory")) {
356✔
4008
                        /*
4009
                         * Make sure that memory.limit_in_bytes is placed before
4010
                         * memory.memsw.limit_in_bytes in the list of values
4011
                         */
4012
                        int memsw_limit = -1;
69✔
4013
                        int mem_limit = -1;
69✔
4014

4015
                        for (j = 0; j < cgc->index; j++) {
1,472✔
4016
                                if (!strcmp(cgc->values[j]->name, "memory.memsw.limit_in_bytes"))
1,403✔
4017
                                        memsw_limit = j;
2✔
4018
                                else if (!strcmp(cgc->values[j]->name, "memory.limit_in_bytes"))
1,401✔
4019
                                        mem_limit = j;
2✔
4020
                        }
4021

4022
                        if (memsw_limit >= 0 && memsw_limit < mem_limit) {
69✔
4023
                                struct control_value *val = cgc->values[memsw_limit];
×
4024

4025
                                cgc->values[memsw_limit] = cgc->values[mem_limit];
×
4026
                                cgc->values[mem_limit] = val;
×
4027
                        }
4028
                }
4029
        }
4030

4031
        /*
4032
         * Check if the group really exists or not.  The cgrp->index controller count can't
4033
         * be used in this case because cgroup v2 allows controllers to be enabled/disabled in
4034
         * the subtree_control file.  Rather, cgroup_get_cgroup() tracks the number of possible
4035
         * controllers in the controller_cnt variable and uses that to determine if the cgroup
4036
         * exists or not.
4037
         */
4038
        if (!controller_cnt) {
72✔
4039
                error = ECGROUPNOTEXIST;
×
4040
                goto unlock_error;
×
4041
        }
4042

4043
        pthread_rwlock_unlock(&cg_mount_table_lock);
72✔
4044

4045
        return 0;
72✔
4046

4047
unlock_error:
1✔
4048
        pthread_rwlock_unlock(&cg_mount_table_lock);
1✔
4049
        /*
4050
         * XX: Need to figure out how to cleanup? Cleanup just the stuff
4051
         * we added, or the whole structure.
4052
         */
4053
        cgroup_free_controllers(cgrp);
1✔
4054
        cgrp = NULL;
1✔
4055

4056
        return error;
1✔
4057
}
4058

4059
/**
4060
 * cg_prepare_cgroup Process the selected rule. Prepare the cgroup structure
4061
 * which can be used to add the task to destination cgroup.
4062
 *
4063
 *  returns 0 on success.
4064
 */
4065
static int cg_prepare_cgroup(struct cgroup *cgrp, pid_t pid, const char *dest,
48✔
4066
                             const char * const controllers[])
4067
{
4068
        struct cgroup_controller *cptr = NULL;
48✔
4069
        const char *controller = NULL;
48✔
4070
        int ret = 0, i;
48✔
4071

4072
        /* Fill in cgroup details.  */
4073
        cgroup_dbg("Will move pid %d to cgroup '%s'\n", pid, dest);
48✔
4074

4075
        strncpy(cgrp->name, dest, FILENAME_MAX);
48✔
4076
        cgrp->name[FILENAME_MAX-1] = '\0';
48✔
4077

4078
        /* Scan all the controllers */
4079
        for (i = 0; i < CG_CONTROLLER_MAX; i++) {
111✔
4080
                int j = 0;
111✔
4081

4082
                if (!controllers[i])
111✔
4083
                        return 0;
48✔
4084
                controller = controllers[i];
63✔
4085

4086
                /* If first string is "*" that means all the mounted controllers. */
4087
                if (strcmp(controller, "*") == 0) {
63✔
4088
                        pthread_rwlock_rdlock(&cg_mount_table_lock);
×
4089

4090
                        for (j = 0; j < CG_CONTROLLER_MAX &&
×
4091
                                cg_mount_table[j].name[0] != '\0'; j++) {
×
4092
                                cgroup_dbg("Adding controller %s\n", cg_mount_table[j].name);
×
4093
                                cptr = cgroup_add_controller(cgrp, cg_mount_table[j].name);
×
4094
                                if (!cptr) {
×
4095
                                        cgroup_warn("adding controller '%s' failed\n",
×
4096
                                                    cg_mount_table[j].name);
4097
                                        pthread_rwlock_unlock(&cg_mount_table_lock);
×
4098
                                        cgroup_free_controllers(cgrp);
×
4099
                                        return ECGROUPNOTALLOWED;
×
4100
                                }
4101
                        }
4102
                        pthread_rwlock_unlock(&cg_mount_table_lock);
×
4103
                        return ret;
×
4104
                }
4105

4106
                /* It is individual controller names and not "*" */
4107
                cgroup_dbg("Adding controller %s\n", controller);
63✔
4108
                cptr = cgroup_add_controller(cgrp, controller);
63✔
4109
                if (!cptr) {
63✔
4110
                        cgroup_warn("adding controller '%s' failed\n", controller);
×
4111
                        cgroup_free_controllers(cgrp);
×
4112
                        return ECGROUPNOTALLOWED;
×
4113
                }
4114
        }
4115

4116
        return ret;
×
4117
}
4118

4119
/**
4120
 * Determines if the rule is a wildcard rule and if so, compares the wildcard
4121
 * rule against the new process.  If the new process matches the wildcard rule,
4122
 * then this function returns true. Otherwise it returns false.
4123
 *
4124
 *        @param rule_procname The procname field of the rule
4125
 *        @param procname The name of the new process
4126
 *        @return True if the procname matches the rule.  False otherwise
4127
 */
4128
STATIC bool cgroup_compare_wildcard_procname(const char * const rule_procname,
500✔
4129
                                             const char * const procname)
4130
{
4131
        size_t rule_strlen = strlen(rule_procname);
500✔
4132

4133
        if (rule_procname[rule_strlen - 1] != '*')
500✔
4134
                /* This rule does not end in a wildcard */
4135
                return false;
493✔
4136

4137
        /* Compare the two strings up to the asterisk */
4138
        if (strncmp(rule_procname, procname, rule_strlen - 1) != 0)
7✔
4139
                /* The strings did not match */
4140
                return false;
3✔
4141

4142
        /* All checks passed.  The wildcarded process matched this rule */
4143
        return true;
4✔
4144
}
4145

4146
static int cgroup_find_matching_destination(char *cgrp_list[], const char * const rule_dest,
14✔
4147
                                            int *matching_index)
4148
{
4149
        size_t rule_strlen = strlen(rule_dest);
14✔
4150
        int ret = -ENODATA;
14✔
4151
        int i;
4152

4153
        for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
34✔
4154
                if (cgrp_list[i] == NULL)
34✔
4155
                        break;
5✔
4156

4157
                if (rule_dest[rule_strlen - 1] == '/') {
29✔
4158
                        /*
4159
                         * Avoid a weird corner case where given a rule dest
4160
                         * like 'folder/', we _don't_ want to match 'folder1'
4161
                         */
4162
                        if (strlen(cgrp_list[i]) >= rule_strlen &&
7✔
4163
                            cgrp_list[i][rule_strlen - 1] != '/')
6✔
4164
                                continue;
3✔
4165

4166
                        /*
4167
                         * Strip off the '/' at the end of the rule, as
4168
                         * the destination from the cgrp_list will not
4169
                         * have a trailing '/'
4170
                         */
4171
                        rule_strlen--;
4✔
4172
                }
4173

4174
                if (strncmp(rule_dest, cgrp_list[i], rule_strlen) == 0) {
26✔
4175
                        *matching_index = i;
9✔
4176
                        ret = 0;
9✔
4177
                        break;
9✔
4178
                }
4179
        }
4180

4181
        return ret;
14✔
4182
}
4183

4184
static int cgroup_find_matching_controller(char * const *rule_controllers,
10✔
4185
                                           const char * const pid_controller, int *matching_index)
4186
{
4187
        int ret = -ENODATA;
10✔
4188
        int i;
4189

4190
        for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
11✔
4191
                if (rule_controllers[i] == NULL)
11✔
4192
                        break;
1✔
4193

4194
                if (strlen(rule_controllers[i]) != strlen(pid_controller))
10✔
4195
                        continue;
1✔
4196

4197
                if (strncmp(pid_controller, rule_controllers[i], strlen(pid_controller)) == 0) {
9✔
4198
                        *matching_index = i;
9✔
4199
                        ret = 0;
9✔
4200
                        break;
9✔
4201
                }
4202
        }
4203

4204
        return ret;
10✔
4205
}
4206

4207
static bool cgroup_is_rt_task(const pid_t pid)
14✔
4208
{
4209
        int sched_prio_min, sched_prio_max;
4210
        struct sched_param pid_param;
4211
        int ret;
4212

4213
        ret = sched_getparam(pid, &pid_param);
14✔
4214
        if (ret == -1) {
14✔
4215
                ret = ECGOTHER;
12✔
4216
                last_errno = errno;
12✔
4217
                return false;
12✔
4218
        }
4219

4220
        sched_prio_min = sched_get_priority_min(SCHED_RR);
2✔
4221
        sched_prio_max = sched_get_priority_max(SCHED_RR);
2✔
4222

4223
        if (pid_param.sched_priority >= sched_prio_min &&
2✔
4224
            pid_param.sched_priority <= sched_prio_max)
×
4225
                return true;
×
4226

4227
        return false;
2✔
4228
}
4229

4230
/**
4231
 * Evaluates if rule is an ignore rule and the pid/procname match this rule.
4232
 * If rule is an ignore rule and the pid/procname match this rule, then this
4233
 * function returns true.  Otherwise it returns false.
4234
 *
4235
 *        @param rule The rule being evaluated
4236
 *        @param pid PID of the process being compared
4237
 *        @param procname Process name of the process being compared
4238
 *        @return True if the rule is an ignore rule and this pid/procname
4239
 *                match the rule.  False otherwise
4240
 */
4241
STATIC bool cgroup_compare_ignore_rule(const struct cgroup_rule * const rule, pid_t pid,
508✔
4242
                                       const char * const procname)
4243
{
4244
        char *controller_list[MAX_MNT_ELEMENTS] = { NULL };
508✔
4245
        char *cgrp_list[MAX_MNT_ELEMENTS] = { NULL };
508✔
4246
        int rule_matching_controller_idx;
4247
        int cgrp_list_matching_idx = 0;
508✔
4248
        bool found_match = false;
508✔
4249
        char *token, *saveptr;
4250
        int ret, i;
4251

4252
        if (!rule->is_ignore)
508✔
4253
                /* Immediately return if the 'ignore' option is not set */
4254
                return false;
494✔
4255

4256
        /* If the rule is "ignore", move only non-rt tasks */
4257
        if (rule->is_ignore == CGRULE_OPT_IGNORE && cgroup_is_rt_task(pid) == true)
14✔
4258
                return false;
×
4259
        /* If the rule is "ignore_rt", move only non-rt tasks */
4260
        else if (rule->is_ignore == CGRULE_OPT_IGNORE_RT && cgroup_is_rt_task(pid) == false)
14✔
4261
                return false;
×
4262

4263
        /* If the rule is "ignore" and "ignore_rt", move all tasks */
4264

4265
        ret = cg_get_cgroups_from_proc_cgroups(pid, cgrp_list, controller_list,
14✔
4266
                                               MAX_MNT_ELEMENTS);
4267
        if (ret < 0)
14✔
4268
                goto out;
×
4269

4270
        if (strcmp(rule->destination, "*")) {
14✔
4271
                ret = cgroup_find_matching_destination(cgrp_list, rule->destination,
14✔
4272
                                                       &cgrp_list_matching_idx);
4273
                if (ret < 0)
14✔
4274
                        /* No cgroups matched */
4275
                        goto out;
5✔
4276
        }
4277

4278
        token = strtok_r(controller_list[cgrp_list_matching_idx], ",", &saveptr);
9✔
4279
        while (token != NULL) {
10✔
4280

4281
                ret = cgroup_find_matching_controller(rule->controllers, token,
10✔
4282
                                                      &rule_matching_controller_idx);
4283
                if (ret == 0)
10✔
4284
                        /* We found a matching controller */
4285
                        break;
9✔
4286

4287
                token = strtok_r(NULL, ",", &saveptr);
1✔
4288
        }
4289

4290
        if (!rule->procname) {
9✔
4291
                /*
4292
                 * The rule procname is empty, thus it's a wildcard and
4293
                 * all processes match.
4294
                 */
4295
                found_match = true;
5✔
4296
                goto out;
5✔
4297
        }
4298

4299
        if (!strcmp(rule->procname, procname)) {
4✔
4300
                found_match = true;
2✔
4301
                goto out;
2✔
4302
        }
4303

4304
        if (cgroup_compare_wildcard_procname(rule->procname, procname))
2✔
4305
                found_match = true;
1✔
4306

4307
out:
1✔
4308
        for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
252✔
4309
                if (controller_list[i])
238✔
4310
                        free(controller_list[i]);
36✔
4311
                if (cgrp_list[i])
238✔
4312
                        free(cgrp_list[i]);
36✔
4313
        }
4314

4315
        return found_match;
14✔
4316
}
4317

4318
static struct cgroup_rule *cgroup_find_matching_rule_uid_gid(uid_t uid, gid_t gid,
493✔
4319
                                                             struct cgroup_rule *rule)
4320
{
4321
        /* Temporary user data */
4322
        struct passwd *usr = NULL;
493✔
4323

4324
        /* Temporary group data */
4325
        struct group *grp = NULL;
493✔
4326

4327
        /* Temporary string pointer */
4328
        char *sp = NULL;
493✔
4329

4330
        /* Loop variable */
4331
        int i = 0;
493✔
4332
        int loglevel;
4333
        bool match_found = false;
493✔
4334

4335
        loglevel = cgroup_get_loglevel();
493✔
4336

4337
        while (rule) {
493✔
4338
                /* Skip "%" which indicates continuation of previous rule. */
4339
                if (rule->username[0] == '%') {
493✔
4340
                        rule = rule->next;
×
4341
                        continue;
×
4342
                }
4343
                /* The wildcard rule always matches. */
4344
                if ((rule->uid == CGRULE_WILD) && (rule->gid == CGRULE_WILD))
493✔
4345
                        return rule;
493✔
4346

4347
                /* This is the simple case of the UID matching. */
4348
                if (rule->uid == uid)
×
4349
                        return rule;
×
4350

4351
                /* This is the simple case of the GID matching. */
4352
                if (rule->gid == gid)
×
4353
                        return rule;
×
4354

4355
                /* If this is a group rule, the UID might be a member. */
4356
                if (rule->username[0] == '@') {
×
4357
                        /* Get the group data. */
4358
                        sp = &(rule->username[1]);
×
4359
                        grp = getgrnam(sp);
×
4360
                        if (!grp) {
×
4361
                                rule = rule->next;
×
4362
                                continue;
×
4363
                        }
4364

4365
                        /* Get the data for UID. */
4366
                        usr = getpwuid(uid);
×
4367
                        if (!usr) {
×
4368
                                rule = rule->next;
×
4369
                                continue;
×
4370
                        }
4371

4372
                        cgroup_dbg("User name: %s UID: %d Group name: %s GID: %d\n",
×
4373
                                   usr->pw_name, uid, grp->gr_name, grp->gr_gid);
4374
                        if (grp->gr_mem[0])
×
4375
                                cgroup_dbg("Group member(s):\n");
×
4376

4377
                        /* If UID is a member of group, we matched. */
4378
                        for (i = 0; grp->gr_mem[i]; i++) {
×
4379
                                if (!(strcmp(usr->pw_name, grp->gr_mem[i])))
×
4380
                                        match_found = true;
×
4381

4382
                                if (match_found && loglevel < CGROUP_LOG_DEBUG)
×
4383
                                        /*
4384
                                         * Only continue to run through the loop if debugging is
4385
                                         * enabled so that we can see all of the group members
4386
                                         */
4387
                                        break;
×
4388

4389
                                cgroup_dbg("\t%s\n", grp->gr_mem[i]);
×
4390
                        }
4391

4392
                        if (match_found)
×
4393
                                return rule;
×
4394
                }
4395

4396
                /* If we haven't matched, try the next rule. */
4397
                rule = rule->next;
×
4398
        }
4399

4400
        /* If we get here, no rules matched. */
4401
        return NULL;
×
4402
}
4403

4404
/**
4405
 * Finds the first rule in the cached list that matches the given UID, GID
4406
 * or PROCESS NAME, and returns a pointer to that rule.
4407
 * This function uses rl_lock.
4408
 *
4409
 * This function may NOT be thread safe.
4410
 *        @param uid The UID to match
4411
 *        @param gid The GID to match
4412
 *        @param procname The PROCESS NAME to match
4413
 *        @return Pointer to the first matching rule, or NULL if no match
4414
 * TODO: Determine thread-safeness and fix if not safe.
4415
 */
4416
static struct cgroup_rule *cgroup_find_matching_rule(uid_t uid, gid_t gid, pid_t pid,
493✔
4417
                                                     const char *procname)
4418
{
4419
        /* Return value */
4420
        struct cgroup_rule *ret = rl.head;
493✔
4421
        char *base = NULL;
493✔
4422

4423
        pthread_rwlock_wrlock(&rl_lock);
493✔
4424
        while (ret) {
984✔
4425
                ret = cgroup_find_matching_rule_uid_gid(uid, gid, ret);
493✔
4426
                if (!ret)
493✔
4427
                        break;
×
4428
                if (cgroup_compare_ignore_rule(ret, pid, procname))
493✔
4429
                        /*
4430
                         * This pid matched a rule that instructs the
4431
                         * cgrules daemon to ignore this process.
4432
                         */
4433
                        break;
×
4434
                if (ret->is_ignore) {
493✔
4435
                        /*
4436
                         * The rule currently being examined is an ignore
4437
                         * rule, but it didn't match this pid. Move on to
4438
                         * the next rule
4439
                         */
4440
                        ret = ret->next;
×
4441
                        continue;
×
4442
                }
4443
                if (!procname)
493✔
4444
                        /* If procname is NULL, return a rule matching UID or GID. */
4445
                        break;
×
4446
                if (!ret->procname)
493✔
4447
                        /* If no process name in a rule, that means wildcard */
4448
                        break;
×
4449
                if (!strcmp(ret->procname, procname))
493✔
4450
                        break;
2✔
4451

4452
                base = cgroup_basename(procname);
491✔
4453
                if (!strcmp(ret->procname, base))
491✔
4454
                        /* Check a rule of basename. */
4455
                        break;
×
4456
                if (cgroup_compare_wildcard_procname(ret->procname, procname))
491✔
4457
                        break;
×
4458
                ret = ret->next;
491✔
4459
                free(base);
491✔
4460
                base = NULL;
491✔
4461
        }
4462
        pthread_rwlock_unlock(&rl_lock);
493✔
4463

4464
        if (base)
493✔
4465
                free(base);
×
4466

4467
        return ret;
493✔
4468
}
4469

4470
/*
4471
 * Procedure the existence of cgroup "prefix" is in subsystem
4472
 * controller_name return 0 on success
4473
 */
4474
int cgroup_exist_in_subsystem(char *controller_name, char *prefix)
×
4475
{
4476
        char path[FILENAME_MAX];
4477
        char *ret_path;
4478
        DIR *dir;
4479
        int ret;
4480

4481
        pthread_rwlock_rdlock(&cg_mount_table_lock);
×
4482
        ret_path = cg_build_path_locked(prefix, path, controller_name);
×
4483
        pthread_rwlock_unlock(&cg_mount_table_lock);
×
4484
        if (!ret_path) {
×
4485
                ret = 1;
×
4486
                goto end;
×
4487
        }
4488

4489
        dir = opendir(path);
×
4490
        if (dir == NULL) {
×
4491
                /* cgroup in wanted subsystem does not exist */
4492
                ret = 1;
×
4493
        } else {
4494
                /* cgroup in wanted subsystem exists */
4495
                ret = 0;
×
4496
                closedir(dir);
×
4497
        }
4498
end:
×
4499
        return ret;
×
4500
}
4501

4502
/*
4503
 * Auxiliary function return a pointer to the string which is copy of
4504
 * input string and end with the slash
4505
 */
4506
char *cgroup_copy_with_slash(char *input)
×
4507
{
4508
        int len = strlen(input);
×
4509
        char *output;
4510

4511
        /* If input does not end with '/', allocate one more space for it */
4512
        if ((input[len-1]) != '/')
×
4513
                len = len+1;
×
4514

4515
        output = (char *)malloc(sizeof(char)*(len+1));
×
4516
        if (output == NULL)
×
4517
                return NULL;
×
4518

4519
        strcpy(output, input);
×
4520
        output[len-1] = '/';
×
4521
        output[len] = '\0';
×
4522

4523
        return output;
×
4524
}
4525

4526
/* Add controller to a group if it is not exists create it */
4527
static int add_controller(struct cgroup **pcgrp, char *cgrp_name,
×
4528
                          char controller_name[FILENAME_MAX])
4529
{
4530
        struct cgroup_controller *controller = NULL;
×
4531
        struct cgroup *cgrp = pcgrp[0];
×
4532
        int ret = 0;
×
4533

4534
        if  (cgrp == NULL) {
×
4535
                /* It is the first controller the cgrp have to be created */
4536
                cgrp = cgroup_new_cgroup(cgrp_name);
×
4537
                if (cgrp == NULL) {
×
4538
                        ret = ECGFAIL;
×
4539
                        goto end;
×
4540
                }
4541
                pcgrp[0] = cgrp;
×
4542
        }
4543

4544
        controller = cgroup_add_controller(cgrp, controller_name);
×
4545
        if (controller == NULL) {
×
4546
                cgroup_free(&cgrp);
×
4547
                ret = ECGFAIL;
×
4548
        }
4549
end:
×
4550
        return ret;
×
4551
}
4552

4553
/*
4554
 * Create control group based given template if the group already don't exist
4555
 * dest is template name with substitute variables tmp is used cgrules rule.
4556
 */
4557
static int cgroup_create_template_group(char *orig_group_name, struct cgroup_rule *tmp, int flags)
×
4558
{
4559

4560
        struct cgroup *template_group = NULL;
×
4561
        char *template_name = NULL;        /* Name of the template.              */
×
4562
        char *group_name = NULL;        /* Name of the group based on         */
×
4563
                                        /* template variables are substituted.*/
4564
        char *template_position;        /* Denotes directory in template      */
4565
                                        /* path which is investigated.        */
4566
        char *group_position;                /* Denotes directory in cgroup path   */
4567
                                        /* which is investigated.             */
4568

4569
        int ret = 0;
×
4570
        int exist;
4571
        int i;
4572

4573
        /* Template name and group name have to have '/' sign at the end */
4574
        template_name = cgroup_copy_with_slash(tmp->destination);
×
4575
        if (template_name == NULL) {
×
4576
                ret = ECGOTHER;
×
4577
                last_errno = errno;
×
4578
                goto end;
×
4579
        }
4580
        group_name = cgroup_copy_with_slash(orig_group_name);
×
4581
        if (group_name == NULL) {
×
4582
                ret = ECGOTHER;
×
4583
                last_errno = errno;
×
4584
                free(template_name);
×
4585
                template_name = NULL;
×
4586
                goto end;
×
4587
        }
4588

4589
        /* Set start positions */
4590
        template_position = strchr(template_name, '/');
×
4591
        group_position = strchr(group_name, '/');
×
4592

4593
        /*
4594
         * Go recursively through whole path to template group and create
4595
         * given directory if it does not exist yet
4596
         */
4597
        while ((group_position != NULL) && (template_position != NULL)) {
×
4598
                /* Set new subpath */
4599
                group_position[0] = '\0';
×
4600
                template_position[0] = '\0';
×
4601
                template_group = NULL;
×
4602

4603
                /* Test for which controllers wanted group does not exist */
4604
                i = 0;
×
4605
                while (i < MAX_MNT_ELEMENTS && tmp->controllers[i] != NULL) {
×
4606
                        exist = cgroup_exist_in_subsystem(tmp->controllers[i], group_name);
×
4607

4608
                        if (exist != 0) {
×
4609
                                /* The cgroup does not exist */
4610
                                ret = add_controller(&template_group, group_name,
×
4611
                                                     tmp->controllers[i]);
4612
                                if  (ret != 0)
×
4613
                                        goto while_end;
×
4614
                        }
4615
                        i++;
×
4616
                }
4617

4618
                if (template_group != NULL) {
×
4619
                        /*  New group have to be created */
4620
                        if (strcmp(group_name, template_name) == 0) {
×
4621
                                /* The prefix cgroup without template */
4622
                                ret = cgroup_create_cgroup(template_group, 0);
×
4623
                        } else {
4624
                                /* Use template to create relevant cgroup */
4625
                                ret = cgroup_config_create_template_group(template_group,
×
4626
                                                                          template_name, flags);
4627
                        }
4628

4629
                        if (ret != 0) {
×
4630
                                cgroup_free(&template_group);
×
4631
                                goto while_end;
×
4632
                        }
4633
                        cgroup_dbg("Group %s created - based on template %s\n", group_name,
×
4634
                                   template_name);
4635

4636
                        cgroup_free(&template_group);
×
4637
                }
4638
                template_position[0] = '/';
×
4639
                group_position[0] = '/';
×
4640
                template_position = strchr(++template_position, '/');
×
4641
                group_position = strchr(++group_position, '/');
×
4642
        }
4643

4644
while_end:
×
4645
        if ((template_position != NULL) && (template_position[0] == '\0'))
×
4646
                template_position[0] = '/';
×
4647
        if ((group_position != NULL) && (group_position[0] == '\0'))
×
4648
                group_position[0] = '/';
×
4649

4650
end:
×
4651
        if (group_name != NULL)
×
4652
                free(group_name);
×
4653
        if (template_name != NULL)
×
4654
                free(template_name);
×
4655

4656
        return ret;
×
4657
}
4658

4659
int cgroup_change_cgroup_flags(uid_t uid, gid_t gid, const char *procname, pid_t pid, int flags)
493✔
4660
{
4661
        /* Temporary pointer to a rule */
4662
        struct cgroup_rule *tmp = NULL;
493✔
4663

4664
        /* Temporary variables for destination substitution */
4665
        char newdest[FILENAME_MAX];
4666
        struct passwd *user_info;
4667
        struct group *group_info;
4668
        int available;
4669
        int written;
4670
        int i, j;
4671

4672
        /* Return codes */
4673
        int ret = 0;
493✔
4674

4675
        /* We need to check this before doing anything else! */
4676
        if (!cgroup_initialized) {
493✔
4677
                cgroup_warn("libcgroup is not initialized\n");
×
4678
                ret = ECGROUPNOTINITIALIZED;
×
4679
                goto finished;
×
4680
        }
4681

4682
        /*
4683
         * User had asked to find the matching rule (if one exist) in the
4684
         * cached rules but the list might be empty due to the inactive
4685
         * cgrulesengd. Lets emulate its behaviour of caching the rules by
4686
         * reloading the rules from the configuration file.
4687
         */
4688
        if ((flags & CGFLAG_USECACHE) && (rl.head == NULL)) {
493✔
4689
                cgroup_warn("no cached rules found, trying to reload from %s.\n",
×
4690
                            CGRULES_CONF_FILE);
4691
                ret = cgroup_reload_cached_rules();
×
4692
                if (ret != 0)
×
4693
                        goto finished;
×
4694
        }
4695

4696
        /*
4697
         * If the user did not ask for cached rules, we must parse the
4698
         * configuration to find a matching rule (if one exists).
4699
         * Else, we'll find the first match in the cached list (rl).
4700
         */
4701
        if (!(flags & CGFLAG_USECACHE)) {
493✔
4702
                cgroup_dbg("Not using cached rules for PID %d.\n", pid);
×
4703
                ret = cgroup_parse_rules(false, uid, gid, procname);
×
4704

4705
                /* The configuration file has an error!  We must exit now. */
4706
                if (ret != -1 && ret != 0) {
×
4707
                        cgroup_err("failed to parse the configuration rules\n");
×
4708
                        goto finished;
×
4709
                }
4710

4711
                /* We did not find a matching rule, so we're done. */
4712
                if (ret == 0) {
×
4713
                        cgroup_dbg("No rule found to match PID: %d, UID: %d, GID: %d\n",
×
4714
                                   pid, uid, gid);
4715
                        goto finished;
×
4716
                }
4717

4718
                /* Otherwise, we did match a rule and it's in trl. */
4719
                tmp = trl.head;
×
4720
        } else {
4721
                /* Find the first matching rule in the cached list. */
4722
                tmp = cgroup_find_matching_rule(uid, gid, pid, procname);
493✔
4723
                if (!tmp) {
493✔
4724
                        cgroup_dbg("No rule found to match PID: %d, UID: %d, GID: %d\n",
491✔
4725
                                   pid, uid, gid);
4726
                        ret = 0;
491✔
4727
                        goto finished;
491✔
4728
                }
4729
        }
4730
        cgroup_dbg("Found matching rule %s for PID: %d, UID: %d, GID: %d\n",
2✔
4731
                   tmp->username, pid, uid, gid);
4732

4733
        if (tmp->is_ignore) {
2✔
4734
                /*
4735
                 * This rule has instructed us that this pid is not to be
4736
                 * processed and should be ignored
4737
                 */
4738
                cgroup_dbg("Matching rule is an ignore rule\n");
×
4739
                ret = 0;
×
4740
                goto finished;
×
4741
        }
4742

4743
        /* If we are here, then we found a matching rule, so execute it. */
4744
        do {
4745
                cgroup_dbg("Executing rule %s for PID %d... ", tmp->username, pid);
2✔
4746

4747
                /* Destination substitutions */
4748
                for (j = i = 0; i < strlen(tmp->destination) &&
22✔
4749
                        (j < FILENAME_MAX - 2); ++i, ++j) {
20✔
4750
                        if (tmp->destination[i] == '%') {
20✔
4751
                                /* How many bytes did we write / error check */
4752
                                written = 0;
×
4753
                                /* How many bytes can we write */
4754
                                available = FILENAME_MAX - j - 2;
×
4755
                                /* Substitution */
4756
                                switch (tmp->destination[++i]) {
×
4757
                                case 'U':
×
4758
                                        written = snprintf(newdest+j, available, "%d", uid);
×
4759
                                        break;
×
4760
                                case 'u':
×
4761
                                        user_info = getpwuid(uid);
×
4762
                                        if (user_info) {
×
4763
                                                written = snprintf(newdest + j, available, "%s",
×
4764
                                                                   user_info->pw_name);
4765
                                        } else {
4766
                                                written = snprintf(newdest + j, available, "%d",
×
4767
                                                                   uid);
4768
                                        }
4769
                                        break;
×
4770
                                case 'G':
×
4771
                                        written = snprintf(newdest + j,        available, "%d", gid);
×
4772
                                        break;
×
4773
                                case 'g':
×
4774
                                        group_info = getgrgid(gid);
×
4775
                                        if (group_info) {
×
4776
                                                written = snprintf(newdest + j,        available, "%s",
×
4777
                                                                   group_info->gr_name);
4778
                                        } else {
4779
                                                written = snprintf(newdest + j,        available, "%d",
×
4780
                                                                   gid);
4781
                                        }
4782
                                        break;
×
4783
                                case 'P':
×
4784
                                        written = snprintf(newdest + j,        available, "%d", pid);
×
4785
                                        break;
×
4786
                                case 'p':
×
4787
                                        if (procname) {
×
4788
                                                written = snprintf(newdest + j,        available, "%s",
×
4789
                                                                   procname);
4790
                                        } else {
4791
                                                written = snprintf(newdest + j,        available, "%d",
×
4792
                                                                   pid);
4793
                                        }
4794
                                        break;
×
4795
                                }
4796
                                written = min(written, available);
×
4797
                                /*
4798
                                 * written<1 only when either error occurred
4799
                                 * during snprintf or if no substitution was made
4800
                                 * at all. In both cases, we want to just copy
4801
                                 * input string.
4802
                                 */
4803
                                if (written < 1) {
×
4804
                                        newdest[j] = '%';
×
4805
                                        if (available > 1)
×
4806
                                                newdest[++j] = tmp->destination[i];
×
4807
                                } else {
4808
                                        /*
4809
                                         * In next iteration, we will write just
4810
                                         * after the substitution, but j will get
4811
                                         * incremented in the meantime.
4812
                                         */
4813
                                        j += written - 1;
×
4814
                                }
4815
                        } else {
4816
                                if (tmp->destination[i] == '\\')
20✔
4817
                                        ++i;
×
4818
                                newdest[j] = tmp->destination[i];
20✔
4819
                        }
4820
                }
4821

4822
                newdest[j] = 0;
2✔
4823
                if (strcmp(newdest, tmp->destination) != 0) {
2✔
4824
                        /* Destination tag contains templates */
4825

4826
                        cgroup_dbg("control group %s is template\n", newdest);
×
4827
                        ret = cgroup_create_template_group(newdest, tmp, flags);
×
4828
                        if (ret) {
×
4829
                                cgroup_warn("failed to create cgroup based on template %s\n",
×
4830
                                            newdest);
4831
                                goto finished;
×
4832
                        }
4833
                }
4834

4835
                /* Apply the rule */
4836
                ret = cgroup_change_cgroup_path(newdest, pid,
2✔
4837
                                                (const char * const *)tmp->controllers);
2✔
4838
                if (ret) {
2✔
4839
                        cgroup_warn("failed to apply the rule. Error was: %d\n", ret);
×
4840
                        goto finished;
×
4841
                }
4842
                cgroup_dbg("OK!\n");
2✔
4843

4844
                /*
4845
                 * Now, check for multi-line rules.  As long as the "next"
4846
                 * rule starts with '%', it's actually part of the rule that
4847
                 * we just executed.
4848
                 */
4849
                tmp = tmp->next;
2✔
4850
        } while (tmp && (tmp->username[0] == '%'));
2✔
4851

4852
finished:
2✔
4853
        return ret;
493✔
4854
}
4855

4856
int cgroup_change_cgroup_uid_gid_flags(uid_t uid, gid_t gid, pid_t pid, int flags)
×
4857
{
4858
        return cgroup_change_cgroup_flags(uid, gid, NULL, pid, flags);
×
4859
}
4860

4861
/**
4862
 * Provides backwards-compatibility with older versions of the API.
4863
 * This function is deprecated, and cgroup_change_cgroup_uid_gid_flags()
4864
 * should be used instead.  In fact, this function simply calls the newer
4865
 * one with flags set to 0 (none).
4866
 *        @param uid The UID to match
4867
 *        @param gid The GID to match
4868
 *        @param pid The PID of the process to move
4869
 *        @return 0 on success, > 0 on error
4870
 */
4871
int cgroup_change_cgroup_uid_gid(uid_t uid, gid_t gid, pid_t pid)
×
4872
{
4873
        return cgroup_change_cgroup_uid_gid_flags(uid, gid, pid, 0);
×
4874
}
4875

4876
/**
4877
 * Changes the cgroup of a program based on the path provided.  In this case,
4878
 * the user must already know into which cgroup the task should be placed and
4879
 * no rules will be parsed.
4880
 *
4881
 *  returns 0 on success.
4882
 */
4883
int cgroup_change_cgroup_path(const char *dest, pid_t pid, const char *const controllers[])
49✔
4884
{
4885
        struct dirent *task_dir = NULL;
49✔
4886
        char path[FILENAME_MAX];
4887
        struct cgroup cgrp;
4888
        int nr, ret;
4889
        pid_t tid;
4890
        DIR *dir;
4891

4892
        if (!cgroup_initialized) {
49✔
4893
                cgroup_warn("libcgroup is not initialized\n");
×
4894
                return ECGROUPNOTINITIALIZED;
×
4895
        }
4896
        memset(&cgrp, 0, sizeof(struct cgroup));
49✔
4897

4898

4899
        if (is_cgroup_mode_unified() && !controllers) {
49✔
4900
                /*
4901
                 * Do not require the user to pass in an array of controller strings on
4902
                 * cgroup v2 systems.  The hierarchy will be the same regardless of
4903
                 * whether controllers are provided or not.
4904
                 */
4905
                strncpy(cgrp.name, dest, FILENAME_MAX);
1✔
4906
                cgrp.name[FILENAME_MAX-1] = '\0';
1✔
4907
        } else {
4908
                if (!controllers)
48✔
4909
                        return ECGINVAL;
×
4910

4911
                ret = cg_prepare_cgroup(&cgrp, pid, dest, controllers);
48✔
4912
                if (ret)
48✔
4913
                        return ret;
×
4914
        }
4915

4916
        /* Add process to cgroup */
4917
        ret = cgroup_attach_task_pid(&cgrp, pid);
49✔
4918
        if (ret) {
49✔
4919
                cgroup_warn("cgroup_attach_task_pid failed: %d\n", ret);
4✔
4920
                goto finished;
4✔
4921
        }
4922

4923
        /* Add all threads to cgroup */
4924
        snprintf(path, FILENAME_MAX, "/proc/%d/task/", pid);
45✔
4925
        dir = opendir(path);
45✔
4926
        if (!dir) {
45✔
4927
                last_errno = errno;
×
4928
                ret = ECGOTHER;
×
4929
                goto finished;
×
4930
        }
4931

4932
        while ((task_dir = readdir(dir)) != NULL) {
186✔
4933
                nr = sscanf(task_dir->d_name, "%i", &tid);
141✔
4934
                if (nr < 1)
141✔
4935
                        continue;
90✔
4936

4937
                if (tid == pid)
51✔
4938
                        continue;
45✔
4939

4940
                ret = cgroup_attach_task_pid(&cgrp, tid);
6✔
4941
                if (ret) {
6✔
4942
                        cgroup_warn("cgroup_attach_task_pid failed: %d\n", ret);
×
4943
                        break;
×
4944
                }
4945
        }
4946

4947
        closedir(dir);
45✔
4948

4949
finished:
49✔
4950
        cgroup_free_controllers(&cgrp);
49✔
4951

4952
        return ret;
49✔
4953
}
4954

4955
/**
4956
 * Changes the cgroup of all running PIDs based on the rules in the config file.
4957
 * If a rules exists for a PID, then the PID is placed in the correct group.
4958
 *
4959
 * This function may be called after creating new control groups to move
4960
 * running PIDs into the newly created control groups.
4961
 *        @return 0 on success, < 0 on error
4962
 */
4963
int cgroup_change_all_cgroups(void)
2✔
4964
{
4965
        struct dirent *pid_dir = NULL;
2✔
4966
        char *path = "/proc/";
2✔
4967
        DIR *dir;
4968

4969
        dir = opendir(path);
2✔
4970
        if (!dir)
2✔
4971
                return -ECGOTHER;
×
4972

4973
        while ((pid_dir = readdir(dir)) != NULL) {
488✔
4974
                int err, pid;
4975
                uid_t euid;
4976
                gid_t egid;
4977
                char *procname = NULL;
486✔
4978

4979
                err = sscanf(pid_dir->d_name, "%i", &pid);
486✔
4980
                if (err < 1)
486✔
4981
                        continue;
129✔
4982

4983
                err = cgroup_get_uid_gid_from_procfs(pid, &euid, &egid);
357✔
4984
                if (err)
357✔
4985
                        continue;
×
4986

4987
                err = cgroup_get_procname_from_procfs(pid, &procname);
357✔
4988
                if (err)
357✔
4989
                        continue;
×
4990

4991
                err = cgroup_change_cgroup_flags(euid, egid, procname, pid, CGFLAG_USECACHE);
357✔
4992
                if (err)
357✔
4993
                        cgroup_dbg("cgroup change pid %i failed\n", pid);
×
4994

4995
                free(procname);
357✔
4996
        }
4997

4998
        closedir(dir);
2✔
4999
        return 0;
2✔
5000
}
5001

5002
/**
5003
 * Print the cached rules table.  This function should be called only after
5004
 * first calling cgroup_parse_config(), but it will work with an empty rule
5005
 * list.
5006
 *        @param fp The file stream to print to
5007
 */
5008
void cgroup_print_rules_config(FILE *fp)
2✔
5009
{
5010
        /* Iterator */
5011
        struct cgroup_rule *itr = NULL;
2✔
5012

5013
        /* Loop variable */
5014
        int i = 0;
2✔
5015

5016
        pthread_rwlock_rdlock(&rl_lock);
2✔
5017

5018
        if (!(rl.head)) {
2✔
5019
                fprintf(fp, "The rules table is empty.\n\n");
×
5020
                pthread_rwlock_unlock(&rl_lock);
×
5021
                return;
×
5022
        }
5023

5024
        itr = rl.head;
2✔
5025
        while (itr) {
4✔
5026
                fprintf(fp, "Rule: %s", itr->username);
2✔
5027
                if (itr->procname)
2✔
5028
                        fprintf(fp, ":%s", itr->procname);
2✔
5029
                fprintf(fp, "\n");
2✔
5030

5031
                if (itr->uid == CGRULE_WILD)
2✔
5032
                        fprintf(fp, "  UID: any\n");
2✔
5033
                else if (itr->uid == CGRULE_INVALID)
×
5034
                        fprintf(fp, "  UID: N/A\n");
×
5035
                else
5036
                        fprintf(fp, "  UID: %d\n", itr->uid);
×
5037

5038
                if (itr->gid == CGRULE_WILD)
2✔
5039
                        fprintf(fp, "  GID: any\n");
2✔
5040
                else if (itr->gid == CGRULE_INVALID)
×
5041
                        fprintf(fp, "  GID: N/A\n");
×
5042
                else
5043
                        fprintf(fp, "  GID: %d\n", itr->gid);
×
5044

5045
                fprintf(fp, "  DEST: %s\n", itr->destination);
2✔
5046

5047
                fprintf(fp, "  CONTROLLERS:\n");
2✔
5048
                for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
36✔
5049
                        if (itr->controllers[i])
34✔
5050
                                fprintf(fp, "    %s\n", itr->controllers[i]);
2✔
5051
                }
5052
                fprintf(fp, "  OPTIONS:\n");
2✔
5053
                if (itr->is_ignore)
2✔
5054
                        fprintf(fp, "    IS_IGNORE: True\n");
×
5055
                else
5056
                        fprintf(fp, "    IS_IGNORE: False\n");
2✔
5057
                fprintf(fp, "\n");
2✔
5058
                itr = itr->next;
2✔
5059
        }
5060
        pthread_rwlock_unlock(&rl_lock);
2✔
5061
}
5062

5063
/**
5064
 * Reloads the rules list, using the given configuration file.
5065
 * This function is probably NOT thread safe (calls cgroup_parse_rules()).
5066
 *        @return 0 on success, > 0 on failure
5067
 */
5068
int cgroup_reload_cached_rules(void)
×
5069
{
5070
        /* Return codes */
5071
        int ret = 0;
×
5072

5073
        cgroup_dbg("Reloading cached rules from %s.\n", CGRULES_CONF_FILE);
×
5074
        ret = cgroup_parse_rules(true, CGRULE_INVALID, CGRULE_INVALID, NULL);
×
5075
        if (ret) {
×
5076
                cgroup_warn("error parsing configuration file '%s': %d\n", CGRULES_CONF_FILE, ret);
×
5077
                ret = ECGRULESPARSEFAIL;
×
5078
                goto finished;
×
5079
        }
5080

5081
        #ifdef CGROUP_DEBUG
5082
        cgroup_print_rules_config(stdout);
5083
        #endif
5084
finished:
×
5085
        return ret;
×
5086
}
5087

5088
/**
5089
 * Initializes the rules cache.
5090
 *        @return 0 on success, > 0 on error
5091
 */
5092
int cgroup_init_rules_cache(void)
2✔
5093
{
5094
        /* Return codes */
5095
        int ret = 0;
2✔
5096

5097
        /* Attempt to read the configuration file and cache the rules. */
5098
        ret = cgroup_parse_rules(true, CGRULE_INVALID, CGRULE_INVALID, NULL);
2✔
5099
        if (ret)
2✔
5100
                cgroup_dbg("Could not initialize rule cache, error was: %d\n", ret);
×
5101

5102
        return ret;
2✔
5103
}
5104

5105
/**
5106
 * cgroup_get_current_controller_path
5107
 * @pid: pid of the current process for which the path is to be determined
5108
 * @controller: name of the controller for which to determine current path
5109
 * @current_path: a pointer that is filled with the value of the current
5110
 *                path as seen in /proc/<pid>/cgroup
5111
 */
5112
int cgroup_get_current_controller_path(pid_t pid, const char *controller, char **current_path)
7✔
5113
{
5114
        enum cg_version_t version;
5115
        enum cg_setup_mode_t mode;
5116
        FILE *pid_cgrp_fd = NULL;
7✔
5117
        bool unified = false;
7✔
5118
        char *path = NULL;
7✔
5119
        int ret;
5120

5121
        if (!cgroup_initialized) {
7✔
5122
                cgroup_warn("libcgroup is not initialized\n");
×
5123
                return ECGROUPNOTINITIALIZED;
×
5124
        }
5125

5126
        if (!current_path)
7✔
5127
                return ECGOTHER;
×
5128

5129
        mode = cgroup_setup_mode();
7✔
5130
        if (mode == CGROUP_MODE_LEGACY && !controller)
7✔
5131
                return ECGOTHER;
×
5132

5133
        /*
5134
         * both unified/hybrid can have the controller mounted as
5135
         * cgroup v2 version.
5136
         */
5137
        if  (!controller) {
7✔
5138
                unified = true;
4✔
5139
        } else {
5140
                ret = cgroup_get_controller_version(controller, &version);
3✔
5141
                if (ret) {
3✔
5142
                        cgroup_warn("Failed to get version of the controller: %s\n", controller);
1✔
5143
                        ret = ECGINVAL;
1✔
5144
                        goto cleanup_path;
1✔
5145
                }
5146
                unified = (version == CGROUP_V2);
2✔
5147
        }
5148

5149
        ret = asprintf(&path, "/proc/%d/cgroup", pid);
6✔
5150
        if (ret <= 0) {
6✔
5151
                cgroup_warn("cannot allocate memory (/proc/pid/cgroup) ret %d\n", ret);
×
5152
                return ret;
×
5153
        }
5154

5155
        ret = ECGROUPNOTEXIST;
6✔
5156
        pid_cgrp_fd = fopen(path, "re");
6✔
5157
        if (!pid_cgrp_fd)
6✔
5158
                goto cleanup_path;
×
5159

5160
        /*
5161
         * Why do we grab the cg_mount_table_lock?, the reason is that the
5162
         * cgroup of a pid can change via the cgroup_attach_task_pid() call.
5163
         * To make sure, we return consistent and safe results, we acquire the
5164
         * lock upfront. We can optimize by acquiring and releasing
5165
         * the lock in the while loop, but that will be more expensive.
5166
         */
5167
        pthread_rwlock_rdlock(&cg_mount_table_lock);
6✔
5168
        while (!feof(pid_cgrp_fd)) {
6✔
5169
                char controllers[FILENAME_MAX];
5170
                char cgrp_path[FILENAME_MAX];
5171
                char *savedptr;
5172
                char *token;
5173
                int num;
5174

5175
                /*
5176
                 * with unified mode, the /proc/pid/cgroup the output is
5177
                 * similar to that of cgroup legacy and hybrid modes:
5178
                 * hierarchy-ID:controller-list:cgroup-path
5179
                 *
5180
                 * the difference is that in cgroup v2:
5181
                 * - hierarchy-ID is always 0 (one hierarchy allowed)
5182
                 * - controller-list is empty
5183
                 */
5184
                if (mode == CGROUP_MODE_UNIFIED || unified) {
6✔
5185
                        ret = fscanf(pid_cgrp_fd, "%d::%4095s\n", &num, cgrp_path);
6✔
5186
                        if (ret != 2) {
6✔
5187
                                /*
5188
                                 * we are interested only in unified format
5189
                                 * line, skip this line.
5190
                                 */
5191
                                if (unified) {
×
5192
                                        ret = fscanf(pid_cgrp_fd, "%*[^\n]\n");
×
5193
                                        if (ret == 0)
×
5194
                                                continue;
×
5195

5196
                                        if (ret == EOF) {
×
5197
                                                last_errno = errno;
×
5198
                                                ret = ECGEOF;
×
5199
                                                goto done;
6✔
5200
                                        }
5201
                                }
5202

5203
                                cgroup_warn("read failed for pid_cgroup_fd ret %d\n", ret);
×
5204
                                last_errno = errno;
×
5205
                                ret = ECGOTHER;
×
5206
                                goto done;
×
5207
                        }
5208

5209
                        /* check if the controller is enabled in cgroup v2 */
5210
                        if (controller) {
6✔
5211
                                ret = cgroupv2_controller_enabled(cgrp_path, controller);
2✔
5212
                                if (ret)
2✔
5213
                                        goto done;
1✔
5214
                        }
5215

5216
                        *current_path = strdup(cgrp_path);
5✔
5217
                        if (!*current_path) {
5✔
5218
                                last_errno = errno;
×
5219
                                ret = ECGOTHER;
×
5220
                                goto done;
×
5221
                        }
5222
                        ret = 0;
5✔
5223
                        goto done;
5✔
5224
                }
5225

5226
                /*
5227
                 * 4095 == FILENAME_MAX - 1, keeping coverity happy with precision
5228
                 * for the cgrp_path.
5229
                 */
5230
                ret = fscanf(pid_cgrp_fd, "%d:%[^:]:%4095s\n", &num, controllers, cgrp_path);
×
5231
                /*
5232
                 * Magic numbers like "3" seem to be integrating into my daily
5233
                 * life, I need some magic to help make them disappear :)
5234
                 */
5235
                if (ret != 3) {
×
5236
                        cgroup_warn("read failed for pid_cgrp_fd ret %d\n", ret);
×
5237
                        last_errno = errno;
×
5238
                        ret = ECGOTHER;
×
5239
                        goto done;
×
5240
                }
5241

5242
                token = strtok_r(controllers, ",", &savedptr);
×
5243
                while (token) {
×
5244
                        if (strncmp(controller, token, strlen(controller) + 1) == 0) {
×
5245
                                *current_path = strdup(cgrp_path);
×
5246
                                if (!*current_path) {
×
5247
                                        last_errno = errno;
×
5248
                                        ret = ECGOTHER;
×
5249
                                        goto done;
×
5250
                                }
5251
                                ret = 0;
×
5252
                                goto done;
×
5253
                        }
5254
                        token = strtok_r(NULL, ",", &savedptr);
×
5255
                }
5256
        }
5257

5258
done:
×
5259
        pthread_rwlock_unlock(&cg_mount_table_lock);
6✔
5260
        fclose(pid_cgrp_fd);
6✔
5261
cleanup_path:
7✔
5262
        free(path);
7✔
5263

5264
        return ret;
7✔
5265
}
5266

5267
const char *cgroup_strerror(int code)
48✔
5268
{
5269
        int idx = code % ECGROUPNOTCOMPILED;
48✔
5270

5271
        if (code == ECGOTHER) {
48✔
5272
#ifdef STRERROR_R_CHAR_P
5273
                return strerror_r(cgroup_get_last_errno(), errtext, MAXLEN);
20✔
5274
#else
5275
                return strerror_r(cgroup_get_last_errno(), errtext, sizeof(errtext)) ?
5276
                        "unknown error" : errtext;
5277
#endif
5278
        }
5279
        if (idx >= ARRAY_SIZE(cgroup_strerror_codes))
28✔
5280
                return "Invalid error code";
×
5281

5282
        return cgroup_strerror_codes[idx];
28✔
5283
}
5284

5285
/**
5286
 * Return last errno, which caused ECGOTHER error.
5287
 */
5288
int cgroup_get_last_errno(void)
20✔
5289
{
5290
        return last_errno;
20✔
5291
}
5292

5293
static int cg_walk_node(FTS *fts, FTSENT *ent, const int depth, struct cgroup_file_info *info,
8,819✔
5294
                        int dir)
5295
{
5296
        int ret = 0;
8,819✔
5297

5298
        if (!cgroup_initialized)
8,819✔
5299
                return ECGROUPNOTINITIALIZED;
×
5300

5301
        cgroup_dbg("seeing file %s\n", ent->fts_path);
8,819✔
5302

5303
        info->path = ent->fts_name;
8,819✔
5304
        info->parent = ent->fts_parent->fts_name;
8,819✔
5305
        info->full_path = ent->fts_path;
8,819✔
5306
        info->depth = ent->fts_level;
8,819✔
5307
        info->type = CGROUP_FILE_TYPE_OTHER;
8,819✔
5308

5309
        if (depth && (info->depth > depth))
8,819✔
5310
                return 0;
×
5311

5312
        switch (ent->fts_info) {
8,819✔
5313
        case FTS_DNR:
×
5314
        case FTS_ERR:
5315
                errno = ent->fts_errno;
×
5316
                break;
×
5317
        case FTS_D:
168✔
5318
                if (dir & CGROUP_WALK_TYPE_PRE_DIR)
168✔
5319
                        info->type = CGROUP_FILE_TYPE_DIR;
124✔
5320
                break;
168✔
5321
        case FTS_DC:
168✔
5322
        case FTS_NSOK:
5323
        case FTS_NS:
5324
        case FTS_DP:
5325
                if (dir & CGROUP_WALK_TYPE_POST_DIR)
168✔
5326
                        info->type = CGROUP_FILE_TYPE_DIR;
71✔
5327
                break;
168✔
5328
        case FTS_F:
8,483✔
5329
                info->type = CGROUP_FILE_TYPE_FILE;
8,483✔
5330
                break;
8,483✔
5331
        case FTS_DEFAULT:
×
5332
                break;
×
5333
        }
5334
        return ret;
8,819✔
5335
}
5336

5337
int cgroup_walk_tree_next(int depth, void **handle, struct cgroup_file_info *info, int base_level)
8,819✔
5338
{
5339
        struct cgroup_tree_handle *entry;
5340
        FTSENT *ent;
5341
        int ret = 0;
8,819✔
5342

5343
        if (!cgroup_initialized)
8,819✔
5344
                return ECGROUPNOTINITIALIZED;
×
5345

5346
        if (!handle)
8,819✔
5347
                return ECGINVAL;
×
5348

5349
        entry = (struct cgroup_tree_handle *) *handle;
8,819✔
5350

5351
        ent = fts_read(entry->fts);
8,819✔
5352
        if (!ent)
8,819✔
5353
                return ECGEOF;
41✔
5354
        if (!base_level && depth)
8,778✔
5355
                base_level = ent->fts_level + depth;
×
5356

5357
        ret = cg_walk_node(entry->fts, ent, base_level, info, entry->flags);
8,778✔
5358

5359
        *handle = entry;
8,778✔
5360
        return ret;
8,778✔
5361
}
5362

5363
int cgroup_walk_tree_end(void **handle)
41✔
5364
{
5365
        struct cgroup_tree_handle *entry;
5366

5367
        if (!cgroup_initialized)
41✔
5368
                return ECGROUPNOTINITIALIZED;
×
5369

5370
        if (!handle)
41✔
5371
                return ECGINVAL;
×
5372

5373
        entry = (struct cgroup_tree_handle *) *handle;
41✔
5374

5375
        fts_close(entry->fts);
41✔
5376
        free(entry);
41✔
5377
        *handle = NULL;
41✔
5378

5379
        return 0;
41✔
5380
}
5381

5382
/* TODO: Need to decide a better place to put this function. */
5383
int cgroup_walk_tree_begin(const char *controller, const char *base_path, int depth, void **handle,
41✔
5384
                           struct cgroup_file_info *info, int *base_level)
5385
{
5386
        struct cgroup_tree_handle *entry;
5387
        char full_path[FILENAME_MAX];
5388
        char *cg_path[2];
5389
        FTSENT *ent;
5390
        int ret = 0;
41✔
5391

5392
        if (!cgroup_initialized)
41✔
5393
                return ECGROUPNOTINITIALIZED;
×
5394

5395
        if (!handle)
41✔
5396
                return ECGINVAL;
×
5397

5398
        cgroup_dbg("path is %s\n", base_path);
41✔
5399

5400
        if (!cg_build_path(base_path, full_path, controller))
41✔
5401
                return ECGOTHER;
×
5402

5403
        entry = calloc(1, sizeof(struct cgroup_tree_handle));
41✔
5404

5405
        if (!entry) {
41✔
5406
                last_errno = errno;
×
5407
                *handle = NULL;
×
5408
                return ECGOTHER;
×
5409
        }
5410

5411
        entry->flags |= CGROUP_WALK_TYPE_PRE_DIR;
41✔
5412

5413
        *base_level = 0;
41✔
5414
        cg_path[0] = full_path;
41✔
5415
        cg_path[1] = NULL;
41✔
5416

5417
        entry->fts = fts_open(cg_path, FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT, NULL);
41✔
5418
        if (entry->fts == NULL) {
41✔
5419
                free(entry);
×
5420
                last_errno = errno;
×
5421
                *handle = NULL;
×
5422
                return ECGOTHER;
×
5423
        }
5424
        ent = fts_read(entry->fts);
41✔
5425
        if (!ent) {
41✔
5426
                cgroup_warn("fts_read failed\n");
×
5427
                fts_close(entry->fts);
×
5428
                free(entry);
×
5429
                *handle = NULL;
×
5430
                return ECGINVAL;
×
5431
        }
5432
        if (!*base_level && depth)
41✔
5433
                *base_level = ent->fts_level + depth;
×
5434

5435
        ret = cg_walk_node(entry->fts, ent, *base_level, info, entry->flags);
41✔
5436
        if (ret != 0) {
41✔
5437
                fts_close(entry->fts);
×
5438
                free(entry);
×
5439
                *handle = NULL;
×
5440
        } else {
5441
                *handle = entry;
41✔
5442
        }
5443
        return ret;
41✔
5444
}
5445

5446
int cgroup_walk_tree_set_flags(void **handle, int flags)
27✔
5447
{
5448
        struct cgroup_tree_handle *entry;
5449

5450
        if (!cgroup_initialized)
27✔
5451
                return ECGROUPNOTINITIALIZED;
×
5452

5453
        if (!handle)
27✔
5454
                return ECGINVAL;
×
5455

5456
        if ((flags & CGROUP_WALK_TYPE_PRE_DIR) &&
27✔
5457
            (flags & CGROUP_WALK_TYPE_POST_DIR))
×
5458
                return ECGINVAL;
×
5459

5460
        entry = (struct cgroup_tree_handle *) *handle;
27✔
5461
        entry->flags = flags;
27✔
5462

5463
        *handle = entry;
27✔
5464
        return 0;
27✔
5465
}
5466

5467
/*
5468
 * This parses a stat line which is in the form of (name value) pair
5469
 * separated by a space.
5470
 */
5471
static int cg_read_stat(FILE *fp, struct cgroup_stat *cgrp_stat)
×
5472
{
5473
        char *saveptr = NULL;
×
5474
        ssize_t read_bytes;
5475
        char *line = NULL;
×
5476
        size_t len = 0;
×
5477
        char *token;
5478
        int ret = 0;
×
5479

5480
        read_bytes = getline(&line, &len, fp);
×
5481
        if (read_bytes == -1) {
×
5482
                ret = ECGEOF;
×
5483
                goto out_free;
×
5484
        }
5485

5486
        token = strtok_r(line, " ", &saveptr);
×
5487
        if (!token) {
×
5488
                ret = ECGINVAL;
×
5489
                goto out_free;
×
5490
        }
5491
        strncpy(cgrp_stat->name, token, FILENAME_MAX - 1);
×
5492
        cgrp_stat->name[FILENAME_MAX - 1] = '\0';
×
5493

5494
        token = strtok_r(NULL, " ", &saveptr);
×
5495
        if (!token) {
×
5496
                ret = ECGINVAL;
×
5497
                goto out_free;
×
5498
        }
5499
        strncpy(cgrp_stat->value, token, CG_VALUE_MAX - 1);
×
5500
        cgrp_stat->value[CG_VALUE_MAX - 1] = '\0';
×
5501

5502
out_free:
×
5503
        free(line);
×
5504

5505
        return ret;
×
5506
}
5507

5508
int cgroup_read_value_end(void **handle)
219✔
5509
{
5510
        FILE *fp;
5511

5512
        if (!cgroup_initialized)
219✔
5513
                return ECGROUPNOTINITIALIZED;
×
5514

5515
        if (!handle)
219✔
5516
                return ECGINVAL;
×
5517

5518
        fp = (FILE *)*handle;
219✔
5519
        fclose(fp);
219✔
5520
        *handle = NULL;
219✔
5521

5522
        return 0;
219✔
5523
}
5524

5525
int cgroup_read_value_next(void **handle, char *buffer, int max)
291✔
5526
{
5527
        char *ret_c;
5528
        int ret = 0;
291✔
5529
        FILE *fp;
5530

5531
        if (!cgroup_initialized)
291✔
5532
                return ECGROUPNOTINITIALIZED;
×
5533

5534
        if (!buffer || !handle)
291✔
5535
                return ECGINVAL;
×
5536

5537
        fp = (FILE *)*handle;
291✔
5538
        ret_c = fgets(buffer, max, fp);
291✔
5539
        if (ret_c == NULL)
291✔
5540
                ret = ECGEOF;
181✔
5541

5542
        return ret;
291✔
5543
}
5544

5545
int cgroup_read_value_begin(const char * const controller, const char *path,
222✔
5546
                            const char * const name, void **handle, char *buffer, int max)
5547
{
5548
        char stat_file[FILENAME_MAX + sizeof(name)];
5549
        char stat_path[FILENAME_MAX];
5550
        char *ret_c = NULL;
222✔
5551
        int ret = 0;
222✔
5552
        FILE *fp;
5553

5554
        if (!cgroup_initialized)
222✔
5555
                return ECGROUPNOTINITIALIZED;
×
5556

5557
        if (!buffer || !handle)
222✔
5558
                return ECGINVAL;
×
5559

5560
        if (!cg_build_path(path, stat_path, controller))
222✔
5561
                return ECGOTHER;
×
5562

5563
        snprintf(stat_file, sizeof(stat_file), "%s/%s", stat_path, name);
222✔
5564
        fp = fopen(stat_file, "re");
222✔
5565
        if (!fp) {
222✔
5566
                cgroup_warn("fopen failed\n");
3✔
5567
                last_errno = errno;
3✔
5568
                *handle = NULL;
3✔
5569
                return ECGOTHER;
3✔
5570
        }
5571

5572
        ret_c = fgets(buffer, max, fp);
219✔
5573
        if (ret_c == NULL)
219✔
5574
                ret = ECGEOF;
8✔
5575

5576
        *handle = fp;
219✔
5577

5578
        return ret;
219✔
5579
}
5580

5581
int cgroup_read_stats_end(void **handle)
×
5582
{
5583
        FILE *fp;
5584

5585
        if (!cgroup_initialized)
×
5586
                return ECGROUPNOTINITIALIZED;
×
5587

5588
        if (!handle)
×
5589
                return ECGINVAL;
×
5590

5591
        fp = (FILE *)*handle;
×
5592
        if (fp == NULL)
×
5593
                return ECGINVAL;
×
5594

5595
        fclose(fp);
×
5596

5597
        return 0;
×
5598
}
5599

5600
int cgroup_read_stats_next(void **handle, struct cgroup_stat *cgrp_stat)
×
5601
{
5602
        FILE *fp;
5603
        int ret = 0;
×
5604

5605
        if (!cgroup_initialized)
×
5606
                return ECGROUPNOTINITIALIZED;
×
5607

5608
        if (!handle || !cgrp_stat)
×
5609
                return ECGINVAL;
×
5610

5611
        fp = (FILE *)*handle;
×
5612
        ret = cg_read_stat(fp, cgrp_stat);
×
5613
        *handle = fp;
×
5614

5615
        return ret;
×
5616
}
5617

5618
/* TODO: Need to decide a better place to put this function. */
5619
int cgroup_read_stats_begin(const char *controller, const char *path, void **handle,
×
5620
                            struct cgroup_stat *cgrp_stat)
5621
{
5622
        char stat_file[FILENAME_MAX + sizeof(".stat")];
5623
        char stat_path[FILENAME_MAX];
5624
        int ret = 0;
×
5625
        FILE *fp;
5626

5627
        if (!cgroup_initialized)
×
5628
                return ECGROUPNOTINITIALIZED;
×
5629

5630
        if (!cgrp_stat || !handle)
×
5631
                return ECGINVAL;
×
5632

5633
        if (!cg_build_path(path, stat_path, controller))
×
5634
                return ECGOTHER;
×
5635

5636
        snprintf(stat_file, sizeof(stat_file), "%s/%s.stat", stat_path, controller);
×
5637

5638
        fp = fopen(stat_file, "re");
×
5639
        if (!fp) {
×
5640
                cgroup_warn("fopen failed\n");
×
5641
                return ECGINVAL;
×
5642
        }
5643

5644
        ret = cg_read_stat(fp, cgrp_stat);
×
5645
        *handle = fp;
×
5646

5647
        return ret;
×
5648
}
5649

5650
int cgroup_get_task_end(void **handle)
×
5651
{
5652
        if (!cgroup_initialized)
×
5653
                return ECGROUPNOTINITIALIZED;
×
5654

5655
        if (!*handle)
×
5656
                return ECGINVAL;
×
5657

5658
        fclose((FILE *) *handle);
×
5659
        *handle = NULL;
×
5660

5661
        return 0;
×
5662
}
5663

5664
int cgroup_get_task_next(void **handle, pid_t *pid)
×
5665
{
5666
        int ret;
5667

5668
        if (!cgroup_initialized)
×
5669
                return ECGROUPNOTINITIALIZED;
×
5670

5671
        if (!handle)
×
5672
                return ECGINVAL;
×
5673

5674
        ret = fscanf((FILE *) *handle, "%u", pid);
×
5675

5676
        if (ret != 1) {
×
5677
                if (ret == EOF)
×
5678
                        return ECGEOF;
×
5679
                last_errno = errno;
×
5680
                return ECGOTHER;
×
5681
        }
5682

5683
        return 0;
×
5684
}
5685

5686
int cgroup_get_task_begin(const char *cgrp, const char *controller, void **handle, pid_t *pid)
×
5687
{
5688
        char path[FILENAME_MAX];
5689
        char *fullpath = NULL;
×
5690
        int ret = 0;
×
5691

5692
        if (!cgroup_initialized)
×
5693
                return ECGROUPNOTINITIALIZED;
×
5694

5695
        if (!cg_build_path(cgrp, path, controller))
×
5696
                return ECGOTHER;
×
5697

5698
        ret = asprintf(&fullpath, "%s/tasks", path);
×
5699

5700
        if (ret < 0) {
×
5701
                last_errno = errno;
×
5702
                return ECGOTHER;
×
5703
        }
5704

5705
        *handle = (void *) fopen(fullpath, "re");
×
5706
        free(fullpath);
×
5707

5708
        if (!*handle) {
×
5709
                last_errno = errno;
×
5710
                return ECGOTHER;
×
5711
        }
5712
        ret = cgroup_get_task_next(handle, pid);
×
5713

5714
        return ret;
×
5715
}
5716

5717
int cgroup_get_controller_end(void **handle)
8✔
5718
{
5719
        int *pos = (int *) *handle;
8✔
5720

5721
        if (!cgroup_initialized)
8✔
5722
                return ECGROUPNOTINITIALIZED;
×
5723

5724
        if (!pos)
8✔
5725
                return ECGINVAL;
×
5726

5727
        free(pos);
8✔
5728
        *handle = NULL;
8✔
5729

5730
        return 0;
8✔
5731
}
5732

5733
int cgroup_get_controller_next(void **handle, struct cgroup_mount_point *info)
72✔
5734
{
5735
        int *pos = (int *) *handle;
72✔
5736
        int ret = 0;
72✔
5737

5738
        if (!cgroup_initialized)
72✔
5739
                return ECGROUPNOTINITIALIZED;
×
5740

5741
        if (!pos)
72✔
5742
                return ECGINVAL;
×
5743

5744
        if (!info)
72✔
5745
                return ECGINVAL;
×
5746

5747
        pthread_rwlock_rdlock(&cg_mount_table_lock);
72✔
5748

5749
        if (cg_mount_table[*pos].name[0] == '\0') {
72✔
5750
                ret = ECGEOF;
×
5751
                goto out_unlock;
×
5752
        }
5753

5754
        if (strncmp(cg_mount_table[*pos].name, CGRP_FILE_PREFIX, CONTROL_NAMELEN_MAX) == 0)
72✔
5755
                /*
5756
                 * For now, hide the "cgroup" pseudo-controller from the user.  This may be
5757
                 * worth revisiting in the future.
5758
                 */
5759
                (*pos)++;
8✔
5760

5761
        if (cg_mount_table[*pos].name[0] == '\0') {
72✔
5762
                ret = ECGEOF;
8✔
5763
                goto out_unlock;
8✔
5764
        }
5765

5766
        strncpy(info->name, cg_mount_table[*pos].name, FILENAME_MAX - 1);
64✔
5767
        info->name[FILENAME_MAX - 1] = '\0';
64✔
5768

5769
        strncpy(info->path, cg_mount_table[*pos].mount.path, FILENAME_MAX - 1);
64✔
5770
        info->path[FILENAME_MAX - 1] = '\0';
64✔
5771

5772
        (*pos)++;
64✔
5773
        *handle = pos;
64✔
5774

5775
out_unlock:
72✔
5776
        pthread_rwlock_unlock(&cg_mount_table_lock);
72✔
5777

5778
        return ret;
72✔
5779
}
5780

5781
int cgroup_get_controller_begin(void **handle, struct cgroup_mount_point *info)
8✔
5782
{
5783
        int *pos;
5784

5785
        if (!cgroup_initialized)
8✔
5786
                return ECGROUPNOTINITIALIZED;
×
5787

5788
        if (!info)
8✔
5789
                return ECGINVAL;
×
5790

5791
        pos = malloc(sizeof(int));
8✔
5792

5793
        if (!pos) {
8✔
5794
                last_errno = errno;
×
5795
                return ECGOTHER;
×
5796
        }
5797

5798
        *pos = 0;
8✔
5799

5800
        *handle = pos;
8✔
5801

5802
        return cgroup_get_controller_next(handle, info);
8✔
5803
}
5804

5805
/**
5806
 * Get process data (euid and egid) from /proc/<pid>/status file.
5807
 * @param pid: The process id
5808
 * @param euid: The uid of param pid
5809
 * @param egid: The gid of param pid
5810
 * @return 0 on success, > 0 on error.
5811
 */
5812
int cgroup_get_uid_gid_from_procfs(pid_t pid, uid_t *euid, gid_t *egid)
493✔
5813
{
5814
        char path[FILENAME_MAX];
5815
        uid_t ruid, suid, fsuid;
5816
        gid_t rgid, sgid, fsgid;
5817
        bool found_euid = false;
493✔
5818
        bool found_egid = false;
493✔
5819
        char buf[4092];
5820
        FILE *f;
5821

5822
        sprintf(path, "/proc/%d/status", pid);
493✔
5823
        f = fopen(path, "re");
493✔
5824
        if (!f)
493✔
5825
                return ECGROUPNOTEXIST;
×
5826

5827
        while (fgets(buf, sizeof(buf), f)) {
4,930✔
5828
                if (!strncmp(buf, "Uid:", 4)) {
4,930✔
5829
                        if (sscanf((buf + strlen("Uid:") + 1), "%d%d%d%d",
493✔
5830
                                    &ruid, euid, &suid, &fsuid) != 4)
5831
                                break;
×
5832
                        cgroup_dbg("Scanned proc values are %d %d %d %d\n",
493✔
5833
                                   ruid, *euid, suid, fsuid);
5834
                        found_euid = true;
493✔
5835
                } else if (!strncmp(buf, "Gid:", 4)) {
4,437✔
5836
                        if (sscanf((buf + strlen("Gid:") + 1), "%d%d%d%d",
493✔
5837
                                   &rgid, egid, &sgid, &fsgid) != 4)
5838
                                break;
×
5839
                        cgroup_dbg("Scanned proc values are %d %d %d %d\n",
493✔
5840
                                   rgid, *egid, sgid, fsgid);
5841
                        found_egid = true;
493✔
5842
                }
5843
                if (found_euid && found_egid)
4,930✔
5844
                        break;
493✔
5845
        }
5846
        fclose(f);
493✔
5847

5848
        if (!found_euid || !found_egid) {
493✔
5849
                /*
5850
                 * This method doesn't match the file format of
5851
                 * /proc/<pid>/status. The format has been changed and we
5852
                 * should catch up the change.
5853
                 */
5854
                cgroup_warn("invalid file format of /proc/%d/status\n", pid);
×
5855
                return ECGFAIL;
×
5856
        }
5857
        return 0;
493✔
5858
}
5859

5860
/**
5861
 * Given a pid, this function will return the controllers and cgroups that
5862
 * the pid is a member of. The caller is expected to allocate the
5863
 * controller_list[] and cgroup_list[] arrays as well as null each entry in
5864
 * the arrays.  This function will allocate the necessary memory for each
5865
 * string within the arrays.
5866
 *
5867
 *        @param pid The process id
5868
 *        @param cgrp_list[] An array of char pointers to hold the cgroups
5869
 *        @param controller_list[] An array of char pointers to hold the list
5870
 *               of controllers
5871
 *        @param list_len The size of the arrays
5872
 */
5873
STATIC int cg_get_cgroups_from_proc_cgroups(pid_t pid, char *cgrp_list[],
18✔
5874
                                            char *controller_list[], int list_len)
5875
{
5876
        char path[FILENAME_MAX];
5877
        char *stok_buff = NULL;
18✔
5878
        size_t buff_len;
5879
        char buf[4092];
5880
        int ret = 0;
18✔
5881
        int idx = 0;
18✔
5882
        FILE *f;
5883

5884
#ifdef UNIT_TEST
5885
        sprintf(path, "%s", TEST_PROC_PID_CGROUP_FILE);
18✔
5886
#else
5887
        sprintf(path, "/proc/%d/cgroup", pid);
×
5888
#endif
5889
        f = fopen(path, "re");
18✔
5890
        if (!f)
18✔
5891
                return ECGROUPNOTEXIST;
×
5892

5893
        while (fgets(buf, sizeof(buf), f)) {
71✔
5894
                /*
5895
                 * Each line in /proc/{pid}/cgroup is like the following:
5896
                 *
5897
                 * {cg#}:{controller}:{cgname}
5898
                 *
5899
                 * e.g.
5900
                 * 7:devices:/user.slice
5901
                 */
5902

5903
                /* Read in the cgroup number.  We don't care about it */
5904
                stok_buff = strtok(buf, ":");
54✔
5905
                /* Read in the controller name */
5906
                stok_buff = strtok(NULL, ":");
54✔
5907

5908
                /*
5909
                 * After this point, we have allocated memory.  If we return
5910
                 * an error code after this, it's up to us to free the memory
5911
                 * we allocated
5912
                 */
5913
                controller_list[idx] = strndup(stok_buff, strlen(stok_buff) + 1);
54✔
5914

5915
                /* Read in the cgroup name */
5916
                stok_buff = strtok(NULL, ":");
54✔
5917

5918
                if (stok_buff == NULL) {
54✔
5919
                        /*
5920
                         * An empty controller is reported on some kernels.
5921
                         * It may look like this:
5922
                         * 0::/user.slice/user-1000.slice/session-1.scope
5923
                         *
5924
                         * Ignore this controller and move on.  Note that we
5925
                         * need to free the controller list entry we made.
5926
                         */
5927
                        free(controller_list[idx]);
4✔
5928
                        controller_list[idx] = NULL;
4✔
5929
                        continue;
4✔
5930
                }
5931

5932
                buff_len = strlen(stok_buff);
50✔
5933
                if (stok_buff[buff_len - 1] == '\n')
50✔
5934
                        buff_len--; /* Don't copy the trailing newline char */
38✔
5935

5936
                /* Read in the cgroup name */
5937
                if (buff_len > 1) {
50✔
5938
                        /* Strip off the leading '/' for every cgroup but the root cgroup */
5939
                        cgrp_list[idx] = malloc(buff_len);
34✔
5940
                        if (!cgrp_list[idx]) {
34✔
5941
                                cgroup_err("malloc failed: %s\n", strerror(errno));
×
5942
                                fclose(f);
×
5943
                                return ECGOTHER;
×
5944
                        }
5945
                        snprintf(cgrp_list[idx], buff_len, "%s", &stok_buff[1]);
34✔
5946
                } else {
5947
                        /* Retain the leading '/' since we're in the root cgroup */
5948
                        cgrp_list[idx] = strndup(stok_buff, buff_len);
16✔
5949
                }
5950

5951
                idx++;
50✔
5952
                if (idx >= list_len) {
50✔
5953
                        cgroup_warn("Maximum mount elements reached. Consider increasing ");
1✔
5954
                        cgroup_cont("MAX_MNT_ELEMENTS\n");
1✔
5955
                        break;
1✔
5956
                }
5957
        }
5958
        fclose(f);
18✔
5959
        return ret;
18✔
5960
}
5961

5962
/**
5963
 * Get process name from /proc/<pid>/status file.
5964
 * @param pid: The process id
5965
 * @param pname_status : The process name
5966
 * @return 0 on success, > 0 on error.
5967
 */
5968
static int cg_get_procname_from_proc_status(pid_t pid, char **procname_status)
493✔
5969
{
5970
        char path[FILENAME_MAX];
5971
        int ret = ECGFAIL;
493✔
5972
        char buf[4092];
5973
        FILE *f;
5974
        int len;
5975

5976
        sprintf(path, "/proc/%d/status", pid);
493✔
5977
        f = fopen(path, "re");
493✔
5978
        if (!f)
493✔
5979
                return ECGROUPNOTEXIST;
×
5980

5981
        while (fgets(buf, sizeof(buf), f)) {
493✔
5982
                if (!strncmp(buf, "Name:", 5)) {
493✔
5983
                        len = strlen(buf);
493✔
5984
                        if (buf[len - 1] == '\n')
493✔
5985
                                buf[len - 1] = '\0';
493✔
5986
                        *procname_status = strdup(buf + strlen("Name:") + 1);
493✔
5987
                        if (*procname_status == NULL) {
493✔
5988
                                last_errno = errno;
×
5989
                                ret = ECGOTHER;
×
5990
                                break;
×
5991
                        }
5992
                        ret = 0;
493✔
5993
                        break;
493✔
5994
                }
5995
        }
5996
        fclose(f);
493✔
5997
        return ret;
493✔
5998
}
5999

6000
/**
6001
 * Get process name from /proc/<pid>/cmdline file.
6002
 * This function is mainly for getting a script name (shell, perl, etc).
6003
 * A script name is written into the second or later argument of
6004
 * /proc/<pid>/cmdline. This function gets each argument and
6005
 * compares it to a process name taken from /proc/<pid>/status.
6006
 * @param pid: The process id
6007
 * @param pname_status : The process name taken from /proc/<pid>/status
6008
 * @param pname_cmdline: The process name taken from /proc/<pid>/cmdline
6009
 * @return 0 on success, > 0 on error.
6010
 */
6011
static int cg_get_procname_from_proc_cmdline(pid_t pid, const char *pname_status,
30✔
6012
                                             char **pname_cmdline)
6013
{
6014
        char pid_cwd_path[FILENAME_MAX];
6015
        char pid_cmd_path[FILENAME_MAX];
6016
        char buf_pname[FILENAME_MAX];
6017
        char buf_cwd[FILENAME_MAX];
6018
        int ret = ECGFAIL;
30✔
6019
        int len = 0;
30✔
6020
        int c = 0;
30✔
6021
        FILE *f;
6022

6023
        memset(buf_cwd, '\0', sizeof(buf_cwd));
30✔
6024
        sprintf(pid_cwd_path, "/proc/%d/cwd", pid);
30✔
6025

6026
        if (readlink(pid_cwd_path, buf_cwd, sizeof(buf_cwd)) < 0)
30✔
6027
                return ECGROUPNOTEXIST;
×
6028

6029
        /* readlink doesn't append a null */
6030
        buf_cwd[FILENAME_MAX - 1] = '\0';
30✔
6031

6032
        sprintf(pid_cmd_path, "/proc/%d/cmdline", pid);
30✔
6033
        f = fopen(pid_cmd_path, "re");
30✔
6034
        if (!f)
30✔
6035
                return ECGROUPNOTEXIST;
×
6036

6037
        while (c != EOF) {
461✔
6038
                c = fgetc(f);
460✔
6039
                if ((c != EOF) && (c != '\0') && (len < FILENAME_MAX - 1)) {
460✔
6040
                        buf_pname[len] = c;
425✔
6041
                        len++;
425✔
6042
                        continue;
425✔
6043
                }
6044
                buf_pname[len] = '\0';
35✔
6045

6046
                if (len == FILENAME_MAX - 1)
35✔
6047
                        while ((c != EOF) && (c != '\0'))
×
6048
                                c = fgetc(f);
×
6049

6050
                /*
6051
                 * The taken process name from /proc/<pid>/status is
6052
                 * shortened to 15 characters if it is over. So the name
6053
                 * should be compared by its length.
6054
                 */
6055
                if (strncmp(pname_status, basename(buf_pname), TASK_COMM_LEN - 1)) {
35✔
6056
                        len = 0;
6✔
6057
                        continue;
6✔
6058
                }
6059

6060
                if (buf_pname[0] == '/') {
29✔
6061
                        *pname_cmdline = strdup(buf_pname);
17✔
6062
                        if (*pname_cmdline == NULL) {
17✔
6063
                                last_errno = errno;
×
6064
                                ret = ECGOTHER;
×
6065
                                break;
×
6066
                        }
6067
                        ret = 0;
17✔
6068
                        break;
17✔
6069
                }
6070

6071
                strcat(buf_cwd, "/");
12✔
6072
                strcat(buf_cwd, buf_pname);
12✔
6073
                if (!realpath(buf_cwd, pid_cmd_path)) {
12✔
6074
                        last_errno = errno;
11✔
6075
                        ret = ECGOTHER;
11✔
6076
                        break;
11✔
6077
                }
6078

6079
                *pname_cmdline = strdup(pid_cmd_path);
1✔
6080
                if (*pname_cmdline == NULL) {
1✔
6081
                        last_errno = errno;
×
6082
                        ret = ECGOTHER;
×
6083
                        break;
×
6084
                }
6085
                ret = 0;
1✔
6086
                break;
1✔
6087
        }
6088
        fclose(f);
30✔
6089
        return ret;
30✔
6090
}
6091

6092
/**
6093
 * Get a process name from /proc file system.
6094
 * This function allocates memory for a process name, writes a process
6095
 * name onto it. So a caller should free the memory when unusing it.
6096
 * @param pid: The process id
6097
 * @param procname: The process name
6098
 * @return 0 on success, > 0 on error.
6099
 */
6100
int cgroup_get_procname_from_procfs(pid_t pid, char **procname)
493✔
6101
{
6102
        char path[FILENAME_MAX];
6103
        char buf[FILENAME_MAX];
6104
        char *pname_cmdline;
6105
        char *pname_status;
6106
        int ret;
6107

6108
        ret = cg_get_procname_from_proc_status(pid, &pname_status);
493✔
6109
        if (ret)
493✔
6110
                return ret;
×
6111

6112
        /* Get the full patch of process name from /proc/<pid>/exe. */
6113
        memset(buf, '\0', sizeof(buf));
493✔
6114
        snprintf(path, FILENAME_MAX, "/proc/%d/exe", pid);
493✔
6115
        if (readlink(path, buf, sizeof(buf)) < 0) {
493✔
6116
                /*
6117
                 * readlink() fails if a kernel thread, and a process name
6118
                 * is taken from /proc/<pid>/status.
6119
                 */
6120
                *procname = pname_status;
261✔
6121
                return 0;
261✔
6122
        }
6123
        /* readlink doesn't append a null */
6124
        buf[FILENAME_MAX - 1] = '\0';
232✔
6125

6126
        if (!strncmp(pname_status, basename(buf), TASK_COMM_LEN - 1)) {
232✔
6127
                /*
6128
                 * The taken process name from /proc/<pid>/status is
6129
                 * shortened to 15 characters if it is over. So the name
6130
                 * should be compared by its length.
6131
                 */
6132
                free(pname_status);
202✔
6133
                *procname = strdup(buf);
202✔
6134
                if (*procname == NULL) {
202✔
6135
                        last_errno = errno;
×
6136
                        return ECGOTHER;
×
6137
                }
6138
                return 0;
202✔
6139
        }
6140

6141
        /*
6142
         * The above strncmp() is not 0 if a shell script, because
6143
         * /proc/<pid>/exe links a shell command (/bin/bash etc.) and the
6144
         * pname_status represents a shell script name. Then the full path
6145
         * of a shell script is taken from /proc/<pid>/cmdline.
6146
         */
6147
        ret = cg_get_procname_from_proc_cmdline(pid, pname_status, &pname_cmdline);
30✔
6148
        if (!ret) {
30✔
6149
                *procname = pname_cmdline;
18✔
6150
                free(pname_status);
18✔
6151
                return 0;
18✔
6152
        }
6153

6154
        /*
6155
         * The above strncmp() is not 0 also if executing a symbolic link,
6156
         * /proc/pid/exe points to real executable name then. Return it as
6157
         * the last resort.
6158
         */
6159
        free(pname_status);
12✔
6160
        *procname = strdup(buf);
12✔
6161
        if (*procname == NULL) {
12✔
6162
                last_errno = errno;
×
6163
                return ECGOTHER;
×
6164
        }
6165

6166
        return 0;
12✔
6167
}
6168

6169
int cgroup_register_unchanged_process(pid_t pid, int flags)
4✔
6170
{
6171
        char buff[sizeof(CGRULE_SUCCESS_STORE_PID)];
6172
        struct sockaddr_un addr;
6173
        size_t ret_len;
6174
        int ret = 1;
4✔
6175
        int sk;
6176

6177
        sk = socket(PF_UNIX, SOCK_STREAM, 0);
4✔
6178
        if (sk < 0)
4✔
6179
                return 1;
×
6180

6181
        memset(&addr, 0, sizeof(addr));
4✔
6182
        addr.sun_family = AF_UNIX;
4✔
6183
        strcpy(addr.sun_path, CGRULE_CGRED_SOCKET_PATH);
4✔
6184

6185
        if (connect(sk, (struct sockaddr *)&addr,
4✔
6186
            sizeof(addr.sun_family) + strlen(CGRULE_CGRED_SOCKET_PATH)) < 0) {
6187
                /* If the daemon does not work, this function returns 0 as success. */
6188
                ret = 0;
4✔
6189
                goto close;
4✔
6190
        }
6191
        if (write(sk, &pid, sizeof(pid)) < 0)
×
6192
                goto close;
×
6193

6194
        if (write(sk, &flags, sizeof(flags)) < 0)
×
6195
                goto close;
×
6196

6197
        ret_len = read(sk, buff, sizeof(buff));
×
6198
        if (ret_len != sizeof(buff))
×
6199
                goto close;
×
6200

6201
        if (strncmp(buff, CGRULE_SUCCESS_STORE_PID, sizeof(buff)))
×
6202
                goto close;
×
6203

6204
        ret = 0;
×
6205
close:
4✔
6206
        close(sk);
4✔
6207

6208
        return ret;
4✔
6209
}
6210

6211
int cgroup_get_subsys_mount_point(const char *controller, char **mount_point)
×
6212
{
6213
        int ret = ECGROUPNOTEXIST;
×
6214
        int i;
6215

6216
        if (!cgroup_initialized)
×
6217
                return ECGROUPNOTINITIALIZED;
×
6218

6219
        if (!controller)
×
6220
                return ECGINVAL;
×
6221

6222
        pthread_rwlock_rdlock(&cg_mount_table_lock);
×
6223
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
×
6224
                if (strncmp(cg_mount_table[i].name, controller, FILENAME_MAX))
×
6225
                        continue;
×
6226

6227
                *mount_point = strdup(cg_mount_table[i].mount.path);
×
6228

6229
                if (!*mount_point) {
×
6230
                        last_errno = errno;
×
6231
                        ret = ECGOTHER;
×
6232
                        goto out_exit;
×
6233
                }
6234

6235
                ret = 0;
×
6236
                break;
×
6237
        }
6238
out_exit:
×
6239
        pthread_rwlock_unlock(&cg_mount_table_lock);
×
6240

6241
        return ret;
×
6242
}
6243

6244
int cgroup_get_all_controller_end(void **handle)
180✔
6245
{
6246
        FILE *proc_cgrp = (FILE *) *handle;
180✔
6247

6248
        if (!proc_cgrp)
180✔
6249
                return ECGINVAL;
×
6250

6251
        fclose(proc_cgrp);
180✔
6252
        *handle = NULL;
180✔
6253

6254
        return 0;
180✔
6255
}
6256

6257
int cgroup_get_all_controller_next(void **handle, struct controller_data *info)
2,623✔
6258
{
6259
        FILE *proc_cgrp = (FILE *) *handle;
2,623✔
6260
        int hierarchy, num_cgrps, enabled;
6261
        char subsys_name[FILENAME_MAX];
6262
        int err = 0;
2,623✔
6263

6264
        if (!proc_cgrp)
2,623✔
6265
                return ECGINVAL;
×
6266

6267
        if (!info)
2,623✔
6268
                return ECGINVAL;
×
6269

6270
        /*
6271
         * check Linux Kernel sources/kernel/cgroup/cgroup.c cgroup_init_early(),
6272
         * MAX_CGROUP_TYPE_NAMELEN check for details on why 32 is used.
6273
         */
6274
        err = fscanf(proc_cgrp, "%32s %d %d %d\n", subsys_name, &hierarchy, &num_cgrps,
2,623✔
6275
                     &enabled);
6276

6277
        if (err != 4)
2,623✔
6278
                return ECGEOF;
180✔
6279

6280
        strncpy(info->name, subsys_name, FILENAME_MAX);
2,443✔
6281
        info->name[FILENAME_MAX-1] = '\0';
2,443✔
6282
        info->hierarchy = hierarchy;
2,443✔
6283
        info->num_cgroups = num_cgrps;
2,443✔
6284
        info->enabled = enabled;
2,443✔
6285

6286
        return 0;
2,443✔
6287
}
6288

6289
int cgroup_get_all_controller_begin(void **handle, struct controller_data *info)
180✔
6290
{
6291
        FILE *proc_cgroup = NULL;
180✔
6292
        char buf[FILENAME_MAX];
6293
        int ret;
6294

6295
        if (!info)
180✔
6296
                return ECGINVAL;
×
6297

6298
        proc_cgroup = fopen("/proc/cgroups", "re");
180✔
6299
        if (!proc_cgroup) {
180✔
6300
                last_errno = errno;
×
6301
                return ECGOTHER;
×
6302
        }
6303

6304
        if (!fgets(buf, FILENAME_MAX, proc_cgroup)) {
180✔
6305
                last_errno = errno;
×
6306
                fclose(proc_cgroup);
×
6307
                *handle = NULL;
×
6308
                return ECGOTHER;
×
6309
        }
6310
        *handle = proc_cgroup;
180✔
6311

6312
        ret = cgroup_get_all_controller_next(handle, info);
180✔
6313
        if (ret != 0) {
180✔
6314
                fclose(proc_cgroup);
×
6315
                *handle = NULL;
×
6316
        }
6317

6318
        return ret;
180✔
6319
}
6320

6321
static int pid_compare(const void *a, const void *b)
84✔
6322
{
6323
        const pid_t *pid1, *pid2;
6324

6325
        pid1 = (pid_t *) a;
84✔
6326
        pid2 = (pid_t *) b;
84✔
6327

6328
        return (*pid1 - *pid2);
84✔
6329
}
6330

6331
/* pids needs to be completely uninitialized so that we can set it up
6332
 *
6333
 * Caller must free up pids.
6334
 */
6335
static int read_pids(char *path, pid_t **pids, int *size)
10✔
6336
{
6337
        int tot_pids = 16;
10✔
6338
        pid_t *tmp_list;
6339
        FILE *pid_file;
6340
        int n = 0;
10✔
6341
        int err;
6342

6343
        pid_file = fopen(path, "r");
10✔
6344
        if (!pid_file) {
10✔
6345
                last_errno = errno;
×
6346
                *pids = NULL;
×
6347
                *size = 0;
×
6348
                if (errno == ENOENT)
×
6349
                        return ECGROUPUNSUPP;
×
6350
                else
6351
                        return ECGOTHER;
×
6352
        }
6353

6354
        /* Keep doubling the memory allocated if needed */
6355
        tmp_list = malloc(sizeof(pid_t) * tot_pids);
10✔
6356
        if (!tmp_list) {
10✔
6357
                last_errno = errno;
×
6358
                fclose(pid_file);
×
6359
                return ECGOTHER;
×
6360
        }
6361

6362
        while (!feof(pid_file)) {
22✔
6363
                while (!feof(pid_file) && n < tot_pids) {
62✔
6364
                        pid_t pid;
6365

6366
                        err = fscanf(pid_file, "%u", &pid);
60✔
6367
                        if (err == EOF)
60✔
6368
                                break;
10✔
6369
                        tmp_list[n] = pid;
50✔
6370
                        n++;
50✔
6371
                }
6372
                if (!feof(pid_file)) {
12✔
6373
                        pid_t *orig_list = tmp_list;
2✔
6374

6375
                        tot_pids *= 2;
2✔
6376
                        tmp_list = realloc(tmp_list, sizeof(pid_t) * tot_pids);
2✔
6377
                        if (!tmp_list) {
2✔
6378
                                last_errno = errno;
×
6379
                                fclose(pid_file);
×
6380
                                free(orig_list);
×
6381
                                *pids = NULL;
×
6382
                                *size = 0;
×
6383
                                return ECGOTHER;
×
6384
                        }
6385
                }
6386
        }
6387
        fclose(pid_file);
10✔
6388

6389
        *size = n;
10✔
6390
        qsort(tmp_list, n, sizeof(pid_t), &pid_compare);
10✔
6391
        *pids = tmp_list;
10✔
6392

6393
        return 0;
10✔
6394
}
6395

6396
int cgroup_get_procs(const char *name, const char *controller, pid_t **pids, int *size)
10✔
6397
{
6398
        char cgroup_path[FILENAME_MAX];
6399

6400
        cg_build_path(name, cgroup_path, controller);
10✔
6401
        strncat(cgroup_path, "/cgroup.procs", FILENAME_MAX - strlen(cgroup_path) - 1);
10✔
6402

6403
        return read_pids(cgroup_path, pids, size);
10✔
6404
}
6405

6406
int cgroup_get_threads(const char *name, const char *controller, pid_t **pids, int *size)
×
6407
{
6408
        char cgroup_path[FILENAME_MAX];
6409

6410
        cg_build_path(name, cgroup_path, controller);
×
6411
        strncat(cgroup_path, "/cgroup.threads", FILENAME_MAX - strlen(cgroup_path) - 1);
×
6412

6413
        return read_pids(cgroup_path, pids, size);
×
6414
}
6415

6416
int cgroup_dictionary_create(struct cgroup_dictionary **dict,
12✔
6417
                             int flags)
6418
{
6419
        if (!dict)
12✔
6420
                return ECGINVAL;
×
6421

6422
        *dict = (struct cgroup_dictionary *) calloc(1, sizeof(struct cgroup_dictionary));
12✔
6423
        if (!*dict) {
12✔
6424
                last_errno = errno;
×
6425
                return ECGOTHER;
×
6426
        }
6427
        (*dict)->flags = flags;
12✔
6428

6429
        return 0;
12✔
6430
}
6431

6432
int cgroup_dictionary_add(struct cgroup_dictionary *dict, const char *name, const char *value)
24✔
6433
{
6434
        struct cgroup_dictionary_item *it;
6435

6436
        if (!dict)
24✔
6437
                return ECGINVAL;
×
6438

6439
        it = (struct cgroup_dictionary_item *) malloc(sizeof(struct cgroup_dictionary_item));
24✔
6440
        if (!it) {
24✔
6441
                last_errno = errno;
×
6442
                return ECGOTHER;
×
6443
        }
6444

6445
        it->next = NULL;
24✔
6446
        it->name = name;
24✔
6447
        it->value = value;
24✔
6448

6449
        if (dict->tail) {
24✔
6450
                dict->tail->next = it;
12✔
6451
                dict->tail = it;
12✔
6452
        } else {
6453
                /* It is the first item */
6454
                dict->tail = it;
12✔
6455
                dict->head = it;
12✔
6456
        }
6457

6458
        return 0;
24✔
6459
}
6460

6461
int cgroup_dictionary_free(struct cgroup_dictionary *dict)
16✔
6462
{
6463
        struct cgroup_dictionary_item *it;
6464

6465
        if (!dict)
16✔
6466
                return ECGINVAL;
4✔
6467

6468
        it = dict->head;
12✔
6469
        while (it) {
36✔
6470
                struct cgroup_dictionary_item *del = it;
24✔
6471

6472
                it = it->next;
24✔
6473
                if (!(dict->flags & CG_DICT_DONT_FREE_ITEMS)) {
24✔
6474
                        free((void *)del->value);
24✔
6475
                        free((void *)del->name);
24✔
6476
                }
6477
                free(del);
24✔
6478
        }
6479
        free(dict);
12✔
6480

6481
        return 0;
12✔
6482
}
6483

6484
int cgroup_dictionary_iterator_begin(struct cgroup_dictionary *dict, void **handle,
12✔
6485
                                     const char **name, const char **value)
6486
{
6487
        struct cgroup_dictionary_iterator *iter;
6488

6489
        *handle = NULL;
12✔
6490

6491
        if (!dict)
12✔
6492
                return ECGINVAL;
×
6493

6494
        iter = (struct cgroup_dictionary_iterator *)
6495
                malloc(sizeof(struct cgroup_dictionary_iterator));
12✔
6496
        if (!iter) {
12✔
6497
                last_errno = errno;
×
6498
                return ECGOTHER;
×
6499
        }
6500

6501
        iter->item = dict->head;
12✔
6502
        *handle = iter;
12✔
6503

6504
        return cgroup_dictionary_iterator_next(handle, name, value);
12✔
6505
}
6506

6507
int cgroup_dictionary_iterator_next(void **handle, const char **name, const char **value)
36✔
6508
{
6509
        struct cgroup_dictionary_iterator *iter;
6510

6511
        if (!handle)
36✔
6512
                return ECGINVAL;
×
6513

6514
        iter = *handle;
36✔
6515

6516
        if (!iter)
36✔
6517
                return ECGINVAL;
×
6518

6519
        if (!iter->item)
36✔
6520
                return ECGEOF;
12✔
6521

6522
        *name = iter->item->name;
24✔
6523
        *value = iter->item->value;
24✔
6524
        iter->item = iter->item->next;
24✔
6525

6526
        return 0;
24✔
6527
}
6528

6529
void cgroup_dictionary_iterator_end(void **handle)
12✔
6530
{
6531
        if (!handle)
12✔
6532
                return;
×
6533

6534
        free(*handle);
12✔
6535
        *handle = NULL;
12✔
6536
}
6537

6538
int cgroup_get_subsys_mount_point_begin(const char *controller, void **handle, char *path)
×
6539
{
6540
        int i;
6541

6542
        if (!cgroup_initialized)
×
6543
                return ECGROUPNOTINITIALIZED;
×
6544
        if (!handle || !path || !controller)
×
6545
                return ECGINVAL;
×
6546

6547
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++)
×
6548
                if (strcmp(controller, cg_mount_table[i].name) == 0)
×
6549
                        break;
×
6550

6551
        if (cg_mount_table[i].name[0] == '\0') {
×
6552
                /* The controller is not mounted at all */
6553
                *handle = NULL;
×
6554
                *path = '\0';
×
6555
                return ECGEOF;
×
6556
        }
6557

6558
        /*
6559
         * 'handle' is pointer to struct cg_mount_point, which should be
6560
         * returned next.
6561
         */
6562
        *handle = cg_mount_table[i].mount.next;
×
6563
        strcpy(path, cg_mount_table[i].mount.path);
×
6564

6565
        return 0;
×
6566
}
6567

6568
int cgroup_get_subsys_mount_point_next(void **handle, char *path)
×
6569
{
6570
        struct cg_mount_point *it;
6571

6572
        if (!cgroup_initialized)
×
6573
                return ECGROUPNOTINITIALIZED;
×
6574

6575
        if (!handle || !path)
×
6576
                return ECGINVAL;
×
6577

6578
        it = *handle;
×
6579
        if (!it) {
×
6580
                *handle = NULL;
×
6581
                *path = '\0';
×
6582
                return ECGEOF;
×
6583
        }
6584

6585
        *handle = it->next;
×
6586
        strcpy(path, it->path);
×
6587

6588
        return 0;
×
6589
}
6590

6591
int cgroup_get_subsys_mount_point_end(void **handle)
×
6592
{
6593
        if (!cgroup_initialized)
×
6594
                return ECGROUPNOTINITIALIZED;
×
6595

6596
        if (!handle)
×
6597
                return ECGINVAL;
×
6598

6599
        *handle = NULL;
×
6600

6601
        return 0;
×
6602
}
6603

6604
int cgroup_get_controller_version(const char * const controller, enum cg_version_t * const version)
3,250✔
6605
{
6606
        int i;
6607

6608
        if (!version)
3,250✔
6609
                return ECGINVAL;
×
6610

6611
        if (!controller && strlen(cg_cgroup_v2_mount_path) > 0) {
3,250✔
6612
                *version = CGROUP_V2;
56✔
6613
                return 0;
56✔
6614
        }
6615

6616
        if (!controller)
3,194✔
6617
                return ECGINVAL;
×
6618

6619
        *version = CGROUP_UNK;
3,194✔
6620

6621
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
9,221✔
6622
                if (strncmp(cg_mount_table[i].name, controller,
9,218✔
6623
                            sizeof(cg_mount_table[i].name)) == 0) {
6624
                        *version = cg_mount_table[i].version;
3,191✔
6625
                        return 0;
3,191✔
6626
                }
6627
        }
6628

6629
        return ECGROUPNOTEXIST;
3✔
6630
}
6631

6632
static int search_and_append_mnt_path(struct cg_mount_point **mount_point, char *path)
19✔
6633
{
6634
        struct cg_mount_point *mnt_point, *mnt_tmp, *mnt_prev;
6635

6636
        mnt_tmp = *mount_point;
19✔
6637
        while (mnt_tmp) {
19✔
6638
                if (strcmp(mnt_tmp->path, path) == 0)
17✔
6639
                        return ECGVALUEEXISTS;
17✔
6640

6641
                mnt_prev = mnt_tmp;
×
6642
                mnt_tmp = mnt_tmp->next;
×
6643
        }
6644

6645
        mnt_point = malloc(sizeof(struct cg_mount_point));
2✔
6646
        if (mnt_point == NULL) {
2✔
6647
                last_errno = errno;
×
6648
                return ECGOTHER;
×
6649
        }
6650

6651
        strncpy(mnt_point->path, path, FILENAME_MAX - 1);
2✔
6652
        mnt_point->path[FILENAME_MAX - 1] = '\0';
2✔
6653

6654
        mnt_point->next = NULL;
2✔
6655

6656
        if (*mount_point == NULL)
2✔
6657
                *mount_point = mnt_point;
2✔
6658
        else
6659
                mnt_prev->next = mnt_point;
×
6660

6661
        return 0;
2✔
6662
}
6663

6664
/**
6665
 * List the mount paths, that matches the specified version
6666
 *
6667
 *        @param cgrp_version The cgroup type/version
6668
 *        @param mount_paths Holds the list of mount paths
6669
 *        @return 0 success and list of mounts paths in mount_paths
6670
 *                ECGOTHER on failure and mount_paths is NULL.
6671
 */
6672
int cgroup_list_mount_points(const enum cg_version_t cgrp_version, char ***mount_paths)
4✔
6673
{
6674
        struct cg_mount_point *mount_point, *mnt_tmp = NULL;
4✔
6675
        char **mnt_paths = NULL;
4✔
6676
        int i, idx = 0;
4✔
6677
        int ret;
6678

6679
        if (!cgroup_initialized)
4✔
6680
                return ECGROUPNOTINITIALIZED;
×
6681

6682
        if (cgrp_version != CGROUP_V1 && cgrp_version != CGROUP_V2)
4✔
6683
                return ECGINVAL;
×
6684

6685
        pthread_rwlock_rdlock(&cg_mount_table_lock);
4✔
6686

6687
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
42✔
6688
                if (cg_mount_table[i].version != cgrp_version)
38✔
6689
                        continue;
19✔
6690

6691
                mount_point = &cg_mount_table[i].mount;
19✔
6692
                while (mount_point) {
38✔
6693
                        ret = search_and_append_mnt_path(&mnt_tmp, mount_point->path);
19✔
6694
                        if (ret != 0 && ret != ECGVALUEEXISTS)
19✔
6695
                                goto err;
×
6696

6697
                        /* Avoid adding duplicate mount points */
6698
                        if (ret != ECGVALUEEXISTS)
19✔
6699
                                idx++;
2✔
6700

6701
                        mount_point = mount_point->next;
19✔
6702
                }
6703
        }
6704

6705
        /*
6706
         * Cgroup v2 can be mounted without any controller and these mount
6707
         * paths are not part of the cg_mount_table.  Check and append
6708
         * them to mnt_paths.
6709
         */
6710
        if (cgrp_version == CGROUP_V2 && cg_cgroup_v2_empty_mount_paths) {
4✔
6711
                mount_point = cg_cgroup_v2_empty_mount_paths;
×
6712
                while (mount_point) {
×
6713
                        ret = search_and_append_mnt_path(&mnt_tmp, mount_point->path);
×
6714
                        if (ret)
×
6715
                                goto err;
×
6716

6717
                        idx++;
×
6718
                        mount_point = mount_point->next;
×
6719
                }
6720
        }
6721

6722
        mnt_paths = malloc(sizeof(char *) * (idx + 1));
4✔
6723
        if (mnt_paths == NULL) {
4✔
6724
                last_errno = errno;
×
6725
                ret = ECGOTHER;
×
6726
                goto err;
×
6727
        }
6728

6729
        for (i = 0, mount_point = mnt_tmp;
4✔
6730
             mount_point;
6✔
6731
             mount_point = mount_point->next, i++) {
2✔
6732

6733
                mnt_paths[i] = strdup(mount_point->path);
2✔
6734
                if (mnt_paths[i] == NULL) {
2✔
6735
                        last_errno = errno;
×
6736
                        ret = ECGOTHER;
×
6737
                        goto err;
×
6738
                }
6739
        }
6740
        mnt_paths[i] = NULL;
4✔
6741

6742
        ret = 0;
4✔
6743
        *mount_paths = mnt_paths;
4✔
6744

6745
err:
4✔
6746
        pthread_rwlock_unlock(&cg_mount_table_lock);
4✔
6747

6748
        while (mnt_tmp) {
6✔
6749
                mount_point = mnt_tmp;
2✔
6750
                mnt_tmp = mnt_tmp->next;
2✔
6751
                free(mount_point);
2✔
6752
        }
6753

6754
        if (ret != 0 && mnt_paths) {
4✔
6755
                for (i = 0; i < idx; i++)
×
6756
                        free(mnt_paths[i]);
×
6757
                free(mnt_paths);
×
6758
                *mount_paths = NULL;
×
6759
        }
6760

6761
        return ret;
4✔
6762
}
6763

6764
const struct cgroup_library_version *cgroup_version(void)
4✔
6765
{
6766
        return &library_version;
4✔
6767
}
6768

6769
/**
6770
 * Finds the current cgroup setup mode (legacy/unified/hybrid).
6771
 * Returns unknown of failure and setup mode on success.
6772
 */
6773
enum cg_setup_mode_t cgroup_setup_mode(void)
194✔
6774
{
6775
#define CGROUP2_SUPER_MAGIC        0x63677270
6776
#define CGROUP_SUPER_MAGIC        0x27E0EB
6777

6778
        unsigned int cg_setup_mode_bitmask = 0U;
194✔
6779
        enum cg_setup_mode_t setup_mode;
6780
        struct statfs cgrp_buf;
6781
        int i, ret = 0;
194✔
6782

6783
        if (!cgroup_initialized)
194✔
6784
                return ECGROUPNOTINITIALIZED;
×
6785

6786
        setup_mode = CGROUP_MODE_UNK;
194✔
6787

6788
        pthread_rwlock_wrlock(&cg_mount_table_lock);
194✔
6789
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
2,103✔
6790
                ret = statfs(cg_mount_table[i].mount.path, &cgrp_buf);
1,909✔
6791
                if (ret) {
1,909✔
6792
                        setup_mode = CGROUP_MODE_UNK;
×
6793
                        cgroup_err("Failed to get stats of '%s'\n", cg_mount_table[i].mount.path);
×
6794
                        goto out;
×
6795
                }
6796

6797
                if (cgrp_buf.f_type == CGROUP2_SUPER_MAGIC)
1,909✔
6798
                        cg_setup_mode_bitmask |= (1U << 0);
1,909✔
6799
                else if (cgrp_buf.f_type == CGROUP_SUPER_MAGIC)
×
6800
                        cg_setup_mode_bitmask |= (1U << 1);
×
6801
        }
6802

6803
        if (cg_cgroup_v2_empty_mount_paths)
194✔
6804
                cg_setup_mode_bitmask |= (1U << 0);
×
6805

6806
        if (cg_setup_mode_bitmask & (1U << 0) && cg_setup_mode_bitmask & (1U << 1))
194✔
6807
                setup_mode = CGROUP_MODE_HYBRID;
×
6808
        else if (cg_setup_mode_bitmask & (1U << 0))
194✔
6809
                setup_mode = CGROUP_MODE_UNIFIED;
194✔
6810
        else if (cg_setup_mode_bitmask & (1U << 1))
×
6811
                setup_mode = CGROUP_MODE_LEGACY;
×
6812

6813
out:
×
6814
        pthread_rwlock_unlock(&cg_mount_table_lock);
194✔
6815
        return setup_mode;
194✔
6816
}
6817

6818
int cgroup_get_controller_count(struct cgroup *cgrp)
25✔
6819
{
6820
        if (!cgrp)
25✔
6821
                return -1;
×
6822

6823
        return cgrp->index;
25✔
6824
}
6825

6826
struct cgroup_controller *cgroup_get_controller_by_index(struct cgroup *cgrp, int index)
83✔
6827
{
6828
        if (!cgrp)
83✔
6829
                return NULL;
×
6830

6831
        if (index >= cgrp->index)
83✔
6832
                return NULL;
×
6833

6834
        return cgrp->controller[index];
83✔
6835
}
6836

6837
char *cgroup_get_controller_name(struct cgroup_controller *controller)
83✔
6838
{
6839
        if (!controller)
83✔
6840
                return NULL;
×
6841

6842
        return controller->name;
83✔
6843
}
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