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

drakenclimber / libcgroup / 16273229335

09 Jul 2025 08:07PM UTC coverage: 56.217% (+2.2%) from 54.02%
16273229335

push

github

drakenclimber
ftests: Fix false positive in 013 testcase

Fix the false positive in the testcase number 013, the return value
should be by default TEST_FAILED, instead of TEST_PASSED. The flow of
the testcase is designed to set the return value to TEST_PASSED only
if the current kernel's cpu controller settings matches any of the
expected cpu controller output list.

Fixes: 6bea4df6d283 ("ftests/013: Refactor code to match outputs with same line")
Signed-off-by: Kamalesh Babulal <kamalesh.babulal@oracle.com>
Signed-off-by: Tom Hromatka <tom.hromatka@oracle.com>

5597 of 9956 relevant lines covered (56.22%)

584.87 hits per line

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

61.83
/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,330✔
144
{
145
        if (owner == NO_UID_GID)
14,330✔
146
                owner = getuid();
13,566✔
147
        if (group == NO_UID_GID)
14,330✔
148
                group = getgid();
13,566✔
149

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

157
        cgroup_dbg("chown: seeing file %s\n", filename);
14,329✔
158
        switch (ent->fts_info) {
14,329✔
159
        case FTS_ERR:
×
160
                errno = ent->fts_errno;
×
161
                break;
×
162
        case FTS_D:
14,329✔
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,329✔
171
                break;
14,329✔
172
        }
173
        if (ret < 0) {
14,329✔
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,329✔
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,329✔
196
                FTSENT *ent;
197

198
                ent = fts_read(fts);
14,566✔
199
                if (!ent) {
14,566✔
200
                        cgroup_warn("fts_read failed\n");
237✔
201
                        break;
237✔
202
                }
203
                ret = cg_chown_file(fts, ent, owner, group);
14,329✔
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)
442✔
211
{
212
        mode_t mask = -1U;
442✔
213
        struct stat buf;
214
        int fd;
215

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

220
        if (owner_is_umask) {
442✔
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)
442✔
228
                        goto fail;
×
229

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

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

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

241
        close(fd);
442✔
242

243
        return 0;
442✔
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,329✔
256
                  int filem_change, int owner_is_umask)
257
{
258
        const char *filename = fts->fts_path;
14,329✔
259
        int ret = 0;
14,329✔
260

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

263
        switch (ent->fts_info) {
14,329✔
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,855✔
275
        case FTS_NSOK:
276
        case FTS_NS:
277
        case FTS_DEFAULT:
278
                if (filem_change)
13,855✔
279
                        ret = cg_chmod_path(filename, file_mode, owner_is_umask);
429✔
280
                break;
13,855✔
281
        }
282

283
        return ret;
14,329✔
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,329✔
312
                FTSENT *ent;
313

314
                ent = fts_read(fts);
14,566✔
315
                if (!ent) {
14,566✔
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,329✔
325
                if (ignore_list != NULL)
14,329✔
326
                        for (i = 0; ignore_list[i] != NULL; i++)
28,658✔
327
                                if (!strcmp(ignore_list[i], ent->fts_name)) {
14,329✔
328
                                        ignored = 1;
×
329
                                        break;
×
330
                                }
331
                if (ignored)
14,329✔
332
                        continue;
×
333

334
                ret = cg_chmod_file(fts, ent, dir_mode, dirm_change, file_mode, filem_change,
14,329✔
335
                                    owner_is_umask);
336
                if (ret) {
14,329✔
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)
531✔
390
{
391
        char *tmp_string;
392
        char *base;
393

394
        tmp_string = strdup(path);
531✔
395

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

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

401
        free(tmp_string);
531✔
402

403
        return base;
531✔
404
}
405

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

410
        pthread_rwlock_rdlock(&cg_mount_table_lock);
880✔
411

412
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
2,256✔
413
                if (strncmp(cg_mount_table[i].name, name, sizeof(cg_mount_table[i].name)) == 0) {
2,256✔
414
                        pthread_rwlock_unlock(&cg_mount_table_lock);
809✔
415
                        return 1;
809✔
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)
13,421✔
1161
{
1162
        int i, shared_mnt = 0;
13,421✔
1163

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

1173
        return shared_mnt;
13,421✔
1174
}
1175

1176
static void cgroup_cg_mount_table_append(const char *name, const char *mount_path,
13,413✔
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;
13,413✔
1181

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

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

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

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

1194
        (*mnt_tbl_idx)++;
13,413✔
1195
}
13,413✔
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)
367✔
1206
{
1207
        char *strtok_buffer = NULL, *mntopt = NULL;
367✔
1208
        int shared_mnt, duplicate;
1209
        int i, j, ret = 0;
367✔
1210
        char c = 0;
367✔
1211

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

1215
                if (!mntopt)
5,104✔
1216
                        continue;
5,102✔
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");
367✔
1256

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

1262
#ifdef OPAQUE_HIERARCHY
1263
                /* Ignore the opaque hierarchy. */
1264
                if (strcmp(mntopt, OPAQUE_HIERARCHY) == 0)
365✔
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);
364✔
1269

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

1279
                if (duplicate) {
364✔
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,
364✔
1287
                                             ent->mnt_opts, shared_mnt);
364✔
1288
        }
1289

1290
out:
2✔
1291
        return ret;
367✔
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,452✔
1302
{
1303
        char *ret_c = NULL, line[CGV2_CONTROLLERS_LL_MAX], *stok_buff = NULL;
1,452✔
1304
        char *controller = NULL, *controllers = NULL;
1,452✔
1305
        char cgrp_controllers_path[FILENAME_MAX];
1306
        int ret = 0, i, duplicate, shared_mnt;
1,452✔
1307
        FILE *fp = NULL;
1,452✔
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,452✔
1314
        cg_cgroup_v2_mount_path[FILENAME_MAX-1] = '\0';
1,452✔
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,452✔
1318
        fp = fopen(cgrp_controllers_path, "re");
1,452✔
1319
        if (!fp) {
1,452✔
1320
                ret = ECGOTHER;
×
1321
                goto out;
×
1322
        }
1323

1324
        ret_c = fgets(line, CGV2_CONTROLLERS_LL_MAX, fp);
1,452✔
1325
        if (ret_c == NULL) {
1,452✔
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,451✔
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,451✔
1363
        if (!controllers) {
1,451✔
1364
                ret = ECGOTHER;
×
1365
                goto out;
×
1366
        }
1367

1368
        sprintf(controllers, "%s %s", ret_c, CGRP_FILE_PREFIX);
1,451✔
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,451✔
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,055✔
1379

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

1389
                if (duplicate) {
13,055✔
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,048✔
1402
                                             controller, shared_mnt);
1403

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

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

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

1415
        return ret;
1,452✔
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++) {
5,970✔
1428
                mount = cg_mount_table[i].mount.next;
4,521✔
1429

1430
                while (mount) {
4,521✔
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,735✔
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,735✔
1562
                             &enabled);
1563
                if (err < 0)
21,735✔
1564
                        break;
1,449✔
1565

1566
                controllers[i] = strdup(subsys_name);
20,286✔
1567
                if (controllers[i] == NULL) {
20,286✔
1568
                        last_errno = errno;
×
1569
                        ret = ECGOTHER;
×
1570
                        break;
×
1571
                }
1572
                i++;
20,286✔
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,
51,832✔
1621
                                  sizeof(mntent_buffer))) != NULL) {
51,832✔
1622

1623
                if (strcmp(ent->mnt_type, "cgroup") == 0) {
50,387✔
1624
                        if (controllers[0] == NULL) {
364✔
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);
364✔
1632
                        if (ret)
364✔
1633
                                goto err;
×
1634

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

1638
                        continue;
360✔
1639
                }
1640

1641
                if (strcmp(ent->mnt_type, "cgroup2") == 0) {
50,023✔
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,735✔
1713
                free(controllers[i]);
20,286✔
1714
                controllers[i] = NULL;
20,286✔
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 &&
12,915✔
1748
               strcmp(ent->mnt_type, "cgroup2") != 0) {
12,915✔
1749
                ent = getmntent_r(proc_mount, temp_ent, mntent_buff, sizeof(mntent_buff));
11,925✔
1750
                if (ent == NULL) {
11,925✔
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)
28,219✔
1768
{
1769
        if ((suf[strlen(suf)-1] == '/') || ((strlen(suf) == 0) && (pref[strlen(pref)-1] == '/')))
28,219✔
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));
28,101✔
1773

1774
        path[FILENAME_MAX-1] = '\0';
28,219✔
1775

1776
        return path;
28,219✔
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)
28,898✔
1782
{
1783
        char *tmp_systemd_default_cgrp, *_path = NULL;
28,898✔
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;
28,898✔
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));
28,898✔
1797
        if (!tmp_systemd_default_cgrp) {
28,898✔
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')
15,498✔
1809
                tmp_systemd_default_cgrp[0] = '\0';
1810
        else
1811
                snprintf(tmp_systemd_default_cgrp, len, "%s/", systemd_default_cgroup);
15,498✔
1812

1813
        /* allocate more space for systemd_default_cgroup + '/' */
1814
        len += (FILENAME_MAX + 1);
15,498✔
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);
28,898✔
1823
        if (!_path) {
28,898✔
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) {
28,898✔
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++) {
95,198✔
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) ||
95,196✔
1866
                    (type && strcmp(type, CGRP_FILE_PREFIX) == 0 &&
66,789✔
1867
                     cg_mount_table[i].version == CGROUP_V2)) {
364✔
1868

1869
                        if (cg_namespace_table[i])
28,771✔
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,
28,767✔
1874
                                               tmp_systemd_default_cgrp);
1875

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

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

1882
                        if (name) {
28,771✔
1883
                                char *tmp;
1884

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

1889
                                cg_concat_path(tmp, name, path);
28,094✔
1890
                                free(tmp);
28,094✔
1891
                        }
1892
                        goto out;
28,771✔
1893
                }
1894
        }
1895
        path = NULL;
2✔
1896

1897
out:
28,898✔
1898
        if (_path)
28,898✔
1899
                free(_path);
28,898✔
1900

1901
        if (tmp_systemd_default_cgrp)
28,898✔
1902
                free(tmp_systemd_default_cgrp);
28,898✔
1903

1904
        return path;
28,898✔
1905
}
1906

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

1913
        return path;
1,894✔
1914
}
1915

1916
static int cgroup_get_cg_type(const char * const path, char * const type,
584✔
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;
584✔
1922
        FILE *fp = NULL;
584✔
1923

1924
        snprintf(cg_type_path, FILENAME_MAX, "%scgroup.type", path);
584✔
1925
        fp = fopen(cg_type_path, "re");
584✔
1926
        if (!fp) {
584✔
1927
                if (errno == ENOENT) {
128✔
1928
                        /* file cgroup.type, doesn't exist for root cgroup. */
1929
                        snprintf(type, type_sz, "cgroup.procs");
128✔
1930
                        goto out;
128✔
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) {
456✔
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;
456✔
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         ||
456✔
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");
452✔
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:
584✔
1971
        if (fp)
584✔
1972
                fclose(fp);
456✔
1973

1974
        return err;
584✔
1975
}
1976

1977
int cgroup_build_tasks_procs_path(char * const path, size_t path_sz, const char * const cg_name,
588✔
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;
588✔
1983

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

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

1991
        switch (version) {
587✔
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:
584✔
1997
                err = cgroup_get_cg_type(path, cg_type, sizeof(cg_type), 0);
584✔
1998
                if (err)
584✔
1999
                        goto error;
×
2000

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

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

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

2014
        return err;
588✔
2015
}
2016

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

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

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

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

2036
        if (strncmp(cg_name, "/", strlen(cg_name)) == 0)
96✔
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))
95✔
2044
                goto err;
×
2045

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

2052
        dname = dirname(parent);
95✔
2053

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

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

2064
        return error;
95✔
2065
}
2066

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

2072
        tasks = fopen(path, "we");
115✔
2073
        if (!tasks) {
115✔
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);
113✔
2087
        if (ret < 0) {
113✔
2088
                last_errno = errno;
×
2089
                ret = ECGOTHER;
×
2090
                goto err;
×
2091
        }
2092
        ret = fflush(tasks);
113✔
2093
        if (ret) {
113✔
2094
                last_errno = errno;
2✔
2095
                ret = ECGOTHER;
2✔
2096
                goto err;
2✔
2097
        }
2098
        fclose(tasks);
111✔
2099
        return 0;
111✔
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)
79✔
2151
{
2152
        char path[FILENAME_MAX] = {0};
79✔
2153
        char *controller_name = NULL;
79✔
2154
        int empty_cgrp = 0;
79✔
2155
        int i, ret = 0;
79✔
2156

2157
        if (!cgroup_initialized) {
79✔
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) {
79✔
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++) {
20✔
2166
                        ret = cgroup_build_tasks_procs_path(path, sizeof(path), NULL,
18✔
2167
                                                            cg_mount_table[i].name);
18✔
2168
                        if (ret)
18✔
2169
                                return ret;
×
2170

2171
                        if (move_tids) {
18✔
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);
18✔
2178
                        if (ret) {
18✔
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++) {
168✔
2186
                        if (!cgroup_test_subsys_mounted(cgrp->controller[i]->name)) {
91✔
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)
77✔
2194
                        /* Valid empty cgroup v2 with no controllers added. */
2195
                        empty_cgrp = 1;
6✔
2196

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

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

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

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

2213
                        if (move_tids) {
97✔
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);
97✔
2220
                        if (ret)
97✔
2221
                                return ret;
4✔
2222
                }
2223
        }
2224
        return 0;
75✔
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)
77✔
2237
{
2238
        return cgroup_attach_task_tid(cgroup, tid, 0);
77✔
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
                        path_dir_end = '\0';
×
2385

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2544
        return error;
615✔
2545
}
2546

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

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

2568
        *enabled = false;
1,184✔
2569

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

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

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

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

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

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

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

2622
out:
305✔
2623
        if (path_copy)
1,184✔
2624
                free(path_copy);
1,184✔
2625
        if (fp)
1,184✔
2626
                fclose(fp);
1,184✔
2627

2628
        return error;
1,184✔
2629
}
2630

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2730
        return 0;
10✔
2731
}
2732

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2875
                dst_val = dst->values[i];
658✔
2876
                strncpy(dst_val->value, src_val->value, CG_CONTROL_VALUE_MAX);
658✔
2877
                strncpy(dst_val->name, src_val->name, FILENAME_MAX);
658✔
2878

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

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

2907
        return ret;
656✔
2908

2909
err:
×
2910
        dst->index = 0;
×
2911
        for (i = 0; i < src->index; i++) {
×
2912
                if (dst->values[i]) {
×
2913
                        if (dst->values[i]->multiline_value)
×
2914
                                free(dst->values[i]->multiline_value);
×
2915

2916
                        if (dst->values[i]->prev_name)
×
2917
                                free(dst->values[i]->prev_name);
×
2918

2919
                        free(dst->values[i]);
×
2920
                }
2921
        }
2922

2923
        return ret;
×
2924
}
2925

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

2937
        if (!dst || !src)
417✔
2938
                return ECGROUPNOTEXIST;
×
2939

2940
        /* Should we just use the restrict keyword instead? */
2941
        if (dst == src)
417✔
2942
                return ECGFAIL;
×
2943

2944
        cgroup_free_controllers(dst);
417✔
2945

2946
        for (i = 0; i < src->index; i++, dst->index++) {
813✔
2947
                struct cgroup_controller *src_ctlr = src->controller[i];
396✔
2948
                struct cgroup_controller *dst_ctlr;
2949

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

2957
                dst_ctlr = dst->controller[i];
396✔
2958
                ret = cgroup_copy_controller_values(dst_ctlr, src_ctlr);
396✔
2959
                if (ret)
396✔
2960
                        goto err;
×
2961
        }
2962
err:
417✔
2963
        return ret;
417✔
2964
}
2965

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

2978
        tasks_path = (char *)malloc(FILENAME_MAX);
1✔
2979
        if (tasks_path == NULL)
1✔
2980
                return ECGOTHER;
×
2981

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

2989
        error = cg_chown(tasks_path, uid, gid);
1✔
2990
        if (!error && fperm != NO_PERMS)
1✔
2991
                error = cg_chmod_path(tasks_path, fperm, 1);
1✔
2992

2993
        if (error) {
1✔
2994
                last_errno = errno;
×
2995
                error = ECGOTHER;
×
2996
        }
2997

2998
err:
1✔
2999
        if (tasks_path)
1✔
3000
                free(tasks_path);
1✔
3001

3002
        return error;
1✔
3003
}
3004

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

3015
        fts_path[0] = (char *)malloc(FILENAME_MAX);
248✔
3016
        if (!fts_path[0]) {
248✔
3017
                last_errno = errno;
×
3018
                return ECGOTHER;
×
3019
        }
3020
        fts_path[1] = NULL;
248✔
3021
        path = fts_path[0];
248✔
3022

3023
        if (controller) {
248✔
3024
                if (!cg_build_path(cgrp->name, path, controller->name)) {
225✔
3025
                        error = ECGOTHER;
×
3026
                        goto err;
×
3027
                }
3028

3029
                error = cgroup_get_controller_version(controller->name, &version);
225✔
3030
                if (error)
225✔
3031
                        goto err;
×
3032

3033
                if (version == CGROUP_V2) {
225✔
3034
                        char *parent, *dname;
3035

3036
                        parent = strdup(path);
223✔
3037
                        if (!parent) {
223✔
3038
                                error = ECGOTHER;
×
3039
                                goto err;
×
3040
                        }
3041

3042
                        dname = dirname(parent);
223✔
3043

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

3056
        error = cg_create_control_group(path);
247✔
3057
        if (error)
247✔
3058
                goto err;
×
3059

3060
        base = strdup(path);
247✔
3061

3062
        if (!base) {
247✔
3063
                last_errno = errno;
×
3064
                error = ECGOTHER;
×
3065
                goto err;
×
3066
        }
3067

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

3080
        if (error)
247✔
3081
                goto err;
×
3082

3083
        if (controller) {
247✔
3084
                error = cgroup_set_values_recursive(base, controller, false);
224✔
3085
                if (error)
224✔
3086
                        goto err;
20✔
3087
        }
3088

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

3101
        return error;
248✔
3102
}
3103

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

3118
        if (!cgroup_initialized)
215✔
3119
                return ECGROUPNOTINITIALIZED;
×
3120

3121
        if (!cgrp)
215✔
3122
                return ECGROUPNOTALLOWED;
×
3123

3124
        for (i = 0; i < cgrp->index; i++) {
440✔
3125
                if (!cgroup_test_subsys_mounted(cgrp->controller[i]->name))
225✔
3126
                        return ECGROUPSUBSYSNOTMOUNTED;
×
3127
        }
3128

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

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

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

3165
        return 0;
194✔
3166
}
3167

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

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

3195
        pdir = dirname(dir);
188✔
3196
        cgroup_dbg("parent's group name is %s\n", pdir);
188✔
3197

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

3211
        return ret;
188✔
3212
}
3213

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

3224
        if (!controller)
193✔
3225
                return ret;
22✔
3226

3227
        pthread_rwlock_rdlock(&cg_mount_table_lock);
171✔
3228

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

3231
                if (strncmp(cg_mount_table[i].name, controller, CONTROL_NAMELEN_MAX) == 0 &&
395✔
3232
                    cg_mount_table[i].shared_mnt) {
171✔
3233
                        ret = 1;
171✔
3234
                        break;
171✔
3235
                }
3236
        }
3237

3238
        pthread_rwlock_unlock(&cg_mount_table_lock);
171✔
3239

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

3265
        *parent = NULL;
193✔
3266

3267
        pthread_rwlock_rdlock(&cg_mount_table_lock);
193✔
3268
        if (!cg_build_path_locked(cgrp->name, child_path, controller)) {
193✔
3269
                pthread_rwlock_unlock(&cg_mount_table_lock);
×
3270
                return ECGFAIL;
×
3271
        }
3272
        pthread_rwlock_unlock(&cg_mount_table_lock);
193✔
3273

3274
        cgroup_dbg("path is %s\n", child_path);
193✔
3275

3276
        if (asprintf(&parent_path, "%s/..", child_path) < 0)
193✔
3277
                return ECGFAIL;
×
3278

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

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

3292
        if (stat(parent_path, &stat_parent) < 0) {
188✔
3293
                last_errno = errno;
×
3294
                ret = ECGOTHER;
×
3295
                goto free_parent;
×
3296
        }
3297

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

3307
free_parent:
193✔
3308
        free(parent_path);
193✔
3309
        return ret;
193✔
3310
}
3311

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

3327
        if (!cgroup_initialized)
×
3328
                return ECGROUPNOTINITIALIZED;
×
3329

3330
        ret = cgroup_get_parent_name(cgrp, &parent);
×
3331
        if (ret)
×
3332
                return ret;
×
3333

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

3342
        cgroup_dbg("parent is %s\n", parent);
×
3343
        parent_cgrp = cgroup_new_cgroup(parent);
×
3344
        if (!parent_cgrp) {
×
3345
                ret = ECGFAIL;
×
3346
                goto err_nomem;
×
3347
        }
3348

3349
        if (cgroup_get_cgroup(parent_cgrp)) {
×
3350
                ret = ECGFAIL;
×
3351
                goto err_parent;
×
3352
        }
3353

3354
        cgroup_dbg("got parent group for %s\n", parent_cgrp->name);
×
3355
        ret = cgroup_copy_cgroup(cgrp, parent_cgrp);
×
3356
        if (ret)
×
3357
                goto err_parent;
×
3358

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

3362
err_parent:
×
3363
        cgroup_free(&parent_cgrp);
×
3364
err_nomem:
×
3365
        free(parent);
×
3366
        return ret;
×
3367
}
3368

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

3380
        while (!feof(input_tasks)) {
240✔
3381
                ret = fscanf(input_tasks, "%d", &tids);
240✔
3382
                if (ret == EOF || ret == 0) {
240✔
3383
                        ret = 0;
220✔
3384
                        break;
220✔
3385
                }
3386
                if (ret < 0)
20✔
3387
                        break;
×
3388

3389
                ret = fprintf(output_tasks, "%d", tids);
20✔
3390
                if (ret < 0 && errno != ESRCH)
20✔
3391
                        break;
×
3392

3393
                /* Flush the file, we need only one process per write() call. */
3394
                ret = fflush(output_tasks);
20✔
3395
                if (ret < 0) {
20✔
3396
                        if (errno == ESRCH)
×
3397
                                ret = 0;
×
3398
                        else
3399
                                break;
×
3400
                }
3401
        }
3402

3403
        if (ret < 0) {
220✔
3404
                last_errno = errno;
×
3405
                return ECGOTHER;
×
3406
        }
3407
        return 0;
220✔
3408
}
3409

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

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

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

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

3462
                if (ret != 0 && !(flags & CGFLAG_DELETE_IGNORE_MIGRATION))
220✔
3463
                        return ret;
×
3464
        }
3465

3466
        /* Remove the group. */
3467
        if (!cg_build_path(cgrp_name, path, controller))
220✔
3468
                return ECGROUPSUBSYSNOTMOUNTED;
×
3469

3470
        ret = rmdir(path);
220✔
3471
        if (ret == 0 || errno == ENOENT)
220✔
3472
                return 0;
220✔
3473

3474
        if ((flags & CGFLAG_DELETE_EMPTY_ONLY) && (errno == EBUSY))
×
3475
                return ECGNONEMPTY;
×
3476

3477
        cgroup_warn("cannot remove directory %s: %s\n", path, strerror(errno));
×
3478
        last_errno = errno;
×
3479

3480
        return ECGOTHER;
×
3481
}
3482

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

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

3505
        ret = cgroup_walk_tree_begin(controller, cgrp_name, 0, &handle, &info, &level);
21✔
3506
        if (ret == 0)
21✔
3507
                ret = cgroup_walk_tree_set_flags(&handle, CGROUP_WALK_TYPE_POST_DIR);
21✔
3508

3509
        if (ret != 0) {
21✔
3510
                cgroup_walk_tree_end(&handle);
×
3511
                return ret;
×
3512
        }
3513

3514
        group_len = strlen(info.full_path);
21✔
3515

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

3519
        while (ret == 0) {
2,507✔
3520
                if (info.type == CGROUP_FILE_TYPE_DIR && info.depth > 0) {
2,486✔
3521
                        snprintf(child_name, sizeof(child_name), "%s/%s", cgrp_name,
32✔
3522
                                 info.full_path + group_len);
32✔
3523

3524
                        ret = cg_delete_cgrp_controller(child_name, controller, target_tasks,
32✔
3525
                                                          flags);
3526
                        if (ret != 0)
32✔
3527
                                break;
×
3528
                }
3529

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

3539
        cgroup_walk_tree_end(&handle);
21✔
3540

3541
        return ret;
21✔
3542
}
3543

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

3554
        return cgroup_delete_cgroup_ext(cgrp, flags);
27✔
3555
}
3556

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

3569
        if (!cgroup_initialized)
192✔
3570
                return ECGROUPNOTINITIALIZED;
×
3571

3572
        if (!cgrp)
192✔
3573
                return ECGROUPNOTALLOWED;
×
3574

3575
        if ((flags & CGFLAG_DELETE_RECURSIVE)
192✔
3576
            && (flags & CGFLAG_DELETE_EMPTY_ONLY))
25✔
3577
                return ECGINVAL;
×
3578

3579
        if (cgrp->index == 0)
192✔
3580
                /* Valid empty cgroup v2 with not controllers added. */
3581
                empty_cgrp = 1;
22✔
3582

3583
        for (i = 0; i < cgrp->index; i++) {
363✔
3584
                if (!cgroup_test_subsys_mounted(cgrp->controller[i]->name))
171✔
3585
                        return ECGROUPSUBSYSNOTMOUNTED;
×
3586
        }
3587

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

3594
                ret = 0;
193✔
3595
                controller_name = NULL;
193✔
3596

3597
                if (cgrp->controller[i])
193✔
3598
                        controller_name = cgrp->controller[i]->name;
171✔
3599

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

3624
                        if (is_cgrp_ctrl_shared_mnt(controller_name))
188✔
3625
                                cgrp_del_on_shared_mnt = 1;
166✔
3626

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

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

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

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

3705
        /*
3706
         * Restore the last_errno to the first errno from
3707
         * cg_delete_cgroup_controller[_ext].
3708
         */
3709
        if (first_errno != 0)
192✔
3710
                last_errno = first_errno;
4✔
3711

3712
        return first_error;
192✔
3713
}
3714

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

3725
        if (!cg_build_path_locked(cgrp, path, subsys))
3,470✔
3726
                return ECGFAIL;
×
3727

3728
        strncat(path, file, sizeof(path) - strlen(path));
3,470✔
3729
        ctrl_file = fopen(path, "re");
3,470✔
3730
        if (!ctrl_file)
3,470✔
3731
                return ECGROUPVALUENOTEXIST;
10✔
3732

3733
        *value = calloc(CG_CONTROL_VALUE_MAX, 1);
3,460✔
3734
        if (!*value) {
3,460✔
3735
                fclose(ctrl_file);
×
3736
                last_errno = errno;
×
3737
                return ECGOTHER;
×
3738
        }
3739

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

3751
        fclose(ctrl_file);
3,460✔
3752

3753
        return 0;
3,460✔
3754
}
3755

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

3773
        d_name = strdup(ctrl_dir->d_name);
22,028✔
3774

3775
        if (!strcmp(d_name, ".") || !strcmp(d_name, "..")) {
22,028✔
3776
                error = ECGINVAL;
×
3777
                goto fill_error;
×
3778
        }
3779

3780
        /*
3781
         * This part really needs to be optimized out. Probably use some
3782
         * sort of a flag, but this is fine for now.
3783
         */
3784
        cg_build_path_locked(cgrp->name, path, cg_mount_table[cg_index].name);
22,028✔
3785
        strncat(path, d_name, sizeof(path) - strlen(path));
22,028✔
3786

3787
        error = stat(path, &stat_buffer);
22,028✔
3788
        if (error) {
22,028✔
3789
                error = ECGFAIL;
×
3790
                goto fill_error;
×
3791
        }
3792

3793
        /*
3794
         * We have already stored the tasks_uid & tasks_gid. This check is
3795
         * to avoid the overwriting of the values stored in
3796
         * control_uid & cotrol_gid. tasks file will have the uid and gid of
3797
         * the user who is capable of putting a task to this cgroup.
3798
         * control_uid and control_gid is meant for the users who are capable
3799
         * of managing the cgroup shares.
3800
         *
3801
         * The strstr() function will return the pointer to the
3802
         * beginning of the sub string "/tasks".
3803
         */
3804
        tmp_len = strlen(path) - strlen("/tasks");
22,028✔
3805

3806
        /* tmp_path would be pointing to the last six characters */
3807
        tmp_path = (char *)path + tmp_len;
22,028✔
3808

3809
        /*
3810
         * Checking to see, if this is actually a 'tasks' file We need to
3811
         * compare the last 6 bytes
3812
         */
3813
        if (strcmp(tmp_path, "/tasks")) {
22,028✔
3814
                cgrp->control_uid = stat_buffer.st_uid;
22,019✔
3815
                cgrp->control_gid = stat_buffer.st_gid;
22,019✔
3816
        }
3817

3818
        ctrl_name = strtok_r(d_name, ".", &buffer);
22,028✔
3819
        if (!ctrl_name) {
22,028✔
3820
                error = ECGFAIL;
×
3821
                goto fill_error;
×
3822
        }
3823

3824
        ctrl_file = strtok_r(NULL, ".", &buffer);
22,028✔
3825
        if (!ctrl_file) {
22,028✔
3826
                error = ECGINVAL;
9✔
3827
                goto fill_error;
9✔
3828
        }
3829

3830
        if (strcmp(ctrl_name, cg_mount_table[cg_index].name) == 0) {
22,019✔
3831
                error = cg_rd_ctrl_file(cg_mount_table[cg_index].name, cgrp->name,
3,470✔
3832
                                        ctrl_dir->d_name, &ctrl_value);
3,470✔
3833
                if (error || !ctrl_value)
3,470✔
3834
                        goto fill_error;
10✔
3835

3836
                if (cgroup_add_value_string(cgc, ctrl_dir->d_name, ctrl_value)) {
3,460✔
3837
                        error = ECGFAIL;
×
3838
                        goto fill_error;
×
3839
                }
3840
        }
3841
fill_error:
22,009✔
3842
        if (ctrl_value)
22,028✔
3843
                free(ctrl_value);
3,460✔
3844
        free(d_name);
22,028✔
3845

3846
        return error;
22,028✔
3847
}
3848

3849
/*
3850
 * cgroup_get_cgroup reads the cgroup data from the filesystem.
3851
 * struct cgroup has the name of the group to be populated
3852
 *
3853
 * return 0 on success.
3854
 */
3855
int cgroup_get_cgroup(struct cgroup *cgrp)
73✔
3856
{
3857
        char cgrp_ctrl_path[FILENAME_MAX];
3858
        struct dirent *ctrl_dir = NULL;
73✔
3859
        char mnt_path[FILENAME_MAX];
3860
        int initial_controller_cnt;
3861
        char *control_path = NULL;
73✔
3862
        int controller_cnt = 0;
73✔
3863
        DIR *dir = NULL;
73✔
3864
        int error;
3865
        int i, j;
3866
        int ret;
3867

3868
        if (!cgroup_initialized) {
73✔
3869
                /* ECGROUPNOTINITIALIZED */
3870
                return ECGROUPNOTINITIALIZED;
×
3871
        }
3872

3873
        if (!cgrp) {
73✔
3874
                /* ECGROUPNOTALLOWED */
3875
                return ECGROUPNOTALLOWED;
×
3876
        }
3877

3878
        initial_controller_cnt = cgrp->index;
73✔
3879

3880
        pthread_rwlock_rdlock(&cg_mount_table_lock);
73✔
3881
        for (i = 0; i < CG_CONTROLLER_MAX && cg_mount_table[i].name[0] != '\0'; i++) {
723✔
3882
                struct cgroup_controller *cgc;
3883
                struct stat stat_buffer;
3884
                int mnt_path_len;
3885

3886
                if (initial_controller_cnt > 0) {
651✔
3887
                        bool skip_this_controller = true;
54✔
3888

3889
                        /*
3890
                         * The user has specified a list of controllers they are interested
3891
                         * in.  Only operate on the specified controllers
3892
                         */
3893
                        for (j = 0; j < cgrp->index; j++) {
198✔
3894
                                if (strncmp(cg_mount_table[i].name, cgrp->controller[j]->name,
144✔
3895
                                            CONTROL_NAMELEN_MAX) == 0)
3896
                                        skip_this_controller = false;
16✔
3897
                        }
3898

3899
                        if (skip_this_controller)
54✔
3900
                                continue;
314✔
3901
                }
3902

3903
                if (!cg_build_path_locked(NULL, mnt_path, cg_mount_table[i].name))
613✔
3904
                        continue;
×
3905

3906
                mnt_path_len = strlen(mnt_path);
613✔
3907
                strncat(mnt_path, cgrp->name, FILENAME_MAX - mnt_path_len - 1);
613✔
3908
                mnt_path[sizeof(mnt_path) - 1] = '\0';
613✔
3909

3910
                if (access(mnt_path, F_OK))
613✔
3911
                        continue;
2✔
3912

3913
                if (!cg_build_path_locked(cgrp->name, cgrp_ctrl_path, cg_mount_table[i].name)) {
611✔
3914
                        /* This fails when the cgroup does not exist for that controller. */
3915
                        continue;
×
3916
                }
3917

3918
                /* Get the uid and gid information. */
3919
                if (cg_mount_table[i].version == CGROUP_V1) {
611✔
3920
                        ret = asprintf(&control_path, "%s/tasks", cgrp_ctrl_path);
10✔
3921

3922
                        if (ret < 0) {
10✔
3923
                                last_errno = errno;
×
3924
                                error = ECGOTHER;
×
3925
                                goto unlock_error;
1✔
3926
                        }
3927

3928
                        if (stat(control_path, &stat_buffer)) {
10✔
3929
                                last_errno = errno;
1✔
3930
                                free(control_path);
1✔
3931
                                error = ECGOTHER;
1✔
3932
                                goto unlock_error;
1✔
3933
                        }
3934

3935
                        cgrp->tasks_uid = stat_buffer.st_uid;
9✔
3936
                        cgrp->tasks_gid = stat_buffer.st_gid;
9✔
3937

3938
                        free(control_path);
9✔
3939
                } else { /* cgroup v2 */
3940
                        bool enabled;
3941

3942
                        error = cgroupv2_get_controllers(cgrp_ctrl_path, cg_mount_table[i].name,
601✔
3943
                                                         &enabled);
3944
                        if (error == ECGROUPNOTMOUNTED) {
601✔
3945
                                /*
3946
                                 * This controller isn't enabled.  Only hide it from the
3947
                                 * user if they've chosen to view all enabled controllers.
3948
                                 *
3949
                                 * If they've specified the controllers they're interested in
3950
                                 * and we've made it this far, then they are explicitly
3951
                                 * interested in this controller and we should not remove it.
3952
                                 */
3953
                                if (initial_controller_cnt == 0) {
278✔
3954
                                        controller_cnt++;
274✔
3955
                                        continue;
274✔
3956
                                }
3957
                        } else if (error) {
323✔
3958
                                goto unlock_error;
×
3959
                        }
3960
                }
3961

3962
                if (initial_controller_cnt)
336✔
3963
                        cgc = cgroup_get_controller(cgrp, cg_mount_table[i].name);
16✔
3964
                else
3965
                        cgc = cgroup_add_controller(cgrp, cg_mount_table[i].name);
320✔
3966
                if (!cgc) {
336✔
3967
                        error = ECGINVAL;
×
3968
                        goto unlock_error;
×
3969
                }
3970

3971
                dir = opendir(cgrp_ctrl_path);
336✔
3972
                if (!dir) {
336✔
3973
                        last_errno = errno;
×
3974
                        error = ECGOTHER;
×
3975
                        goto unlock_error;
×
3976
                }
3977

3978
                controller_cnt++;
336✔
3979

3980
                while ((ctrl_dir = readdir(dir)) != NULL) {
20,325✔
3981
                        /* Skip over non regular files */
3982
                        if (ctrl_dir->d_type != DT_REG)
19,989✔
3983
                                continue;
917✔
3984

3985
                        error = cgroup_fill_cgc(ctrl_dir, cgrp, cgc, i);
19,072✔
3986
                        for (j = 0; j < cgc->index; j++)
111,683✔
3987
                                cgc->values[j]->dirty = false;
92,611✔
3988

3989
                        if (error == ECGFAIL) {
19,072✔
3990
                                closedir(dir);
×
3991
                                goto unlock_error;
×
3992
                        }
3993
                }
3994
                closedir(dir);
336✔
3995

3996
                if (!strcmp(cgc->name, "memory")) {
336✔
3997
                        /*
3998
                         * Make sure that memory.limit_in_bytes is placed before
3999
                         * memory.memsw.limit_in_bytes in the list of values
4000
                         */
4001
                        int memsw_limit = -1;
69✔
4002
                        int mem_limit = -1;
69✔
4003

4004
                        for (j = 0; j < cgc->index; j++) {
1,472✔
4005
                                if (!strcmp(cgc->values[j]->name, "memory.memsw.limit_in_bytes"))
1,403✔
4006
                                        memsw_limit = j;
2✔
4007
                                else if (!strcmp(cgc->values[j]->name, "memory.limit_in_bytes"))
1,401✔
4008
                                        mem_limit = j;
2✔
4009
                        }
4010

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

4014
                                cgc->values[memsw_limit] = cgc->values[mem_limit];
×
4015
                                cgc->values[mem_limit] = val;
×
4016
                        }
4017
                }
4018
        }
4019

4020
        /*
4021
         * Check if the group really exists or not.  The cgrp->index controller count can't
4022
         * be used in this case because cgroup v2 allows controllers to be enabled/disabled in
4023
         * the subtree_control file.  Rather, cgroup_get_cgroup() tracks the number of possible
4024
         * controllers in the controller_cnt variable and uses that to determine if the cgroup
4025
         * exists or not.
4026
         */
4027
        if (!controller_cnt) {
72✔
4028
                error = ECGROUPNOTEXIST;
×
4029
                goto unlock_error;
×
4030
        }
4031

4032
        pthread_rwlock_unlock(&cg_mount_table_lock);
72✔
4033

4034
        return 0;
72✔
4035

4036
unlock_error:
1✔
4037
        pthread_rwlock_unlock(&cg_mount_table_lock);
1✔
4038
        /*
4039
         * XX: Need to figure out how to cleanup? Cleanup just the stuff
4040
         * we added, or the whole structure.
4041
         */
4042
        cgroup_free_controllers(cgrp);
1✔
4043
        cgrp = NULL;
1✔
4044

4045
        return error;
1✔
4046
}
4047

4048
/**
4049
 * cg_prepare_cgroup Process the selected rule. Prepare the cgroup structure
4050
 * which can be used to add the task to destination cgroup.
4051
 *
4052
 *  returns 0 on success.
4053
 */
4054
static int cg_prepare_cgroup(struct cgroup *cgrp, pid_t pid, const char *dest,
68✔
4055
                             const char * const controllers[])
4056
{
4057
        struct cgroup_controller *cptr = NULL;
68✔
4058
        const char *controller = NULL;
68✔
4059
        int ret = 0, i;
68✔
4060

4061
        /* Fill in cgroup details.  */
4062
        cgroup_dbg("Will move pid %d to cgroup '%s'\n", pid, dest);
68✔
4063

4064
        strncpy(cgrp->name, dest, FILENAME_MAX);
68✔
4065
        cgrp->name[FILENAME_MAX-1] = '\0';
68✔
4066

4067
        /* Scan all the controllers */
4068
        for (i = 0; i < CG_CONTROLLER_MAX; i++) {
151✔
4069
                int j = 0;
151✔
4070

4071
                if (!controllers[i])
151✔
4072
                        return 0;
68✔
4073
                controller = controllers[i];
83✔
4074

4075
                /* If first string is "*" that means all the mounted controllers. */
4076
                if (strcmp(controller, "*") == 0) {
83✔
4077
                        pthread_rwlock_rdlock(&cg_mount_table_lock);
×
4078

4079
                        for (j = 0; j < CG_CONTROLLER_MAX &&
×
4080
                                cg_mount_table[j].name[0] != '\0'; j++) {
×
4081
                                cgroup_dbg("Adding controller %s\n", cg_mount_table[j].name);
×
4082
                                cptr = cgroup_add_controller(cgrp, cg_mount_table[j].name);
×
4083
                                if (!cptr) {
×
4084
                                        cgroup_warn("adding controller '%s' failed\n",
×
4085
                                                    cg_mount_table[j].name);
4086
                                        pthread_rwlock_unlock(&cg_mount_table_lock);
×
4087
                                        cgroup_free_controllers(cgrp);
×
4088
                                        return ECGROUPNOTALLOWED;
×
4089
                                }
4090
                        }
4091
                        pthread_rwlock_unlock(&cg_mount_table_lock);
×
4092
                        return ret;
×
4093
                }
4094

4095
                /* It is individual controller names and not "*" */
4096
                cgroup_dbg("Adding controller %s\n", controller);
83✔
4097
                cptr = cgroup_add_controller(cgrp, controller);
83✔
4098
                if (!cptr) {
83✔
4099
                        cgroup_warn("adding controller '%s' failed\n", controller);
×
4100
                        cgroup_free_controllers(cgrp);
×
4101
                        return ECGROUPNOTALLOWED;
×
4102
                }
4103
        }
4104

4105
        return ret;
×
4106
}
4107

4108
/**
4109
 * Determines if the rule is a wildcard rule and if so, compares the wildcard
4110
 * rule against the new process.  If the new process matches the wildcard rule,
4111
 * then this function returns true. Otherwise it returns false.
4112
 *
4113
 *        @param rule_procname The procname field of the rule
4114
 *        @param procname The name of the new process
4115
 *        @return True if the procname matches the rule.  False otherwise
4116
 */
4117
STATIC bool cgroup_compare_wildcard_procname(const char * const rule_procname,
540✔
4118
                                             const char * const procname)
4119
{
4120
        size_t rule_strlen = strlen(rule_procname);
540✔
4121

4122
        if (rule_procname[rule_strlen - 1] != '*')
540✔
4123
                /* This rule does not end in a wildcard */
4124
                return false;
533✔
4125

4126
        /* Compare the two strings up to the asterisk */
4127
        if (strncmp(rule_procname, procname, rule_strlen - 1) != 0)
7✔
4128
                /* The strings did not match */
4129
                return false;
3✔
4130

4131
        /* All checks passed.  The wildcarded process matched this rule */
4132
        return true;
4✔
4133
}
4134

4135
static int cgroup_find_matching_destination(char *cgrp_list[], const char * const rule_dest,
14✔
4136
                                            int *matching_index)
4137
{
4138
        size_t rule_strlen = strlen(rule_dest);
14✔
4139
        int ret = -ENODATA;
14✔
4140
        int i;
4141

4142
        for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
34✔
4143
                if (cgrp_list[i] == NULL)
34✔
4144
                        break;
5✔
4145

4146
                if (rule_dest[rule_strlen - 1] == '/') {
29✔
4147
                        /*
4148
                         * Avoid a weird corner case where given a rule dest
4149
                         * like 'folder/', we _don't_ want to match 'folder1'
4150
                         */
4151
                        if (strlen(cgrp_list[i]) >= rule_strlen &&
7✔
4152
                            cgrp_list[i][rule_strlen - 1] != '/')
6✔
4153
                                continue;
3✔
4154

4155
                        /*
4156
                         * Strip off the '/' at the end of the rule, as
4157
                         * the destination from the cgrp_list will not
4158
                         * have a trailing '/'
4159
                         */
4160
                        rule_strlen--;
4✔
4161
                }
4162

4163
                if (strncmp(rule_dest, cgrp_list[i], rule_strlen) == 0) {
26✔
4164
                        *matching_index = i;
9✔
4165
                        ret = 0;
9✔
4166
                        break;
9✔
4167
                }
4168
        }
4169

4170
        return ret;
14✔
4171
}
4172

4173
static int cgroup_find_matching_controller(char * const *rule_controllers,
10✔
4174
                                           const char * const pid_controller, int *matching_index)
4175
{
4176
        int ret = -ENODATA;
10✔
4177
        int i;
4178

4179
        for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
11✔
4180
                if (rule_controllers[i] == NULL)
11✔
4181
                        break;
1✔
4182

4183
                if (strlen(rule_controllers[i]) != strlen(pid_controller))
10✔
4184
                        continue;
1✔
4185

4186
                if (strncmp(pid_controller, rule_controllers[i], strlen(pid_controller)) == 0) {
9✔
4187
                        *matching_index = i;
9✔
4188
                        ret = 0;
9✔
4189
                        break;
9✔
4190
                }
4191
        }
4192

4193
        return ret;
10✔
4194
}
4195

4196
static bool cgroup_is_rt_task(const pid_t pid)
14✔
4197
{
4198
        int sched_prio_min, sched_prio_max;
4199
        struct sched_param pid_param;
4200
        int ret;
4201

4202
        ret = sched_getparam(pid, &pid_param);
14✔
4203
        if (ret == -1) {
14✔
4204
                ret = ECGOTHER;
14✔
4205
                last_errno = errno;
14✔
4206
                return false;
14✔
4207
        }
4208

4209
        sched_prio_min = sched_get_priority_min(SCHED_RR);
×
4210
        sched_prio_max = sched_get_priority_max(SCHED_RR);
×
4211

4212
        if (pid_param.sched_priority >= sched_prio_min &&
×
4213
            pid_param.sched_priority <= sched_prio_max)
×
4214
                return true;
×
4215

4216
        return false;
×
4217
}
4218

4219
/**
4220
 * Evaluates if rule is an ignore rule and the pid/procname match this rule.
4221
 * If rule is an ignore rule and the pid/procname match this rule, then this
4222
 * function returns true.  Otherwise it returns false.
4223
 *
4224
 *        @param rule The rule being evaluated
4225
 *        @param pid PID of the process being compared
4226
 *        @param procname Process name of the process being compared
4227
 *        @return True if the rule is an ignore rule and this pid/procname
4228
 *                match the rule.  False otherwise
4229
 */
4230
STATIC bool cgroup_compare_ignore_rule(const struct cgroup_rule * const rule, pid_t pid,
568✔
4231
                                       const char * const procname)
4232
{
4233
        char *controller_list[MAX_MNT_ELEMENTS] = { '\0' };
568✔
4234
        char *cgrp_list[MAX_MNT_ELEMENTS] = { '\0' };
568✔
4235
        int rule_matching_controller_idx;
4236
        int cgrp_list_matching_idx = 0;
568✔
4237
        bool found_match = false;
568✔
4238
        char *token, *saveptr;
4239
        int ret, i;
4240

4241
        if (!rule->is_ignore)
568✔
4242
                /* Immediately return if the 'ignore' option is not set */
4243
                return false;
554✔
4244

4245
        /* If the rule is "ignore", move only non-rt tasks */
4246
        if (rule->is_ignore == CGRULE_OPT_IGNORE && cgroup_is_rt_task(pid) == true)
14✔
4247
                return false;
×
4248
        /* If the rule is "ignore_rt", move only non-rt tasks */
4249
        else if (rule->is_ignore == CGRULE_OPT_IGNORE_RT && cgroup_is_rt_task(pid) == false)
14✔
4250
                return false;
×
4251

4252
        /* If the rule is "ignore" and "ignore_rt", move all tasks */
4253

4254
        ret = cg_get_cgroups_from_proc_cgroups(pid, cgrp_list, controller_list,
14✔
4255
                                               MAX_MNT_ELEMENTS);
4256
        if (ret < 0)
14✔
4257
                goto out;
×
4258

4259
        if (strcmp(rule->destination, "*")) {
14✔
4260
                ret = cgroup_find_matching_destination(cgrp_list, rule->destination,
14✔
4261
                                                       &cgrp_list_matching_idx);
4262
                if (ret < 0)
14✔
4263
                        /* No cgroups matched */
4264
                        goto out;
5✔
4265
        }
4266

4267
        token = strtok_r(controller_list[cgrp_list_matching_idx], ",", &saveptr);
9✔
4268
        while (token != NULL) {
10✔
4269

4270
                ret = cgroup_find_matching_controller(rule->controllers, token,
10✔
4271
                                                      &rule_matching_controller_idx);
4272
                if (ret == 0)
10✔
4273
                        /* We found a matching controller */
4274
                        break;
9✔
4275

4276
                token = strtok_r(NULL, ",", &saveptr);
1✔
4277
        }
4278

4279
        if (!rule->procname) {
9✔
4280
                /*
4281
                 * The rule procname is empty, thus it's a wildcard and
4282
                 * all processes match.
4283
                 */
4284
                found_match = true;
5✔
4285
                goto out;
5✔
4286
        }
4287

4288
        if (!strcmp(rule->procname, procname)) {
4✔
4289
                found_match = true;
2✔
4290
                goto out;
2✔
4291
        }
4292

4293
        if (cgroup_compare_wildcard_procname(rule->procname, procname))
2✔
4294
                found_match = true;
1✔
4295

4296
out:
1✔
4297
        for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
252✔
4298
                if (controller_list[i])
238✔
4299
                        free(controller_list[i]);
36✔
4300
                if (cgrp_list[i])
238✔
4301
                        free(cgrp_list[i]);
36✔
4302
        }
4303

4304
        return found_match;
14✔
4305
}
4306

4307
static struct cgroup_rule *cgroup_find_matching_rule_uid_gid(uid_t uid, gid_t gid,
553✔
4308
                                                             struct cgroup_rule *rule)
4309
{
4310
        /* Temporary user data */
4311
        struct passwd *usr = NULL;
553✔
4312

4313
        /* Temporary group data */
4314
        struct group *grp = NULL;
553✔
4315

4316
        /* Temporary string pointer */
4317
        char *sp = NULL;
553✔
4318

4319
        /* Loop variable */
4320
        int i = 0;
553✔
4321
        int loglevel;
4322
        bool match_found = false;
553✔
4323

4324
        loglevel = cgroup_get_loglevel();
553✔
4325

4326
        while (rule) {
553✔
4327
                /* Skip "%" which indicates continuation of previous rule. */
4328
                if (rule->username[0] == '%') {
553✔
4329
                        rule = rule->next;
×
4330
                        continue;
×
4331
                }
4332
                /* The wildcard rule always matches. */
4333
                if ((rule->uid == CGRULE_WILD) && (rule->gid == CGRULE_WILD))
553✔
4334
                        return rule;
553✔
4335

4336
                /* This is the simple case of the UID matching. */
4337
                if (rule->uid == uid)
×
4338
                        return rule;
×
4339

4340
                /* This is the simple case of the GID matching. */
4341
                if (rule->gid == gid)
×
4342
                        return rule;
×
4343

4344
                /* If this is a group rule, the UID might be a member. */
4345
                if (rule->username[0] == '@') {
×
4346
                        /* Get the group data. */
4347
                        sp = &(rule->username[1]);
×
4348
                        grp = getgrnam(sp);
×
4349
                        if (!grp) {
×
4350
                                rule = rule->next;
×
4351
                                continue;
×
4352
                        }
4353

4354
                        /* Get the data for UID. */
4355
                        usr = getpwuid(uid);
×
4356
                        if (!usr) {
×
4357
                                rule = rule->next;
×
4358
                                continue;
×
4359
                        }
4360

4361
                        cgroup_dbg("User name: %s UID: %d Group name: %s GID: %d\n",
×
4362
                                   usr->pw_name, uid, grp->gr_name, grp->gr_gid);
4363
                        if (grp->gr_mem[0])
×
4364
                                cgroup_dbg("Group member(s):\n");
×
4365

4366
                        /* If UID is a member of group, we matched. */
4367
                        for (i = 0; grp->gr_mem[i]; i++) {
×
4368
                                if (!(strcmp(usr->pw_name, grp->gr_mem[i])))
×
4369
                                        match_found = true;
×
4370

4371
                                if (match_found && loglevel < CGROUP_LOG_DEBUG)
×
4372
                                        /*
4373
                                         * Only continue to run through the loop if debugging is
4374
                                         * enabled so that we can see all of the group members
4375
                                         */
4376
                                        break;
×
4377

4378
                                cgroup_dbg("\t%s\n", grp->gr_mem[i]);
×
4379
                        }
4380

4381
                        if (match_found)
×
4382
                                return rule;
×
4383
                }
4384

4385
                /* If we haven't matched, try the next rule. */
4386
                rule = rule->next;
×
4387
        }
4388

4389
        /* If we get here, no rules matched. */
4390
        return NULL;
×
4391
}
4392

4393
/**
4394
 * Finds the first rule in the cached list that matches the given UID, GID
4395
 * or PROCESS NAME, and returns a pointer to that rule.
4396
 * This function uses rl_lock.
4397
 *
4398
 * This function may NOT be thread safe.
4399
 *        @param uid The UID to match
4400
 *        @param gid The GID to match
4401
 *        @param procname The PROCESS NAME to match
4402
 *        @return Pointer to the first matching rule, or NULL if no match
4403
 * TODO: Determine thread-safeness and fix if not safe.
4404
 */
4405
static struct cgroup_rule *cgroup_find_matching_rule(uid_t uid, gid_t gid, pid_t pid,
553✔
4406
                                                     const char *procname)
4407
{
4408
        /* Return value */
4409
        struct cgroup_rule *ret = rl.head;
553✔
4410
        char *base = NULL;
553✔
4411

4412
        pthread_rwlock_wrlock(&rl_lock);
553✔
4413
        while (ret) {
1,084✔
4414
                ret = cgroup_find_matching_rule_uid_gid(uid, gid, ret);
553✔
4415
                if (!ret)
553✔
4416
                        break;
×
4417
                if (cgroup_compare_ignore_rule(ret, pid, procname))
553✔
4418
                        /*
4419
                         * This pid matched a rule that instructs the
4420
                         * cgrules daemon to ignore this process.
4421
                         */
4422
                        break;
×
4423
                if (ret->is_ignore) {
553✔
4424
                        /*
4425
                         * The rule currently being examined is an ignore
4426
                         * rule, but it didn't match this pid. Move on to
4427
                         * the next rule
4428
                         */
4429
                        ret = ret->next;
×
4430
                        continue;
×
4431
                }
4432
                if (!procname)
553✔
4433
                        /* If procname is NULL, return a rule matching UID or GID. */
4434
                        break;
×
4435
                if (!ret->procname)
553✔
4436
                        /* If no process name in a rule, that means wildcard */
4437
                        break;
×
4438
                if (!strcmp(ret->procname, procname))
553✔
4439
                        break;
22✔
4440

4441
                base = cgroup_basename(procname);
531✔
4442
                if (!strcmp(ret->procname, base))
531✔
4443
                        /* Check a rule of basename. */
4444
                        break;
×
4445
                if (cgroup_compare_wildcard_procname(ret->procname, procname))
531✔
4446
                        break;
×
4447
                ret = ret->next;
531✔
4448
                free(base);
531✔
4449
                base = NULL;
531✔
4450
        }
4451
        pthread_rwlock_unlock(&rl_lock);
553✔
4452

4453
        if (base)
553✔
4454
                free(base);
×
4455

4456
        return ret;
553✔
4457
}
4458

4459
/*
4460
 * Procedure the existence of cgroup "prefix" is in subsystem
4461
 * controller_name return 0 on success
4462
 */
4463
int cgroup_exist_in_subsystem(char *controller_name, char *prefix)
×
4464
{
4465
        char path[FILENAME_MAX];
4466
        char *ret_path;
4467
        DIR *dir;
4468
        int ret;
4469

4470
        pthread_rwlock_rdlock(&cg_mount_table_lock);
×
4471
        ret_path = cg_build_path_locked(prefix, path, controller_name);
×
4472
        pthread_rwlock_unlock(&cg_mount_table_lock);
×
4473
        if (!ret_path) {
×
4474
                ret = 1;
×
4475
                goto end;
×
4476
        }
4477

4478
        dir = opendir(path);
×
4479
        if (dir == NULL) {
×
4480
                /* cgroup in wanted subsystem does not exist */
4481
                ret = 1;
×
4482
        } else {
4483
                /* cgroup in wanted subsystem exists */
4484
                ret = 0;
×
4485
                closedir(dir);
×
4486
        }
4487
end:
×
4488
        return ret;
×
4489
}
4490

4491
/*
4492
 * Auxiliary function return a pointer to the string which is copy of
4493
 * input string and end with the slash
4494
 */
4495
char *cgroup_copy_with_slash(char *input)
×
4496
{
4497
        int len = strlen(input);
×
4498
        char *output;
4499

4500
        /* If input does not end with '/', allocate one more space for it */
4501
        if ((input[len-1]) != '/')
×
4502
                len = len+1;
×
4503

4504
        output = (char *)malloc(sizeof(char)*(len+1));
×
4505
        if (output == NULL)
×
4506
                return NULL;
×
4507

4508
        strcpy(output, input);
×
4509
        output[len-1] = '/';
×
4510
        output[len] = '\0';
×
4511

4512
        return output;
×
4513
}
4514

4515
/* Add controller to a group if it is not exists create it */
4516
static int add_controller(struct cgroup **pcgrp, char *cgrp_name,
×
4517
                          char controller_name[FILENAME_MAX])
4518
{
4519
        struct cgroup_controller *controller = NULL;
×
4520
        struct cgroup *cgrp = pcgrp[0];
×
4521
        int ret = 0;
×
4522

4523
        if  (cgrp == NULL) {
×
4524
                /* It is the first controller the cgrp have to be created */
4525
                cgrp = cgroup_new_cgroup(cgrp_name);
×
4526
                if (cgrp == NULL) {
×
4527
                        ret = ECGFAIL;
×
4528
                        goto end;
×
4529
                }
4530
                pcgrp[0] = cgrp;
×
4531
        }
4532

4533
        controller = cgroup_add_controller(cgrp, controller_name);
×
4534
        if (controller == NULL) {
×
4535
                cgroup_free(&cgrp);
×
4536
                ret = ECGFAIL;
×
4537
        }
4538
end:
×
4539
        return ret;
×
4540
}
4541

4542
/*
4543
 * Create control group based given template if the group already don't exist
4544
 * dest is template name with substitute variables tmp is used cgrules rule.
4545
 */
4546
static int cgroup_create_template_group(char *orig_group_name, struct cgroup_rule *tmp, int flags)
×
4547
{
4548

4549
        struct cgroup *template_group = NULL;
×
4550
        char *template_name = NULL;        /* Name of the template.              */
×
4551
        char *group_name = NULL;        /* Name of the group based on         */
×
4552
                                        /* template variables are substituted.*/
4553
        char *template_position;        /* Denotes directory in template      */
4554
                                        /* path which is investigated.        */
4555
        char *group_position;                /* Denotes directory in cgroup path   */
4556
                                        /* which is investigated.             */
4557

4558
        int ret = 0;
×
4559
        int exist;
4560
        int i;
4561

4562
        /* Template name and group name have to have '/' sign at the end */
4563
        template_name = cgroup_copy_with_slash(tmp->destination);
×
4564
        if (template_name == NULL) {
×
4565
                ret = ECGOTHER;
×
4566
                last_errno = errno;
×
4567
                goto end;
×
4568
        }
4569
        group_name = cgroup_copy_with_slash(orig_group_name);
×
4570
        if (group_name == NULL) {
×
4571
                ret = ECGOTHER;
×
4572
                last_errno = errno;
×
4573
                free(template_name);
×
4574
                template_name = NULL;
×
4575
                goto end;
×
4576
        }
4577

4578
        /* Set start positions */
4579
        template_position = strchr(template_name, '/');
×
4580
        group_position = strchr(group_name, '/');
×
4581

4582
        /*
4583
         * Go recursively through whole path to template group and create
4584
         * given directory if it does not exist yet
4585
         */
4586
        while ((group_position != NULL) && (template_position != NULL)) {
×
4587
                /* Set new subpath */
4588
                group_position[0] = '\0';
×
4589
                template_position[0] = '\0';
×
4590
                template_group = NULL;
×
4591

4592
                /* Test for which controllers wanted group does not exist */
4593
                i = 0;
×
4594
                while (i < MAX_MNT_ELEMENTS && tmp->controllers[i] != NULL) {
×
4595
                        exist = cgroup_exist_in_subsystem(tmp->controllers[i], group_name);
×
4596

4597
                        if (exist != 0) {
×
4598
                                /* The cgroup does not exist */
4599
                                ret = add_controller(&template_group, group_name,
×
4600
                                                     tmp->controllers[i]);
4601
                                if  (ret != 0)
×
4602
                                        goto while_end;
×
4603
                        }
4604
                        i++;
×
4605
                }
4606

4607
                if (template_group != NULL) {
×
4608
                        /*  New group have to be created */
4609
                        if (strcmp(group_name, template_name) == 0) {
×
4610
                                /* The prefix cgroup without template */
4611
                                ret = cgroup_create_cgroup(template_group, 0);
×
4612
                        } else {
4613
                                /* Use template to create relevant cgroup */
4614
                                ret = cgroup_config_create_template_group(template_group,
×
4615
                                                                          template_name, flags);
4616
                        }
4617

4618
                        if (ret != 0) {
×
4619
                                cgroup_free(&template_group);
×
4620
                                goto while_end;
×
4621
                        }
4622
                        cgroup_dbg("Group %s created - based on template %s\n", group_name,
×
4623
                                   template_name);
4624

4625
                        cgroup_free(&template_group);
×
4626
                }
4627
                template_position[0] = '/';
×
4628
                group_position[0] = '/';
×
4629
                template_position = strchr(++template_position, '/');
×
4630
                group_position = strchr(++group_position, '/');
×
4631
        }
4632

4633
while_end:
×
4634
        if ((template_position != NULL) && (template_position[0] == '\0'))
×
4635
                template_position[0] = '/';
×
4636
        if ((group_position != NULL) && (group_position[0] == '\0'))
×
4637
                group_position[0] = '/';
×
4638

4639
end:
×
4640
        if (group_name != NULL)
×
4641
                free(group_name);
×
4642
        if (template_name != NULL)
×
4643
                free(template_name);
×
4644

4645
        return ret;
×
4646
}
4647

4648
int cgroup_change_cgroup_flags(uid_t uid, gid_t gid, const char *procname, pid_t pid, int flags)
553✔
4649
{
4650
        /* Temporary pointer to a rule */
4651
        struct cgroup_rule *tmp = NULL;
553✔
4652

4653
        /* Temporary variables for destination substitution */
4654
        char newdest[FILENAME_MAX];
4655
        struct passwd *user_info;
4656
        struct group *group_info;
4657
        int available;
4658
        int written;
4659
        int i, j;
4660

4661
        /* Return codes */
4662
        int ret = 0;
553✔
4663

4664
        /* We need to check this before doing anything else! */
4665
        if (!cgroup_initialized) {
553✔
4666
                cgroup_warn("libcgroup is not initialized\n");
×
4667
                ret = ECGROUPNOTINITIALIZED;
×
4668
                goto finished;
×
4669
        }
4670

4671
        /*
4672
         * User had asked to find the matching rule (if one exist) in the
4673
         * cached rules but the list might be empty due to the inactive
4674
         * cgrulesengd. Lets emulate its behaviour of caching the rules by
4675
         * reloading the rules from the configuration file.
4676
         */
4677
        if ((flags & CGFLAG_USECACHE) && (rl.head == NULL)) {
553✔
4678
                cgroup_warn("no cached rules found, trying to reload from %s.\n",
×
4679
                            CGRULES_CONF_FILE);
4680
                ret = cgroup_reload_cached_rules();
×
4681
                if (ret != 0)
×
4682
                        goto finished;
×
4683
        }
4684

4685
        /*
4686
         * If the user did not ask for cached rules, we must parse the
4687
         * configuration to find a matching rule (if one exists).
4688
         * Else, we'll find the first match in the cached list (rl).
4689
         */
4690
        if (!(flags & CGFLAG_USECACHE)) {
553✔
4691
                cgroup_dbg("Not using cached rules for PID %d.\n", pid);
×
4692
                ret = cgroup_parse_rules(false, uid, gid, procname);
×
4693

4694
                /* The configuration file has an error!  We must exit now. */
4695
                if (ret != -1 && ret != 0) {
×
4696
                        cgroup_err("failed to parse the configuration rules\n");
×
4697
                        goto finished;
×
4698
                }
4699

4700
                /* We did not find a matching rule, so we're done. */
4701
                if (ret == 0) {
×
4702
                        cgroup_dbg("No rule found to match PID: %d, UID: %d, GID: %d\n",
×
4703
                                   pid, uid, gid);
4704
                        goto finished;
×
4705
                }
4706

4707
                /* Otherwise, we did match a rule and it's in trl. */
4708
                tmp = trl.head;
×
4709
        } else {
4710
                /* Find the first matching rule in the cached list. */
4711
                tmp = cgroup_find_matching_rule(uid, gid, pid, procname);
553✔
4712
                if (!tmp) {
553✔
4713
                        cgroup_dbg("No rule found to match PID: %d, UID: %d, GID: %d\n",
531✔
4714
                                   pid, uid, gid);
4715
                        ret = 0;
531✔
4716
                        goto finished;
531✔
4717
                }
4718
        }
4719
        cgroup_dbg("Found matching rule %s for PID: %d, UID: %d, GID: %d\n",
22✔
4720
                   tmp->username, pid, uid, gid);
4721

4722
        if (tmp->is_ignore) {
22✔
4723
                /*
4724
                 * This rule has instructed us that this pid is not to be
4725
                 * processed and should be ignored
4726
                 */
4727
                cgroup_dbg("Matching rule is an ignore rule\n");
×
4728
                ret = 0;
×
4729
                goto finished;
×
4730
        }
4731

4732
        /* If we are here, then we found a matching rule, so execute it. */
4733
        do {
4734
                cgroup_dbg("Executing rule %s for PID %d... ", tmp->username, pid);
22✔
4735

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

4811
                newdest[j] = 0;
22✔
4812
                if (strcmp(newdest, tmp->destination) != 0) {
22✔
4813
                        /* Destination tag contains templates */
4814

4815
                        cgroup_dbg("control group %s is template\n", newdest);
×
4816
                        ret = cgroup_create_template_group(newdest, tmp, flags);
×
4817
                        if (ret) {
×
4818
                                cgroup_warn("failed to create cgroup based on template %s\n",
×
4819
                                            newdest);
4820
                                goto finished;
×
4821
                        }
4822
                }
4823

4824
                /* Apply the rule */
4825
                ret = cgroup_change_cgroup_path(newdest, pid,
22✔
4826
                                                (const char * const *)tmp->controllers);
22✔
4827
                if (ret) {
22✔
4828
                        cgroup_warn("failed to apply the rule. Error was: %d\n", ret);
×
4829
                        goto finished;
×
4830
                }
4831
                cgroup_dbg("OK!\n");
22✔
4832

4833
                /*
4834
                 * Now, check for multi-line rules.  As long as the "next"
4835
                 * rule starts with '%', it's actually part of the rule that
4836
                 * we just executed.
4837
                 */
4838
                tmp = tmp->next;
22✔
4839
        } while (tmp && (tmp->username[0] == '%'));
22✔
4840

4841
finished:
22✔
4842
        return ret;
553✔
4843
}
4844

4845
int cgroup_change_cgroup_uid_gid_flags(uid_t uid, gid_t gid, pid_t pid, int flags)
×
4846
{
4847
        return cgroup_change_cgroup_flags(uid, gid, NULL, pid, flags);
×
4848
}
4849

4850
/**
4851
 * Provides backwards-compatibility with older versions of the API.
4852
 * This function is deprecated, and cgroup_change_cgroup_uid_gid_flags()
4853
 * should be used instead.  In fact, this function simply calls the newer
4854
 * one with flags set to 0 (none).
4855
 *        @param uid The UID to match
4856
 *        @param gid The GID to match
4857
 *        @param pid The PID of the process to move
4858
 *        @return 0 on success, > 0 on error
4859
 */
4860
int cgroup_change_cgroup_uid_gid(uid_t uid, gid_t gid, pid_t pid)
×
4861
{
4862
        return cgroup_change_cgroup_uid_gid_flags(uid, gid, pid, 0);
×
4863
}
4864

4865
/**
4866
 * Changes the cgroup of a program based on the path provided.  In this case,
4867
 * the user must already know into which cgroup the task should be placed and
4868
 * no rules will be parsed.
4869
 *
4870
 *  returns 0 on success.
4871
 */
4872
int cgroup_change_cgroup_path(const char *dest, pid_t pid, const char *const controllers[])
69✔
4873
{
4874
        struct dirent *task_dir = NULL;
69✔
4875
        char path[FILENAME_MAX];
4876
        struct cgroup cgrp;
4877
        int nr, ret;
4878
        pid_t tid;
4879
        DIR *dir;
4880

4881
        if (!cgroup_initialized) {
69✔
4882
                cgroup_warn("libcgroup is not initialized\n");
×
4883
                return ECGROUPNOTINITIALIZED;
×
4884
        }
4885
        memset(&cgrp, 0, sizeof(struct cgroup));
69✔
4886

4887

4888
        if (is_cgroup_mode_unified() && !controllers) {
69✔
4889
                /*
4890
                 * Do not require the user to pass in an array of controller strings on
4891
                 * cgroup v2 systems.  The hierarchy will be the same regardless of
4892
                 * whether controllers are provided or not.
4893
                 */
4894
                strncpy(cgrp.name, dest, FILENAME_MAX);
1✔
4895
                cgrp.name[FILENAME_MAX-1] = '\0';
1✔
4896
        } else {
4897
                if (!controllers)
68✔
4898
                        return ECGINVAL;
×
4899

4900
                ret = cg_prepare_cgroup(&cgrp, pid, dest, controllers);
68✔
4901
                if (ret)
68✔
4902
                        return ret;
×
4903
        }
4904

4905
        /* Add process to cgroup */
4906
        ret = cgroup_attach_task_pid(&cgrp, pid);
69✔
4907
        if (ret) {
69✔
4908
                cgroup_warn("cgroup_attach_task_pid failed: %d\n", ret);
4✔
4909
                goto finished;
4✔
4910
        }
4911

4912
        /* Add all threads to cgroup */
4913
        snprintf(path, FILENAME_MAX, "/proc/%d/task/", pid);
65✔
4914
        dir = opendir(path);
65✔
4915
        if (!dir) {
65✔
4916
                last_errno = errno;
×
4917
                ret = ECGOTHER;
×
4918
                goto finished;
×
4919
        }
4920

4921
        while ((task_dir = readdir(dir)) != NULL) {
266✔
4922
                nr = sscanf(task_dir->d_name, "%i", &tid);
201✔
4923
                if (nr < 1)
201✔
4924
                        continue;
130✔
4925

4926
                if (tid == pid)
71✔
4927
                        continue;
65✔
4928

4929
                ret = cgroup_attach_task_pid(&cgrp, tid);
6✔
4930
                if (ret) {
6✔
4931
                        cgroup_warn("cgroup_attach_task_pid failed: %d\n", ret);
×
4932
                        break;
×
4933
                }
4934
        }
4935

4936
        closedir(dir);
65✔
4937

4938
finished:
69✔
4939
        cgroup_free_controllers(&cgrp);
69✔
4940

4941
        return ret;
69✔
4942
}
4943

4944
/**
4945
 * Changes the cgroup of all running PIDs based on the rules in the config file.
4946
 * If a rules exists for a PID, then the PID is placed in the correct group.
4947
 *
4948
 * This function may be called after creating new control groups to move
4949
 * running PIDs into the newly created control groups.
4950
 *        @return 0 on success, < 0 on error
4951
 */
4952
int cgroup_change_all_cgroups(void)
2✔
4953
{
4954
        struct dirent *pid_dir = NULL;
2✔
4955
        char *path = "/proc/";
2✔
4956
        DIR *dir;
4957

4958
        dir = opendir(path);
2✔
4959
        if (!dir)
2✔
4960
                return -ECGOTHER;
×
4961

4962
        while ((pid_dir = readdir(dir)) != NULL) {
543✔
4963
                int err, pid;
4964
                uid_t euid;
4965
                gid_t egid;
4966
                char *procname = NULL;
541✔
4967

4968
                err = sscanf(pid_dir->d_name, "%i", &pid);
541✔
4969
                if (err < 1)
541✔
4970
                        continue;
129✔
4971

4972
                err = cgroup_get_uid_gid_from_procfs(pid, &euid, &egid);
412✔
4973
                if (err)
412✔
4974
                        continue;
×
4975

4976
                err = cgroup_get_procname_from_procfs(pid, &procname);
412✔
4977
                if (err)
412✔
4978
                        continue;
×
4979

4980
                err = cgroup_change_cgroup_flags(euid, egid, procname, pid, CGFLAG_USECACHE);
412✔
4981
                if (err)
412✔
4982
                        cgroup_dbg("cgroup change pid %i failed\n", pid);
×
4983

4984
                free(procname);
412✔
4985
        }
4986

4987
        closedir(dir);
2✔
4988
        return 0;
2✔
4989
}
4990

4991
/**
4992
 * Print the cached rules table.  This function should be called only after
4993
 * first calling cgroup_parse_config(), but it will work with an empty rule
4994
 * list.
4995
 *        @param fp The file stream to print to
4996
 */
4997
void cgroup_print_rules_config(FILE *fp)
2✔
4998
{
4999
        /* Iterator */
5000
        struct cgroup_rule *itr = NULL;
2✔
5001

5002
        /* Loop variable */
5003
        int i = 0;
2✔
5004

5005
        pthread_rwlock_rdlock(&rl_lock);
2✔
5006

5007
        if (!(rl.head)) {
2✔
5008
                fprintf(fp, "The rules table is empty.\n\n");
×
5009
                pthread_rwlock_unlock(&rl_lock);
×
5010
                return;
×
5011
        }
5012

5013
        itr = rl.head;
2✔
5014
        while (itr) {
4✔
5015
                fprintf(fp, "Rule: %s", itr->username);
2✔
5016
                if (itr->procname)
2✔
5017
                        fprintf(fp, ":%s", itr->procname);
2✔
5018
                fprintf(fp, "\n");
2✔
5019

5020
                if (itr->uid == CGRULE_WILD)
2✔
5021
                        fprintf(fp, "  UID: any\n");
2✔
5022
                else if (itr->uid == CGRULE_INVALID)
×
5023
                        fprintf(fp, "  UID: N/A\n");
×
5024
                else
5025
                        fprintf(fp, "  UID: %d\n", itr->uid);
×
5026

5027
                if (itr->gid == CGRULE_WILD)
2✔
5028
                        fprintf(fp, "  GID: any\n");
2✔
5029
                else if (itr->gid == CGRULE_INVALID)
×
5030
                        fprintf(fp, "  GID: N/A\n");
×
5031
                else
5032
                        fprintf(fp, "  GID: %d\n", itr->gid);
×
5033

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

5036
                fprintf(fp, "  CONTROLLERS:\n");
2✔
5037
                for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
36✔
5038
                        if (itr->controllers[i])
34✔
5039
                                fprintf(fp, "    %s\n", itr->controllers[i]);
2✔
5040
                }
5041
                fprintf(fp, "  OPTIONS:\n");
2✔
5042
                if (itr->is_ignore)
2✔
5043
                        fprintf(fp, "    IS_IGNORE: True\n");
×
5044
                else
5045
                        fprintf(fp, "    IS_IGNORE: False\n");
2✔
5046
                fprintf(fp, "\n");
2✔
5047
                itr = itr->next;
2✔
5048
        }
5049
        pthread_rwlock_unlock(&rl_lock);
2✔
5050
}
5051

5052
/**
5053
 * Reloads the rules list, using the given configuration file.
5054
 * This function is probably NOT thread safe (calls cgroup_parse_rules()).
5055
 *        @return 0 on success, > 0 on failure
5056
 */
5057
int cgroup_reload_cached_rules(void)
×
5058
{
5059
        /* Return codes */
5060
        int ret = 0;
×
5061

5062
        cgroup_dbg("Reloading cached rules from %s.\n", CGRULES_CONF_FILE);
×
5063
        ret = cgroup_parse_rules(true, CGRULE_INVALID, CGRULE_INVALID, NULL);
×
5064
        if (ret) {
×
5065
                cgroup_warn("error parsing configuration file '%s': %d\n", CGRULES_CONF_FILE, ret);
×
5066
                ret = ECGRULESPARSEFAIL;
×
5067
                goto finished;
×
5068
        }
5069

5070
        #ifdef CGROUP_DEBUG
5071
        cgroup_print_rules_config(stdout);
5072
        #endif
5073
finished:
×
5074
        return ret;
×
5075
}
5076

5077
/**
5078
 * Initializes the rules cache.
5079
 *        @return 0 on success, > 0 on error
5080
 */
5081
int cgroup_init_rules_cache(void)
2✔
5082
{
5083
        /* Return codes */
5084
        int ret = 0;
2✔
5085

5086
        /* Attempt to read the configuration file and cache the rules. */
5087
        ret = cgroup_parse_rules(true, CGRULE_INVALID, CGRULE_INVALID, NULL);
2✔
5088
        if (ret)
2✔
5089
                cgroup_dbg("Could not initialize rule cache, error was: %d\n", ret);
×
5090

5091
        return ret;
2✔
5092
}
5093

5094
/**
5095
 * cgroup_get_current_controller_path
5096
 * @pid: pid of the current process for which the path is to be determined
5097
 * @controller: name of the controller for which to determine current path
5098
 * @current_path: a pointer that is filled with the value of the current
5099
 *                path as seen in /proc/<pid>/cgroup
5100
 */
5101
int cgroup_get_current_controller_path(pid_t pid, const char *controller, char **current_path)
7✔
5102
{
5103
        enum cg_version_t version;
5104
        enum cg_setup_mode_t mode;
5105
        FILE *pid_cgrp_fd = NULL;
7✔
5106
        bool unified = false;
7✔
5107
        char *path = NULL;
7✔
5108
        int ret;
5109

5110
        if (!cgroup_initialized) {
7✔
5111
                cgroup_warn("libcgroup is not initialized\n");
×
5112
                return ECGROUPNOTINITIALIZED;
×
5113
        }
5114

5115
        if (!current_path)
7✔
5116
                return ECGOTHER;
×
5117

5118
        mode = cgroup_setup_mode();
7✔
5119
        if (mode == CGROUP_MODE_LEGACY && !controller)
7✔
5120
                return ECGOTHER;
×
5121

5122
        /*
5123
         * both unified/hybrid can have the controller mounted as
5124
         * cgroup v2 version.
5125
         */
5126
        if  (!controller) {
7✔
5127
                unified = true;
4✔
5128
        } else {
5129
                ret = cgroup_get_controller_version(controller, &version);
3✔
5130
                if (ret) {
3✔
5131
                        cgroup_warn("Failed to get version of the controller: %s\n", controller);
1✔
5132
                        ret = ECGINVAL;
1✔
5133
                        goto cleanup_path;
1✔
5134
                }
5135
                unified = (version == CGROUP_V2);
2✔
5136
        }
5137

5138
        ret = asprintf(&path, "/proc/%d/cgroup", pid);
6✔
5139
        if (ret <= 0) {
6✔
5140
                cgroup_warn("cannot allocate memory (/proc/pid/cgroup) ret %d\n", ret);
×
5141
                return ret;
×
5142
        }
5143

5144
        ret = ECGROUPNOTEXIST;
6✔
5145
        pid_cgrp_fd = fopen(path, "re");
6✔
5146
        if (!pid_cgrp_fd)
6✔
5147
                goto cleanup_path;
×
5148

5149
        /*
5150
         * Why do we grab the cg_mount_table_lock?, the reason is that the
5151
         * cgroup of a pid can change via the cgroup_attach_task_pid() call.
5152
         * To make sure, we return consistent and safe results, we acquire the
5153
         * lock upfront. We can optimize by acquiring and releasing
5154
         * the lock in the while loop, but that will be more expensive.
5155
         */
5156
        pthread_rwlock_rdlock(&cg_mount_table_lock);
6✔
5157
        while (!feof(pid_cgrp_fd)) {
6✔
5158
                char controllers[FILENAME_MAX];
5159
                char cgrp_path[FILENAME_MAX];
5160
                char *savedptr;
5161
                char *token;
5162
                int num;
5163

5164
                /*
5165
                 * with unified mode, the /proc/pid/cgroup the output is
5166
                 * similar to that of cgroup legacy and hybrid modes:
5167
                 * hierarchy-ID:controller-list:cgroup-path
5168
                 *
5169
                 * the difference is that in cgroup v2:
5170
                 * - hierarchy-ID is always 0 (one hierarchy allowed)
5171
                 * - controller-list is empty
5172
                 */
5173
                if (mode == CGROUP_MODE_UNIFIED || unified) {
6✔
5174
                        ret = fscanf(pid_cgrp_fd, "%d::%4096s\n", &num, cgrp_path);
6✔
5175
                        if (ret != 2) {
6✔
5176
                                /*
5177
                                 * we are interested only in unified format
5178
                                 * line, skip this line.
5179
                                 */
5180
                                if (unified) {
×
5181
                                        ret = fscanf(pid_cgrp_fd, "%*[^\n]\n");
×
5182
                                        if (ret == 0)
×
5183
                                                continue;
×
5184

5185
                                        if (ret == EOF) {
×
5186
                                                last_errno = errno;
×
5187
                                                ret = ECGEOF;
×
5188
                                                goto done;
6✔
5189
                                        }
5190
                                }
5191

5192
                                cgroup_warn("read failed for pid_cgroup_fd ret %d\n", ret);
×
5193
                                last_errno = errno;
×
5194
                                ret = ECGOTHER;
×
5195
                                goto done;
×
5196
                        }
5197

5198
                        /* check if the controller is enabled in cgroup v2 */
5199
                        if (controller) {
6✔
5200
                                ret = cgroupv2_controller_enabled(cgrp_path, controller);
2✔
5201
                                if (ret)
2✔
5202
                                        goto done;
1✔
5203
                        }
5204

5205
                        *current_path = strdup(cgrp_path);
5✔
5206
                        if (!*current_path) {
5✔
5207
                                last_errno = errno;
×
5208
                                ret = ECGOTHER;
×
5209
                                goto done;
×
5210
                        }
5211
                        ret = 0;
5✔
5212
                        goto done;
5✔
5213
                }
5214

5215
                /*
5216
                 * 4096 == FILENAME_MAX, keeping the coverity happy with precision
5217
                 * for the cgrp_path.
5218
                 */
5219
                ret = fscanf(pid_cgrp_fd, "%d:%[^:]:%4096s\n", &num, controllers, cgrp_path);
×
5220
                /*
5221
                 * Magic numbers like "3" seem to be integrating into my daily
5222
                 * life, I need some magic to help make them disappear :)
5223
                 */
5224
                if (ret != 3) {
×
5225
                        cgroup_warn("read failed for pid_cgrp_fd ret %d\n", ret);
×
5226
                        last_errno = errno;
×
5227
                        ret = ECGOTHER;
×
5228
                        goto done;
×
5229
                }
5230

5231
                token = strtok_r(controllers, ",", &savedptr);
×
5232
                while (token) {
×
5233
                        if (strncmp(controller, token, strlen(controller) + 1) == 0) {
×
5234
                                *current_path = strdup(cgrp_path);
×
5235
                                if (!*current_path) {
×
5236
                                        last_errno = errno;
×
5237
                                        ret = ECGOTHER;
×
5238
                                        goto done;
×
5239
                                }
5240
                                ret = 0;
×
5241
                                goto done;
×
5242
                        }
5243
                        token = strtok_r(NULL, ",", &savedptr);
×
5244
                }
5245
        }
5246

5247
done:
×
5248
        pthread_rwlock_unlock(&cg_mount_table_lock);
6✔
5249
        fclose(pid_cgrp_fd);
6✔
5250
cleanup_path:
7✔
5251
        free(path);
7✔
5252

5253
        return ret;
7✔
5254
}
5255

5256
const char *cgroup_strerror(int code)
48✔
5257
{
5258
        int idx = code % ECGROUPNOTCOMPILED;
48✔
5259

5260
        if (code == ECGOTHER) {
48✔
5261
#ifdef STRERROR_R_CHAR_P
5262
                return strerror_r(cgroup_get_last_errno(), errtext, MAXLEN);
20✔
5263
#else
5264
                return strerror_r(cgroup_get_last_errno(), errtext, sizeof(errtext)) ?
5265
                        "unknown error" : errtext;
5266
#endif
5267
        }
5268
        if (idx >= ARRAY_SIZE(cgroup_strerror_codes))
28✔
5269
                return "Invalid error code";
×
5270

5271
        return cgroup_strerror_codes[idx];
28✔
5272
}
5273

5274
/**
5275
 * Return last errno, which caused ECGOTHER error.
5276
 */
5277
int cgroup_get_last_errno(void)
20✔
5278
{
5279
        return last_errno;
20✔
5280
}
5281

5282
static int cg_walk_node(FTS *fts, FTSENT *ent, const int depth, struct cgroup_file_info *info,
8,678✔
5283
                        int dir)
5284
{
5285
        int ret = 0;
8,678✔
5286

5287
        if (!cgroup_initialized)
8,678✔
5288
                return ECGROUPNOTINITIALIZED;
×
5289

5290
        cgroup_dbg("seeing file %s\n", ent->fts_path);
8,678✔
5291

5292
        info->path = ent->fts_name;
8,678✔
5293
        info->parent = ent->fts_parent->fts_name;
8,678✔
5294
        info->full_path = ent->fts_path;
8,678✔
5295
        info->depth = ent->fts_level;
8,678✔
5296
        info->type = CGROUP_FILE_TYPE_OTHER;
8,678✔
5297

5298
        if (depth && (info->depth > depth))
8,678✔
5299
                return 0;
×
5300

5301
        switch (ent->fts_info) {
8,678✔
5302
        case FTS_DNR:
×
5303
        case FTS_ERR:
5304
                errno = ent->fts_errno;
×
5305
                break;
×
5306
        case FTS_D:
168✔
5307
                if (dir & CGROUP_WALK_TYPE_PRE_DIR)
168✔
5308
                        info->type = CGROUP_FILE_TYPE_DIR;
124✔
5309
                break;
168✔
5310
        case FTS_DC:
168✔
5311
        case FTS_NSOK:
5312
        case FTS_NS:
5313
        case FTS_DP:
5314
                if (dir & CGROUP_WALK_TYPE_POST_DIR)
168✔
5315
                        info->type = CGROUP_FILE_TYPE_DIR;
71✔
5316
                break;
168✔
5317
        case FTS_F:
8,342✔
5318
                info->type = CGROUP_FILE_TYPE_FILE;
8,342✔
5319
                break;
8,342✔
5320
        case FTS_DEFAULT:
×
5321
                break;
×
5322
        }
5323
        return ret;
8,678✔
5324
}
5325

5326
int cgroup_walk_tree_next(int depth, void **handle, struct cgroup_file_info *info, int base_level)
8,678✔
5327
{
5328
        struct cgroup_tree_handle *entry;
5329
        FTSENT *ent;
5330
        int ret = 0;
8,678✔
5331

5332
        if (!cgroup_initialized)
8,678✔
5333
                return ECGROUPNOTINITIALIZED;
×
5334

5335
        if (!handle)
8,678✔
5336
                return ECGINVAL;
×
5337

5338
        entry = (struct cgroup_tree_handle *) *handle;
8,678✔
5339

5340
        ent = fts_read(entry->fts);
8,678✔
5341
        if (!ent)
8,678✔
5342
                return ECGEOF;
41✔
5343
        if (!base_level && depth)
8,637✔
5344
                base_level = ent->fts_level + depth;
×
5345

5346
        ret = cg_walk_node(entry->fts, ent, base_level, info, entry->flags);
8,637✔
5347

5348
        *handle = entry;
8,637✔
5349
        return ret;
8,637✔
5350
}
5351

5352
int cgroup_walk_tree_end(void **handle)
41✔
5353
{
5354
        struct cgroup_tree_handle *entry;
5355

5356
        if (!cgroup_initialized)
41✔
5357
                return ECGROUPNOTINITIALIZED;
×
5358

5359
        if (!handle)
41✔
5360
                return ECGINVAL;
×
5361

5362
        entry = (struct cgroup_tree_handle *) *handle;
41✔
5363

5364
        fts_close(entry->fts);
41✔
5365
        free(entry);
41✔
5366
        *handle = NULL;
41✔
5367

5368
        return 0;
41✔
5369
}
5370

5371
/* TODO: Need to decide a better place to put this function. */
5372
int cgroup_walk_tree_begin(const char *controller, const char *base_path, int depth, void **handle,
41✔
5373
                           struct cgroup_file_info *info, int *base_level)
5374
{
5375
        struct cgroup_tree_handle *entry;
5376
        char full_path[FILENAME_MAX];
5377
        char *cg_path[2];
5378
        FTSENT *ent;
5379
        int ret = 0;
41✔
5380

5381
        if (!cgroup_initialized)
41✔
5382
                return ECGROUPNOTINITIALIZED;
×
5383

5384
        if (!handle)
41✔
5385
                return ECGINVAL;
×
5386

5387
        cgroup_dbg("path is %s\n", base_path);
41✔
5388

5389
        if (!cg_build_path(base_path, full_path, controller))
41✔
5390
                return ECGOTHER;
×
5391

5392
        entry = calloc(1, sizeof(struct cgroup_tree_handle));
41✔
5393

5394
        if (!entry) {
41✔
5395
                last_errno = errno;
×
5396
                *handle = NULL;
×
5397
                return ECGOTHER;
×
5398
        }
5399

5400
        entry->flags |= CGROUP_WALK_TYPE_PRE_DIR;
41✔
5401

5402
        *base_level = 0;
41✔
5403
        cg_path[0] = full_path;
41✔
5404
        cg_path[1] = NULL;
41✔
5405

5406
        entry->fts = fts_open(cg_path, FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT, NULL);
41✔
5407
        if (entry->fts == NULL) {
41✔
5408
                free(entry);
×
5409
                last_errno = errno;
×
5410
                *handle = NULL;
×
5411
                return ECGOTHER;
×
5412
        }
5413
        ent = fts_read(entry->fts);
41✔
5414
        if (!ent) {
41✔
5415
                cgroup_warn("fts_read failed\n");
×
5416
                fts_close(entry->fts);
×
5417
                free(entry);
×
5418
                *handle = NULL;
×
5419
                return ECGINVAL;
×
5420
        }
5421
        if (!*base_level && depth)
41✔
5422
                *base_level = ent->fts_level + depth;
×
5423

5424
        ret = cg_walk_node(entry->fts, ent, *base_level, info, entry->flags);
41✔
5425
        if (ret != 0) {
41✔
5426
                fts_close(entry->fts);
×
5427
                free(entry);
×
5428
                *handle = NULL;
×
5429
        } else {
5430
                *handle = entry;
41✔
5431
        }
5432
        return ret;
41✔
5433
}
5434

5435
int cgroup_walk_tree_set_flags(void **handle, int flags)
27✔
5436
{
5437
        struct cgroup_tree_handle *entry;
5438

5439
        if (!cgroup_initialized)
27✔
5440
                return ECGROUPNOTINITIALIZED;
×
5441

5442
        if (!handle)
27✔
5443
                return ECGINVAL;
×
5444

5445
        if ((flags & CGROUP_WALK_TYPE_PRE_DIR) &&
27✔
5446
            (flags & CGROUP_WALK_TYPE_POST_DIR))
×
5447
                return ECGINVAL;
×
5448

5449
        entry = (struct cgroup_tree_handle *) *handle;
27✔
5450
        entry->flags = flags;
27✔
5451

5452
        *handle = entry;
27✔
5453
        return 0;
27✔
5454
}
5455

5456
/*
5457
 * This parses a stat line which is in the form of (name value) pair
5458
 * separated by a space.
5459
 */
5460
static int cg_read_stat(FILE *fp, struct cgroup_stat *cgrp_stat)
×
5461
{
5462
        char *saveptr = NULL;
×
5463
        ssize_t read_bytes;
5464
        char *line = NULL;
×
5465
        size_t len = 0;
×
5466
        char *token;
5467
        int ret = 0;
×
5468

5469
        read_bytes = getline(&line, &len, fp);
×
5470
        if (read_bytes == -1) {
×
5471
                ret = ECGEOF;
×
5472
                goto out_free;
×
5473
        }
5474

5475
        token = strtok_r(line, " ", &saveptr);
×
5476
        if (!token) {
×
5477
                ret = ECGINVAL;
×
5478
                goto out_free;
×
5479
        }
5480
        strncpy(cgrp_stat->name, token, FILENAME_MAX - 1);
×
5481

5482
        token = strtok_r(NULL, " ", &saveptr);
×
5483
        if (!token) {
×
5484
                ret = ECGINVAL;
×
5485
                goto out_free;
×
5486
        }
5487
        strncpy(cgrp_stat->value, token, CG_VALUE_MAX - 1);
×
5488

5489
out_free:
×
5490
        free(line);
×
5491

5492
        return ret;
×
5493
}
5494

5495
int cgroup_read_value_end(void **handle)
219✔
5496
{
5497
        FILE *fp;
5498

5499
        if (!cgroup_initialized)
219✔
5500
                return ECGROUPNOTINITIALIZED;
×
5501

5502
        if (!handle)
219✔
5503
                return ECGINVAL;
×
5504

5505
        fp = (FILE *)*handle;
219✔
5506
        fclose(fp);
219✔
5507
        *handle = NULL;
219✔
5508

5509
        return 0;
219✔
5510
}
5511

5512
int cgroup_read_value_next(void **handle, char *buffer, int max)
291✔
5513
{
5514
        char *ret_c;
5515
        int ret = 0;
291✔
5516
        FILE *fp;
5517

5518
        if (!cgroup_initialized)
291✔
5519
                return ECGROUPNOTINITIALIZED;
×
5520

5521
        if (!buffer || !handle)
291✔
5522
                return ECGINVAL;
×
5523

5524
        fp = (FILE *)*handle;
291✔
5525
        ret_c = fgets(buffer, max, fp);
291✔
5526
        if (ret_c == NULL)
291✔
5527
                ret = ECGEOF;
181✔
5528

5529
        return ret;
291✔
5530
}
5531

5532
int cgroup_read_value_begin(const char * const controller, const char *path,
222✔
5533
                            const char * const name, void **handle, char *buffer, int max)
5534
{
5535
        char stat_file[FILENAME_MAX + sizeof(name)];
5536
        char stat_path[FILENAME_MAX];
5537
        char *ret_c = NULL;
222✔
5538
        int ret = 0;
222✔
5539
        FILE *fp;
5540

5541
        if (!cgroup_initialized)
222✔
5542
                return ECGROUPNOTINITIALIZED;
×
5543

5544
        if (!buffer || !handle)
222✔
5545
                return ECGINVAL;
×
5546

5547
        if (!cg_build_path(path, stat_path, controller))
222✔
5548
                return ECGOTHER;
×
5549

5550
        snprintf(stat_file, sizeof(stat_file), "%s/%s", stat_path, name);
222✔
5551
        fp = fopen(stat_file, "re");
222✔
5552
        if (!fp) {
222✔
5553
                cgroup_warn("fopen failed\n");
3✔
5554
                last_errno = errno;
3✔
5555
                *handle = NULL;
3✔
5556
                return ECGOTHER;
3✔
5557
        }
5558

5559
        ret_c = fgets(buffer, max, fp);
219✔
5560
        if (ret_c == NULL)
219✔
5561
                ret = ECGEOF;
8✔
5562

5563
        *handle = fp;
219✔
5564

5565
        return ret;
219✔
5566
}
5567

5568
int cgroup_read_stats_end(void **handle)
×
5569
{
5570
        FILE *fp;
5571

5572
        if (!cgroup_initialized)
×
5573
                return ECGROUPNOTINITIALIZED;
×
5574

5575
        if (!handle)
×
5576
                return ECGINVAL;
×
5577

5578
        fp = (FILE *)*handle;
×
5579
        if (fp == NULL)
×
5580
                return ECGINVAL;
×
5581

5582
        fclose(fp);
×
5583

5584
        return 0;
×
5585
}
5586

5587
int cgroup_read_stats_next(void **handle, struct cgroup_stat *cgrp_stat)
×
5588
{
5589
        FILE *fp;
5590
        int ret = 0;
×
5591

5592
        if (!cgroup_initialized)
×
5593
                return ECGROUPNOTINITIALIZED;
×
5594

5595
        if (!handle || !cgrp_stat)
×
5596
                return ECGINVAL;
×
5597

5598
        fp = (FILE *)*handle;
×
5599
        ret = cg_read_stat(fp, cgrp_stat);
×
5600
        *handle = fp;
×
5601

5602
        return ret;
×
5603
}
5604

5605
/* TODO: Need to decide a better place to put this function. */
5606
int cgroup_read_stats_begin(const char *controller, const char *path, void **handle,
×
5607
                            struct cgroup_stat *cgrp_stat)
5608
{
5609
        char stat_file[FILENAME_MAX + sizeof(".stat")];
5610
        char stat_path[FILENAME_MAX];
5611
        int ret = 0;
×
5612
        FILE *fp;
5613

5614
        if (!cgroup_initialized)
×
5615
                return ECGROUPNOTINITIALIZED;
×
5616

5617
        if (!cgrp_stat || !handle)
×
5618
                return ECGINVAL;
×
5619

5620
        if (!cg_build_path(path, stat_path, controller))
×
5621
                return ECGOTHER;
×
5622

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

5625
        fp = fopen(stat_file, "re");
×
5626
        if (!fp) {
×
5627
                cgroup_warn("fopen failed\n");
×
5628
                return ECGINVAL;
×
5629
        }
5630

5631
        ret = cg_read_stat(fp, cgrp_stat);
×
5632
        *handle = fp;
×
5633

5634
        return ret;
×
5635
}
5636

5637
int cgroup_get_task_end(void **handle)
×
5638
{
5639
        if (!cgroup_initialized)
×
5640
                return ECGROUPNOTINITIALIZED;
×
5641

5642
        if (!*handle)
×
5643
                return ECGINVAL;
×
5644

5645
        fclose((FILE *) *handle);
×
5646
        *handle = NULL;
×
5647

5648
        return 0;
×
5649
}
5650

5651
int cgroup_get_task_next(void **handle, pid_t *pid)
×
5652
{
5653
        int ret;
5654

5655
        if (!cgroup_initialized)
×
5656
                return ECGROUPNOTINITIALIZED;
×
5657

5658
        if (!handle)
×
5659
                return ECGINVAL;
×
5660

5661
        ret = fscanf((FILE *) *handle, "%u", pid);
×
5662

5663
        if (ret != 1) {
×
5664
                if (ret == EOF)
×
5665
                        return ECGEOF;
×
5666
                last_errno = errno;
×
5667
                return ECGOTHER;
×
5668
        }
5669

5670
        return 0;
×
5671
}
5672

5673
int cgroup_get_task_begin(const char *cgrp, const char *controller, void **handle, pid_t *pid)
×
5674
{
5675
        char path[FILENAME_MAX];
5676
        char *fullpath = NULL;
×
5677
        int ret = 0;
×
5678

5679
        if (!cgroup_initialized)
×
5680
                return ECGROUPNOTINITIALIZED;
×
5681

5682
        if (!cg_build_path(cgrp, path, controller))
×
5683
                return ECGOTHER;
×
5684

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

5687
        if (ret < 0) {
×
5688
                last_errno = errno;
×
5689
                return ECGOTHER;
×
5690
        }
5691

5692
        *handle = (void *) fopen(fullpath, "re");
×
5693
        free(fullpath);
×
5694

5695
        if (!*handle) {
×
5696
                last_errno = errno;
×
5697
                return ECGOTHER;
×
5698
        }
5699
        ret = cgroup_get_task_next(handle, pid);
×
5700

5701
        return ret;
×
5702
}
5703

5704
int cgroup_get_controller_end(void **handle)
8✔
5705
{
5706
        int *pos = (int *) *handle;
8✔
5707

5708
        if (!cgroup_initialized)
8✔
5709
                return ECGROUPNOTINITIALIZED;
×
5710

5711
        if (!pos)
8✔
5712
                return ECGINVAL;
×
5713

5714
        free(pos);
8✔
5715
        *handle = NULL;
8✔
5716

5717
        return 0;
8✔
5718
}
5719

5720
int cgroup_get_controller_next(void **handle, struct cgroup_mount_point *info)
72✔
5721
{
5722
        int *pos = (int *) *handle;
72✔
5723
        int ret = 0;
72✔
5724

5725
        if (!cgroup_initialized)
72✔
5726
                return ECGROUPNOTINITIALIZED;
×
5727

5728
        if (!pos)
72✔
5729
                return ECGINVAL;
×
5730

5731
        if (!info)
72✔
5732
                return ECGINVAL;
×
5733

5734
        pthread_rwlock_rdlock(&cg_mount_table_lock);
72✔
5735

5736
        if (cg_mount_table[*pos].name[0] == '\0') {
72✔
5737
                ret = ECGEOF;
×
5738
                goto out_unlock;
×
5739
        }
5740

5741
        if (strncmp(cg_mount_table[*pos].name, CGRP_FILE_PREFIX, CONTROL_NAMELEN_MAX) == 0)
72✔
5742
                /*
5743
                 * For now, hide the "cgroup" pseudo-controller from the user.  This may be
5744
                 * worth revisiting in the future.
5745
                 */
5746
                (*pos)++;
8✔
5747

5748
        if (cg_mount_table[*pos].name[0] == '\0') {
72✔
5749
                ret = ECGEOF;
8✔
5750
                goto out_unlock;
8✔
5751
        }
5752

5753
        strncpy(info->name, cg_mount_table[*pos].name, FILENAME_MAX - 1);
64✔
5754
        info->name[FILENAME_MAX - 1] = '\0';
64✔
5755

5756
        strncpy(info->path, cg_mount_table[*pos].mount.path, FILENAME_MAX - 1);
64✔
5757
        info->path[FILENAME_MAX - 1] = '\0';
64✔
5758

5759
        (*pos)++;
64✔
5760
        *handle = pos;
64✔
5761

5762
out_unlock:
72✔
5763
        pthread_rwlock_unlock(&cg_mount_table_lock);
72✔
5764

5765
        return ret;
72✔
5766
}
5767

5768
int cgroup_get_controller_begin(void **handle, struct cgroup_mount_point *info)
8✔
5769
{
5770
        int *pos;
5771

5772
        if (!cgroup_initialized)
8✔
5773
                return ECGROUPNOTINITIALIZED;
×
5774

5775
        if (!info)
8✔
5776
                return ECGINVAL;
×
5777

5778
        pos = malloc(sizeof(int));
8✔
5779

5780
        if (!pos) {
8✔
5781
                last_errno = errno;
×
5782
                return ECGOTHER;
×
5783
        }
5784

5785
        *pos = 0;
8✔
5786

5787
        *handle = pos;
8✔
5788

5789
        return cgroup_get_controller_next(handle, info);
8✔
5790
}
5791

5792
/**
5793
 * Get process data (euid and egid) from /proc/<pid>/status file.
5794
 * @param pid: The process id
5795
 * @param euid: The uid of param pid
5796
 * @param egid: The gid of param pid
5797
 * @return 0 on success, > 0 on error.
5798
 */
5799
int cgroup_get_uid_gid_from_procfs(pid_t pid, uid_t *euid, gid_t *egid)
553✔
5800
{
5801
        char path[FILENAME_MAX];
5802
        uid_t ruid, suid, fsuid;
5803
        gid_t rgid, sgid, fsgid;
5804
        bool found_euid = false;
553✔
5805
        bool found_egid = false;
553✔
5806
        char buf[4092];
5807
        FILE *f;
5808

5809
        sprintf(path, "/proc/%d/status", pid);
553✔
5810
        f = fopen(path, "re");
553✔
5811
        if (!f)
553✔
5812
                return ECGROUPNOTEXIST;
×
5813

5814
        while (fgets(buf, sizeof(buf), f)) {
5,530✔
5815
                if (!strncmp(buf, "Uid:", 4)) {
5,530✔
5816
                        if (sscanf((buf + strlen("Uid:") + 1), "%d%d%d%d",
553✔
5817
                                    &ruid, euid, &suid, &fsuid) != 4)
5818
                                break;
×
5819
                        cgroup_dbg("Scanned proc values are %d %d %d %d\n",
553✔
5820
                                   ruid, *euid, suid, fsuid);
5821
                        found_euid = true;
553✔
5822
                } else if (!strncmp(buf, "Gid:", 4)) {
4,977✔
5823
                        if (sscanf((buf + strlen("Gid:") + 1), "%d%d%d%d",
553✔
5824
                                   &rgid, egid, &sgid, &fsgid) != 4)
5825
                                break;
×
5826
                        cgroup_dbg("Scanned proc values are %d %d %d %d\n",
553✔
5827
                                   rgid, *egid, sgid, fsgid);
5828
                        found_egid = true;
553✔
5829
                }
5830
                if (found_euid && found_egid)
5,530✔
5831
                        break;
553✔
5832
        }
5833
        fclose(f);
553✔
5834

5835
        if (!found_euid || !found_egid) {
553✔
5836
                /*
5837
                 * This method doesn't match the file format of
5838
                 * /proc/<pid>/status. The format has been changed and we
5839
                 * should catch up the change.
5840
                 */
5841
                cgroup_warn("invalid file format of /proc/%d/status\n", pid);
×
5842
                return ECGFAIL;
×
5843
        }
5844
        return 0;
553✔
5845
}
5846

5847
/**
5848
 * Given a pid, this function will return the controllers and cgroups that
5849
 * the pid is a member of. The caller is expected to allocate the
5850
 * controller_list[] and cgroup_list[] arrays as well as null each entry in
5851
 * the arrays.  This function will allocate the necessary memory for each
5852
 * string within the arrays.
5853
 *
5854
 *        @param pid The process id
5855
 *        @param cgrp_list[] An array of char pointers to hold the cgroups
5856
 *        @param controller_list[] An array of char pointers to hold the list
5857
 *               of controllers
5858
 *        @param list_len The size of the arrays
5859
 */
5860
STATIC int cg_get_cgroups_from_proc_cgroups(pid_t pid, char *cgrp_list[],
18✔
5861
                                            char *controller_list[], int list_len)
5862
{
5863
        char path[FILENAME_MAX];
5864
        char *stok_buff = NULL;
18✔
5865
        size_t buff_len;
5866
        char buf[4092];
5867
        int ret = 0;
18✔
5868
        int idx = 0;
18✔
5869
        FILE *f;
5870

5871
#ifdef UNIT_TEST
5872
        sprintf(path, "%s", TEST_PROC_PID_CGROUP_FILE);
18✔
5873
#else
5874
        sprintf(path, "/proc/%d/cgroup", pid);
×
5875
#endif
5876
        f = fopen(path, "re");
18✔
5877
        if (!f)
18✔
5878
                return ECGROUPNOTEXIST;
×
5879

5880
        while (fgets(buf, sizeof(buf), f)) {
71✔
5881
                /*
5882
                 * Each line in /proc/{pid}/cgroup is like the following:
5883
                 *
5884
                 * {cg#}:{controller}:{cgname}
5885
                 *
5886
                 * e.g.
5887
                 * 7:devices:/user.slice
5888
                 */
5889

5890
                /* Read in the cgroup number.  We don't care about it */
5891
                stok_buff = strtok(buf, ":");
54✔
5892
                /* Read in the controller name */
5893
                stok_buff = strtok(NULL, ":");
54✔
5894

5895
                /*
5896
                 * After this point, we have allocated memory.  If we return
5897
                 * an error code after this, it's up to us to free the memory
5898
                 * we allocated
5899
                 */
5900
                controller_list[idx] = strndup(stok_buff, strlen(stok_buff) + 1);
54✔
5901

5902
                /* Read in the cgroup name */
5903
                stok_buff = strtok(NULL, ":");
54✔
5904

5905
                if (stok_buff == NULL) {
54✔
5906
                        /*
5907
                         * An empty controller is reported on some kernels.
5908
                         * It may look like this:
5909
                         * 0::/user.slice/user-1000.slice/session-1.scope
5910
                         *
5911
                         * Ignore this controller and move on.  Note that we
5912
                         * need to free the controller list entry we made.
5913
                         */
5914
                        free(controller_list[idx]);
4✔
5915
                        controller_list[idx] = NULL;
4✔
5916
                        continue;
4✔
5917
                }
5918

5919
                buff_len = strlen(stok_buff);
50✔
5920
                if (stok_buff[buff_len - 1] == '\n')
50✔
5921
                        buff_len--; /* Don't copy the trailing newline char */
38✔
5922

5923
                /* Read in the cgroup name */
5924
                if (buff_len > 1) {
50✔
5925
                        /* Strip off the leading '/' for every cgroup but the root cgroup */
5926
                        cgrp_list[idx] = malloc(buff_len);
34✔
5927
                        snprintf(cgrp_list[idx], buff_len, "%s", &stok_buff[1]);
34✔
5928
                } else {
5929
                        /* Retain the leading '/' since we're in the root cgroup */
5930
                        cgrp_list[idx] = strndup(stok_buff, buff_len);
16✔
5931
                }
5932

5933
                idx++;
50✔
5934
                if (idx >= list_len) {
50✔
5935
                        cgroup_warn("Maximum mount elements reached. Consider increasing ");
1✔
5936
                        cgroup_cont("MAX_MNT_ELEMENTS\n");
1✔
5937
                        break;
1✔
5938
                }
5939
        }
5940
        fclose(f);
18✔
5941
        return ret;
18✔
5942
}
5943

5944
/**
5945
 * Get process name from /proc/<pid>/status file.
5946
 * @param pid: The process id
5947
 * @param pname_status : The process name
5948
 * @return 0 on success, > 0 on error.
5949
 */
5950
static int cg_get_procname_from_proc_status(pid_t pid, char **procname_status)
553✔
5951
{
5952
        char path[FILENAME_MAX];
5953
        int ret = ECGFAIL;
553✔
5954
        char buf[4092];
5955
        FILE *f;
5956
        int len;
5957

5958
        sprintf(path, "/proc/%d/status", pid);
553✔
5959
        f = fopen(path, "re");
553✔
5960
        if (!f)
553✔
5961
                return ECGROUPNOTEXIST;
×
5962

5963
        while (fgets(buf, sizeof(buf), f)) {
553✔
5964
                if (!strncmp(buf, "Name:", 5)) {
553✔
5965
                        len = strlen(buf);
553✔
5966
                        if (buf[len - 1] == '\n')
553✔
5967
                                buf[len - 1] = '\0';
553✔
5968
                        *procname_status = strdup(buf + strlen("Name:") + 1);
553✔
5969
                        if (*procname_status == NULL) {
553✔
5970
                                last_errno = errno;
×
5971
                                ret = ECGOTHER;
×
5972
                                break;
×
5973
                        }
5974
                        ret = 0;
553✔
5975
                        break;
553✔
5976
                }
5977
        }
5978
        fclose(f);
553✔
5979
        return ret;
553✔
5980
}
5981

5982
/**
5983
 * Get process name from /proc/<pid>/cmdline file.
5984
 * This function is mainly for getting a script name (shell, perl, etc).
5985
 * A script name is written into the second or later argument of
5986
 * /proc/<pid>/cmdline. This function gets each argument and
5987
 * compares it to a process name taken from /proc/<pid>/status.
5988
 * @param pid: The process id
5989
 * @param pname_status : The process name taken from /proc/<pid>/status
5990
 * @param pname_cmdline: The process name taken from /proc/<pid>/cmdline
5991
 * @return 0 on success, > 0 on error.
5992
 */
5993
static int cg_get_procname_from_proc_cmdline(pid_t pid, const char *pname_status,
74✔
5994
                                             char **pname_cmdline)
5995
{
5996
        char pid_cwd_path[FILENAME_MAX];
5997
        char pid_cmd_path[FILENAME_MAX];
5998
        char buf_pname[FILENAME_MAX];
5999
        char buf_cwd[FILENAME_MAX];
6000
        int ret = ECGFAIL;
74✔
6001
        int len = 0;
74✔
6002
        int c = 0;
74✔
6003
        FILE *f;
6004

6005
        memset(buf_cwd, '\0', sizeof(buf_cwd));
74✔
6006
        sprintf(pid_cwd_path, "/proc/%d/cwd", pid);
74✔
6007

6008
        if (readlink(pid_cwd_path, buf_cwd, sizeof(buf_cwd)) < 0)
74✔
6009
                return ECGROUPNOTEXIST;
×
6010

6011
        /* readlink doesn't append a null */
6012
        buf_cwd[FILENAME_MAX - 1] = '\0';
74✔
6013

6014
        sprintf(pid_cmd_path, "/proc/%d/cmdline", pid);
74✔
6015
        f = fopen(pid_cmd_path, "re");
74✔
6016
        if (!f)
74✔
6017
                return ECGROUPNOTEXIST;
×
6018

6019
        while (c != EOF) {
961✔
6020
                c = fgetc(f);
959✔
6021
                if ((c != EOF) && (c != '\0') && (len < FILENAME_MAX - 1)) {
959✔
6022
                        buf_pname[len] = c;
873✔
6023
                        len++;
873✔
6024
                        continue;
873✔
6025
                }
6026
                buf_pname[len] = '\0';
86✔
6027

6028
                if (len == FILENAME_MAX - 1)
86✔
6029
                        while ((c != EOF) && (c != '\0'))
×
6030
                                c = fgetc(f);
×
6031

6032
                /*
6033
                 * The taken process name from /proc/<pid>/status is
6034
                 * shortened to 15 characters if it is over. So the name
6035
                 * should be compared by its length.
6036
                 */
6037
                if (strncmp(pname_status, basename(buf_pname), TASK_COMM_LEN - 1)) {
86✔
6038
                        len = 0;
14✔
6039
                        continue;
14✔
6040
                }
6041

6042
                if (buf_pname[0] == '/') {
72✔
6043
                        *pname_cmdline = strdup(buf_pname);
40✔
6044
                        if (*pname_cmdline == NULL) {
40✔
6045
                                last_errno = errno;
×
6046
                                ret = ECGOTHER;
×
6047
                                break;
×
6048
                        }
6049
                        ret = 0;
40✔
6050
                        break;
40✔
6051
                }
6052

6053
                strcat(buf_cwd, "/");
32✔
6054
                strcat(buf_cwd, buf_pname);
32✔
6055
                if (!realpath(buf_cwd, pid_cmd_path)) {
32✔
6056
                        last_errno = errno;
31✔
6057
                        ret = ECGOTHER;
31✔
6058
                        break;
31✔
6059
                }
6060

6061
                *pname_cmdline = strdup(pid_cmd_path);
1✔
6062
                if (*pname_cmdline == NULL) {
1✔
6063
                        last_errno = errno;
×
6064
                        ret = ECGOTHER;
×
6065
                        break;
×
6066
                }
6067
                ret = 0;
1✔
6068
                break;
1✔
6069
        }
6070
        fclose(f);
74✔
6071
        return ret;
74✔
6072
}
6073

6074
/**
6075
 * Get a process name from /proc file system.
6076
 * This function allocates memory for a process name, writes a process
6077
 * name onto it. So a caller should free the memory when unusing it.
6078
 * @param pid: The process id
6079
 * @param procname: The process name
6080
 * @return 0 on success, > 0 on error.
6081
 */
6082
int cgroup_get_procname_from_procfs(pid_t pid, char **procname)
553✔
6083
{
6084
        char path[FILENAME_MAX];
6085
        char buf[FILENAME_MAX];
6086
        char *pname_cmdline;
6087
        char *pname_status;
6088
        int ret;
6089

6090
        ret = cg_get_procname_from_proc_status(pid, &pname_status);
553✔
6091
        if (ret)
553✔
6092
                return ret;
×
6093

6094
        /* Get the full patch of process name from /proc/<pid>/exe. */
6095
        memset(buf, '\0', sizeof(buf));
553✔
6096
        snprintf(path, FILENAME_MAX, "/proc/%d/exe", pid);
553✔
6097
        if (readlink(path, buf, sizeof(buf)) < 0) {
553✔
6098
                /*
6099
                 * readlink() fails if a kernel thread, and a process name
6100
                 * is taken from /proc/<pid>/status.
6101
                 */
6102
                *procname = pname_status;
255✔
6103
                return 0;
255✔
6104
        }
6105
        /* readlink doesn't append a null */
6106
        buf[FILENAME_MAX - 1] = '\0';
298✔
6107

6108
        if (!strncmp(pname_status, basename(buf), TASK_COMM_LEN - 1)) {
298✔
6109
                /*
6110
                 * The taken process name from /proc/<pid>/status is
6111
                 * shortened to 15 characters if it is over. So the name
6112
                 * should be compared by its length.
6113
                 */
6114
                free(pname_status);
224✔
6115
                *procname = strdup(buf);
224✔
6116
                if (*procname == NULL) {
224✔
6117
                        last_errno = errno;
×
6118
                        return ECGOTHER;
×
6119
                }
6120
                return 0;
224✔
6121
        }
6122

6123
        /*
6124
         * The above strncmp() is not 0 if a shell script, because
6125
         * /proc/<pid>/exe links a shell command (/bin/bash etc.) and the
6126
         * pname_status represents a shell script name. Then the full path
6127
         * of a shell script is taken from /proc/<pid>/cmdline.
6128
         */
6129
        ret = cg_get_procname_from_proc_cmdline(pid, pname_status, &pname_cmdline);
74✔
6130
        if (!ret) {
74✔
6131
                *procname = pname_cmdline;
41✔
6132
                free(pname_status);
41✔
6133
                return 0;
41✔
6134
        }
6135

6136
        /*
6137
         * The above strncmp() is not 0 also if executing a symbolic link,
6138
         * /proc/pid/exe points to real executable name then. Return it as
6139
         * the last resort.
6140
         */
6141
        free(pname_status);
33✔
6142
        *procname = strdup(buf);
33✔
6143
        if (*procname == NULL) {
33✔
6144
                last_errno = errno;
×
6145
                return ECGOTHER;
×
6146
        }
6147

6148
        return 0;
33✔
6149
}
6150

6151
int cgroup_register_unchanged_process(pid_t pid, int flags)
4✔
6152
{
6153
        char buff[sizeof(CGRULE_SUCCESS_STORE_PID)];
6154
        struct sockaddr_un addr;
6155
        size_t ret_len;
6156
        int ret = 1;
4✔
6157
        int sk;
6158

6159
        sk = socket(PF_UNIX, SOCK_STREAM, 0);
4✔
6160
        if (sk < 0)
4✔
6161
                return 1;
×
6162

6163
        memset(&addr, 0, sizeof(addr));
4✔
6164
        addr.sun_family = AF_UNIX;
4✔
6165
        strcpy(addr.sun_path, CGRULE_CGRED_SOCKET_PATH);
4✔
6166

6167
        if (connect(sk, (struct sockaddr *)&addr,
4✔
6168
            sizeof(addr.sun_family) + strlen(CGRULE_CGRED_SOCKET_PATH)) < 0) {
6169
                /* If the daemon does not work, this function returns 0 as success. */
6170
                ret = 0;
4✔
6171
                goto close;
4✔
6172
        }
6173
        if (write(sk, &pid, sizeof(pid)) < 0)
×
6174
                goto close;
×
6175

6176
        if (write(sk, &flags, sizeof(flags)) < 0)
×
6177
                goto close;
×
6178

6179
        ret_len = read(sk, buff, sizeof(buff));
×
6180
        if (ret_len != sizeof(buff))
×
6181
                goto close;
×
6182

6183
        if (strncmp(buff, CGRULE_SUCCESS_STORE_PID, sizeof(buff)))
×
6184
                goto close;
×
6185

6186
        ret = 0;
×
6187
close:
4✔
6188
        close(sk);
4✔
6189

6190
        return ret;
4✔
6191
}
6192

6193
int cgroup_get_subsys_mount_point(const char *controller, char **mount_point)
×
6194
{
6195
        int ret = ECGROUPNOTEXIST;
×
6196
        int i;
6197

6198
        if (!cgroup_initialized)
×
6199
                return ECGROUPNOTINITIALIZED;
×
6200

6201
        if (!controller)
×
6202
                return ECGINVAL;
×
6203

6204
        pthread_rwlock_rdlock(&cg_mount_table_lock);
×
6205
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
×
6206
                if (strncmp(cg_mount_table[i].name, controller, FILENAME_MAX))
×
6207
                        continue;
×
6208

6209
                *mount_point = strdup(cg_mount_table[i].mount.path);
×
6210

6211
                if (!*mount_point) {
×
6212
                        last_errno = errno;
×
6213
                        ret = ECGOTHER;
×
6214
                        goto out_exit;
×
6215
                }
6216

6217
                ret = 0;
×
6218
                break;
×
6219
        }
6220
out_exit:
×
6221
        pthread_rwlock_unlock(&cg_mount_table_lock);
×
6222

6223
        return ret;
×
6224
}
6225

6226
int cgroup_get_all_controller_end(void **handle)
180✔
6227
{
6228
        FILE *proc_cgrp = (FILE *) *handle;
180✔
6229

6230
        if (!proc_cgrp)
180✔
6231
                return ECGINVAL;
×
6232

6233
        fclose(proc_cgrp);
180✔
6234
        *handle = NULL;
180✔
6235

6236
        return 0;
180✔
6237
}
6238

6239
int cgroup_get_all_controller_next(void **handle, struct controller_data *info)
2,700✔
6240
{
6241
        FILE *proc_cgrp = (FILE *) *handle;
2,700✔
6242
        int hierarchy, num_cgrps, enabled;
6243
        char subsys_name[FILENAME_MAX];
6244
        int err = 0;
2,700✔
6245

6246
        if (!proc_cgrp)
2,700✔
6247
                return ECGINVAL;
×
6248

6249
        if (!info)
2,700✔
6250
                return ECGINVAL;
×
6251

6252
        /*
6253
         * check Linux Kernel sources/kernel/cgroup/cgroup.c cgroup_init_early(),
6254
         * MAX_CGROUP_TYPE_NAMELEN check for details on why 32 is used.
6255
         */
6256
        err = fscanf(proc_cgrp, "%32s %d %d %d\n", subsys_name, &hierarchy, &num_cgrps,
2,700✔
6257
                     &enabled);
6258

6259
        if (err != 4)
2,700✔
6260
                return ECGEOF;
180✔
6261

6262
        strncpy(info->name, subsys_name, FILENAME_MAX);
2,520✔
6263
        info->name[FILENAME_MAX-1] = '\0';
2,520✔
6264
        info->hierarchy = hierarchy;
2,520✔
6265
        info->num_cgroups = num_cgrps;
2,520✔
6266
        info->enabled = enabled;
2,520✔
6267

6268
        return 0;
2,520✔
6269
}
6270

6271
int cgroup_get_all_controller_begin(void **handle, struct controller_data *info)
180✔
6272
{
6273
        FILE *proc_cgroup = NULL;
180✔
6274
        char buf[FILENAME_MAX];
6275
        int ret;
6276

6277
        if (!info)
180✔
6278
                return ECGINVAL;
×
6279

6280
        proc_cgroup = fopen("/proc/cgroups", "re");
180✔
6281
        if (!proc_cgroup) {
180✔
6282
                last_errno = errno;
×
6283
                return ECGOTHER;
×
6284
        }
6285

6286
        if (!fgets(buf, FILENAME_MAX, proc_cgroup)) {
180✔
6287
                last_errno = errno;
×
6288
                fclose(proc_cgroup);
×
6289
                *handle = NULL;
×
6290
                return ECGOTHER;
×
6291
        }
6292
        *handle = proc_cgroup;
180✔
6293

6294
        ret = cgroup_get_all_controller_next(handle, info);
180✔
6295
        if (ret != 0) {
180✔
6296
                fclose(proc_cgroup);
×
6297
                *handle = NULL;
×
6298
        }
6299

6300
        return ret;
180✔
6301
}
6302

6303
static int pid_compare(const void *a, const void *b)
84✔
6304
{
6305
        const pid_t *pid1, *pid2;
6306

6307
        pid1 = (pid_t *) a;
84✔
6308
        pid2 = (pid_t *) b;
84✔
6309

6310
        return (*pid1 - *pid2);
84✔
6311
}
6312

6313
/* pids needs to be completely uninitialized so that we can set it up
6314
 *
6315
 * Caller must free up pids.
6316
 */
6317
static int read_pids(char *path, pid_t **pids, int *size)
10✔
6318
{
6319
        int tot_pids = 16;
10✔
6320
        pid_t *tmp_list;
6321
        FILE *pid_file;
6322
        int n = 0;
10✔
6323
        int err;
6324

6325
        pid_file = fopen(path, "r");
10✔
6326
        if (!pid_file) {
10✔
6327
                last_errno = errno;
×
6328
                *pids = NULL;
×
6329
                *size = 0;
×
6330
                if (errno == ENOENT)
×
6331
                        return ECGROUPUNSUPP;
×
6332
                else
6333
                        return ECGOTHER;
×
6334
        }
6335

6336
        /* Keep doubling the memory allocated if needed */
6337
        tmp_list = malloc(sizeof(pid_t) * tot_pids);
10✔
6338
        if (!tmp_list) {
10✔
6339
                last_errno = errno;
×
6340
                fclose(pid_file);
×
6341
                return ECGOTHER;
×
6342
        }
6343

6344
        while (!feof(pid_file)) {
22✔
6345
                while (!feof(pid_file) && n < tot_pids) {
62✔
6346
                        pid_t pid;
6347

6348
                        err = fscanf(pid_file, "%u", &pid);
60✔
6349
                        if (err == EOF)
60✔
6350
                                break;
10✔
6351
                        tmp_list[n] = pid;
50✔
6352
                        n++;
50✔
6353
                }
6354
                if (!feof(pid_file)) {
12✔
6355
                        pid_t *orig_list = tmp_list;
2✔
6356

6357
                        tot_pids *= 2;
2✔
6358
                        tmp_list = realloc(tmp_list, sizeof(pid_t) * tot_pids);
2✔
6359
                        if (!tmp_list) {
2✔
6360
                                last_errno = errno;
×
6361
                                fclose(pid_file);
×
6362
                                free(orig_list);
×
6363
                                *pids = NULL;
×
6364
                                *size = 0;
×
6365
                                return ECGOTHER;
×
6366
                        }
6367
                }
6368
        }
6369
        fclose(pid_file);
10✔
6370

6371
        *size = n;
10✔
6372
        qsort(tmp_list, n, sizeof(pid_t), &pid_compare);
10✔
6373
        *pids = tmp_list;
10✔
6374

6375
        return 0;
10✔
6376
}
6377

6378
int cgroup_get_procs(const char *name, const char *controller, pid_t **pids, int *size)
10✔
6379
{
6380
        char cgroup_path[FILENAME_MAX];
6381

6382
        cg_build_path(name, cgroup_path, controller);
10✔
6383
        strncat(cgroup_path, "/cgroup.procs", FILENAME_MAX-strlen(cgroup_path));
10✔
6384

6385
        return read_pids(cgroup_path, pids, size);
10✔
6386
}
6387

6388
int cgroup_get_threads(const char *name, const char *controller, pid_t **pids, int *size)
×
6389
{
6390
        char cgroup_path[FILENAME_MAX];
6391

6392
        cg_build_path(name, cgroup_path, controller);
×
6393
        strncat(cgroup_path, "/cgroup.threads", FILENAME_MAX-strlen(cgroup_path));
×
6394

6395
        return read_pids(cgroup_path, pids, size);
×
6396
}
6397

6398
int cgroup_dictionary_create(struct cgroup_dictionary **dict,
12✔
6399
                             int flags)
6400
{
6401
        if (!dict)
12✔
6402
                return ECGINVAL;
×
6403

6404
        *dict = (struct cgroup_dictionary *) calloc(1, sizeof(struct cgroup_dictionary));
12✔
6405
        if (!*dict) {
12✔
6406
                last_errno = errno;
×
6407
                return ECGOTHER;
×
6408
        }
6409
        (*dict)->flags = flags;
12✔
6410

6411
        return 0;
12✔
6412
}
6413

6414
int cgroup_dictionary_add(struct cgroup_dictionary *dict, const char *name, const char *value)
24✔
6415
{
6416
        struct cgroup_dictionary_item *it;
6417

6418
        if (!dict)
24✔
6419
                return ECGINVAL;
×
6420

6421
        it = (struct cgroup_dictionary_item *) malloc(sizeof(struct cgroup_dictionary_item));
24✔
6422
        if (!it) {
24✔
6423
                last_errno = errno;
×
6424
                return ECGOTHER;
×
6425
        }
6426

6427
        it->next = NULL;
24✔
6428
        it->name = name;
24✔
6429
        it->value = value;
24✔
6430

6431
        if (dict->tail) {
24✔
6432
                dict->tail->next = it;
12✔
6433
                dict->tail = it;
12✔
6434
        } else {
6435
                /* It is the first item */
6436
                dict->tail = it;
12✔
6437
                dict->head = it;
12✔
6438
        }
6439

6440
        return 0;
24✔
6441
}
6442

6443
int cgroup_dictionary_free(struct cgroup_dictionary *dict)
16✔
6444
{
6445
        struct cgroup_dictionary_item *it;
6446

6447
        if (!dict)
16✔
6448
                return ECGINVAL;
4✔
6449

6450
        it = dict->head;
12✔
6451
        while (it) {
36✔
6452
                struct cgroup_dictionary_item *del = it;
24✔
6453

6454
                it = it->next;
24✔
6455
                if (!(dict->flags & CG_DICT_DONT_FREE_ITEMS)) {
24✔
6456
                        free((void *)del->value);
24✔
6457
                        free((void *)del->name);
24✔
6458
                }
6459
                free(del);
24✔
6460
        }
6461
        free(dict);
12✔
6462

6463
        return 0;
12✔
6464
}
6465

6466
int cgroup_dictionary_iterator_begin(struct cgroup_dictionary *dict, void **handle,
12✔
6467
                                     const char **name, const char **value)
6468
{
6469
        struct cgroup_dictionary_iterator *iter;
6470

6471
        *handle = NULL;
12✔
6472

6473
        if (!dict)
12✔
6474
                return ECGINVAL;
×
6475

6476
        iter = (struct cgroup_dictionary_iterator *)
6477
                malloc(sizeof(struct cgroup_dictionary_iterator));
12✔
6478
        if (!iter) {
12✔
6479
                last_errno = errno;
×
6480
                return ECGOTHER;
×
6481
        }
6482

6483
        iter->item = dict->head;
12✔
6484
        *handle = iter;
12✔
6485

6486
        return cgroup_dictionary_iterator_next(handle, name, value);
12✔
6487
}
6488

6489
int cgroup_dictionary_iterator_next(void **handle, const char **name, const char **value)
36✔
6490
{
6491
        struct cgroup_dictionary_iterator *iter;
6492

6493
        if (!handle)
36✔
6494
                return ECGINVAL;
×
6495

6496
        iter = *handle;
36✔
6497

6498
        if (!iter)
36✔
6499
                return ECGINVAL;
×
6500

6501
        if (!iter->item)
36✔
6502
                return ECGEOF;
12✔
6503

6504
        *name = iter->item->name;
24✔
6505
        *value = iter->item->value;
24✔
6506
        iter->item = iter->item->next;
24✔
6507

6508
        return 0;
24✔
6509
}
6510

6511
void cgroup_dictionary_iterator_end(void **handle)
12✔
6512
{
6513
        if (!handle)
12✔
6514
                return;
×
6515

6516
        free(*handle);
12✔
6517
        *handle = NULL;
12✔
6518
}
6519

6520
int cgroup_get_subsys_mount_point_begin(const char *controller, void **handle, char *path)
×
6521
{
6522
        int i;
6523

6524
        if (!cgroup_initialized)
×
6525
                return ECGROUPNOTINITIALIZED;
×
6526
        if (!handle || !path || !controller)
×
6527
                return ECGINVAL;
×
6528

6529
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++)
×
6530
                if (strcmp(controller, cg_mount_table[i].name) == 0)
×
6531
                        break;
×
6532

6533
        if (cg_mount_table[i].name[0] == '\0') {
×
6534
                /* The controller is not mounted at all */
6535
                *handle = NULL;
×
6536
                *path = '\0';
×
6537
                return ECGEOF;
×
6538
        }
6539

6540
        /*
6541
         * 'handle' is pointer to struct cg_mount_point, which should be
6542
         * returned next.
6543
         */
6544
        *handle = cg_mount_table[i].mount.next;
×
6545
        strcpy(path, cg_mount_table[i].mount.path);
×
6546

6547
        return 0;
×
6548
}
6549

6550
int cgroup_get_subsys_mount_point_next(void **handle, char *path)
×
6551
{
6552
        struct cg_mount_point *it;
6553

6554
        if (!cgroup_initialized)
×
6555
                return ECGROUPNOTINITIALIZED;
×
6556

6557
        if (!handle || !path)
×
6558
                return ECGINVAL;
×
6559

6560
        it = *handle;
×
6561
        if (!it) {
×
6562
                *handle = NULL;
×
6563
                *path = '\0';
×
6564
                return ECGEOF;
×
6565
        }
6566

6567
        *handle = it->next;
×
6568
        strcpy(path, it->path);
×
6569

6570
        return 0;
×
6571
}
6572

6573
int cgroup_get_subsys_mount_point_end(void **handle)
×
6574
{
6575
        if (!cgroup_initialized)
×
6576
                return ECGROUPNOTINITIALIZED;
×
6577

6578
        if (!handle)
×
6579
                return ECGINVAL;
×
6580

6581
        *handle = NULL;
×
6582

6583
        return 0;
×
6584
}
6585

6586
int cgroup_get_controller_version(const char * const controller, enum cg_version_t * const version)
3,288✔
6587
{
6588
        int i;
6589

6590
        if (!version)
3,288✔
6591
                return ECGINVAL;
×
6592

6593
        if (!controller && strlen(cg_cgroup_v2_mount_path) > 0) {
3,288✔
6594
                *version = CGROUP_V2;
56✔
6595
                return 0;
56✔
6596
        }
6597

6598
        if (!controller)
3,232✔
6599
                return ECGINVAL;
×
6600

6601
        *version = CGROUP_UNK;
3,232✔
6602

6603
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
9,130✔
6604
                if (strncmp(cg_mount_table[i].name, controller,
9,127✔
6605
                            sizeof(cg_mount_table[i].name)) == 0) {
6606
                        *version = cg_mount_table[i].version;
3,229✔
6607
                        return 0;
3,229✔
6608
                }
6609
        }
6610

6611
        return ECGROUPNOTEXIST;
3✔
6612
}
6613

6614
static int search_and_append_mnt_path(struct cg_mount_point **mount_point, char *path)
18✔
6615
{
6616
        struct cg_mount_point *mnt_point, *mnt_tmp, *mnt_prev;
6617

6618
        mnt_tmp = *mount_point;
18✔
6619
        while (mnt_tmp) {
18✔
6620
                if (strcmp(mnt_tmp->path, path) == 0)
16✔
6621
                        return ECGVALUEEXISTS;
16✔
6622

6623
                mnt_prev = mnt_tmp;
×
6624
                mnt_tmp = mnt_tmp->next;
×
6625
        }
6626

6627
        mnt_point = malloc(sizeof(struct cg_mount_point));
2✔
6628
        if (mnt_point == NULL) {
2✔
6629
                last_errno = errno;
×
6630
                return ECGOTHER;
×
6631
        }
6632

6633
        strncpy(mnt_point->path, path, FILENAME_MAX - 1);
2✔
6634
        mnt_point->path[FILENAME_MAX - 1] = '\0';
2✔
6635

6636
        mnt_point->next = NULL;
2✔
6637

6638
        if (*mount_point == NULL)
2✔
6639
                *mount_point = mnt_point;
2✔
6640
        else
6641
                mnt_prev->next = mnt_point;
×
6642

6643
        return 0;
2✔
6644
}
6645

6646
/**
6647
 * List the mount paths, that matches the specified version
6648
 *
6649
 *        @param cgrp_version The cgroup type/version
6650
 *        @param mount_paths Holds the list of mount paths
6651
 *        @return 0 success and list of mounts paths in mount_paths
6652
 *                ECGOTHER on failure and mount_paths is NULL.
6653
 */
6654
int cgroup_list_mount_points(const enum cg_version_t cgrp_version, char ***mount_paths)
4✔
6655
{
6656
        struct cg_mount_point *mount_point, *mnt_tmp = NULL;
4✔
6657
        char **mnt_paths = NULL;
4✔
6658
        int i, idx = 0;
4✔
6659
        int ret;
6660

6661
        if (!cgroup_initialized)
4✔
6662
                return ECGROUPNOTINITIALIZED;
×
6663

6664
        if (cgrp_version != CGROUP_V1 && cgrp_version != CGROUP_V2)
4✔
6665
                return ECGINVAL;
×
6666

6667
        pthread_rwlock_rdlock(&cg_mount_table_lock);
4✔
6668

6669
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
40✔
6670
                if (cg_mount_table[i].version != cgrp_version)
36✔
6671
                        continue;
18✔
6672

6673
                mount_point = &cg_mount_table[i].mount;
18✔
6674
                while (mount_point) {
36✔
6675
                        ret = search_and_append_mnt_path(&mnt_tmp, mount_point->path);
18✔
6676
                        if (ret != 0 && ret != ECGVALUEEXISTS)
18✔
6677
                                goto err;
×
6678

6679
                        /* Avoid adding duplicate mount points */
6680
                        if (ret != ECGVALUEEXISTS)
18✔
6681
                                idx++;
2✔
6682

6683
                        mount_point = mount_point->next;
18✔
6684
                }
6685
        }
6686

6687
        /*
6688
         * Cgroup v2 can be mounted without any controller and these mount
6689
         * paths are not part of the cg_mount_table.  Check and append
6690
         * them to mnt_paths.
6691
         */
6692
        if (cgrp_version == CGROUP_V2 && cg_cgroup_v2_empty_mount_paths) {
4✔
6693
                mount_point = cg_cgroup_v2_empty_mount_paths;
×
6694
                while (mount_point) {
×
6695
                        ret = search_and_append_mnt_path(&mnt_tmp, mount_point->path);
×
6696
                        if (ret)
×
6697
                                goto err;
×
6698

6699
                        idx++;
×
6700
                        mount_point = mount_point->next;
×
6701
                }
6702
        }
6703

6704
        mnt_paths = malloc(sizeof(char *) * (idx + 1));
4✔
6705
        if (mnt_paths == NULL) {
4✔
6706
                last_errno = errno;
×
6707
                ret = ECGOTHER;
×
6708
                goto err;
×
6709
        }
6710

6711
        for (i = 0, mount_point = mnt_tmp;
4✔
6712
             mount_point;
6✔
6713
             mount_point = mount_point->next, i++) {
2✔
6714

6715
                mnt_paths[i] = strdup(mount_point->path);
2✔
6716
                if (mnt_paths[i] == NULL) {
2✔
6717
                        last_errno = errno;
×
6718
                        ret = ECGOTHER;
×
6719
                        goto err;
×
6720
                }
6721
        }
6722
        mnt_paths[i] = '\0';
4✔
6723

6724
        ret = 0;
4✔
6725
        *mount_paths = mnt_paths;
4✔
6726

6727
err:
4✔
6728
        pthread_rwlock_unlock(&cg_mount_table_lock);
4✔
6729

6730
        while (mnt_tmp) {
6✔
6731
                mount_point = mnt_tmp;
2✔
6732
                mnt_tmp = mnt_tmp->next;
2✔
6733
                free(mount_point);
2✔
6734
        }
6735

6736
        if (ret != 0 && mnt_paths) {
4✔
6737
                for (i = 0; i < idx; i++)
×
6738
                        free(mnt_paths[i]);
×
6739
                free(mnt_paths);
×
6740
                *mount_paths = NULL;
×
6741
        }
6742

6743
        return ret;
4✔
6744
}
6745

6746
const struct cgroup_library_version *cgroup_version(void)
4✔
6747
{
6748
        return &library_version;
4✔
6749
}
6750

6751
/**
6752
 * Finds the current cgroup setup mode (legacy/unified/hybrid).
6753
 * Returns unknown of failure and setup mode on success.
6754
 */
6755
enum cg_setup_mode_t cgroup_setup_mode(void)
214✔
6756
{
6757
#define CGROUP2_SUPER_MAGIC        0x63677270
6758
#define CGROUP_SUPER_MAGIC        0x27E0EB
6759

6760
        unsigned int cg_setup_mode_bitmask = 0U;
214✔
6761
        enum cg_setup_mode_t setup_mode;
6762
        struct statfs cgrp_buf;
6763
        int i, ret = 0;
214✔
6764

6765
        if (!cgroup_initialized)
214✔
6766
                return ECGROUPNOTINITIALIZED;
×
6767

6768
        setup_mode = CGROUP_MODE_UNK;
214✔
6769

6770
        pthread_rwlock_wrlock(&cg_mount_table_lock);
214✔
6771
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
2,140✔
6772
                ret = statfs(cg_mount_table[i].mount.path, &cgrp_buf);
1,926✔
6773
                if (ret) {
1,926✔
6774
                        setup_mode = CGROUP_MODE_UNK;
×
6775
                        cgroup_err("Failed to get stats of '%s'\n", cg_mount_table[i].mount.path);
×
6776
                        goto out;
×
6777
                }
6778

6779
                if (cgrp_buf.f_type == CGROUP2_SUPER_MAGIC)
1,926✔
6780
                        cg_setup_mode_bitmask |= (1U << 0);
1,926✔
6781
                else if (cgrp_buf.f_type == CGROUP_SUPER_MAGIC)
×
6782
                        cg_setup_mode_bitmask |= (1U << 1);
×
6783
        }
6784

6785
        if (cg_cgroup_v2_empty_mount_paths)
214✔
6786
                cg_setup_mode_bitmask |= (1U << 0);
×
6787

6788
        if (cg_setup_mode_bitmask & (1U << 0) && cg_setup_mode_bitmask & (1U << 1))
214✔
6789
                setup_mode = CGROUP_MODE_HYBRID;
×
6790
        else if (cg_setup_mode_bitmask & (1U << 0))
214✔
6791
                setup_mode = CGROUP_MODE_UNIFIED;
214✔
6792
        else if (cg_setup_mode_bitmask & (1U << 1))
×
6793
                setup_mode = CGROUP_MODE_LEGACY;
×
6794

6795
out:
×
6796
        pthread_rwlock_unlock(&cg_mount_table_lock);
214✔
6797
        return setup_mode;
214✔
6798
}
6799

6800
int cgroup_get_controller_count(struct cgroup *cgrp)
25✔
6801
{
6802
        if (!cgrp)
25✔
6803
                return -1;
×
6804

6805
        return cgrp->index;
25✔
6806
}
6807

6808
struct cgroup_controller *cgroup_get_controller_by_index(struct cgroup *cgrp, int index)
83✔
6809
{
6810
        if (!cgrp)
83✔
6811
                return NULL;
×
6812

6813
        if (index >= cgrp->index)
83✔
6814
                return NULL;
×
6815

6816
        return cgrp->controller[index];
83✔
6817
}
6818

6819
char *cgroup_get_controller_name(struct cgroup_controller *controller)
83✔
6820
{
6821
        if (!controller)
83✔
6822
                return NULL;
×
6823

6824
        return controller->name;
83✔
6825
}
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