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

drakenclimber / libcgroup / 16730702375

29 Jul 2025 05:49PM UTC coverage: 56.187% (+0.01%) from 56.177%
16730702375

push

github

drakenclimber
github: Add clang workflow

Add a workflow that will build libcgroup using clang.  Do not use the
setup-libcgroup action as it assumes GCC configurations.

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

5594 of 9956 relevant lines covered (56.19%)

408.93 hits per line

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

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

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

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

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

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

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

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

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

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

241
        close(fd);
400✔
242

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

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

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

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

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

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

394
        tmp_string = strdup(path);
219✔
395

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

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

401
        free(tmp_string);
219✔
402

403
        return base;
219✔
404
}
405

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

410
        pthread_rwlock_rdlock(&cg_mount_table_lock);
859✔
411

412
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
2,235✔
413
                if (strncmp(cg_mount_table[i].name, name, sizeof(cg_mount_table[i].name)) == 0) {
2,235✔
414
                        pthread_rwlock_unlock(&cg_mount_table_lock);
788✔
415
                        return 1;
788✔
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)
1✔
490
{
491
        char *itr;
492

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

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

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

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

512
        return itr;
1✔
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)
30✔
564
{
565
        char *quote_start = NULL, *quote_end = NULL;
30✔
566
        char *tmp, *_rule = rule;
30✔
567
        int len = 0;
30✔
568

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

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

576
        tmp = rule;
28✔
577

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

587
        if (quote_start) {
28✔
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) {
26✔
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;
19✔
611
                if (len >= field_len)
19✔
612
                        return ECGINVAL;
1✔
613

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

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

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

624
        return len;
23✔
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,
1✔
651
                                   const char *mprocname)
652
{
653
        /* File descriptor for the configuration file */
654
        FILE *fp = NULL;
1✔
655

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

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

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

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

668
        /* Rule to add to the list */
669
        struct cgroup_rule *newrule = NULL;
1✔
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' };
1✔
679
        char user[LOGIN_NAME_MAX] = { '\0' };
1✔
680
        char controllers[CG_CONTROLLER_MAX] = { '\0' };
1✔
681
        char destination[FILENAME_MAX] = { '\0' };
1✔
682
        char options[CG_OPTIONS_MAX] = { '\0' };
1✔
683
        uid_t uid = CGRULE_INVALID;
1✔
684
        gid_t gid = CGRULE_INVALID;
1✔
685
        bool has_options = false;
1✔
686
        size_t len_username;
687
        int len_procname;
688

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

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

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

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

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

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

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

713
        /* Open the configuration file. */
714
        fp = fopen(filename, "re");
1✔
715
        if (!fp) {
1✔
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);
1✔
725
        while (fgets(buff, sizeof(buff), fp) != NULL) {
2✔
726
                linenum++;
1✔
727

728
                itr = cg_skip_unused_charactors_in_rule(buff);
1✔
729
                if (!itr)
1✔
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 == '%') {
1✔
737
                        cgroup_warn("skipped child of invalid rule, line %d.\n", linenum);
×
738
                        continue;
×
739
                }
740

741
                /* clear the buffer. */
742
                grp = NULL;
1✔
743
                pwd = NULL;
1✔
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;
1✔
750

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

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

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

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

778
                procname = strchr(key, ':');
1✔
779
                if (procname) {
1✔
780
                        /* <user>:<procname>  <subsystem>  <destination> */
781
                        procname++;        /* skip ':' */
1✔
782
                        len_username = procname - key - 1;
1✔
783
                        len_procname = strlen(procname);
1✔
784
                        if (len_procname < 0) {
1✔
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);
1✔
794
                memset(user, '\0', sizeof(user));
1✔
795
                strncpy(user, key, len_username);
1✔
796
                user[sizeof(user) - 1] = '\0';
1✔
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)) {
1✔
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) {
1✔
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) {
1✔
827
                        /* Special wildcard rule. */
828
                        uid = CGRULE_WILD;
1✔
829
                        gid = CGRULE_WILD;
1✔
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) {
1✔
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)
1✔
866
                        matched = true;
1✔
867

868
                if (!cache) {
1✔
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));
1✔
902
                if (!newrule) {
1✔
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;
1✔
910
                newrule->gid = gid;
1✔
911
                newrule->is_ignore = 0;
1✔
912

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

917
                if (len_procname) {
1✔
918
                        newrule->procname = strdup(procname);
1✔
919
                        if (!newrule->procname) {
1✔
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);
1✔
931
                newrule->destination[sizeof(newrule->destination) - 1] = '\0';
1✔
932

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

939
                newrule->next = NULL;
1✔
940

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

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

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

964
                /* Now, push the rule. */
965
                if (lst->head == NULL) {
1✔
966
                        lst->head = newrule;
1✔
967
                        lst->tail = newrule;
1✔
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:",
1✔
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++)
2✔
978
                        cgroup_dbg(" %s", lst->tail->controllers[i]);
1✔
979
                cgroup_dbg("\n");
1✔
980
        }
981

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

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

990
parsefail:
×
991
        ret = ECGRULESPARSEFAIL;
×
992

993
close:
1✔
994
        fclose(fp);
1✔
995
finish:
1✔
996
        return ret;
1✔
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)
1✔
1031
{
1032
        /* Pointer to the list that we're using */
1033
        struct cgroup_rule_list *lst = NULL;
1✔
1034

1035
        /* Directory variables */
1036
        const char *dirname = CGRULES_CONF_DIR;
1✔
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)
1✔
1046
                lst = &rl;
1✔
1047
        else
1048
                lst = &trl;
×
1049

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

1054
        pthread_rwlock_wrlock(&rl_lock);
1✔
1055

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

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

1068
        /* Continue parsing */
1069
        d = opendir(dirname);
1✔
1070
        if (!d) {
1✔
1071
                cgroup_warn("Failed to open directory %s: %s\n", dirname, strerror(errno));
1✔
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);
1✔
1078

1079
                return 0;
1✔
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,412✔
1161
{
1162
        int i, shared_mnt = 0;
13,412✔
1163

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

1173
        return shared_mnt;
13,412✔
1174
}
1175

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

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

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

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

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

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

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

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

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

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

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

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

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

1415
        return ret;
1,451✔
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,448✔
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,969✔
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,448✔
1438
        memset(&cg_cgroup_v2_mount_path, 0, sizeof(cg_cgroup_v2_mount_path));
1,448✔
1439
        memset(&cg_cgroup_v2_empty_mount_paths, 0, sizeof(cg_cgroup_v2_empty_mount_paths));
643✔
1440
}
1,448✔
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,448✔
1512
{
1513
        int hierarchy, num_cgrps, enabled;
1514
        char subsys_name[FILENAME_MAX];
1515
        char mnt_opt[] = "subset=pid";
1,448✔
1516
        char *buf = NULL;
1,448✔
1517
        FILE *proc_cgrp;
1518
        int mnt_opt_set;
1519
        int ret = 0;
1,448✔
1520
        int err, i = 0;
1,448✔
1521

1522
        proc_cgrp = fopen("/proc/cgroups", "re");
1,448✔
1523
        if (!proc_cgrp) {
1,448✔
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,448✔
1543
        if (!buf) {
1,448✔
1544
                last_errno = errno;
×
1545
                ret = ECGOTHER;
×
1546
                goto err;
×
1547
        }
1548

1549
        if (!fgets(buf, CGV2_CONTROLLERS_LL_MAX, proc_cgrp)) {
1,448✔
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,720✔
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,720✔
1562
                             &enabled);
1563
                if (err < 0)
21,720✔
1564
                        break;
1,448✔
1565

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

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

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

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

1589
        return ret;
1,448✔
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,448✔
1598
{
1599
        char mntent_buffer[4 * FILENAME_MAX];
1600
        struct mntent *ent, *temp_ent = NULL;
1,448✔
1601
        int found_mnt = 0;
1,448✔
1602
        FILE *proc_mount;
1603
        int ret = 0;
1,448✔
1604

1605
        proc_mount = fopen("/proc/self/mounts", "re");
1,448✔
1606
        if (proc_mount == NULL) {
1,448✔
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,448✔
1614
        if (!temp_ent) {
1,448✔
1615
                last_errno = errno;
×
1616
                ret = ECGOTHER;
×
1617
                goto err;
×
1618
        }
1619

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

1623
                if (strcmp(ent->mnt_type, "cgroup") == 0) {
50,323✔
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) {
49,959✔
1642
                        ret = cgroup_process_v2_mnt(ent, &found_mnt);
1,448✔
1643
                        if (ret == ECGEOF) {
1,448✔
1644
                        /* The controllers file was empty.  Ignore and move on. */
1645
                                ret = 0;
×
1646
                                continue;
×
1647
                        }
1648

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

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

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

1660
        if (found_mnt >= CG_CONTROLLER_MAX) {
1,448✔
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,444✔
1672
        if (proc_mount)
1,448✔
1673
                fclose(proc_mount);
1,448✔
1674

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

1678
        return ret;
1,448✔
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,448✔
1689
{
1690
        static char *controllers[CG_CONTROLLER_MAX];
1691
        int ret = 0;
1,448✔
1692
        int i;
1693

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

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

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

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

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

1709
        cgroup_initialized = 1;
1,444✔
1710

1711
unlock_exit:
1,448✔
1712
        for (i = 0; controllers[i]; i++) {
21,720✔
1713
                free(controllers[i]);
20,272✔
1714
                controllers[i] = NULL;
20,272✔
1715
        }
1716

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

1719
        return ret;
1,448✔
1720
}
1721

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

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

1734
        temp_ent = (struct mntent *) malloc(sizeof(struct mntent));
992✔
1735
        if (!temp_ent) {
992✔
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));
992✔
1742
        if (!ent) {
992✔
1743
                ret = 0;
×
1744
                goto done;
×
1745
        }
1746

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

1759
        return ret;
992✔
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)
10,230✔
1768
{
1769
        if ((suf[strlen(suf)-1] == '/') || ((strlen(suf) == 0) && (pref[strlen(pref)-1] == '/')))
10,230✔
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));
10,112✔
1773

1774
        path[FILENAME_MAX-1] = '\0';
10,230✔
1775

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

1813
        /* allocate more space for systemd_default_cgroup + '/' */
1814
        len += (FILENAME_MAX + 1);
6,212✔
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);
10,603✔
1823
        if (!_path) {
10,603✔
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) {
10,603✔
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++) {
38,680✔
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) ||
38,678✔
1866
                    (type && strcmp(type, CGRP_FILE_PREFIX) == 0 &&
28,498✔
1867
                     cg_mount_table[i].version == CGROUP_V2)) {
296✔
1868

1869
                        if (cg_namespace_table[i])
10,476✔
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,
10,472✔
1874
                                               tmp_systemd_default_cgrp);
1875

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

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

1882
                        if (name) {
10,476✔
1883
                                char *tmp;
1884

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

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

1897
out:
10,603✔
1898
        if (_path)
10,603✔
1899
                free(_path);
10,603✔
1900

1901
        if (tmp_systemd_default_cgrp)
10,603✔
1902
                free(tmp_systemd_default_cgrp);
10,603✔
1903

1904
        return path;
10,603✔
1905
}
1906

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

1913
        return path;
1,818✔
1914
}
1915

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

1924
        snprintf(cg_type_path, FILENAME_MAX, "%scgroup.type", path);
529✔
1925
        fp = fopen(cg_type_path, "re");
529✔
1926
        if (!fp) {
529✔
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) {
401✔
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;
401✔
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         ||
401✔
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");
397✔
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:
529✔
1971
        if (fp)
529✔
1972
                fclose(fp);
401✔
1973

1974
        return err;
529✔
1975
}
1976

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

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

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

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

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

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

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

2014
        return err;
533✔
2015
}
2016

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

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

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

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

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

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

2052
        dname = dirname(parent);
74✔
2053

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

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

2064
        return error;
74✔
2065
}
2066

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

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

2157
        if (!cgroup_initialized) {
58✔
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) {
58✔
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++) {
126✔
2186
                        if (!cgroup_test_subsys_mounted(cgrp->controller[i]->name)) {
70✔
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)
56✔
2194
                        /* Valid empty cgroup v2 with no controllers added. */
2195
                        empty_cgrp = 1;
6✔
2196

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

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

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

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

2213
                        if (move_tids) {
76✔
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);
76✔
2220
                        if (ret)
76✔
2221
                                return ret;
4✔
2222
                }
2223
        }
2224
        return 0;
54✔
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)
56✔
2237
{
2238
        return cgroup_attach_task_tid(cgroup, tid, 0);
56✔
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)
496✔
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())
496✔
2364
                return ECGROUPNOTMOUNTED;
×
2365

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2543
        return error;
615✔
2544
}
2545

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

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

2567
        *enabled = false;
859✔
2568

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

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

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

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

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

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

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

2621
out:
236✔
2622
        if (path_copy)
859✔
2623
                free(path_copy);
859✔
2624
        if (fp)
859✔
2625
                fclose(fp);
859✔
2626

2627
        return error;
859✔
2628
}
2629

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2729
        return 0;
12✔
2730
}
2731

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2906
        return ret;
656✔
2907

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

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

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

2922
        return ret;
×
2923
}
2924

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

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

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

2943
        cgroup_free_controllers(dst);
417✔
2944

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

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

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

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

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

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

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

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

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

3001
        return error;
1✔
3002
}
3003

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

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

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

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

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

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

3041
                        dname = dirname(parent);
223✔
3042

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

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

3059
        base = strdup(path);
247✔
3060

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

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

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

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

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

3100
        return error;
248✔
3101
}
3102

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

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

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

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

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

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

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

3164
        return 0;
194✔
3165
}
3166

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

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

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

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

3210
        return ret;
188✔
3211
}
3212

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

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

3226
        pthread_rwlock_rdlock(&cg_mount_table_lock);
171✔
3227

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

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

3237
        pthread_rwlock_unlock(&cg_mount_table_lock);
171✔
3238

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

3264
        *parent = NULL;
193✔
3265

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3479
        return ECGOTHER;
×
3480
}
3481

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

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

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

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

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

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

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

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

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

3538
        cgroup_walk_tree_end(&handle);
21✔
3539

3540
        return ret;
21✔
3541
}
3542

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3711
        return first_error;
192✔
3712
}
3713

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

3724
        if (!cg_build_path_locked(cgrp, path, subsys))
1,302✔
3725
                return ECGFAIL;
×
3726

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

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

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

3750
        fclose(ctrl_file);
1,292✔
3751

3752
        return 0;
1,292✔
3753
}
3754

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

3772
        d_name = strdup(ctrl_dir->d_name);
6,589✔
3773

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

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

3786
        error = stat(path, &stat_buffer);
6,589✔
3787
        if (error) {
6,589✔
3788
                error = ECGFAIL;
×
3789
                goto fill_error;
×
3790
        }
3791

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

3805
        /* tmp_path would be pointing to the last six characters */
3806
        tmp_path = (char *)path + tmp_len;
6,589✔
3807

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

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

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

3829
        if (strcmp(ctrl_name, cg_mount_table[cg_index].name) == 0) {
6,580✔
3830
                error = cg_rd_ctrl_file(cg_mount_table[cg_index].name, cgrp->name,
1,302✔
3831
                                        ctrl_dir->d_name, &ctrl_value);
1,302✔
3832
                if (error || !ctrl_value)
1,302✔
3833
                        goto fill_error;
10✔
3834

3835
                if (cgroup_add_value_string(cgc, ctrl_dir->d_name, ctrl_value)) {
1,292✔
3836
                        error = ECGFAIL;
×
3837
                        goto fill_error;
×
3838
                }
3839
        }
3840
fill_error:
6,570✔
3841
        if (ctrl_value)
6,589✔
3842
                free(ctrl_value);
1,292✔
3843
        free(d_name);
6,589✔
3844

3845
        return error;
6,589✔
3846
}
3847

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

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

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

3877
        initial_controller_cnt = cgrp->index;
39✔
3878

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

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

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

3898
                        if (skip_this_controller)
54✔
3899
                                continue;
243✔
3900
                }
3901

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

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

3909
                if (access(mnt_path, F_OK))
307✔
3910
                        continue;
2✔
3911

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

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

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

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

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

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

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

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

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

3977
                controller_cnt++;
101✔
3978

3979
                while ((ctrl_dir = readdir(dir)) != NULL) {
4,685✔
3980
                        /* Skip over non regular files */
3981
                        if (ctrl_dir->d_type != DT_REG)
4,584✔
3982
                                continue;
247✔
3983

3984
                        error = cgroup_fill_cgc(ctrl_dir, cgrp, cgc, i);
4,337✔
3985
                        for (j = 0; j < cgc->index; j++)
28,696✔
3986
                                cgc->values[j]->dirty = false;
24,359✔
3987

3988
                        if (error == ECGFAIL) {
4,337✔
3989
                                closedir(dir);
×
3990
                                goto unlock_error;
×
3991
                        }
3992
                }
3993
                closedir(dir);
101✔
3994

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

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

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

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

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

4031
        pthread_rwlock_unlock(&cg_mount_table_lock);
38✔
4032

4033
        return 0;
38✔
4034

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

4044
        return error;
1✔
4045
}
4046

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

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

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

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

4070
                if (!controllers[i])
109✔
4071
                        return 0;
47✔
4072
                controller = controllers[i];
62✔
4073

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

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

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

4104
        return ret;
×
4105
}
4106

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

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

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

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

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

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

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

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

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

4169
        return ret;
14✔
4170
}
4171

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

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

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

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

4192
        return ret;
10✔
4193
}
4194

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

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

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

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

4215
        return false;
×
4216
}
4217

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

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

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

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

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

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

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

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

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

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

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

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

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

4303
        return found_match;
14✔
4304
}
4305

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

4312
        /* Temporary group data */
4313
        struct group *grp = NULL;
220✔
4314

4315
        /* Temporary string pointer */
4316
        char *sp = NULL;
220✔
4317

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

4323
        loglevel = cgroup_get_loglevel();
220✔
4324

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

4452
        if (base)
220✔
4453
                free(base);
×
4454

4455
        return ret;
220✔
4456
}
4457

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

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

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

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

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

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

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

4511
        return output;
×
4512
}
4513

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

4644
        return ret;
×
4645
}
4646

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

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

4660
        /* Return codes */
4661
        int ret = 0;
220✔
4662

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

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

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

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

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

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

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

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

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

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

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

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

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

4840
finished:
1✔
4841
        return ret;
220✔
4842
}
4843

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

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

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

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

4886

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

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

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

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

4920
        while ((task_dir = readdir(dir)) != NULL) {
182✔
4921
                nr = sscanf(task_dir->d_name, "%i", &tid);
138✔
4922
                if (nr < 1)
138✔
4923
                        continue;
88✔
4924

4925
                if (tid == pid)
50✔
4926
                        continue;
44✔
4927

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

4935
        closedir(dir);
44✔
4936

4937
finished:
48✔
4938
        cgroup_free_controllers(&cgrp);
48✔
4939

4940
        return ret;
48✔
4941
}
4942

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

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

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

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

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

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

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

4983
                free(procname);
186✔
4984
        }
4985

4986
        closedir(dir);
1✔
4987
        return 0;
1✔
4988
}
4989

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

5001
        /* Loop variable */
5002
        int i = 0;
1✔
5003

5004
        pthread_rwlock_rdlock(&rl_lock);
1✔
5005

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

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

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

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

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

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

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

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

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

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

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

5090
        return ret;
1✔
5091
}
5092

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

5252
        return ret;
7✔
5253
}
5254

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

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

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

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

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

5286
        if (!cgroup_initialized)
6,358✔
5287
                return ECGROUPNOTINITIALIZED;
×
5288

5289
        cgroup_dbg("seeing file %s\n", ent->fts_path);
6,358✔
5290

5291
        info->path = ent->fts_name;
6,358✔
5292
        info->parent = ent->fts_parent->fts_name;
6,358✔
5293
        info->full_path = ent->fts_path;
6,358✔
5294
        info->depth = ent->fts_level;
6,358✔
5295
        info->type = CGROUP_FILE_TYPE_OTHER;
6,358✔
5296

5297
        if (depth && (info->depth > depth))
6,358✔
5298
                return 0;
×
5299

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

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

5331
        if (!cgroup_initialized)
6,358✔
5332
                return ECGROUPNOTINITIALIZED;
×
5333

5334
        if (!handle)
6,358✔
5335
                return ECGINVAL;
×
5336

5337
        entry = (struct cgroup_tree_handle *) *handle;
6,358✔
5338

5339
        ent = fts_read(entry->fts);
6,358✔
5340
        if (!ent)
6,358✔
5341
                return ECGEOF;
41✔
5342
        if (!base_level && depth)
6,317✔
5343
                base_level = ent->fts_level + depth;
×
5344

5345
        ret = cg_walk_node(entry->fts, ent, base_level, info, entry->flags);
6,317✔
5346

5347
        *handle = entry;
6,317✔
5348
        return ret;
6,317✔
5349
}
5350

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

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

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

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

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

5367
        return 0;
41✔
5368
}
5369

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

5491
        return ret;
×
5492
}
5493

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

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

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

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

5508
        return 0;
219✔
5509
}
5510

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

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

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

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

5528
        return ret;
291✔
5529
}
5530

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

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

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

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

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

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

5562
        *handle = fp;
219✔
5563

5564
        return ret;
219✔
5565
}
5566

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

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

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

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

5581
        fclose(fp);
×
5582

5583
        return 0;
×
5584
}
5585

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

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

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

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

5601
        return ret;
×
5602
}
5603

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

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

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

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

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

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

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

5633
        return ret;
×
5634
}
5635

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

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

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

5647
        return 0;
×
5648
}
5649

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

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

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

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

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

5669
        return 0;
×
5670
}
5671

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

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

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

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

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

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

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

5700
        return ret;
×
5701
}
5702

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

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

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

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

5716
        return 0;
8✔
5717
}
5718

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

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

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

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

5733
        pthread_rwlock_rdlock(&cg_mount_table_lock);
72✔
5734

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

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

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

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

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

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

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

5764
        return ret;
72✔
5765
}
5766

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

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

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

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

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

5784
        *pos = 0;
8✔
5785

5786
        *handle = pos;
8✔
5787

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

6018
        while (c != EOF) {
327✔
6019
                c = fgetc(f);
326✔
6020
                if ((c != EOF) && (c != '\0') && (len < FILENAME_MAX - 1)) {
326✔
6021
                        buf_pname[len] = c;
304✔
6022
                        len++;
304✔
6023
                        continue;
304✔
6024
                }
6025
                buf_pname[len] = '\0';
22✔
6026

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

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

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

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

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

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

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

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

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

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

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

6147
        return 0;
7✔
6148
}
6149

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

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

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

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

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

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

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

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

6189
        return ret;
4✔
6190
}
6191

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

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

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

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

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

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

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

6222
        return ret;
×
6223
}
6224

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

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

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

6235
        return 0;
180✔
6236
}
6237

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

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

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

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

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

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

6267
        return 0;
2,520✔
6268
}
6269

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

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

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

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

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

6299
        return ret;
180✔
6300
}
6301

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

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

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

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

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

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

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

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

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

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

6374
        return 0;
10✔
6375
}
6376

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

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

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

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

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

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

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

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

6410
        return 0;
12✔
6411
}
6412

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

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

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

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

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

6439
        return 0;
24✔
6440
}
6441

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

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

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

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

6462
        return 0;
12✔
6463
}
6464

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

6470
        *handle = NULL;
12✔
6471

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

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

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

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

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

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

6495
        iter = *handle;
36✔
6496

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

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

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

6507
        return 0;
24✔
6508
}
6509

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

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

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

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

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

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

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

6546
        return 0;
×
6547
}
6548

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

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

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

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

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

6569
        return 0;
×
6570
}
6571

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

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

6580
        *handle = NULL;
×
6581

6582
        return 0;
×
6583
}
6584

6585
int cgroup_get_controller_version(const char * const controller, enum cg_version_t * const version)
2,956✔
6586
{
6587
        int i;
6588

6589
        if (!version)
2,956✔
6590
                return ECGINVAL;
×
6591

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

6597
        if (!controller)
2,900✔
6598
                return ECGINVAL;
×
6599

6600
        *version = CGROUP_UNK;
2,900✔
6601

6602
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
8,349✔
6603
                if (strncmp(cg_mount_table[i].name, controller,
8,346✔
6604
                            sizeof(cg_mount_table[i].name)) == 0) {
6605
                        *version = cg_mount_table[i].version;
2,897✔
6606
                        return 0;
2,897✔
6607
                }
6608
        }
6609

6610
        return ECGROUPNOTEXIST;
3✔
6611
}
6612

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

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

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

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

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

6635
        mnt_point->next = NULL;
2✔
6636

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

6642
        return 0;
2✔
6643
}
6644

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

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

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

6666
        pthread_rwlock_rdlock(&cg_mount_table_lock);
4✔
6667

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

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

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

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

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

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

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

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

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

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

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

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

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

6742
        return ret;
4✔
6743
}
6744

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

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

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

6764
        if (!cgroup_initialized)
193✔
6765
                return ECGROUPNOTINITIALIZED;
×
6766

6767
        setup_mode = CGROUP_MODE_UNK;
193✔
6768

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

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

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

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

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

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

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

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

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

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

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

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