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

drakenclimber / libcgroup / 13140969669

31 Jan 2025 08:59PM UTC coverage: 59.425% (-0.02%) from 59.449%
13140969669

push

github

drakenclimber
tools-common.c: Fix resource leak in cgroup_string_list_add_directory()

Fix a Coverity warning about resource leak:

CID 465889: (#4 of 4): Resource leak (RESOURCE_LEAK)
32. leaked_storage: Variable fullpath going out of scope leaks the
storage it points to.

Fix it by releasing the 'fullpath', when file name does not matches
'*.conf' code path(s).

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

2 of 4 new or added lines in 1 file covered. (50.0%)

287 existing lines in 3 files now uncovered.

5870 of 9878 relevant lines covered (59.42%)

1008.37 hits per line

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

63.3
/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)
15,697✔
144
{
145
        if (owner == NO_UID_GID)
15,697✔
146
                owner = getuid();
15,201✔
147
        if (group == NO_UID_GID)
15,697✔
148
                group = getgid();
15,201✔
149

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

157
        cgroup_dbg("chown: seeing file %s\n", filename);
15,401✔
158
        switch (ent->fts_info) {
15,401✔
159
        case FTS_ERR:
×
160
                errno = ent->fts_errno;
×
161
                break;
×
162
        case FTS_D:
15,401✔
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);
15,401✔
171
                break;
15,401✔
172
        }
173
        if (ret < 0) {
15,401✔
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;
15,401✔
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)
478✔
183
{
184
        int ret = 0;
478✔
185
        FTS *fts;
186

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

195
        while (1) {
15,401✔
196
                FTSENT *ent;
197

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

207
        return ret;
478✔
208
}
209

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

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

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

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

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

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

241
        close(fd);
284✔
242

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

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

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

283
        return ret;
15,083✔
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,
478✔
291
                                         mode_t file_mode, int filem_change, int owner_is_umask,
292
                                         const char * const *ignore_list)
293
{
294
        int final_ret = 0;
478✔
295
        char *fts_path[2];
296
        int i, ignored;
297
        int ret = 0;
478✔
298
        FTS *fts;
299

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

304
        fts = fts_open(fts_path, FTS_PHYSICAL | FTS_NOCHDIR | FTS_NOSTAT, NULL);
478✔
305
        if (fts == NULL) {
478✔
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) {
15,401✔
312
                FTSENT *ent;
313

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

324
                ignored = 0;
15,401✔
325
                if (ignore_list != NULL)
15,401✔
326
                        for (i = 0; ignore_list[i] != NULL; i++)
30,484✔
327
                                if (!strcmp(ignore_list[i], ent->fts_name)) {
15,401✔
328
                                        ignored = 1;
318✔
329
                                        break;
318✔
330
                                }
331
                if (ignored)
15,401✔
332
                        continue;
318✔
333

334
                ret = cg_chmod_file(fts, ent, dir_mode, dirm_change, file_mode, filem_change,
15,083✔
335
                                    owner_is_umask);
336
                if (ret) {
15,083✔
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);
478✔
343

344
        return final_ret;
478✔
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,
31✔
376
                            mode_t task_fperm)
377
{
378
        if (!cgrp) {
31✔
379
                /* ECGROUPNOTALLOWED */
380
                cgroup_err("Cgroup, operation not allowed\n");
2✔
381
                return;
2✔
382
        }
383

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

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

394
        tmp_string = strdup(path);
1,370✔
395

396
        if (!tmp_string)
1,370✔
397
                return NULL;
×
398

399
        base = strdup(basename(tmp_string));
1,370✔
400

401
        free(tmp_string);
1,370✔
402

403
        return base;
1,370✔
404
}
405

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

410
        pthread_rwlock_rdlock(&cg_mount_table_lock);
1,548✔
411

412
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
7,682✔
413
                if (strncmp(cg_mount_table[i].name, name, sizeof(cg_mount_table[i].name)) == 0) {
7,682✔
414
                        pthread_rwlock_unlock(&cg_mount_table_lock);
1,501✔
415
                        return 1;
1,501✔
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 &&
6,181✔
424
                    cg_mount_table[i].version == CGROUP_V2) {
47✔
425
                        pthread_rwlock_unlock(&cg_mount_table_lock);
47✔
426
                        return 1;
47✔
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)
5✔
490
{
491
        char *itr;
492

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

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

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

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

512
        return itr;
5✔
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)
12✔
524
{
525
        char *stok_buff = NULL;
12✔
526
        size_t cmp_len;
527
        int ret = 0;
12✔
528

529
        stok_buff = strtok(options, ",");
12✔
530
        if (!stok_buff) {
12✔
531
                cgroup_err("failed to parse options: %s\n", options);
4✔
532
                return -EINVAL;
4✔
533
        }
534

535
        do {
536
                cmp_len = min(strlen(stok_buff), strlen(CGRULE_OPTION_IGNORE));
10✔
537
                if (strlen(stok_buff) == strlen(CGRULE_OPTION_IGNORE) &&
10✔
538
                    strncmp(stok_buff, CGRULE_OPTION_IGNORE, cmp_len) == 0) {
8✔
539
                        rule->is_ignore |= CGRULE_OPT_IGNORE;
6✔
540
                        continue;
6✔
541
                }
542

543
                cmp_len = min(strlen(stok_buff), strlen(CGRULE_OPTION_IGNORE_RT));
4✔
544
                if (strlen(stok_buff) == strlen(CGRULE_OPTION_IGNORE_RT) &&
4✔
545
                    strncmp(stok_buff, CGRULE_OPTION_IGNORE_RT, cmp_len) == 0) {
×
546
                        rule->is_ignore |= CGRULE_OPT_IGNORE_RT;
×
547
                        continue;
×
548
                }
549

550
                cgroup_err("Unsupported option: %s\n", stok_buff);
4✔
551
                ret = -EINVAL;
4✔
552
                break;
4✔
553
        } while ((stok_buff = strtok(NULL, ",")));
6✔
554

555
        return ret;
8✔
556
}
557
/**
558
 * Parse the configuration file that maps UID/GIDs to cgroups.  If ever the
559
 * configuration file is modified, applications should call this function to
560
 * load the new configuration rules.
561
 *
562
 * The function caller is responsible for calling free() on each rule in the
563
 * list.
564
 *
565
 * The cache parameter alters the behavior of this function.  If true, this
566
 * function will read the entire configuration file and store the results in
567
 * rl (global rules list).  If false, this function will only parse until it
568
 * finds a rule matching the given UID or GID.  It will store this rule in
569
 * trl, as well as any children rules (rules that begin with a %) that it has.
570
 *
571
 * This function is NOT thread safe!
572
 *        @param filename configuration file to parse
573
 *        @param cache True to cache rules, else false
574
 *        @param muid If cache is false, the UID to match against
575
 *        @param mgid If cache is false, the GID to match against
576
 *        @return 0 on success, -1 if no cache and match found, > 0 on error.
577
 * TODO: Make this function thread safe!
578
 *
579
 */
580
static int cgroup_parse_rules_file(char *filename, bool cache, uid_t muid, gid_t mgid,
5✔
581
                                   const char *mprocname)
582
{
583
        /* File descriptor for the configuration file */
584
        FILE *fp = NULL;
5✔
585

586
        /* Buffer to store the line we're working on */
587
        char buff[CGRP_RULE_MAXLINE] = { '\0' };
5✔
588

589
        /* Iterator for the line we're working on */
590
        char *itr = NULL;
5✔
591

592
        /* Pointer to process name in a line of the configuration file */
593
        char *procname = NULL;
5✔
594

595
        /* Pointer to the list that we're using */
596
        struct cgroup_rule_list *lst = NULL;
5✔
597

598
        /* Rule to add to the list */
599
        struct cgroup_rule *newrule = NULL;
5✔
600

601
        /* Structure to get GID from group name */
602
        struct group *grp;
603

604
        /* Structure to get UID from user name */
605
        struct passwd *pwd;
606

607
        /* Temporary storage for a configuration rule */
608
        char key[CGRP_RULE_MAXKEY] = { '\0' };
5✔
609
        char user[LOGIN_NAME_MAX] = { '\0' };
5✔
610
        char controllers[CG_CONTROLLER_MAX] = { '\0' };
5✔
611
        char destination[FILENAME_MAX] = { '\0' };
5✔
612
        char options[CG_OPTIONS_MAX] = { '\0' };
5✔
613
        uid_t uid = CGRULE_INVALID;
5✔
614
        gid_t gid = CGRULE_INVALID;
5✔
615
        bool has_options = false;
5✔
616
        size_t len_username;
617
        int len_procname;
618

619
        /* The current line number */
620
        unsigned int linenum = 0;
5✔
621

622
        /* Did we skip the previous line? */
623
        bool skipped = false;
5✔
624

625
        /* Have we found a matching rule (non-cache mode)? */
626
        bool matched = false;
5✔
627

628
        /* Return codes */
629
        int ret = 0;
5✔
630

631
        /* Temporary buffer for strtok() */
632
        char *stok_buff = NULL;
5✔
633

634
        /* Loop variable. */
635
        int i = 0;
5✔
636

637
        /* Determine which list we're using. */
638
        if (cache)
5✔
639
                lst = &rl;
5✔
640
        else
641
                lst = &trl;
×
642

643
        /* Open the configuration file. */
644
        fp = fopen(filename, "re");
5✔
645
        if (!fp) {
5✔
646
                cgroup_warn("failed to open configuration file %s: %s\n",
×
647
                            filename, strerror(errno));
648
                /* originally ret = 0, but this is parse fail, not success */
649
                ret = ECGRULESPARSEFAIL;
×
650
                goto finish;
×
651
        }
652

653
        /* Now, parse the configuration file one line at a time. */
654
        cgroup_dbg("Parsing configuration file %s.\n", filename);
5✔
655
        while (fgets(buff, sizeof(buff), fp) != NULL) {
10✔
656
                linenum++;
5✔
657

658
                itr = cg_skip_unused_charactors_in_rule(buff);
5✔
659
                if (!itr)
5✔
660
                        continue;
×
661

662
                /*
663
                 * If we skipped the last rule and this rule is a continuation
664
                 * of it (begins with %), then we should skip this rule too.
665
                 */
666
                if (skipped && *itr == '%') {
5✔
667
                        cgroup_warn("skipped child of invalid rule, line %d.\n", linenum);
×
668
                        continue;
×
669
                }
670

671
                /* clear the buffer. */
672
                grp = NULL;
5✔
673
                pwd = NULL;
5✔
674

675
                /*
676
                 * If there is something left, it should be a rule.  Otherwise,
677
                 * there's an error in the configuration file.
678
                 */
679
                skipped = false;
5✔
680
                i = sscanf(itr, "%s%s%s%s", key, controllers, destination, options);
5✔
681
                if (i < 3) {
5✔
682
                        cgroup_err("failed to parse configuration file on line %d\n", linenum);
×
683
                        goto parsefail;
×
684
                } else if (i == 3) {
5✔
685
                        has_options = false;
5✔
686
                } else if (i == 4) {
×
687
                        has_options = true;
×
688
                }
689

690
                procname = strchr(key, ':');
5✔
691
                if (procname) {
5✔
692
                        /* <user>:<procname>  <subsystem>  <destination> */
693
                        procname++;        /* skip ':' */
5✔
694
                        len_username = procname - key - 1;
5✔
695
                        len_procname = strlen(procname);
5✔
696
                        if (len_procname < 0) {
5✔
697
                                cgroup_err("failed to parse configuration file on line %d\n",
×
698
                                           linenum);
699
                                goto parsefail;
×
700
                        }
701
                } else {
702
                        len_username = strlen(key);
×
703
                        len_procname = 0;
×
704
                }
705
                len_username = min(len_username, sizeof(user) - 1);
5✔
706
                memset(user, '\0', sizeof(user));
5✔
707
                strncpy(user, key, len_username);
5✔
708
                user[sizeof(user) - 1] = '\0';
5✔
709

710
                /*
711
                 * Next, check the user/group.  If it's a % sign, then we are
712
                 * continuing another rule and UID/GID should not be reset.
713
                 * If it's a @, we're dealing with a GID rule.  If it's a *,
714
                 * then we do not need to do a lookup because the rule always
715
                 * applies (it's a wildcard).  If we're using non-cache mode
716
                 * and we've found a matching rule, we only continue to parse
717
                 * if we're looking at a child rule.
718
                 */
719
                if ((!cache) && matched && (strncmp(user, "%", 1) != 0)) {
5✔
720
                        /* If we make it here, we finished (non-cache). */
721
                        cgroup_dbg("Parsing of configuration file complete.\n\n");
×
722
                        ret = -1;
×
723
                        goto close;
×
724
                }
725
                if (strncmp(user, "@", 1) == 0) {
5✔
726
                        /* New GID rule. */
727
                        itr = &(user[1]);
×
728
                        grp = getgrnam(itr);
×
729
                        if (grp) {
×
730
                                uid = CGRULE_INVALID;
×
731
                                gid = grp->gr_gid;
×
732
                        } else {
733
                                cgroup_warn("Entry for %s not found. Skipping rule on line %d.\n",
×
734
                                            itr, linenum);
735
                                skipped = true;
×
736
                                continue;
×
737
                        }
738
                } else if (strncmp(user, "*", 1) == 0) {
5✔
739
                        /* Special wildcard rule. */
740
                        uid = CGRULE_WILD;
5✔
741
                        gid = CGRULE_WILD;
5✔
742
                } else if (*itr != '%') {
×
743
                        /* New UID rule. */
744
                        pwd = getpwnam(user);
×
745
                        if (pwd) {
×
746
                                uid = pwd->pw_uid;
×
747
                                gid = CGRULE_INVALID;
×
748
                        } else {
749
                                cgroup_warn("Entry for %s not found. Skipping rule on line %d.\n",
×
750
                                            user, linenum);
751
                                skipped = true;
×
752
                                continue;
×
753
                        }
754
                } /* Else, we're continuing another rule (UID/GID are okay). */
755

756
                /*
757
                 * If we are not caching rules, then we need to check for a
758
                 * match before doing anything else.  We consider four cases:
759
                 * 1. The UID matches
760
                 * 2. The GID matches
761
                 * 3. The UID is a member of the GID, or
762
                 * 4. We're looking at the wildcard rule, which always matches.
763
                 * If none of these are true, we simply continue to the next
764
                 * line in the file.
765
                 */
766
                if (grp && muid != CGRULE_INVALID) {
5✔
767
                        pwd = getpwuid(muid);
×
768
                        if (!pwd)
×
769
                                continue;
×
770

771
                        for (i = 0; grp->gr_mem[i]; i++) {
×
772
                                if (!(strcmp(pwd->pw_name, grp->gr_mem[i])))
×
773
                                        matched = true;
×
774
                        }
775
                }
776

777
                if (uid == muid || gid == mgid || uid == CGRULE_WILD)
5✔
778
                        matched = true;
5✔
779

780
                if (!cache) {
5✔
781
                        if (!matched)
×
782
                                continue;
×
783
                        if (len_procname) {
×
784
                                char *mproc_base;
785
                                /*
786
                                 * If there is a rule based on process name,
787
                                 * it should be matched with mprocname.
788
                                 */
789
                                if (!mprocname) {
×
790
                                        uid = CGRULE_INVALID;
×
791
                                        gid = CGRULE_INVALID;
×
792
                                        matched = false;
×
793
                                        continue;
×
794
                                }
795

796
                                mproc_base = cgroup_basename(mprocname);
×
797
                                if (strcmp(mprocname, procname) && strcmp(mproc_base, procname)) {
×
798
                                        uid = CGRULE_INVALID;
×
799
                                        gid = CGRULE_INVALID;
×
800
                                        matched = false;
×
801
                                        free(mproc_base);
×
802
                                        continue;
×
803
                                }
804
                                free(mproc_base);
×
805
                        }
806
                }
807

808
                /*
809
                 * Now, we're either caching rules or we found a match.
810
                 * Either way, copy everything into a new rule and push it
811
                 * into the list.
812
                 */
813
                newrule = calloc(1, sizeof(struct cgroup_rule));
5✔
814
                if (!newrule) {
5✔
815
                        cgroup_err("out of memory? Error was: %s\n", strerror(errno));
×
816
                        last_errno = errno;
×
817
                        ret = ECGOTHER;
×
818
                        goto close;
×
819
                }
820

821
                newrule->uid = uid;
5✔
822
                newrule->gid = gid;
5✔
823
                newrule->is_ignore = 0;
5✔
824

825
                len_username = min(len_username, sizeof(newrule->username) - 1);
5✔
826
                strncpy(newrule->username, user, len_username);
5✔
827
                newrule->username[sizeof(newrule->username) - 1] = '\0';
5✔
828

829
                if (len_procname) {
5✔
830
                        newrule->procname = strdup(procname);
5✔
831
                        if (!newrule->procname) {
5✔
832
                                cgroup_err("strdup failed to allocate memory %s\n",
×
833
                                           strerror(errno));
834
                                free(newrule);
×
835
                                last_errno = errno;
×
836
                                ret = ECGOTHER;
×
837
                                goto close;
×
838
                        }
839
                } else {
840
                        newrule->procname = NULL;
×
841
                }
842
                strncpy(newrule->destination, destination, sizeof(newrule->destination) - 1);
5✔
843
                newrule->destination[sizeof(newrule->destination) - 1] = '\0';
5✔
844

845
                if (has_options) {
5✔
846
                        ret = cgroup_parse_rules_options(options, newrule);
×
847
                        if (ret < 0)
×
848
                                goto destroyrule;
×
849
                }
850

851
                newrule->next = NULL;
5✔
852

853
                /* Parse the controller list, and add that to newrule too. */
854
                stok_buff = strtok(controllers, ",");
5✔
855
                if (!stok_buff) {
5✔
856
                        cgroup_err("failed to parse controllers on line %d\n", linenum);
×
857
                        goto destroyrule;
×
858
                }
859

860
                i = 0;
5✔
861
                do {
862
                        if (i >= MAX_MNT_ELEMENTS) {
5✔
863
                                cgroup_err("too many controllers listed on line %d\n", linenum);
×
864
                                goto destroyrule;
×
865
                        }
866

867
                        newrule->controllers[i] =
5✔
868
                                strndup(stok_buff, strlen(stok_buff) + 1);
5✔
869
                        if (!(newrule->controllers[i])) {
5✔
870
                                cgroup_err("out of memory? Error was: %s\n", strerror(errno));
×
871
                                goto destroyrule;
×
872
                        }
873
                        i++;
5✔
874
                } while ((stok_buff = strtok(NULL, ",")));
5✔
875

876
                /* Now, push the rule. */
877
                if (lst->head == NULL) {
5✔
878
                        lst->head = newrule;
5✔
879
                        lst->tail = newrule;
5✔
880
                } else {
881
                        lst->tail->next = newrule;
×
882
                        lst->tail = newrule;
×
883
                }
884

885
                cgroup_dbg("Added rule %s (UID: %d, GID: %d) -> %s for controllers:",
5✔
886
                           lst->tail->username, lst->tail->uid, lst->tail->gid,
887
                           lst->tail->destination);
888

889
                for (i = 0; lst->tail->controllers[i]; i++)
10✔
890
                        cgroup_dbg(" %s", lst->tail->controllers[i]);
5✔
891
                cgroup_dbg("\n");
5✔
892
        }
893

894
        /* If we make it here, there were no errors. */
895
        cgroup_dbg("Parsing of configuration file complete.\n\n");
5✔
896
        ret = (matched && !cache) ? -1 : 0;
5✔
897
        goto close;
5✔
898

899
destroyrule:
×
900
        cgroup_free_rule(newrule);
×
901

902
parsefail:
×
903
        ret = ECGRULESPARSEFAIL;
×
904

905
close:
5✔
906
        fclose(fp);
5✔
907
finish:
5✔
908
        return ret;
5✔
909
}
910

911
/**
912
 * Parse CGRULES_CONF_FILE and all files in CGRULES_CONF_FILE_DIR.
913
 * If CGRULES_CONF_FILE_DIR does not exists or can not be read, parse only
914
 * CGRULES_CONF_FILE. This way we keep the back compatibility.
915
 *
916
 * Original description of this function moved to cgroup_parse_rules_file.
917
 * Also cloned and all occurrences of file changed to files.
918
 *
919
 * Parse the configuration files that maps UID/GIDs to cgroups. If ever the
920
 * configuration files are modified, applications should call this function to
921
 * load the new configuration rules. The function caller is responsible for
922
 * calling free() on each rule in the list.
923
 *
924
 * The cache parameter alters the behavior of this function.  If true, this
925
 * function will read the entire content of all configuration files and store
926
 * the results in rl (global rules list). If false, this function will only
927
 * parse until it finds a file and a rule matching the given UID or GID.
928
 * The remaining files are skipped. It will store this rule in trl, as well as
929
 * any children rules (rules that begin with a %) that it has.
930
 *
931
 * Files can be read in an random order so the first match must not be
932
 * dependent on it. Thus construct the rules the way not to break this
933
 * assumption.
934
 *
935
 * This function is NOT thread safe!
936
 *        @param cache True to cache rules, else false
937
 *        @param muid If cache is false, the UID to match against
938
 *        @param mgid If cache is false, the GID to match against
939
 *        @return 0 on success, -1 if no cache and match found, > 0 on error.
940
 * TODO: Make this function thread safe!
941
 */
942
static int cgroup_parse_rules(bool cache, uid_t muid, gid_t mgid, const char *mprocname)
5✔
943
{
944
        /* Pointer to the list that we're using */
945
        struct cgroup_rule_list *lst = NULL;
5✔
946

947
        /* Directory variables */
948
        const char *dirname = CGRULES_CONF_DIR;
5✔
949
        struct dirent *item;
950
        char *tmp;
951
        int sret;
952
        DIR *d;
953

954
        int ret;
955

956
        /* Determine which list we're using. */
957
        if (cache)
5✔
958
                lst = &rl;
5✔
959
        else
960
                lst = &trl;
×
961

962
        /* If our list already exists, clean it. */
963
        if (lst->head)
5✔
964
                cgroup_free_rule_list(lst);
×
965

966
        pthread_rwlock_wrlock(&rl_lock);
5✔
967

968
        /* Parse CGRULES_CONF_FILE configuration file (back compatibility). */
969
        ret = cgroup_parse_rules_file(CGRULES_CONF_FILE, cache, muid, mgid, mprocname);
5✔
970

971
        /*
972
         * if match (ret = -1), stop parsing other files,
973
         * just return or ret > 0 => error
974
         */
975
        if (ret != 0) {
5✔
976
                pthread_rwlock_unlock(&rl_lock);
×
977
                return ret;
×
978
        }
979

980
        /* Continue parsing */
981
        d = opendir(dirname);
5✔
982
        if (!d) {
5✔
983
                cgroup_warn("Failed to open directory %s: %s\n", dirname, strerror(errno));
5✔
984
                /*
985
                 * Cannot read directory. However, CGRULES_CONF_FILE is
986
                 * successfully parsed. Thus return as a success for back
987
                 * compatibility.
988
                 */
989
                pthread_rwlock_unlock(&rl_lock);
5✔
990

991
                return 0;
5✔
992
        }
993

994
        /* Read all files from CGRULES_CONF_FILE_DIR */
995
        do {
996
                item = readdir(d);
×
997
                if (item && (item->d_type == DT_REG || item->d_type == DT_LNK)) {
×
998

999
                        sret = asprintf(&tmp, "%s/%s", dirname, item->d_name);
×
1000
                        if (sret < 0) {
×
1001
                                cgroup_err("Out of memory\n");
×
1002

1003
                                /*
1004
                                 * Cannot read directory.
1005
                                 * However, CGRULES_CONF_FILE is successfully
1006
                                 * parsed. Thus return as a success for back
1007
                                 * compatibility.
1008
                                 */
1009
                                ret = 0;
×
1010
                                goto unlock_list;
×
1011
                        }
1012

1013
                        cgroup_dbg("Parsing cgrules file: %s\n", tmp);
×
1014
                        ret = cgroup_parse_rules_file(tmp, cache, muid, mgid, mprocname);
×
1015

1016
                        free(tmp);
×
1017

1018
                        /* Match with cache disabled? */
1019
                        if (ret != 0)
×
1020
                                goto unlock_list;
×
1021
                }
1022
                if (!item && errno) {
×
1023
                        cgroup_warn("cannot read %s: %s\n", dirname, strerror(errno));
×
1024
                        /*
1025
                         * Cannot read an item.
1026
                         * But continue for back compatibility as a success.
1027
                         */
1028
                        ret = 0;
×
1029
                        goto unlock_list;
×
1030
                }
1031
        } while (item != NULL);
×
1032

1033
unlock_list:
×
1034
        closedir(d);
×
1035
        pthread_rwlock_unlock(&rl_lock);
×
1036

1037
        return ret;
×
1038
}
1039

1040
int cg_add_duplicate_mount(struct cg_mount_table_s *item, const char *path)
16✔
1041
{
1042
        struct cg_mount_point *mount, *it;
1043

1044
        mount = malloc(sizeof(struct cg_mount_point));
16✔
1045
        if (!mount) {
16✔
1046
                last_errno = errno;
×
1047
                return ECGOTHER;
×
1048
        }
1049
        mount->next = NULL;
16✔
1050

1051
        strncpy(mount->path, path, sizeof(mount->path));
16✔
1052
        mount->path[sizeof(mount->path)-1] = '\0';
16✔
1053

1054
        /*
1055
         * Add the mount point to the end of the list. Assuming the list is
1056
         * short, no optimization is done.
1057
         */
1058
        it = &item->mount;
16✔
1059
        while (it->next)
16✔
1060
                it = it->next;
×
1061

1062
        it->next = mount;
16✔
1063

1064
        return 0;
16✔
1065
}
1066

1067
/*
1068
 * Tries to find if any controller in cg_mount_table have already mounted on
1069
 * the mount_path and if mounted sets the matching controller idx share_mnt
1070
 * flag and Return 1 or 0 otherwise.
1071
 */
1072
static int cgroup_set_cg_mnt_tbl_shared_mnt(char *mount_path, int *mnt_tbl_idx)
29,772✔
1073
{
1074
        int i, shared_mnt = 0;
29,772✔
1075

1076
        /* Check if controllers share mount points */
1077
        for  (i = 0; i < *mnt_tbl_idx; i++) {
231,421✔
1078
                if (strncmp(mount_path, cg_mount_table[i].mount.path, FILENAME_MAX) == 0) {
209,662✔
1079
                        cg_mount_table[i].shared_mnt = 1;
8,013✔
1080
                        shared_mnt = 1;
8,013✔
1081
                        break;
8,013✔
1082
                }
1083
        }
1084

1085
        return shared_mnt;
29,772✔
1086
}
1087

1088
static void cgroup_cg_mount_table_append(const char *name, const char *mount_path,
29,756✔
1089
                                         enum cg_version_t version, int *mnt_tbl_idx,
1090
                                         const char *mnt_opts, int shared_mnt)
1091
{
1092
        int i = *mnt_tbl_idx;
29,756✔
1093

1094
        strncpy(cg_mount_table[i].name,        name, CONTROL_NAMELEN_MAX);
29,756✔
1095
        cg_mount_table[i].name[CONTROL_NAMELEN_MAX-1] = '\0';
29,756✔
1096

1097
        strncpy(cg_mount_table[i].mount.path, mount_path, FILENAME_MAX);
29,756✔
1098
        cg_mount_table[i].mount.path[FILENAME_MAX-1] = '\0';
29,756✔
1099

1100
        cg_mount_table[i].shared_mnt = shared_mnt;
29,756✔
1101
        cg_mount_table[i].version = version;
29,756✔
1102
        cg_mount_table[i].mount.next = NULL;
29,756✔
1103

1104
        cgroup_dbg("Found cgroup option %s, count %d\n", mnt_opts, i);
29,756✔
1105

1106
        (*mnt_tbl_idx)++;
29,756✔
1107
}
29,756✔
1108

1109
/**
1110
 * Process a cgroup v1 mount and add it to cg_mount_table if it's not a
1111
 * duplicate.
1112
 *
1113
 *        @param controllers List of controllers from /proc/cgroups
1114
 *        @param ent File system description of cgroup mount being processed
1115
 *        @param mnt_tbl_idx cg_mount_table index
1116
 */
1117
STATIC int cgroup_process_v1_mnt(char *controllers[], struct mntent *ent, int *mnt_tbl_idx)
22,211✔
1118
{
1119
        char *strtok_buffer = NULL, *mntopt = NULL;
22,211✔
1120
        int shared_mnt, duplicate;
1121
        int i, j, ret = 0;
22,211✔
1122
        char c = 0;
22,211✔
1123

1124
        for (i = 0; controllers[i] != NULL; i++) {
333,097✔
1125
                mntopt = hasmntopt(ent, controllers[i]);
310,886✔
1126

1127
                if (!mntopt)
310,886✔
1128
                        continue;
287,659✔
1129

1130
                c = mntopt[strlen(controllers[i])];
23,227✔
1131

1132
                if (c != '\0' && c != ',')
23,227✔
1133
                        continue;
×
1134

1135
                cgroup_dbg("found %s in %s\n", controllers[i], ent->mnt_opts);
23,227✔
1136

1137
                /* Check if controllers share mount points */
1138
                shared_mnt = cgroup_set_cg_mnt_tbl_shared_mnt(ent->mnt_dir, mnt_tbl_idx);
23,227✔
1139

1140
                /* Do not have duplicates in mount table */
1141
                duplicate = 0;
23,227✔
1142
                for  (j = 0; j < *mnt_tbl_idx; j++) {
189,250✔
1143
                        if (strncmp(controllers[i], cg_mount_table[j].name, FILENAME_MAX) == 0) {
166,025✔
1144
                                duplicate = 1;
2✔
1145
                                break;
2✔
1146
                        }
1147
                }
1148

1149
                if (duplicate) {
23,227✔
1150
                        cgroup_dbg("controller %s is already mounted on %s\n", mntopt,
2✔
1151
                                   cg_mount_table[j].mount.path);
1152
                        ret = cg_add_duplicate_mount(&cg_mount_table[j], ent->mnt_dir);
2✔
1153
                        if (ret)
2✔
1154
                                goto out;
×
1155
                        /* Continue with next controller */
1156
                        continue;
2✔
1157
                }
1158

1159
                cgroup_cg_mount_table_append(controllers[i], ent->mnt_dir, CGROUP_V1, mnt_tbl_idx,
23,225✔
1160
                                             ent->mnt_opts, shared_mnt);
23,225✔
1161

1162
                if ((*mnt_tbl_idx) >= CG_CONTROLLER_MAX)
23,225✔
1163
                        goto out;
×
1164
        }
1165

1166
        /* Doesn't match the controller. Check if it is a named hierarchy. */
1167
        mntopt = hasmntopt(ent, "name");
22,211✔
1168

1169
        if (mntopt) {
22,211✔
1170
                mntopt = strtok_r(mntopt, ",", &strtok_buffer);
2,412✔
1171
                if (!mntopt)
2,412✔
1172
                        goto out;
×
1173

1174
#ifdef OPAQUE_HIERARCHY
1175
                /* Ignore the opaque hierarchy. */
1176
                if (strcmp(mntopt, OPAQUE_HIERARCHY) == 0)
2,412✔
1177
                        goto out;
1,716✔
1178
#endif
1179
                /* Check if controllers share mount points */
1180
                shared_mnt = cgroup_set_cg_mnt_tbl_shared_mnt(ent->mnt_dir, mnt_tbl_idx);
696✔
1181

1182
                /* Check if it is a duplicate */
1183
                duplicate = 0;
696✔
1184
                for (j = 0; j < *mnt_tbl_idx; j++) {
39,650✔
1185
                        if (strncmp(mntopt, cg_mount_table[j].name, FILENAME_MAX) == 0) {
38,954✔
1186
                                duplicate = 1;
×
1187
                                break;
×
1188
                        }
1189
                }
1190

1191
                if (duplicate) {
696✔
1192
                        cgroup_dbg("controller %s is already mounted on %s\n", mntopt,
×
1193
                                   cg_mount_table[j].mount.path);
1194
                        ret = cg_add_duplicate_mount(&cg_mount_table[j], ent->mnt_dir);
×
1195
                        goto out;
×
1196
                }
1197

1198
                cgroup_cg_mount_table_append(mntopt, ent->mnt_dir, CGROUP_V1, mnt_tbl_idx,
696✔
1199
                                             ent->mnt_opts, shared_mnt);
696✔
1200
        }
1201

1202
out:
19,799✔
1203
        return ret;
22,211✔
1204
}
1205

1206
/**
1207
 * Process a cgroup v2 mount and add it to cg_mount_table if it's not a
1208
 * duplicate.
1209
 *
1210
 *        @param ent File system description of cgroup mount being processed
1211
 *        @param mnt_tbl_idx cg_mount_table index
1212
 */
1213
STATIC int cgroup_process_v2_mnt(struct mntent *ent, int *mnt_tbl_idx)
2,195✔
1214
{
1215
        char *ret_c = NULL, line[CGV2_CONTROLLERS_LL_MAX], *stok_buff = NULL;
2,195✔
1216
        char *controller = NULL, *controllers = NULL;
2,195✔
1217
        char cgrp_controllers_path[FILENAME_MAX];
1218
        int ret = 0, i, duplicate, shared_mnt;
2,195✔
1219
        FILE *fp = NULL;
2,195✔
1220

1221
        /*
1222
         * Save off this mount point.  This may be used later to
1223
         * build the cg_path.
1224
         */
1225
        strncpy(cg_cgroup_v2_mount_path, ent->mnt_dir, FILENAME_MAX-1);
2,195✔
1226
        cg_cgroup_v2_mount_path[FILENAME_MAX-1] = '\0';
2,195✔
1227

1228
        /* determine what v2 controllers are available on this mount */
1229
        snprintf(cgrp_controllers_path, FILENAME_MAX, "%s/%s", ent->mnt_dir, CGV2_CONTROLLERS_FILE);
2,195✔
1230
        fp = fopen(cgrp_controllers_path, "re");
2,195✔
1231
        if (!fp) {
2,195✔
1232
                ret = ECGOTHER;
×
1233
                goto out;
×
1234
        }
1235

1236
        ret_c = fgets(line, CGV2_CONTROLLERS_LL_MAX, fp);
2,195✔
1237
        if (ret_c == NULL) {
2,195✔
1238
                struct cg_mount_point *tmp, *t;
1239

1240
                ret = ECGEOF;
943✔
1241

1242
                tmp = malloc(sizeof(struct cg_mount_point));
943✔
1243
                if (tmp == NULL) {
943✔
1244
                        last_errno = errno;
×
1245
                        ret = ECGOTHER;
×
1246
                        goto out;
×
1247
                }
1248

1249
                strncpy(tmp->path, cg_cgroup_v2_mount_path, sizeof(tmp->path) - 1);
943✔
1250
                tmp->path[sizeof(tmp->path)-1] = '\0';
943✔
1251
                tmp->next = NULL;
943✔
1252

1253
                t = cg_cgroup_v2_empty_mount_paths;
943✔
1254
                if (t == NULL) {
943✔
1255
                        cg_cgroup_v2_empty_mount_paths = tmp;
942✔
1256
                        goto out;
942✔
1257
                }
1258

1259
                while (t->next != NULL)
1✔
1260
                        t = t->next;
×
1261
                t->next = tmp;
1✔
1262

1263
                goto out;
1✔
1264
        }
1265

1266
        /* Remove the trailing newline */
1267
        ret_c[strlen(ret_c) - 1] = '\0';
1,252✔
1268

1269
        /*
1270
         * The "cgroup" controller is a pseudo-controller that has settings that a user may
1271
         * wish to read/modify.  Add it to our cg_mount_table so that it can be manipulated
1272
         * like other "normal" controllers
1273
         */
1274
        controllers = malloc(strlen(ret_c) + strlen(CGRP_FILE_PREFIX) + 2);
1,252✔
1275
        if (!controllers) {
1,252✔
1276
                ret = ECGOTHER;
×
1277
                goto out;
×
1278
        }
1279

1280
        sprintf(controllers, "%s %s", ret_c, CGRP_FILE_PREFIX);
1,252✔
1281

1282
        /*
1283
         * cgroup.controllers returns a list of available controllers in
1284
         * the following format:
1285
         *        cpuset cpu io memory pids rdma
1286
         */
1287
        controller = strtok_r(controllers, " ", &stok_buff);
1,252✔
1288
        do {
1289
                /* Check if controllers share mount points */
1290
                shared_mnt = cgroup_set_cg_mnt_tbl_shared_mnt(ent->mnt_dir, mnt_tbl_idx);
5,849✔
1291

1292
                /* Do not have duplicates in mount table */
1293
                duplicate = 0;
5,849✔
1294
                for  (i = 0; i < *mnt_tbl_idx; i++) {
23,806✔
1295
                        if (strncmp(cg_mount_table[i].name, controller, FILENAME_MAX) == 0) {
17,971✔
1296
                                duplicate = 1;
14✔
1297
                                break;
14✔
1298
                        }
1299
                }
1300

1301
                if (duplicate) {
5,849✔
1302
                        cgroup_dbg("controller %s is already mounted on %s\n", controller,
14✔
1303
                                   cg_mount_table[i].mount.path);
1304

1305
                        ret = cg_add_duplicate_mount(&cg_mount_table[i], ent->mnt_dir);
14✔
1306
                        if (ret)
14✔
1307
                                break;
×
1308

1309
                        continue;
14✔
1310
                }
1311

1312
                /* This controller is not in the mount table.  Add it */
1313
                cgroup_cg_mount_table_append(controller, ent->mnt_dir, CGROUP_V2, mnt_tbl_idx,
5,835✔
1314
                                             controller, shared_mnt);
1315

1316
                if ((*mnt_tbl_idx) >= CG_CONTROLLER_MAX)
5,835✔
1317
                        goto out;
×
1318
        } while ((controller = strtok_r(NULL, " ", &stok_buff)));
5,849✔
1319

1320
out:
1,252✔
1321
        if (fp)
2,195✔
1322
                fclose(fp);
2,195✔
1323

1324
        if (controllers)
2,195✔
1325
                free(controllers);
1,252✔
1326

1327
        return ret;
2,195✔
1328
}
1329

1330
/*
1331
 * Free global variables filled by previous cgroup_init(). This function
1332
 * should be called with cg_mount_table_lock taken.
1333
 */
1334
static void cgroup_free_cg_mount_table(void)
2,189✔
1335
{
1336
        struct cg_mount_point *mount, *tmp;
1337
        int i;
1338

1339
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
10,508✔
1340
                mount = cg_mount_table[i].mount.next;
8,319✔
1341

1342
                while (mount) {
8,319✔
1343
                        tmp = mount;
×
1344
                        mount = mount->next;
×
1345
                        free(tmp);
×
1346
                }
1347
        }
1348

1349
        memset(&cg_mount_table, 0, sizeof(cg_mount_table));
2,189✔
1350
        memset(&cg_cgroup_v2_mount_path, 0, sizeof(cg_cgroup_v2_mount_path));
2,189✔
1351
        memset(&cg_cgroup_v2_empty_mount_paths, 0, sizeof(cg_cgroup_v2_empty_mount_paths));
2✔
1352
}
2,189✔
1353

1354
/*
1355
 * Parses the mount options of the given mount point and checks for the
1356
 * option in the list of mount options and sets is_set accordingly.
1357
 * @mnt: Mount point name to search for mount points.
1358
 * @mnt_opt: Mount option to be searched.
1359
 * @is_set: Set to 1, when mount option is found, 0 otherwise.
1360
 *
1361
 * Returns 0, in case of success and ECGOTHER on failure.
1362
 */
1363
static int check_mount_point_opt(const char *mnt, const char *mnt_opt, int * const is_set)
×
1364
{
1365
        struct mntent *ent, *temp_ent = NULL;
×
1366
        char mntent_buffer[4 * FILENAME_MAX];
1367
        char *mntopt = NULL;
×
1368
        char mnt_opt_delim;
1369
        FILE *proc_mount;
1370
        int ret = 0;
×
1371

1372
        if (!mnt || !mnt_opt || !is_set)
×
1373
                return ECGINVAL;
×
1374

1375
        proc_mount = setmntent(mnt, "r");
×
1376
        if (!proc_mount) {
×
1377
                cgroup_err("cannot open %s: %s\n", mnt, strerror(errno));
×
1378
                last_errno = errno;
×
1379
                ret = ECGOTHER;
×
1380
                goto err;
×
1381
        }
1382

1383
        temp_ent = (struct mntent *) malloc(sizeof(struct mntent));
×
1384
        if (!temp_ent) {
×
1385
                last_errno = errno;
×
1386
                ret = ECGOTHER;
×
1387
                goto err;
×
1388
        }
1389

1390
        ent = getmntent_r(proc_mount, temp_ent,        mntent_buffer, sizeof(mntent_buffer));
×
1391
        if (!ent) {
×
1392
                last_errno = errno;
×
1393
                ret = ECGOTHER;
×
1394
                goto err;
×
1395
        }
1396

1397
        *is_set = 0;
×
1398
        while ((mntopt = hasmntopt(ent, mnt_opt))) {
×
1399
                mnt_opt_delim = mntopt[strlen(mnt_opt)];
×
1400
                if (mnt_opt_delim == '\0' || mnt_opt_delim == ',') {
×
1401
                        *is_set  = 1;
×
1402
                        break;
×
1403
                }
1404
        }
1405

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

1409
err:
×
1410
        if (proc_mount)
×
1411
                endmntent(proc_mount);
×
1412

1413
        if (temp_ent)
×
1414
                free(temp_ent);
×
1415

1416
        return ret;
×
1417
}
1418

1419
/*
1420
 * Reads /proc/cgroups and populates the controllers/subsys_name. This
1421
 * function should be called with cg_mount_table_lock taken.
1422
 */
1423
static int cgroup_populate_controllers(char *controllers[CG_CONTROLLER_MAX])
2,189✔
1424
{
1425
        int hierarchy, num_cgrps, enabled;
1426
        char subsys_name[FILENAME_MAX];
1427
        char mnt_opt[] = "subset=pid";
2,189✔
1428
        char *buf = NULL;
2,189✔
1429
        FILE *proc_cgrp;
1430
        int mnt_opt_set;
1431
        int ret = 0;
2,189✔
1432
        int err, i = 0;
2,189✔
1433

1434
        proc_cgrp = fopen("/proc/cgroups", "re");
2,189✔
1435
        if (!proc_cgrp) {
2,189✔
1436
                cgroup_warn("cannot open /proc/cgroups: %s\n", strerror(errno));
×
1437
                ret = check_mount_point_opt("/proc/self/mounts", mnt_opt, &mnt_opt_set);
×
1438
                if (ret)
×
1439
                        goto err;
×
1440

1441
                if (!mnt_opt_set)
×
1442
                        ret = ECGINVAL;
×
1443
                /*
1444
                 * /proc, mounted with subset=pid is valid. cgroup v2 doesn't
1445
                 * depend on /proc/cgroups to parse the available controllers.
1446
                 */
1447
                goto err;
×
1448
        }
1449

1450
        /*
1451
         * The first line of the file has stuff we are not interested in.
1452
         * So just read it and discard the information.
1453
         */
1454
        buf = malloc(CGV2_CONTROLLERS_LL_MAX);
2,189✔
1455
        if (!buf) {
2,189✔
1456
                last_errno = errno;
×
1457
                ret = ECGOTHER;
×
1458
                goto err;
×
1459
        }
1460

1461
        if (!fgets(buf, CGV2_CONTROLLERS_LL_MAX, proc_cgrp)) {
2,189✔
1462
                cgroup_err("cannot read /proc/cgroups: %s\n", strerror(errno));
×
1463
                last_errno = errno;
×
1464
                ret = ECGOTHER;
×
1465
                goto err;
×
1466
        }
1467

1468
        while (!feof(proc_cgrp)) {
32,835✔
1469
                /*
1470
                 * check Linux Kernel sources/kernel/cgroup/cgroup.c cgroup_init_early(),
1471
                 * MAX_CGROUP_TYPE_NAMELEN check for details on why 32 is used.
1472
                 */
1473
                err = fscanf(proc_cgrp, "%32s %d %d %d", subsys_name, &hierarchy, &num_cgrps,
32,835✔
1474
                             &enabled);
1475
                if (err < 0)
32,835✔
1476
                        break;
2,189✔
1477

1478
                controllers[i] = strdup(subsys_name);
30,646✔
1479
                if (controllers[i] == NULL) {
30,646✔
1480
                        last_errno = errno;
×
1481
                        ret = ECGOTHER;
×
1482
                        break;
×
1483
                }
1484
                i++;
30,646✔
1485
        }
1486

1487
err:
×
1488
        if (proc_cgrp)
2,189✔
1489
                fclose(proc_cgrp);
2,189✔
1490

1491
        if (buf)
2,189✔
1492
                free(buf);
2,189✔
1493

1494
        if (ret != 0) {
2,189✔
1495
                for (i = 0; controllers[i]; i++) {
×
1496
                        free(controllers[i]);
×
1497
                        controllers[i] = NULL;
×
1498
                }
1499
        }
1500

1501
        return ret;
2,189✔
1502
}
1503

1504
/*
1505
 * Reads /proc/self/mounts and populates the cgroup v1/v2 mount points into the
1506
 * global cg_mount_table.
1507
 * This function should be called with cg_mount_table_lock taken.
1508
 */
1509
static int cgroup_populate_mount_points(char *controllers[CG_CONTROLLER_MAX])
2,189✔
1510
{
1511
        char mntent_buffer[4 * FILENAME_MAX];
1512
        struct mntent *ent, *temp_ent = NULL;
2,189✔
1513
        int found_mnt = 0;
2,189✔
1514
        FILE *proc_mount;
1515
        int ret = 0;
2,189✔
1516

1517
        proc_mount = fopen("/proc/self/mounts", "re");
2,189✔
1518
        if (proc_mount == NULL) {
2,189✔
1519
                cgroup_err("cannot open /proc/self/mounts: %s\n", strerror(errno));
×
1520
                last_errno = errno;
×
1521
                ret = ECGOTHER;
×
1522
                goto err;
×
1523
        }
1524

1525
        temp_ent = (struct mntent *) malloc(sizeof(struct mntent));
2,189✔
1526
        if (!temp_ent) {
2,189✔
1527
                last_errno = errno;
×
1528
                ret = ECGOTHER;
×
1529
                goto err;
×
1530
        }
1531

1532
        while ((ent = getmntent_r(proc_mount, temp_ent,        mntent_buffer,
110,968✔
1533
                                  sizeof(mntent_buffer))) != NULL) {
19,133✔
1534

1535
                if (strcmp(ent->mnt_type, "cgroup") == 0) {
108,787✔
1536
                        if (controllers[0] == NULL) {
22,205✔
1537
                                cgroup_err("cgroup v1 requires /proc/cgroups, check if /proc ");
×
1538
                                cgroup_cont("is mounted with subset=pid option.\n");
×
1539
                                ret = ECGINVAL;
×
1540
                                goto err;
×
1541
                        }
1542

1543
                        ret = cgroup_process_v1_mnt(controllers, ent, &found_mnt);
22,205✔
1544
                        if (ret)
22,205✔
1545
                                goto err;
×
1546

1547
                        if (found_mnt >= CG_CONTROLLER_MAX)
22,205✔
1548
                                break;
8✔
1549

1550
                        continue;
22,197✔
1551
                }
1552

1553
                if (strcmp(ent->mnt_type, "cgroup2") == 0) {
86,582✔
1554
                        ret = cgroup_process_v2_mnt(ent, &found_mnt);
2,189✔
1555
                        if (ret == ECGEOF) {
2,189✔
1556
                        /* The controllers file was empty.  Ignore and move on. */
1557
                                ret = 0;
941✔
1558
                                continue;
941✔
1559
                        }
1560

1561
                        if (ret)
1,248✔
1562
                                goto err;
×
1563

1564
                        if (found_mnt >= CG_CONTROLLER_MAX)
1,248✔
1565
                                break;
×
1566
                }
1567
        }
1568

1569
        if (!found_mnt)
2,189✔
1570
                ret = ECGROUPNOTMOUNTED;
×
1571

1572
        if (found_mnt >= CG_CONTROLLER_MAX) {
2,189✔
1573
                cgroup_err("Mount points exceeds CG_CONTROLLER_MAX");
8✔
1574
                ret = ECGMAXVALUESEXCEEDED;
8✔
1575
                /*
1576
                 * There are loops in the libcgroup codebase that expect
1577
                 * there to be a null name entry at the end of the
1578
                 * cg_mount_table[].
1579
                 */
1580
                cg_mount_table[CG_CONTROLLER_MAX - 1].name[0] = '\0';
8✔
1581
        }
1582

1583
err:
2,181✔
1584
        if (proc_mount)
2,189✔
1585
                fclose(proc_mount);
2,189✔
1586

1587
        if (temp_ent)
2,189✔
1588
                free(temp_ent);
2,189✔
1589

1590
        return ret;
2,189✔
1591
}
1592

1593
/**
1594
 * cgroup_init(), initializes the MOUNT_POINT.
1595
 *
1596
 * This code is theoretically thread safe now. Its not really tested so it can
1597
 * blow up. If does for you, please let us know with your test case and we can
1598
 * really make it thread safe.
1599
 */
1600
int cgroup_init(void)
2,189✔
1601
{
1602
        static char *controllers[CG_CONTROLLER_MAX];
1603
        int ret = 0;
2,189✔
1604
        int i;
1605

1606
        cgroup_set_default_logger(-1);
2,189✔
1607

1608
        pthread_rwlock_wrlock(&cg_mount_table_lock);
2,189✔
1609

1610
        /* Free global variables filled by previous cgroup_init() */
1611
        cgroup_free_cg_mount_table();
2,189✔
1612

1613
        ret = cgroup_populate_controllers(controllers);
2,189✔
1614
        if (ret)
2,189✔
1615
                goto unlock_exit;
×
1616

1617
        ret = cgroup_populate_mount_points(controllers);
2,189✔
1618
        if (ret)
2,189✔
1619
                goto unlock_exit;
8✔
1620

1621
        cgroup_initialized = 1;
2,181✔
1622

1623
unlock_exit:
2,189✔
1624
        for (i = 0; controllers[i]; i++) {
32,835✔
1625
                free(controllers[i]);
30,646✔
1626
                controllers[i] = NULL;
30,646✔
1627
        }
1628

1629
        pthread_rwlock_unlock(&cg_mount_table_lock);
2,189✔
1630

1631
        return ret;
2,189✔
1632
}
1633

1634
static int cg_test_mounted_fs(void)
1,475✔
1635
{
1636
        char mntent_buff[4 * FILENAME_MAX];
1637
        struct mntent *temp_ent = NULL;
1,475✔
1638
        struct mntent *ent = NULL;
1,475✔
1639
        FILE *proc_mount = NULL;
1,475✔
1640
        int ret = 1;
1,475✔
1641

1642
        proc_mount = fopen("/proc/self/mounts", "re");
1,475✔
1643
        if (proc_mount == NULL)
1,475✔
1644
                return 0;
×
1645

1646
        temp_ent = (struct mntent *) malloc(sizeof(struct mntent));
1,475✔
1647
        if (!temp_ent) {
1,475✔
1648
                /* We just fail at the moment. */
1649
                fclose(proc_mount);
×
1650
                return 0;
×
1651
        }
1652

1653
        ent = getmntent_r(proc_mount, temp_ent, mntent_buff, sizeof(mntent_buff));
1,475✔
1654
        if (!ent) {
1,475✔
1655
                ret = 0;
×
1656
                goto done;
×
1657
        }
1658

1659
        while (strcmp(ent->mnt_type, "cgroup") != 0 &&
31,177✔
1660
               strcmp(ent->mnt_type, "cgroup2") != 0) {
31,177✔
1661
                ent = getmntent_r(proc_mount, temp_ent, mntent_buff, sizeof(mntent_buff));
29,702✔
1662
                if (ent == NULL) {
29,702✔
1663
                        ret = 0;
×
1664
                        goto done;
×
1665
                }
1666
        }
1667
done:
1,475✔
1668
        fclose(proc_mount);
1,475✔
1669
        free(temp_ent);
1,475✔
1670

1671
        return ret;
1,475✔
1672
}
1673

1674
static inline pid_t cg_gettid(void)
×
1675
{
1676
        return syscall(__NR_gettid);
×
1677
}
1678

1679
static char *cg_concat_path(const char *pref, const char *suf, char *path)
25,490✔
1680
{
1681
        if ((suf[strlen(suf)-1] == '/') || ((strlen(suf) == 0) && (pref[strlen(pref)-1] == '/')))
25,490✔
1682
                snprintf(path, FILENAME_MAX, "%s%s", pref, suf+((suf[0] == '/') ? 1 : 0));
122✔
1683
        else
1684
                snprintf(path, FILENAME_MAX, "%s%s/", pref, suf+((suf[0] == '/') ? 1 : 0));
25,368✔
1685

1686
        path[FILENAME_MAX-1] = '\0';
25,490✔
1687

1688
        return path;
25,490✔
1689
}
1690

1691
/* Call with cg_mount_table_lock taken */
1692
/* path value have to have size at least FILENAME_MAX */
1693
char *cg_build_path_locked(const char *name, char *path, const char *type)
27,022✔
1694
{
1695
        char *tmp_systemd_default_cgrp, *_path = NULL;
27,022✔
1696
        /*
1697
         * len is the allocation size for path, that stores:
1698
         * cg_mount_table[i].mount.path + '/' + cg_namespace_table[i] + '/'
1699
         */
1700
        int i, ret, len = (FILENAME_MAX * 2) + 2;
27,022✔
1701

1702
        /*
1703
         * systemd_default_cgroup can't be clobbered.   The user may pass
1704
         * multiple cgroups, hence use temporary variable for manipulations
1705
         * for example:
1706
         * cgget -g cpu:/ -g cpu:cgrp1 -g cpu:/cgrp2
1707
         */
1708
        tmp_systemd_default_cgrp = calloc(1, (sizeof(char) * len));
27,022✔
1709
        if (!tmp_systemd_default_cgrp) {
27,022✔
1710
                cgroup_err("Failed to allocate memory for tmp_systemd_default_cgroup\n");
×
1711
                goto out;
×
1712
        }
1713

1714
#ifdef WITH_SYSTEMD
1715
        /*
1716
         * If the user specifies the name as /<cgroup-name>, they are
1717
         * effectively overriding the systemd_default_cgroup but if the name
1718
         * is "/", the cgroup root path is systemd_default_cgroup
1719
         */
1720
        if (strlen(systemd_default_cgroup) && name && name[0] == '/' && name[1] != '\0')
24,510✔
1721
                tmp_systemd_default_cgrp[0] = '\0';
1722
        else
1723
                snprintf(tmp_systemd_default_cgrp, len, "%s/", systemd_default_cgroup);
24,510✔
1724

1725
        /* allocate more space for systemd_default_cgroup + '/' */
1726
        len += (FILENAME_MAX + 1);
24,510✔
1727
#endif
1728
        /*
1729
         * Recent gcc are unhappy when sizeof(dest) <= sizeof(src) with
1730
         * snprintf()'s.  Alternative is to use multiple strncpy()/strcat(),
1731
         * work around it by allocating large temporary buffer _path and
1732
         * copying the constructed _path into path.
1733
         */
1734
        _path = malloc(len);
27,022✔
1735
        if (!_path) {
27,022✔
1736
                cgroup_err("Failed to allocate memory for _path\n");
×
1737
                goto out;
×
1738
        }
1739

1740
        /*
1741
         * If no type is specified, and there's a valid cgroup v2 mount, then
1742
         * build up a path to this mount (and cgroup name if supplied).
1743
         * This can be used to create a cgroup v2 cgroup that's not attached to
1744
         * any controller.
1745
         */
1746
        if (!type && strlen(cg_cgroup_v2_mount_path) > 0) {
27,022✔
1747
                ret = snprintf(_path, len, "%s/%s", cg_cgroup_v2_mount_path,
128✔
1748
                               tmp_systemd_default_cgrp);
1749
                if (ret >= FILENAME_MAX)
128✔
1750
                        cgroup_dbg("filename too long: %s", _path);
×
1751

1752
                strncpy(path, _path, FILENAME_MAX - 1);
128✔
1753
                path[FILENAME_MAX - 1] = '\0';
128✔
1754

1755
                if (name) {
128✔
1756
                        char *tmp;
1757

1758
                        tmp = strdup(path);
128✔
1759
                        if (tmp == NULL) {
128✔
1760
                                path = NULL;
×
1761
                                goto out;
×
1762
                        }
1763

1764
                        cg_concat_path(tmp, name, path);
128✔
1765
                        free(tmp);
128✔
1766
                }
1767
                goto out;
128✔
1768
        }
1769

1770
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
160,438✔
1771
                /* Two ways to successfully move forward here:
1772
                 * 1. The "type" controller matches the name of a mounted
1773
                 *    controller
1774
                 * 2. The "type" controller requested is "cgroup" and there's
1775
                 *    a "real" controller mounted as cgroup v2
1776
                 */
1777
                if ((type && strcmp(cg_mount_table[i].name, type) == 0) ||
160,434✔
1778
                    (type && strcmp(type, CGRP_FILE_PREFIX) == 0 &&
133,888✔
1779
                     cg_mount_table[i].version == CGROUP_V2)) {
344✔
1780

1781
                        if (cg_namespace_table[i])
26,890✔
1782
                                ret = snprintf(_path, len, "%s/%s%s/", cg_mount_table[i].mount.path,
8✔
1783
                                               tmp_systemd_default_cgrp, cg_namespace_table[i]);
1784
                        else
1785
                                ret = snprintf(_path, len, "%s/%s", cg_mount_table[i].mount.path,
26,882✔
1786
                                               tmp_systemd_default_cgrp);
1787

1788
                        if (ret >= FILENAME_MAX)
26,890✔
1789
                                cgroup_dbg("filename too long: %s", _path);
×
1790

1791
                        strncpy(path, _path, FILENAME_MAX - 1);
26,890✔
1792
                        path[FILENAME_MAX - 1] = '\0';
26,890✔
1793

1794
                        if (name) {
26,890✔
1795
                                char *tmp;
1796

1797
                                tmp = strdup(path);
25,362✔
1798
                                if (tmp == NULL)
25,362✔
1799
                                        break;
×
1800

1801
                                cg_concat_path(tmp, name, path);
25,362✔
1802
                                free(tmp);
25,362✔
1803
                        }
1804
                        goto out;
26,890✔
1805
                }
1806
        }
1807
        path = NULL;
4✔
1808

1809
out:
27,022✔
1810
        if (_path)
27,022✔
1811
                free(_path);
27,022✔
1812

1813
        if (tmp_systemd_default_cgrp)
27,022✔
1814
                free(tmp_systemd_default_cgrp);
27,022✔
1815

1816
        return path;
27,022✔
1817
}
1818

1819
char *cg_build_path(const char *name, char *path, const char *type)
3,215✔
1820
{
1821
        pthread_rwlock_rdlock(&cg_mount_table_lock);
3,215✔
1822
        path = cg_build_path_locked(name, path, type);
3,215✔
1823
        pthread_rwlock_unlock(&cg_mount_table_lock);
3,215✔
1824

1825
        return path;
3,215✔
1826
}
1827

1828
static int cgroup_get_cg_type(const char * const path, char * const type,
387✔
1829
                              size_t type_sz, bool is_tid)
1830
{
1831
        char cg_type_path[FILENAME_MAX];
1832
        char cg_type[CGV2_CONTROLLERS_LL_MAX];
1833
        int len, err = 0;
387✔
1834
        FILE *fp = NULL;
387✔
1835

1836
        snprintf(cg_type_path, FILENAME_MAX, "%scgroup.type", path);
387✔
1837
        fp = fopen(cg_type_path, "re");
387✔
1838
        if (!fp) {
387✔
1839
                if (errno == ENOENT) {
62✔
1840
                        /* file cgroup.type, doesn't exist for root cgroup. */
1841
                        snprintf(type, type_sz, "cgroup.procs");
62✔
1842
                        goto out;
62✔
1843
                } else {
1844
                        cgroup_warn("failed to open file %s: %s\n", cg_type_path, strerror(errno));
×
1845
                        err = ECGOTHER;
×
1846
                        goto out;
×
1847
                }
1848
        }
1849

1850
        if (fgets(cg_type, CGV2_CONTROLLERS_LL_MAX, fp) == NULL) {
325✔
1851
                cgroup_warn("failed to read file %s: %s\n", cg_type_path, strerror(errno));
×
1852
                err = ECGOTHER;
×
1853
                goto out;
×
1854
        }
1855

1856
        len = strlen(cg_type) - 1;
325✔
1857
        /*
1858
         * Append cgroup.threads to the path, if the cgroup.type is 'threaded'
1859
         * or 'domain threaded', with is_tid set. For cgroup.type 'domain' or
1860
         * 'domain invalid' or 'domain threaded', with is_tid is unset, append
1861
         * cgroup.procs to the path.
1862
         *
1863
         * domain type is used for regular cgroup and domain threaded for root
1864
         * of threaded cgroup v2 subtree. Another possible type is domain invalid,
1865
         * it's an invalid state, under the threaded subtree. is_tid is set when
1866
         * called from cgroup_attach_thread_tid() or unset other wise.
1867
         * Refer to Kernel's cgroup v2 documentation for more detailed explanation
1868
         * on domains types.
1869
         */
1870
        if (strncmp(cg_type, "domain", len) == 0         ||
325✔
1871
            strncmp(cg_type, "domain invalid", len) == 0 ||
12✔
1872
            (!is_tid && strncmp(cg_type, "domain threaded", len) == 0)) {
6✔
1873
                snprintf(type, type_sz, "cgroup.procs");
323✔
1874
        } else if (strncmp(cg_type, "threaded", len) == 0 ||
2✔
1875
                   (is_tid && strncmp(cg_type, "domain threaded", len) == 0)) {
×
1876
                snprintf(type, type_sz, "cgroup.threads");
2✔
1877
        } else {
1878
                cgroup_warn("invalid %scgroup.type: %s\n", path, cg_type);
×
1879
                err = ECGOTHER;
×
1880
        }
1881

1882
out:
387✔
1883
        if (fp)
387✔
1884
                fclose(fp);
325✔
1885

1886
        return err;
387✔
1887
}
1888

1889
int cgroup_build_tasks_procs_path(char * const path, size_t path_sz, const char * const cg_name,
1,211✔
1890
                                  const char * const ctrl_name)
1891
{
1892
        enum cg_version_t version;
1893
        char cg_type[CGV2_CONTROLLERS_LL_MAX];
1894
        int err = ECGOTHER;
1,211✔
1895

1896
        if (!cg_build_path(cg_name, path, ctrl_name))
1,211✔
1897
                goto error;
2✔
1898

1899
        err = cgroup_get_controller_version(ctrl_name, &version);
1,209✔
1900
        if (err)
1,209✔
1901
                goto error;
×
1902

1903
        switch (version) {
1,209✔
1904
        case CGROUP_V1:
820✔
1905
                strncat(path, "tasks", path_sz - strlen(path));
820✔
1906
                err = 0;
820✔
1907
                break;
820✔
1908
        case CGROUP_V2:
387✔
1909
                err = cgroup_get_cg_type(path, cg_type, sizeof(cg_type), 0);
387✔
1910
                if (err)
387✔
1911
                        goto error;
×
1912

1913
                strncat(path, cg_type, path_sz - strlen(path));
387✔
1914
                break;
387✔
1915
        default:
2✔
1916
                err = ECGOTHER;
2✔
1917
                break;
2✔
1918
        }
1919

1920
error:
1,211✔
1921
        if (err)
1,211✔
1922
                path[0] = '\0';
4✔
1923

1924
        cgroup_dbg("cgroup build procs path: %s\n", path);
1,211✔
1925

1926
        return err;
1,211✔
1927
}
1928

1929
STATIC int cgroupv2_controller_enabled(const char * const cg_name, const char * const ctrl_name)
211✔
1930
{
1931
        char path[FILENAME_MAX] = {0};
211✔
1932
        char *parent = NULL, *dname;
211✔
1933
        enum cg_version_t version;
1934
        bool enabled;
1935
        int error;
1936

1937
        error = cgroup_get_controller_version(ctrl_name, &version);
211✔
1938
        if (error)
211✔
1939
                return error;
×
1940

1941
        if (version != CGROUP_V2)
211✔
1942
                return 0;
150✔
1943

1944
        if (ctrl_name == NULL)
61✔
1945
                /* cgroup v2 supports cgroups with no controllers. */
1946
                return 0;
2✔
1947

1948
        if (strncmp(cg_name, "/", strlen(cg_name)) == 0)
59✔
1949
                /*
1950
                 * The root cgroup has been requested.  All version 2
1951
                 * controllers are enabled on the root cgroup.
1952
                 */
1953
                return 0;
2✔
1954

1955
        if (!cg_build_path(cg_name, path, ctrl_name))
57✔
1956
                goto err;
×
1957

1958
        parent = strdup(path);
57✔
1959
        if (!parent) {
57✔
1960
                error = ECGOTHER;
×
1961
                goto err;
×
1962
        }
1963

1964
        dname = dirname(parent);
57✔
1965

1966
        error = cgroupv2_get_subtree_control(dname, ctrl_name, &enabled);
57✔
1967
        if (error)
57✔
1968
                goto err;
3✔
1969

1970
        if (enabled)
54✔
1971
                error = 0;
54✔
1972
err:
×
1973
        if (parent)
57✔
1974
                free(parent);
57✔
1975

1976
        return error;
57✔
1977
}
1978

1979
static int __cgroup_attach_task_pid(char *path, pid_t tid)
201✔
1980
{
1981
        FILE *tasks = NULL;
201✔
1982
        int ret = 0;
201✔
1983

1984
        tasks = fopen(path, "we");
201✔
1985
        if (!tasks) {
201✔
1986
                switch (errno) {
2✔
1987
                case EPERM:
×
1988
                        ret = ECGROUPNOTOWNER;
×
1989
                        break;
×
1990
                case ENOENT:
2✔
1991
                        ret = ECGROUPNOTEXIST;
2✔
1992
                        break;
2✔
1993
                default:
×
1994
                        ret = ECGROUPNOTALLOWED;
×
1995
                }
1996
                goto err;
2✔
1997
        }
1998
        ret = fprintf(tasks, "%d", tid);
199✔
1999
        if (ret < 0) {
199✔
2000
                last_errno = errno;
×
2001
                ret = ECGOTHER;
×
2002
                goto err;
×
2003
        }
2004
        ret = fflush(tasks);
199✔
2005
        if (ret) {
199✔
2006
                last_errno = errno;
2✔
2007
                ret = ECGOTHER;
2✔
2008
                goto err;
2✔
2009
        }
2010
        fclose(tasks);
197✔
2011
        return 0;
197✔
2012
err:
4✔
2013
        cgroup_warn("cannot write tid %d to %s:%s\n", tid, path, strerror(errno));
4✔
2014
        if (tasks)
4✔
2015
                fclose(tasks);
2✔
2016
        return ret;
4✔
2017
}
2018

2019
static int cgroup_build_tid_path(const char * const ctrl_name, char *path)
1✔
2020
{
2021
        char cg_type[CGV2_CONTROLLERS_LL_MAX];
2022
        enum cg_version_t version;
2023
        size_t len;
2024
        int ret;
2025

2026
        ret = cgroup_get_controller_version(ctrl_name, &version);
1✔
2027
        if (ret)
1✔
2028
                return ret;
×
2029

2030
        if (version == CGROUP_V2) {
1✔
2031

2032
                len = strlen(path) - 12;
×
2033
                if (strncmp(path + len, "cgroup.procs", 12) != 0) {
×
2034
                        ret = ECGOTHER;
×
2035
                        return ret;
×
2036
                }
2037

2038
                /* right trim cgroup.procs file name in the path */
2039
                path[len] = '\0';
×
2040

2041
                ret = cgroup_get_cg_type(path, cg_type, sizeof(cg_type), 1);
×
2042
                if (ret)
×
2043
                        return ret;
×
2044

2045
                strncat(path, cg_type, FILENAME_MAX - (len + 1));
×
2046
                path[FILENAME_MAX - 1] = '\0';
×
2047
        }
2048

2049
        if (version != CGROUP_V1)
1✔
2050
                return ret;
×
2051

2052
        /* replace tasks with cgroup.procs file name in the path */
2053
        len = strlen(path) - 5;
1✔
2054
        path[len] = '\0';
1✔
2055

2056
        strncat(path, "cgroup.procs", FILENAME_MAX - (len + 1));
1✔
2057
        path[FILENAME_MAX - 1] = '\0';
1✔
2058

2059
        return ret;
1✔
2060
}
2061

2062
static int cgroup_attach_task_tid(struct cgroup *cgrp, pid_t tid, bool move_tids)
141✔
2063
{
2064
        char path[FILENAME_MAX] = {0};
141✔
2065
        char *controller_name = NULL;
141✔
2066
        int empty_cgrp = 0;
141✔
2067
        int i, ret = 0;
141✔
2068

2069
        if (!cgroup_initialized) {
141✔
2070
                cgroup_warn("libcgroup is not initialized\n");
×
2071
                return ECGROUPNOTINITIALIZED;
×
2072
        }
2073

2074
        /* if the cgroup is NULL, attach the task to the root cgroup. */
2075
        if (!cgrp) {
141✔
2076
                pthread_rwlock_rdlock(&cg_mount_table_lock);
×
2077
                for (i = 0; i < CG_CONTROLLER_MAX && cg_mount_table[i].name[0] != '\0'; i++) {
×
2078
                        ret = cgroup_build_tasks_procs_path(path, sizeof(path), NULL,
×
2079
                                                            cg_mount_table[i].name);
×
2080
                        if (ret)
×
2081
                                return ret;
×
2082

2083
                        if (move_tids) {
×
2084
                                ret = cgroup_build_tid_path(controller_name, path);
×
2085
                                if (ret)
×
2086
                                        return ret;
×
2087
                        }
2088

2089
                        ret = __cgroup_attach_task_pid(path, tid);
×
2090
                        if (ret) {
×
2091
                                pthread_rwlock_unlock(&cg_mount_table_lock);
×
2092
                                return ret;
×
2093
                        }
2094
                }
2095
                pthread_rwlock_unlock(&cg_mount_table_lock);
×
2096
        } else {
2097
                for (i = 0; i < cgrp->index; i++) {
340✔
2098
                        if (!cgroup_test_subsys_mounted(cgrp->controller[i]->name)) {
199✔
2099
                                cgroup_warn("subsystem %s is not mounted\n",
×
2100
                                            cgrp->controller[i]->name);
2101
                                return ECGROUPSUBSYSNOTMOUNTED;
×
2102
                        }
2103
                }
2104

2105
                if (cgrp->index == 0)
141✔
2106
                        /* Valid empty cgroup v2 with no controllers added. */
2107
                        empty_cgrp = 1;
2✔
2108

2109
                for (i = 0, controller_name = NULL;
288✔
2110
                     empty_cgrp > 0 || i < cgrp->index;
338✔
2111
                     i++, empty_cgrp--) {
197✔
2112

2113
                        if (cgrp->controller[i])
201✔
2114
                                controller_name = cgrp->controller[i]->name;
199✔
2115

2116
                        ret = cgroupv2_controller_enabled(cgrp->name, controller_name);
201✔
2117
                        if (ret)
201✔
2118
                                return ret;
×
2119

2120
                        ret = cgroup_build_tasks_procs_path(path, sizeof(path), cgrp->name,
201✔
2121
                                                            controller_name);
2122
                        if (ret)
201✔
2123
                                return ret;
×
2124

2125
                        if (move_tids) {
201✔
2126
                                ret = cgroup_build_tid_path(controller_name, path);
1✔
2127
                                if (ret)
1✔
2128
                                        return ret;
×
2129
                        }
2130

2131
                        ret = __cgroup_attach_task_pid(path, tid);
201✔
2132
                        if (ret)
201✔
2133
                                return ret;
4✔
2134
                }
2135
        }
2136
        return 0;
137✔
2137
}
2138

2139
/**
2140
 *  cgroup_attach_task_pid is used to assign tasks to a cgroup.
2141
 *  struct cgroup *cgroup: The cgroup to assign the thread to.
2142
 *  pid_t tid: The thread to be assigned to the cgroup.
2143
 *
2144
 *  returns 0 on success.
2145
 *  returns ECGROUPNOTOWNER if the caller does not have access to the cgroup.
2146
 *  returns ECGROUPNOTALLOWED for other causes of failure.
2147
 */
2148
int cgroup_attach_task_pid(struct cgroup *cgroup, pid_t tid)
140✔
2149
{
2150
        return cgroup_attach_task_tid(cgroup, tid, 0);
140✔
2151
}
2152

2153
/**
2154
 * cgroup_attach_task is used to attach the current thread to a cgroup.
2155
 * struct cgroup *cgroup: The cgroup to assign the current thread to.
2156
 *
2157
 * See cg_attach_task_pid for return values.
2158
 */
2159
int cgroup_attach_task(struct cgroup *cgroup)
×
2160
{
2161
        pid_t tid = cg_gettid();
×
2162

2163
        return cgroup_attach_task_tid(cgroup, tid, 0);
×
2164
}
2165

2166
/**
2167
 *  cgroup_attach_thread_tid is used to assign threads to a cgroup.
2168
 *  struct cgroup *cgroup: The cgroup to assign the thread to.
2169
 *  pid_t tid: The thread to be assigned to the cgroup.
2170
 *
2171
 *  returns 0 on success.
2172
 *  returns ECGROUPNOTOWNER if the caller does not have access to the cgroup.
2173
 *  returns ECGROUPNOTALLOWED for other causes of failure.
2174
 */
2175
int cgroup_attach_thread_tid(struct cgroup *cgroup, pid_t tid)
1✔
2176
{
2177
        return cgroup_attach_task_tid(cgroup, tid, 1);
1✔
2178
}
2179

2180
/**
2181
 * cg_mkdir_p, emulate the mkdir -p command (recursively creating paths)
2182
 * @path: path to create
2183
 */
2184
int cg_mkdir_p(const char *path)
655✔
2185
{
2186
        char *real_path = NULL;
655✔
2187
        char *tmp_path = NULL;
655✔
2188
        struct stat st;
2189
        int ret = 0;
655✔
2190
        int i = 0;
655✔
2191
        char pos;
2192

2193
        real_path = strdup(path);
655✔
2194
        if (!real_path) {
655✔
2195
                last_errno = errno;
×
2196
                return ECGOTHER;
×
2197
        }
2198

2199
        do {
2200
                while (real_path[i] != '\0' && real_path[i] == '/')
7,518✔
2201
                        i++;
3,936✔
2202

2203
                if (real_path[i] == '\0')
3,582✔
2204
                        break; /* The path ends with '/', ignore it. */
488✔
2205

2206
                while (real_path[i] != '\0' && real_path[i] != '/')
21,346✔
2207
                        i++;
18,252✔
2208

2209
                pos = real_path[i];
3,094✔
2210
                real_path[i] = '\0';                /* Temporarily overwrite "/" */
3,094✔
2211

2212
                /* 0775 == S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH */
2213
                ret = mkdir(real_path, 0775);
3,094✔
2214
                if (ret) {
3,094✔
2215
                        switch (errno) {
2,579✔
2216
                        case EEXIST:
2,579✔
2217
                                ret = 0;        /* Not fatal really */
2,579✔
2218
                                break;
2,579✔
2219
                        case EPERM:
×
2220
                                ret = ECGROUPNOTOWNER;
×
2221
                                goto done;
×
2222
                        case EROFS:
×
2223
                                /*
2224
                                 * Check if path exists, use tmp_path to
2225
                                 * keep Coverity happy
2226
                                 */
2227
                                tmp_path = real_path;
×
2228
                                ret = stat(tmp_path, &st);
×
2229
                                if (ret == 0)
×
2230
                                        break;        /* Path exists */
×
2231
                        default: /* fallthrough */
2232
                                ret = ECGROUPNOTALLOWED;
×
2233
                                goto done;
×
2234
                        }
2235
                }
404✔
2236
                real_path[i] = pos;
3,094✔
2237
        } while (real_path[i]);
3,094✔
2238

2239
done:
167✔
2240
        free(real_path);
655✔
2241

2242
        return ret;
655✔
2243
}
2244

2245
/*
2246
 * create_control_group()
2247
 * This is the basic function used to create the control group. This function
2248
 * just makes the group. It does not set any permissions, or any control values.
2249
 * The argument path is the fully qualified path name to make it generic.
2250
 */
2251
static int cg_create_control_group(const char *path)
655✔
2252
{
2253
        int error;
2254

2255
        if (!cg_test_mounted_fs())
655✔
2256
                return ECGROUPNOTMOUNTED;
×
2257

2258
        error = cg_mkdir_p(path);
655✔
2259
        return error;
655✔
2260
}
2261

2262
/*
2263
 * set_control_value()
2264
 * This is the low level function for putting in a value in a control file.
2265
 * This function takes in the complete path and sets the value in val in that file.
2266
 */
2267
static int cg_set_control_value(char *path, const char *val)
820✔
2268
{
2269
        char *str_val_start;
2270
        char *str_val;
2271
        int ctl_file;
2272
        size_t len;
2273
        char *pos;
2274

2275
        if (!cg_test_mounted_fs())
820✔
2276
                return ECGROUPNOTMOUNTED;
×
2277

2278
        ctl_file = open(path, O_RDWR | O_CLOEXEC);
820✔
2279

2280
        if (ctl_file == -1) {
820✔
2281
                if (errno == EPERM) {
×
2282
                        /*
2283
                         * We need to set the correct error value, does the
2284
                         * group exist but we don't have the subsystem mounted
2285
                         * at that point, or is it that the group does not exist.
2286
                         * So we check if the tasks file exist. Before that, we
2287
                         * need to extract the path.
2288
                         */
2289
                        char *path_dir_end;
2290
                        FILE *control_file;
2291
                        char *tasks_path;
2292

2293
                        path_dir_end = strrchr(path, '/');
×
2294
                        if (path_dir_end == NULL)
×
2295
                                return ECGROUPVALUENOTEXIST;
×
2296
                        path_dir_end = '\0';
×
2297

2298
                        /* task_path contain: $path/tasks */
2299
                        tasks_path = (char *)malloc(strlen(path) + 6 + 1);
×
2300
                        if (tasks_path == NULL) {
×
2301
                                last_errno = errno;
×
2302
                                return ECGOTHER;
×
2303
                        }
2304
                        strcpy(tasks_path, path);
×
2305
                        strcat(tasks_path, "/tasks");
×
2306

2307
                        /* Test tasks file for read flag */
2308
                        control_file = fopen(tasks_path, "re");
×
2309
                        if (!control_file) {
×
2310
                                if (errno == ENOENT) {
×
2311
                                        free(tasks_path);
×
2312
                                        return ECGROUPSUBSYSNOTMOUNTED;
×
2313
                                }
2314
                        } else {
2315
                                fclose(control_file);
×
2316
                        }
2317

2318
                        free(tasks_path);
×
2319
                        return ECGROUPNOTALLOWED;
×
2320
                }
2321
                return ECGROUPVALUENOTEXIST;
×
2322
        }
2323

2324
        /*
2325
         * Split the multiline value into lines.  One line is a special
2326
         * case of multiline value.
2327
         */
2328
        str_val = strdup(val);
820✔
2329
        if (str_val == NULL) {
820✔
2330
                last_errno = errno;
×
2331
                close(ctl_file);
×
2332
                return ECGOTHER;
×
2333
        }
2334

2335
        str_val_start = str_val;
820✔
2336
        pos = str_val;
820✔
2337

2338
        do {
2339
                str_val = pos;
824✔
2340
                pos = strchr(str_val, '\n');
824✔
2341

2342
                if (pos) {
824✔
2343
                        *pos = '\0';
4✔
2344
                        ++pos;
4✔
2345
                }
2346

2347
                len = strlen(str_val);
824✔
2348
                if (len > 0) {
824✔
2349
                        if (write(ctl_file, str_val, len) == -1) {
820✔
2350
                                last_errno = errno;
26✔
2351
                                free(str_val_start);
26✔
2352
                                close(ctl_file);
26✔
2353
                                return ECGOTHER;
26✔
2354
                        }
2355
                } else
2356
                        cgroup_warn("skipping empty line for %s\n", path);
4✔
2357
        } while (pos);
798✔
2358

2359
        if (close(ctl_file)) {
794✔
2360
                last_errno = errno;
×
2361
                free(str_val_start);
×
2362
                return ECGOTHER;
×
2363
        }
2364

2365
        free(str_val_start);
794✔
2366
        return 0;
794✔
2367
}
2368

2369
/**
2370
 * Walk the settings in controller and write their values to disk
2371
 *
2372
 * @param base The full path to the base of this cgroup
2373
 * @param controller The controller whose values are being updated
2374
 * @param ignore_non_dirty_values If set skips writing non-dirty controller settings
2375
 */
2376
STATIC int cgroup_set_values_recursive(const char * const base,
931✔
2377
                                       const struct cgroup_controller * const controller,
2378
                                       bool ignore_non_dirty_values)
2379
{
2380
        struct control_value *cv;
2381
        struct stat path_stat;
2382
        int ret, j, error = 0;
931✔
2383
        char *path = NULL;
931✔
2384

2385
        for (j = 0; j < controller->index; j++) {
1,718✔
2386
                cv = controller->values[j];
831✔
2387

2388
                /*
2389
                 * ignore_non_dirty_values is set while writing into
2390
                 * existing cgroup to modify controller settings and
2391
                 * unset during cgroup creation.  The subtle difference
2392
                 * is that dirty flag is unset for all the controller
2393
                 * settings during cgroup creation, whereas some or all
2394
                 * controller settings have the dirty flag set during
2395
                 * modification.
2396
                 *
2397
                 * Be careful with ignore_non_dirty_values flag, setting
2398
                 * it writing only the controller settings that has it
2399
                 * dirty value set.
2400
                 */
2401
                if (ignore_non_dirty_values && cv->dirty == false)
831✔
2402
                        continue;
×
2403

2404
                /* We don't support, writing multiline settings */
2405
                if (strcspn(cv->value, "\n")  < (strlen(cv->value) - 1))
831✔
2406
                        continue;
36✔
2407

2408
                ret = asprintf(&path, "%s%s", base, cv->name);
795✔
2409
                if (ret < 0) {
795✔
2410
                        last_errno = errno;
×
2411
                        error = ECGOTHER;
×
2412
                        goto err;
×
2413
                }
2414

2415
                /* skip read-only settings */
2416
                ret = (stat(path, &path_stat));
795✔
2417
                if (ret < 0) {
795✔
2418
                        last_errno = errno;
18✔
2419
                        error = ECGROUPVALUENOTEXIST;
18✔
2420
                        goto err;
18✔
2421
                }
2422

2423
                /* 0200 == S_IWUSR */
2424
                if (!(path_stat.st_mode & 0200)) {
777✔
2425
                        free(path);
24✔
2426
                        path = NULL;
24✔
2427
                        continue;
24✔
2428
                }
2429

2430
                cgroup_dbg("setting %s to \"%s\", pathlen %d\n", path, cv->value, ret);
753✔
2431

2432
                error = cg_set_control_value(path, cv->value);
753✔
2433
                free(path);
753✔
2434
                path = NULL;
753✔
2435

2436
                if (error) {
753✔
2437
                        /* Ignore the errors on deprecated settings */
2438
                        if (last_errno == EOPNOTSUPP) {
26✔
2439
                                error = 0;
×
2440
                                continue;
×
2441
                        }
2442
                        goto err;
26✔
2443
                }
2444
                cv->dirty = false;
727✔
2445
        }
2446

2447
err:
887✔
2448
        /*
2449
         * As currently written, path should always be null as we are\
2450
         * exiting this function, but let's check just in case, and free it
2451
         * if it's non-null
2452
         */
2453
        if (path)
931✔
2454
                free(path);
18✔
2455

2456
        return error;
931✔
2457
}
2458

2459
/**
2460
 * Check if the requested cgroup controller is enabled in the specified file
2461
 *
2462
 * @param path Cgroup directory
2463
 * @param ctrl_name Name of the controller to check
2464
 * @param output parameter that indicates whether the controller is enabled
2465
 * @param file to open and parse
2466
 *        0 = cgroup.subtree_control
2467
 *        1 = cgroup.controllers
2468
 */
2469
STATIC int __cgroupv2_get_enabled(const char *path, const char *ctrl_name,
778✔
2470
                                  bool * const enabled, int file_enum)
2471
{
2472
        char *path_copy = NULL, *saveptr = NULL, *token, *ret_c, *filename;
778✔
2473
        int ret, error = ECGROUPNOTMOUNTED;
778✔
2474
        char buffer[FILENAME_MAX];
2475
        FILE *fp = NULL;
778✔
2476

2477
        if (!path || !ctrl_name || !enabled)
778✔
2478
                return ECGOTHER;
×
2479

2480
        *enabled = false;
778✔
2481

2482
        switch (file_enum) {
778✔
2483
        case 0: /* cgroup.subtree_control */
387✔
2484
                filename = CGV2_SUBTREE_CTRL_FILE;
387✔
2485
                break;
387✔
2486
        case 1: /* cgroup.controllers */
391✔
2487
                filename = CGV2_CONTROLLERS_FILE;
391✔
2488
                break;
391✔
2489
        default:
×
2490
                return ECGINVAL;
×
2491
        }
2492

2493
        path_copy = (char *)malloc(FILENAME_MAX);
778✔
2494
        if (!path_copy) {
778✔
2495
                error = ECGOTHER;
×
2496
                goto out;
×
2497
        }
2498

2499
        ret = snprintf(path_copy, FILENAME_MAX, "%s/%s", path, filename);
778✔
2500
        if (ret < 0) {
778✔
2501
                error = ECGOTHER;
×
2502
                goto out;
×
2503
        }
2504

2505
        fp = fopen(path_copy, "re");
778✔
2506
        if (!fp) {
778✔
2507
                cgroup_warn("fopen failed\n");
×
2508
                last_errno = errno;
×
2509
                error = ECGOTHER;
×
2510
                goto out;
×
2511
        }
2512

2513
        ret_c = fgets(buffer, sizeof(buffer), fp);
778✔
2514
        if (ret_c == NULL)
778✔
2515
                /* The subtree control file is empty */
2516
                goto out;
123✔
2517

2518
        /* Remove the trailing newline */
2519
        ret_c[strlen(ret_c) - 1] = '\0';
655✔
2520

2521
        /*
2522
         * Split the enabled controllers by " " and evaluate if the
2523
         * requested controller is enabled.
2524
         */
2525
        token = strtok_r(buffer, " ", &saveptr);
655✔
2526
        do {
2527
                if (strncmp(ctrl_name, token, FILENAME_MAX) == 0) {
1,817✔
2528
                        error = 0;
498✔
2529
                        *enabled = true;
498✔
2530
                        break;
498✔
2531
                }
2532
        } while ((token = strtok_r(NULL, " ", &saveptr)));
1,319✔
2533

2534
out:
157✔
2535
        if (path_copy)
778✔
2536
                free(path_copy);
778✔
2537
        if (fp)
778✔
2538
                fclose(fp);
778✔
2539

2540
        return error;
778✔
2541
}
2542

2543
/**
2544
 * Check if the requested cgroup controller is enabled on this subtree
2545
 *
2546
 * @param path Cgroup directory
2547
 * @param ctrl_name Name of the controller to check
2548
 * @param output parameter that indicates whether the controller is enabled
2549
 */
2550
STATIC int cgroupv2_get_subtree_control(const char *path, const char *ctrl_name,
387✔
2551
                                        bool * const enabled)
2552
{
2553
        return __cgroupv2_get_enabled(path, ctrl_name, enabled, 0);
387✔
2554
}
2555
/**
2556
 * Check if the requested cgroup controller is enabled in this cgroup's cgroup.controllers file
2557
 *
2558
 * @param path Cgroup directory
2559
 * @param ctrl_name Name of the controller to check
2560
 * @param output parameter that indicates whether the controller is enabled
2561
 */
2562
static int cgroupv2_get_controllers(const char *path, const char *ctrl_name,
391✔
2563
                                    bool * const enabled)
2564
{
2565
        return __cgroupv2_get_enabled(path, ctrl_name, enabled, 1);
391✔
2566
}
2567

2568
/**
2569
 * Enable/Disable a controller in the cgroup v2 subtree_control file
2570
 *
2571
 * @param path Directory that contains the subtree_control file
2572
 * @param ctrl_name Name of the controller to be enabled/disabled
2573
 * @param enable Enable/Disable the given controller
2574
 */
2575
STATIC int cgroupv2_subtree_control(const char *path, const char *ctrl_name, bool enable)
67✔
2576
{
2577
        int ret, error = ECGOTHER;
67✔
2578
        char *path_copy = NULL;
67✔
2579
        char *value = NULL;
67✔
2580

2581
        if (!path || !ctrl_name)
67✔
2582
                return ECGOTHER;
×
2583

2584
        value = (char *)malloc(FILENAME_MAX);
67✔
2585
        if (!value)
67✔
2586
                goto out;
×
2587

2588
        path_copy = (char *)malloc(FILENAME_MAX);
67✔
2589
        if (!path_copy)
67✔
2590
                goto out;
×
2591

2592
        ret = snprintf(path_copy, FILENAME_MAX, "%s/%s", path, CGV2_SUBTREE_CTRL_FILE);
67✔
2593
        if (ret < 0)
67✔
2594
                goto out;
×
2595

2596
        if (enable)
67✔
2597
                ret = snprintf(value, FILENAME_MAX, "+%s", ctrl_name);
65✔
2598
        else
2599
                ret = snprintf(value, FILENAME_MAX, "-%s", ctrl_name);
2✔
2600
        if (ret < 0)
67✔
2601
                goto out;
×
2602

2603
        error = cg_set_control_value(path_copy, value);
67✔
2604
        if (error)
67✔
2605
                goto out;
×
2606

2607
out:
67✔
2608
        if (value)
67✔
2609
                free(value);
67✔
2610
        if (path_copy)
67✔
2611
                free(path_copy);
67✔
2612
        return error;
67✔
2613
}
2614

2615
/*
2616
 * Test if the controller is enabled in the root_cgrp.subtree_control file
2617
 * and enable the controller, if not enabled.
2618
 */
2619
static int test_and_set_ctrl_mnt_path(const char * const mount_path, const char * const ctrl_name)
151✔
2620
{
2621
        bool enabled;
2622
        int ret;
2623

2624
        /* return if the controller is enabled */
2625
        ret = cgroupv2_get_subtree_control(mount_path, ctrl_name, &enabled);
151✔
2626
        if (ret == ECGOTHER)
151✔
2627
                return ret;
×
2628

2629
        if (enabled == true)
151✔
2630
                return 0;
142✔
2631

2632
        /* check if the controller is available is controllers file */
2633
        ret = cgroupv2_get_controllers(mount_path, ctrl_name, &enabled);
9✔
2634
        if (ret == ECGOTHER || ret == ECGROUPNOTMOUNTED)
9✔
2635
                return ret;
×
2636

2637
        /* enable the controller in the subtree_control file */
2638
        ret = cgroupv2_subtree_control(mount_path, ctrl_name, 1);
9✔
2639
        if (ret)
9✔
2640
                return ret;
×
2641

2642
        return 0;
9✔
2643
}
2644

2645
/**
2646
 * Recursively enable/disable a controller in the cgv2 subtree_control file
2647
 *
2648
 * @param path Directory that contains the subtree_control file
2649
 * @param ctrl_name Name of the controller to be enabled/disabled
2650
 * @param enable Enable/Disable the given controller
2651
 */
2652
STATIC int cgroupv2_subtree_control_recursive(char *path, const char *ctrl_name, bool enable)
151✔
2653
{
2654
        char *path_copy, *tmp_path, *stok_buff = NULL;
151✔
2655
        bool found_mount = false, controller_enabled = false;
151✔
2656
        size_t mount_len;
2657
        int i, error = 0;
151✔
2658

2659
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
322✔
2660
                if (strncmp(cg_mount_table[i].name, ctrl_name,
322✔
2661
                            sizeof(cg_mount_table[i].name)) == 0) {
2662
                        found_mount = true;
151✔
2663
                        break;
151✔
2664
                }
2665
        }
2666

2667
        if (!found_mount)
151✔
2668
                return ECGROUPSUBSYSNOTMOUNTED;
×
2669

2670
        path_copy = strdup(path);
151✔
2671
        if (!path_copy)
151✔
2672
                return ECGOTHER;
×
2673

2674
        /*
2675
         * Null terminate the path_copy to match the string length of the
2676
         * controller mount.  We'll incrementally build up the string, subdir
2677
         * by subdir, and enable the subtree control file each step of the way
2678
         */
2679
        mount_len = strlen(cg_mount_table[i].mount.path);
151✔
2680
        path_copy[mount_len] = '\0';
151✔
2681

2682
        /*
2683
         * systemd by default only enables cpu, cpuset, io, memory, and pids
2684
         * controller in the root_cgroup.subtree_control. Trying to enable
2685
         * hugetlb and misc controller in a nested cgroups, will fail
2686
         * because they are not, enabled the controller in the root_cgroup.
2687
         * i.e. # cgcreate -ghugetlb:foo/bar
2688
         *
2689
         * check if the controller is enabled in the
2690
         * root_cgroup.subtree_control file and if not enable it. Let's
2691
         * check for all controllers, so that it accommodates other
2692
         * controllers systemd decides to disable by default in the future.
2693
         */
2694
        error = test_and_set_ctrl_mnt_path(path_copy, ctrl_name);
151✔
2695
        if (error)
151✔
2696
                goto out;
×
2697

2698
        tmp_path = strtok_r(&path[mount_len], "/", &stok_buff);
151✔
2699
        do {
2700
                if (tmp_path) {
167✔
2701
                        strcat(path_copy, "/");
61✔
2702
                        strcat(path_copy, tmp_path);
61✔
2703
                }
2704

2705
                error = cg_create_control_group(path_copy);
167✔
2706
                if (error)
167✔
2707
                        goto out;
×
2708

2709
                error = cgroupv2_get_subtree_control(path_copy, ctrl_name, &controller_enabled);
167✔
2710
                if (controller_enabled)
167✔
2711
                        continue;
113✔
2712
                if (error != ECGROUPNOTMOUNTED)
54✔
2713
                        goto out;
×
2714

2715
                error = cgroupv2_subtree_control(path_copy, ctrl_name, enable);
54✔
2716
                if (error)
54✔
2717
                        goto out;
×
2718
        } while ((tmp_path = strtok_r(NULL, "/", &stok_buff)));
167✔
2719

2720
out:
151✔
2721
        free(path_copy);
151✔
2722
        return error;
151✔
2723
}
2724

2725
/**
2726
 * cgroup_modify_cgroup modifies the cgroup control files.
2727
 * struct cgroup *cgrp: The name will be the cgroup to be modified.
2728
 * The values will be the values to be modified, those not mentioned in the
2729
 * structure will not be modified.
2730
 *
2731
 * The uids cannot be modified yet.
2732
 *
2733
 * returns 0 on success.
2734
 */
2735

2736
int cgroup_modify_cgroup(struct cgroup *cgrp)
461✔
2737
{
2738
        char base[FILENAME_MAX];
2739
        int error = 0;
461✔
2740
        int i;
2741

2742
        if (!cgroup_initialized)
461✔
2743
                return ECGROUPNOTINITIALIZED;
×
2744

2745
        if (!cgrp)
461✔
2746
                return ECGROUPNOTALLOWED;
×
2747

2748
        for (i = 0; i < cgrp->index; i++) {
914✔
2749
                if (!cgroup_test_subsys_mounted(cgrp->controller[i]->name)) {
453✔
2750
                        cgroup_warn("subsystem %s is not mounted\n", cgrp->controller[i]->name);
×
2751
                        return ECGROUPSUBSYSNOTMOUNTED;
×
2752
                }
2753
        }
2754

2755
        for (i = 0; i < cgrp->index; i++) {
910✔
2756
                if (!cg_build_path(cgrp->name, base, cgrp->controller[i]->name))
453✔
2757
                        continue;
×
2758

2759
                error = cgroup_set_values_recursive(base, cgrp->controller[i], true);
453✔
2760
                if (error)
453✔
2761
                        goto err;
4✔
2762
        }
2763
err:
457✔
2764
        return error;
461✔
2765
}
2766

2767
int cgroup_copy_controller_values(struct cgroup_controller * const dst,
710✔
2768
                                  const struct cgroup_controller * const src)
2769
{
2770
        int i, ret = 0;
710✔
2771

2772
        if (!dst || !src)
710✔
2773
                return ECGFAIL;
×
2774

2775
        strncpy(dst->name, src->name, CONTROL_NAMELEN_MAX);
710✔
2776
        for (i = 0; i < src->index; i++, dst->index++) {
1,622✔
2777
                struct control_value *src_val = src->values[i];
912✔
2778
                struct control_value *dst_val;
2779

2780
                dst->values[i] = calloc(1, sizeof(struct control_value));
912✔
2781
                if (!dst->values[i]) {
912✔
2782
                        last_errno = errno;
×
2783
                        ret = ECGOTHER;
×
2784
                        goto err;
×
2785
                }
2786

2787
                dst_val = dst->values[i];
912✔
2788
                strncpy(dst_val->value, src_val->value, CG_CONTROL_VALUE_MAX);
912✔
2789
                strncpy(dst_val->name, src_val->name, FILENAME_MAX);
912✔
2790

2791
                if (src_val->multiline_value) {
912✔
2792
                        dst_val->multiline_value = strdup(src_val->multiline_value);
×
2793
                        if (!dst_val->multiline_value) {
×
2794
                                last_errno = errno;
×
2795
                                ret = ECGOTHER;
×
2796
                                goto err;
×
2797
                        }
2798
                } else {
2799
                        dst_val->multiline_value = NULL;
912✔
2800
                }
2801

2802
                if (src_val->prev_name) {
912✔
2803
                        dst_val->prev_name = strdup(src_val->prev_name);
×
2804
                        if (!dst_val->prev_name) {
×
2805
                                last_errno = errno;
×
2806
                                ret = ECGOTHER;
×
2807
                                goto err;
×
2808
                        }
2809
                } else {
2810
                        dst_val->prev_name = NULL;
912✔
2811
                }
2812
                /*
2813
                 * set dirty flag unconditionally, as we overwrite
2814
                 * destination controller values.
2815
                 */
2816
                dst_val->dirty = true;
912✔
2817
        }
2818

2819
        return ret;
710✔
2820

2821
err:
×
2822
        dst->index = 0;
×
2823
        for (i = 0; i < src->index; i++) {
×
2824
                if (dst->values[i]) {
×
2825
                        if (dst->values[i]->multiline_value)
×
2826
                                free(dst->values[i]->multiline_value);
×
2827

2828
                        if (dst->values[i]->prev_name)
×
2829
                                free(dst->values[i]->prev_name);
×
2830

2831
                        free(dst->values[i]);
×
2832
                }
2833
        }
2834

2835
        return ret;
×
2836
}
2837

2838
/**
2839
 * @dst: Destination control group
2840
 * @src: Source from which values will be copied to dst
2841
 *
2842
 * Create a duplicate copy of src in dst. This will be useful for those who
2843
 * that intend to create new instances based on an existing control group
2844
 */
2845
int cgroup_copy_cgroup(struct cgroup *dst, struct cgroup *src)
465✔
2846
{
2847
        int ret = 0, i;
465✔
2848

2849
        if (!dst || !src)
465✔
2850
                return ECGROUPNOTEXIST;
×
2851

2852
        /* Should we just use the restrict keyword instead? */
2853
        if (dst == src)
465✔
2854
                return ECGFAIL;
×
2855

2856
        cgroup_free_controllers(dst);
465✔
2857

2858
        for (i = 0; i < src->index; i++, dst->index++) {
922✔
2859
                struct cgroup_controller *src_ctlr = src->controller[i];
457✔
2860
                struct cgroup_controller *dst_ctlr;
2861

2862
                dst->controller[i] = calloc(1, sizeof(struct cgroup_controller));
457✔
2863
                if (!dst->controller[i]) {
457✔
2864
                        last_errno = errno;
×
2865
                        ret = ECGOTHER;
×
2866
                        goto err;
×
2867
                }
2868

2869
                dst_ctlr = dst->controller[i];
457✔
2870
                ret = cgroup_copy_controller_values(dst_ctlr, src_ctlr);
457✔
2871
                if (ret)
457✔
2872
                        goto err;
×
2873
        }
2874
err:
465✔
2875
        return ret;
465✔
2876
}
2877

2878
/**
2879
 * Chown and chmod the tasks file in cg_path
2880
 *
2881
 * @param uid The UID that will own the tasks file
2882
 * @param gid The GID that will own the tasks file
2883
 * @param fperm The permissions to place on the tasks file
2884
 */
2885
STATIC int cgroup_chown_chmod_tasks(const char * const cg_path, uid_t uid, gid_t gid, mode_t fperm)
296✔
2886
{
2887
        int ret, error;
2888
        char *tasks_path = NULL;
296✔
2889

2890
        tasks_path = (char *)malloc(FILENAME_MAX);
296✔
2891
        if (tasks_path == NULL)
296✔
2892
                return ECGOTHER;
×
2893

2894
        ret = snprintf(tasks_path, FILENAME_MAX, "%s/tasks", cg_path);
296✔
2895
        if (ret < 0 || ret >= FILENAME_MAX) {
296✔
2896
                last_errno = errno;
×
2897
                error = ECGOTHER;
×
2898
                goto err;
×
2899
        }
2900

2901
        error = cg_chown(tasks_path, uid, gid);
296✔
2902
        if (!error && fperm != NO_PERMS)
296✔
2903
                error = cg_chmod_path(tasks_path, fperm, 1);
7✔
2904

2905
        if (error) {
296✔
2906
                last_errno = errno;
×
2907
                error = ECGOTHER;
×
2908
        }
2909

2910
err:
296✔
2911
        if (tasks_path)
296✔
2912
                free(tasks_path);
296✔
2913

2914
        return error;
296✔
2915
}
2916

2917
static int _cgroup_create_cgroup(const struct cgroup * const cgrp,
488✔
2918
                                 const struct cgroup_controller * const controller,
2919
                                 int ignore_ownership)
2920
{
2921
        enum cg_version_t version = CGROUP_UNK;
488✔
2922
        char *fts_path[2];
2923
        char *base = NULL;
488✔
2924
        char *path = NULL;
488✔
2925
        int error;
2926

2927
        fts_path[0] = (char *)malloc(FILENAME_MAX);
488✔
2928
        if (!fts_path[0]) {
488✔
2929
                last_errno = errno;
×
2930
                return ECGOTHER;
×
2931
        }
2932
        fts_path[1] = NULL;
488✔
2933
        path = fts_path[0];
488✔
2934

2935
        if (controller) {
488✔
2936
                if (!cg_build_path(cgrp->name, path, controller->name)) {
476✔
2937
                        error = ECGOTHER;
×
2938
                        goto err;
×
2939
                }
2940

2941
                error = cgroup_get_controller_version(controller->name, &version);
476✔
2942
                if (error)
476✔
2943
                        goto err;
×
2944

2945
                if (version == CGROUP_V2) {
476✔
2946
                        char *parent, *dname;
2947

2948
                        parent = strdup(path);
151✔
2949
                        if (!parent) {
151✔
2950
                                error = ECGOTHER;
×
2951
                                goto err;
×
2952
                        }
2953

2954
                        dname = dirname(parent);
151✔
2955

2956
                        error = cgroupv2_subtree_control_recursive(dname, controller->name, true);
151✔
2957
                        free(parent);
151✔
2958
                        if (error)
151✔
2959
                                goto err;
×
2960
                }
2961
        } else {
2962
                if (!cg_build_path(cgrp->name, path, NULL)) {
12✔
2963
                        error = ECGOTHER;
×
2964
                        goto err;
×
2965
                }
2966
        }
2967

2968
        error = cg_create_control_group(path);
488✔
2969
        if (error)
488✔
2970
                goto err;
×
2971

2972
        base = strdup(path);
488✔
2973

2974
        if (!base) {
488✔
2975
                last_errno = errno;
×
2976
                error = ECGOTHER;
×
2977
                goto err;
×
2978
        }
2979

2980
        if (!ignore_ownership) {
488✔
2981
                cgroup_dbg("Changing ownership of %s\n", fts_path[0]);
478✔
2982
                error = cg_chown_recursive(fts_path, cgrp->control_uid, cgrp->control_gid);
478✔
2983
                if (!error)
478✔
2984
                        error = cg_chmod_recursive_controller(fts_path[0],
478✔
2985
                                                              cgrp->control_dperm,
110✔
2986
                                                              cgrp->control_dperm != NO_PERMS,
478✔
2987
                                                              cgrp->control_fperm,
110✔
2988
                                                              cgrp->control_fperm != NO_PERMS,
478✔
2989
                                                              1, cgroup_ignored_tasks_files);
2990
        }
2991

2992
        if (error)
488✔
2993
                goto err;
×
2994

2995
        if (controller) {
488✔
2996
                error = cgroup_set_values_recursive(base, controller, false);
476✔
2997
                if (error)
476✔
2998
                        goto err;
40✔
2999
        }
3000

3001
        if (!ignore_ownership && version == CGROUP_V1) {
448✔
3002
                error = cgroup_chown_chmod_tasks(base, cgrp->tasks_uid, cgrp->tasks_gid,
294✔
3003
                                                 cgrp->task_fperm);
3004
                if (error)
294✔
3005
                        goto err;
×
3006
        }
3007
err:
448✔
3008
        if (path)
488✔
3009
                free(path);
488✔
3010
        if (base)
488✔
3011
                free(base);
488✔
3012

3013
        return error;
488✔
3014
}
3015

3016
/**
3017
 * cgroup_create_cgroup creates a new control group.
3018
 * struct cgroup *cgrp: The control group to be created
3019
 *
3020
 * returns 0 on success. We recommend calling cg_delete_cgroup if this
3021
 * routine fails. That should do the cleanup operation. If ECGCANTSETVALUE
3022
 * is returned, the group was created successfully but not all controller
3023
 * parameters were successfully set.
3024
 */
3025
int cgroup_create_cgroup(struct cgroup *cgrp, int ignore_ownership)
460✔
3026
{
3027
        int error = 0;
460✔
3028
        int i;
3029

3030
        if (!cgroup_initialized)
460✔
3031
                return ECGROUPNOTINITIALIZED;
×
3032

3033
        if (!cgrp)
460✔
3034
                return ECGROUPNOTALLOWED;
×
3035

3036
        for (i = 0; i < cgrp->index; i++) {
936✔
3037
                if (!cgroup_test_subsys_mounted(cgrp->controller[i]->name))
476✔
3038
                        return ECGROUPSUBSYSNOTMOUNTED;
×
3039
        }
3040

3041
        if (cgrp->index == 0) {
460✔
3042
                /* Create an empty cgroup v2 cgroup */
3043
                error = _cgroup_create_cgroup(cgrp, NULL, ignore_ownership);
12✔
3044
                if (error)
12✔
3045
                        /*
3046
                         * Since we only attempted to create a single cgroup,
3047
                         * _cgroup_create_cgroup() can capably undo the failure and no
3048
                         * interventions are required here.
3049
                         */
3050
                        return error;
×
3051
        }
3052

3053
        /*
3054
         * XX: One important test to be done is to check, if you have
3055
         * multiple subsystems mounted at one point, all of them *have* be
3056
         * on the cgroup data structure. If not, we fail.
3057
         */
3058
        for (i = 0; i < cgrp->index; i++) {
896✔
3059
                error = _cgroup_create_cgroup(cgrp, cgrp->controller[i], ignore_ownership);
476✔
3060
                if (error) {
476✔
3061
                        int del_error;
3062

3063
                        /*
3064
                         * This will remove any cgroup directories that were made, but it won't
3065
                         * undo changes that have been written to the parent cgroup's
3066
                         * subtree_control file.  To safely undo changes there, we would need to
3067
                         * save the subtree_control file's previous value and restore it.
3068
                         */
3069
                        del_error = cgroup_delete_cgroup(cgrp, 1);
40✔
3070
                        if (del_error)
40✔
3071
                                cgroup_err("Failed to delete %s: %s\n", cgrp->name,
×
3072
                                           cgroup_strerror(del_error));
3073
                        return error;
40✔
3074
                }
3075
        }
3076

3077
        return 0;
420✔
3078
}
3079

3080
/**
3081
 * Obtain the calculated parent name of specified cgroup; no validation of
3082
 * the existence of the child or parent group is performed.
3083
 *
3084
 * Given the path-like hierarchy of cgroup names, this function returns the
3085
 * dirname() of the cgroup name as the likely parent name; the caller is
3086
 * responsible for validating parent as appropriate.
3087
 *
3088
 * @param cgrp The cgroup to query for parent's name
3089
 * @param parent Output, name of parent's group, or NULL if the
3090
 *        provided cgroup is the root group.
3091
 *        Caller is responsible to free the returned string.
3092
 * @return 0 on success, > 0 on error
3093
 */
3094
static int cgroup_get_parent_name(struct cgroup *cgrp, char **parent)
402✔
3095
{
3096
        char *pdir = NULL;
402✔
3097
        char *dir = NULL;
402✔
3098
        int ret = 0;
402✔
3099

3100
        dir = strdup(cgrp->name);
402✔
3101
        if (!dir) {
402✔
3102
                last_errno = errno;
×
3103
                return ECGOTHER;
×
3104
        }
3105
        cgroup_dbg("group name is %s\n", dir);
402✔
3106

3107
        pdir = dirname(dir);
402✔
3108
        cgroup_dbg("parent's group name is %s\n", pdir);
402✔
3109

3110
        /* Check for root group */
3111
        if (strlen(cgrp->name) == 0 || !strcmp(cgrp->name, pdir)) {
402✔
3112
                cgroup_dbg("specified cgroup \"%s\" is root group\n", cgrp->name);
×
3113
                *parent = NULL;
×
3114
        } else {
3115
                *parent = strdup(pdir);
402✔
3116
                if (*parent == NULL) {
402✔
3117
                        last_errno = errno;
×
3118
                        ret = ECGOTHER;
×
3119
                }
3120
        }
3121
        free(dir);
402✔
3122

3123
        return ret;
402✔
3124
}
3125

3126
/*
3127
 * Checks if the cgroup's controller shares the mount point with any other
3128
 * controller in cg_mount_table.
3129
 * Returns 1 if shared or 0.
3130
 */
3131
static int is_cgrp_ctrl_shared_mnt(char *controller)
428✔
3132
{
3133
        int ret = 0;
428✔
3134
        int i;
3135

3136
        if (!controller)
428✔
3137
                return ret;
12✔
3138

3139
        pthread_rwlock_rdlock(&cg_mount_table_lock);
416✔
3140

3141
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
3,127✔
3142

3143
                if (strncmp(cg_mount_table[i].name, controller, CONTROL_NAMELEN_MAX) == 0 &&
2,977✔
3144
                    cg_mount_table[i].shared_mnt) {
416✔
3145
                        ret = 1;
266✔
3146
                        break;
266✔
3147
                }
3148
        }
3149

3150
        pthread_rwlock_unlock(&cg_mount_table_lock);
416✔
3151

3152
        return ret;
416✔
3153
}
3154
/**
3155
 * Find the parent of the specified directory. It returns the parent in
3156
 * hierarchy of given controller (the parent is usually name/.. unless name
3157
 * is a mount point.  It is assumed both the cgroup (and, therefore, parent)
3158
 * already exist, and will fail otherwise.
3159
 *
3160
 * When namespaces are used, a group can have different parents for
3161
 * different controllers.
3162
 *
3163
 * @param cgrp The cgroup
3164
 * @param controller The controller
3165
 * @param parent Output, name of parent's group (if the group has parent) or
3166
 *        NULL, if the provided cgroup is the root group and has no parent.
3167
 *        Caller is responsible to free the returned string!
3168
 * @return 0 on success, >0 on error.
3169
 */
3170
static int cgroup_find_parent(struct cgroup *cgrp, char *controller, char **parent)
428✔
3171
{
3172
        struct stat stat_child, stat_parent;
3173
        char child_path[FILENAME_MAX];
3174
        char *parent_path = NULL;
428✔
3175
        int ret = 0;
428✔
3176

3177
        *parent = NULL;
428✔
3178

3179
        pthread_rwlock_rdlock(&cg_mount_table_lock);
428✔
3180
        if (!cg_build_path_locked(cgrp->name, child_path, controller)) {
428✔
3181
                pthread_rwlock_unlock(&cg_mount_table_lock);
×
3182
                return ECGFAIL;
×
3183
        }
3184
        pthread_rwlock_unlock(&cg_mount_table_lock);
428✔
3185

3186
        cgroup_dbg("path is %s\n", child_path);
428✔
3187

3188
        if (asprintf(&parent_path, "%s/..", child_path) < 0)
428✔
3189
                return ECGFAIL;
×
3190

3191
        cgroup_dbg("parent's name is %s\n", parent_path);
428✔
3192

3193
        if (stat(child_path, &stat_child) < 0) {
428✔
3194
                if (is_cgrp_ctrl_shared_mnt(controller)) {
26✔
3195
                        last_errno = errno;
22✔
3196
                        ret = ECGROUPNOTEXIST;
22✔
3197
                        goto free_parent;
22✔
3198
                }
3199
                last_errno = errno;
4✔
3200
                ret = ECGOTHER;
4✔
3201
                goto free_parent;
4✔
3202
        }
3203

3204
        if (stat(parent_path, &stat_parent) < 0) {
402✔
3205
                last_errno = errno;
×
3206
                ret = ECGOTHER;
×
3207
                goto free_parent;
×
3208
        }
3209

3210
        /* Is the specified "name" a mount point? */
3211
        if (stat_parent.st_dev != stat_child.st_dev) {
402✔
3212
                *parent = NULL;
×
3213
                ret = 0;
×
3214
                cgroup_dbg("Parent is on different device\n");
×
3215
        } else {
3216
                ret = cgroup_get_parent_name(cgrp, parent);
402✔
3217
        }
3218

3219
free_parent:
428✔
3220
        free(parent_path);
428✔
3221
        return ret;
428✔
3222
}
3223

3224
/**
3225
 * @cgrp: cgroup data structure to be filled with parent values and then
3226
 *        passed down for creation
3227
 * @ignore_ownership: Ignore doing a chown on the newly created cgroup
3228
 * @return 0 on success, > 0 on failure.  If ECGCANTSETVALUE is returned,
3229
 *        the group was created
3230
 * successfully, but not all controller parameters were copied from the
3231
 * parent successfully; unfortunately, this is expected...
3232
 */
3233
int cgroup_create_cgroup_from_parent(struct cgroup *cgrp, int ignore_ownership)
×
3234
{
3235
        struct cgroup *parent_cgrp = NULL;
×
3236
        char *parent = NULL;
×
3237
        int ret = ECGFAIL;
×
3238

3239
        if (!cgroup_initialized)
×
3240
                return ECGROUPNOTINITIALIZED;
×
3241

3242
        ret = cgroup_get_parent_name(cgrp, &parent);
×
3243
        if (ret)
×
3244
                return ret;
×
3245

3246
        if (parent == NULL) {
×
3247
                /*
3248
                 * The group to create is root group!
3249
                 * TODO: find better error code?
3250
                 */
3251
                return ECGFAIL;
×
3252
        }
3253

3254
        cgroup_dbg("parent is %s\n", parent);
×
3255
        parent_cgrp = cgroup_new_cgroup(parent);
×
3256
        if (!parent_cgrp) {
×
3257
                ret = ECGFAIL;
×
3258
                goto err_nomem;
×
3259
        }
3260

3261
        if (cgroup_get_cgroup(parent_cgrp)) {
×
3262
                ret = ECGFAIL;
×
3263
                goto err_parent;
×
3264
        }
3265

3266
        cgroup_dbg("got parent group for %s\n", parent_cgrp->name);
×
3267
        ret = cgroup_copy_cgroup(cgrp, parent_cgrp);
×
3268
        if (ret)
×
3269
                goto err_parent;
×
3270

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

3274
err_parent:
×
3275
        cgroup_free(&parent_cgrp);
×
3276
err_nomem:
×
3277
        free(parent);
×
3278
        return ret;
×
3279
}
3280

3281
/**
3282
 * Move all processes from one task file to another.
3283
 * @param input_tasks Pre-opened file to read tasks from.
3284
 * @param output_tasks Pre-opened file to write tasks to.
3285
 * @return 0 on success, >0 on error.
3286
 */
3287
static int cg_move_task_files(FILE *input_tasks, FILE *output_tasks)
500✔
3288
{
3289
        int ret = 0;
500✔
3290
        int tids;
3291

3292
        while (!feof(input_tasks)) {
564✔
3293
                ret = fscanf(input_tasks, "%d", &tids);
564✔
3294
                if (ret == EOF || ret == 0) {
564✔
3295
                        ret = 0;
500✔
3296
                        break;
500✔
3297
                }
3298
                if (ret < 0)
64✔
3299
                        break;
×
3300

3301
                ret = fprintf(output_tasks, "%d", tids);
64✔
3302
                if (ret < 0 && errno != ESRCH)
64✔
3303
                        break;
×
3304

3305
                /* Flush the file, we need only one process per write() call. */
3306
                ret = fflush(output_tasks);
64✔
3307
                if (ret < 0) {
64✔
3308
                        if (errno == ESRCH)
×
3309
                                ret = 0;
×
3310
                        else
3311
                                break;
×
3312
                }
3313
        }
3314

3315
        if (ret < 0) {
500✔
3316
                last_errno = errno;
×
3317
                return ECGOTHER;
×
3318
        }
3319
        return 0;
500✔
3320
}
3321

3322
/**
3323
 * Remove one cgroup from specific controller. The function moves all
3324
 * processes from it to given target group.
3325
 *
3326
 * The function succeeds if the group to remove is already removed - when
3327
 * cgroup_delete_cgroup is called with group with two controllers mounted
3328
 * to the same hierarchy, this function is called once for each of these
3329
 * controllers. And during the second call the group is already removed...
3330
 *
3331
 * @param cgrp_name Name of the group to remove.
3332
 * @param controller  Name of the controller.
3333
 * @param target_tasks Opened tasks file of the target group, where all
3334
 *        processes should be moved.
3335
 * @param flags Flag indicating whether the errors from task
3336
 *        migration should be ignored (CGROUP_DELETE_IGNORE_MIGRATION) or not (0).
3337
 * @returns 0 on success, >0 on error.
3338
 */
3339
static int cg_delete_cgrp_controller(char *cgrp_name, char *controller, FILE *target_tasks,
500✔
3340
                                     int flags)
3341
{
3342
        char path[FILENAME_MAX];
3343
        FILE *delete_tasks;
3344
        int ret = 0;
500✔
3345

3346
        cgroup_dbg("Removing group %s:%s\n", controller, cgrp_name);
500✔
3347

3348
        if (!(flags & CGFLAG_DELETE_EMPTY_ONLY)) {
500✔
3349
                /* Open tasks file of the group to delete. */
3350
                ret = cgroup_build_tasks_procs_path(path, sizeof(path), cgrp_name, controller);
500✔
3351
                if (ret != 0)
500✔
3352
                        return ECGROUPSUBSYSNOTMOUNTED;
×
3353

3354
                delete_tasks = fopen(path, "re");
500✔
3355
                if (delete_tasks) {
500✔
3356
                        ret = cg_move_task_files(delete_tasks, target_tasks);
500✔
3357
                        if (ret != 0) {
500✔
3358
                                cgroup_warn("removing tasks from %s failed: %s\n", path,
×
3359
                                            cgroup_strerror(ret));
3360
                        }
3361
                        fclose(delete_tasks);
500✔
3362
                } else {
3363
                        /*
3364
                         * Can't open the tasks file. If the file does not exist,
3365
                         * ignore it - the group has been already removed.
3366
                         */
3367
                        if (errno != ENOENT) {
×
3368
                                cgroup_err("cannot open %s: %s\n", path, strerror(errno));
×
3369
                                last_errno = errno;
×
3370
                                ret = ECGOTHER;
×
3371
                        }
3372
                }
3373

3374
                if (ret != 0 && !(flags & CGFLAG_DELETE_IGNORE_MIGRATION))
500✔
3375
                        return ret;
×
3376
        }
3377

3378
        /* Remove the group. */
3379
        if (!cg_build_path(cgrp_name, path, controller))
500✔
3380
                return ECGROUPSUBSYSNOTMOUNTED;
×
3381

3382
        ret = rmdir(path);
500✔
3383
        if (ret == 0 || errno == ENOENT)
500✔
3384
                return 0;
500✔
3385

3386
        if ((flags & CGFLAG_DELETE_EMPTY_ONLY) && (errno == EBUSY))
×
3387
                return ECGNONEMPTY;
×
3388

3389
        cgroup_warn("cannot remove directory %s: %s\n", path, strerror(errno));
×
3390
        last_errno = errno;
×
3391

3392
        return ECGOTHER;
×
3393
}
3394

3395
/**
3396
 * Recursively delete one control group. Moves all tasks from the group and
3397
 * its subgroups to given task file.
3398
 *
3399
 * @param cgrp_name The group to delete.
3400
 * @param controller The controller, where to delete.
3401
 * @param target_tasks Opened file, where all tasks should be moved.
3402
 * @param flags Combination of CGFLAG_DELETE_* flags. The function assumes
3403
 *        that CGFLAG_DELETE_RECURSIVE is set.
3404
 * @param delete_root Whether the group itself should be removed(1) or not(0).
3405
 */
3406
static int cg_delete_cgrp_controller_recursive(char *cgrp_name, char *controller,
58✔
3407
                                               FILE *target_tasks, int flags, int delete_root)
3408
{
3409
        char child_name[FILENAME_MAX + 1];
3410
        struct cgroup_file_info info;
3411
        int level, group_len;
3412
        void *handle;
3413
        int ret;
3414

3415
        cgroup_dbg("Recursively removing %s:%s\n", controller, cgrp_name);
58✔
3416

3417
        ret = cgroup_walk_tree_begin(controller, cgrp_name, 0, &handle, &info, &level);
58✔
3418
        if (ret == 0)
58✔
3419
                ret = cgroup_walk_tree_set_flags(&handle, CGROUP_WALK_TYPE_POST_DIR);
58✔
3420

3421
        if (ret != 0) {
58✔
3422
                cgroup_walk_tree_end(&handle);
×
3423
                return ret;
×
3424
        }
3425

3426
        group_len = strlen(info.full_path);
58✔
3427

3428
        /* Skip the root group, it will be handled explicitly at the end. */
3429
        ret = cgroup_walk_tree_next(0, &handle, &info, level);
58✔
3430

3431
        while (ret == 0) {
4,020✔
3432
                if (info.type == CGROUP_FILE_TYPE_DIR && info.depth > 0) {
3,962✔
3433
                        snprintf(child_name, sizeof(child_name), "%s/%s", cgrp_name,
98✔
3434
                                 info.full_path + group_len);
98✔
3435

3436
                        ret = cg_delete_cgrp_controller(child_name, controller, target_tasks,
98✔
3437
                                                          flags);
3438
                        if (ret != 0)
98✔
3439
                                break;
×
3440
                }
3441

3442
                ret = cgroup_walk_tree_next(0, &handle, &info, level);
3,962✔
3443
        }
3444
        if (ret == ECGEOF) {
58✔
3445
                /* Iteration finished successfully, remove the root group. */
3446
                ret = 0;
58✔
3447
                if (delete_root)
58✔
3448
                        ret = cg_delete_cgrp_controller(cgrp_name, controller, target_tasks, flags);
58✔
3449
        }
3450

3451
        cgroup_walk_tree_end(&handle);
58✔
3452

3453
        return ret;
58✔
3454
}
3455

3456
/**
3457
 * cgroup_delete cgroup deletes a control group.
3458
 * struct cgroup *cgrp takes the group which is to be deleted.
3459
 *
3460
 * returns 0 on success.
3461
 */
3462
int cgroup_delete_cgroup(struct cgroup *cgrp, int ignore_migration)
42✔
3463
{
3464
        int flags = ignore_migration ? CGFLAG_DELETE_IGNORE_MIGRATION : 0;
42✔
3465

3466
        return cgroup_delete_cgroup_ext(cgrp, flags);
42✔
3467
}
3468

3469
int cgroup_delete_cgroup_ext(struct cgroup *cgrp, int flags)
428✔
3470
{
3471
        int first_error = 0, first_errno = 0;
428✔
3472
        int cgrp_del_on_shared_mnt = 0;
428✔
3473
        char parent_path[FILENAME_MAX];
3474
        char *controller_name = NULL;
428✔
3475
        FILE *parent_tasks = NULL;
428✔
3476
        char *parent_name = NULL;
428✔
3477
        int delete_group = 1;
428✔
3478
        int empty_cgrp = 0;
428✔
3479
        int i, ret;
3480

3481
        if (!cgroup_initialized)
428✔
3482
                return ECGROUPNOTINITIALIZED;
×
3483

3484
        if (!cgrp)
428✔
3485
                return ECGROUPNOTALLOWED;
×
3486

3487
        if ((flags & CGFLAG_DELETE_RECURSIVE)
428✔
3488
            && (flags & CGFLAG_DELETE_EMPTY_ONLY))
72✔
3489
                return ECGINVAL;
×
3490

3491
        if (cgrp->index == 0)
428✔
3492
                /* Valid empty cgroup v2 with not controllers added. */
3493
                empty_cgrp = 1;
12✔
3494

3495
        for (i = 0; i < cgrp->index; i++) {
844✔
3496
                if (!cgroup_test_subsys_mounted(cgrp->controller[i]->name))
416✔
3497
                        return ECGROUPSUBSYSNOTMOUNTED;
×
3498
        }
3499

3500
        /*
3501
         * Remove the group from all controllers and in the case of cgroup
3502
         * with no controllers, perform all actions of a single controller.
3503
         */
3504
        for (i = 0; empty_cgrp > 0 || i < cgrp->index; i++, empty_cgrp--) {
856✔
3505

3506
                ret = 0;
428✔
3507
                controller_name = NULL;
428✔
3508

3509
                if (cgrp->controller[i])
428✔
3510
                        controller_name = cgrp->controller[i]->name;
416✔
3511

3512
                /* Find parent, it can be different for each controller */
3513
                if (!(flags & CGFLAG_DELETE_EMPTY_ONLY)) {
428✔
3514
                        ret = cgroup_find_parent(cgrp, controller_name, &parent_name);
428✔
3515
                        if (ret) {
428✔
3516
                                /*
3517
                                 * ECGROPNOTEXIST is returned on cgroup v1, where
3518
                                 * controllers share the mount points. When cgroup
3519
                                 * is deleted on one of symbolic link (controller)
3520
                                 * and they should pass on other controllers sharing
3521
                                 * the mount point.
3522
                                 *
3523
                                 * cgrp_del_shared_mnt serves as an extra check
3524
                                 * flag that gets set, when the cgroup exists and
3525
                                 * is deleted or else sets an error.
3526
                                 */
3527
                                if (first_error == 0 &&
26✔
3528
                                    (ret != ECGROUPNOTEXIST ||
22✔
3529
                                    (ret == ECGROUPNOTEXIST && cgrp_del_on_shared_mnt == 0))) {
22✔
3530
                                        first_errno = last_errno;
26✔
3531
                                        first_error = ECGOTHER;
26✔
3532
                                }
3533
                                continue;
26✔
3534
                        }
3535

3536
                        if (is_cgrp_ctrl_shared_mnt(controller_name))
402✔
3537
                                cgrp_del_on_shared_mnt = 1;
244✔
3538

3539
                        if (parent_name == NULL) {
402✔
3540
                                /* Root group is being deleted. */
3541
                                if (!(flags & CGFLAG_DELETE_RECURSIVE))
×
3542
                                        /* root group is being deleted in non-recursive mode */
3543
                                        continue;
×
3544
                                /*
3545
                                 * Move all tasks to the root group and
3546
                                 * do not delete it afterwards.
3547
                                 */
3548
                                parent_name = strdup(".");
×
3549
                                if (parent_name == NULL) {
×
3550
                                        if (first_error == 0) {
×
3551
                                                first_errno = errno;
×
3552
                                                first_error = ECGOTHER;
×
3553
                                        }
3554
                                        continue;
×
3555
                                }
3556
                                delete_group = 0;
×
3557
                        }
3558
                }
3559

3560
                if (parent_name) {
402✔
3561
                        /* Tasks need to be moved, pre-open target tasks file */
3562
                        ret = cgroup_build_tasks_procs_path(parent_path, sizeof(parent_path),
402✔
3563
                                                            parent_name, controller_name);
3564
                        if (ret != 0) {
402✔
3565
                                if (first_error == 0)
×
3566
                                        first_error = ECGFAIL;
×
3567
                                free(parent_name);
×
3568
                                continue;
×
3569
                        }
3570

3571
                        parent_tasks = fopen(parent_path, "we");
402✔
3572
                        if (!parent_tasks) {
402✔
3573
                                if (first_error == 0) {
×
3574
                                        cgroup_warn("cannot open tasks file %s: %s\n", parent_path,
×
3575
                                                    strerror(errno));
3576
                                        first_errno = errno;
×
3577
                                        first_error = ECGOTHER;
×
3578
                                }
3579
                                free(parent_name);
×
3580
                                continue;
×
3581
                        }
3582
                }
3583
                if (flags & CGFLAG_DELETE_RECURSIVE) {
402✔
3584
                        ret = cg_delete_cgrp_controller_recursive(cgrp->name, controller_name,
58✔
3585
                                                                    parent_tasks, flags,
3586
                                                                    delete_group);
3587
                } else {
3588
                        ret = cg_delete_cgrp_controller(cgrp->name, controller_name, parent_tasks,
344✔
3589
                                                        flags);
3590
                }
3591

3592
                if (parent_tasks) {
402✔
3593
                        fclose(parent_tasks);
402✔
3594
                        parent_tasks = NULL;
402✔
3595
                }
3596
                free(parent_name);
402✔
3597
                parent_name = NULL;
402✔
3598
                /*
3599
                 * If any of the controller delete fails, remember the first
3600
                 * error code, but continue with next controller and try remove
3601
                 * the group from all of them.
3602
                 */
3603
                if (ret) {
402✔
3604
                        /*
3605
                         * ECGNONEMPTY is more or less not an error, but an
3606
                         * indication that something was not removed.
3607
                         * Therefore it should be replaced by any other error.
3608
                         */
3609
                        if (ret != ECGNONEMPTY &&
×
3610
                            (first_error == 0 || first_errno == ECGNONEMPTY)) {
×
3611
                                first_errno = last_errno;
×
3612
                                first_error = ret;
×
3613
                        }
3614
                }
3615
        }
3616

3617
        /*
3618
         * Restore the last_errno to the first errno from
3619
         * cg_delete_cgroup_controller[_ext].
3620
         */
3621
        if (first_errno != 0)
428✔
3622
                last_errno = first_errno;
26✔
3623

3624
        return first_error;
428✔
3625
}
3626

3627
/*
3628
 * This function should really have more checks, but this version will assume
3629
 * that the callers have taken care of everything. Including the locking.
3630
 */
3631
static int cg_rd_ctrl_file(const char *subsys, const char *cgrp, const char *file, char **value)
4,838✔
3632
{
3633
        char path[FILENAME_MAX];
3634
        FILE *ctrl_file = NULL;
4,838✔
3635
        int ret;
3636

3637
        if (!cg_build_path_locked(cgrp, path, subsys))
4,838✔
3638
                return ECGFAIL;
×
3639

3640
        strncat(path, file, sizeof(path) - strlen(path));
4,838✔
3641
        ctrl_file = fopen(path, "re");
4,838✔
3642
        if (!ctrl_file)
4,838✔
3643
                return ECGROUPVALUENOTEXIST;
8✔
3644

3645
        *value = calloc(CG_CONTROL_VALUE_MAX, 1);
4,830✔
3646
        if (!*value) {
4,830✔
3647
                fclose(ctrl_file);
×
3648
                last_errno = errno;
×
3649
                return ECGOTHER;
×
3650
        }
3651

3652
        /* Using %as crashes when we try to read from files like memory.stat */
3653
        ret = fread(*value, 1, CG_CONTROL_VALUE_MAX-1, ctrl_file);
4,830✔
3654
        if (ret < 0) {
4,830✔
3655
                free(*value);
×
3656
                *value = NULL;
×
3657
        } else {
3658
                /* Remove trailing \n */
3659
                if (ret > 0 && (*value)[ret-1] == '\n')
4,830✔
3660
                        (*value)[ret-1] = '\0';
4,193✔
3661
        }
3662

3663
        fclose(ctrl_file);
4,830✔
3664

3665
        return 0;
4,830✔
3666
}
3667

3668
/*
3669
 * Call this function with required locks taken.
3670
 */
3671
int cgroup_fill_cgc(struct dirent *ctrl_dir, struct cgroup *cgrp, struct cgroup_controller *cgc,
16,266✔
3672
                    int cg_index)
3673
{
3674
        char path[FILENAME_MAX+1];
3675
        struct stat stat_buffer;
3676
        char *ctrl_value = NULL;
16,266✔
3677
        char *ctrl_name = NULL;
16,266✔
3678
        char *ctrl_file = NULL;
16,266✔
3679
        char *tmp_path = NULL;
16,266✔
3680
        char *d_name = NULL;
16,266✔
3681
        char *buffer = NULL;
16,266✔
3682
        int tmp_len = 0;
16,266✔
3683
        int error = 0;
16,266✔
3684

3685
        d_name = strdup(ctrl_dir->d_name);
16,266✔
3686

3687
        if (!strcmp(d_name, ".") || !strcmp(d_name, "..")) {
16,266✔
3688
                error = ECGINVAL;
×
3689
                goto fill_error;
×
3690
        }
3691

3692
        /*
3693
         * This part really needs to be optimized out. Probably use some
3694
         * sort of a flag, but this is fine for now.
3695
         */
3696
        cg_build_path_locked(cgrp->name, path, cg_mount_table[cg_index].name);
16,266✔
3697
        strncat(path, d_name, sizeof(path) - strlen(path));
16,266✔
3698

3699
        error = stat(path, &stat_buffer);
16,266✔
3700
        if (error) {
16,266✔
3701
                error = ECGFAIL;
×
3702
                goto fill_error;
×
3703
        }
3704

3705
        /*
3706
         * We have already stored the tasks_uid & tasks_gid. This check is
3707
         * to avoid the overwriting of the values stored in
3708
         * control_uid & cotrol_gid. tasks file will have the uid and gid of
3709
         * the user who is capable of putting a task to this cgroup.
3710
         * control_uid and control_gid is meant for the users who are capable
3711
         * of managing the cgroup shares.
3712
         *
3713
         * The strstr() function will return the pointer to the
3714
         * beginning of the sub string "/tasks".
3715
         */
3716
        tmp_len = strlen(path) - strlen("/tasks");
16,266✔
3717

3718
        /* tmp_path would be pointing to the last six characters */
3719
        tmp_path = (char *)path + tmp_len;
16,266✔
3720

3721
        /*
3722
         * Checking to see, if this is actually a 'tasks' file We need to
3723
         * compare the last 6 bytes
3724
         */
3725
        if (strcmp(tmp_path, "/tasks")) {
16,266✔
3726
                cgrp->control_uid = stat_buffer.st_uid;
15,954✔
3727
                cgrp->control_gid = stat_buffer.st_gid;
15,954✔
3728
        }
3729

3730
        ctrl_name = strtok_r(d_name, ".", &buffer);
16,266✔
3731
        if (!ctrl_name) {
16,266✔
3732
                error = ECGFAIL;
×
3733
                goto fill_error;
×
3734
        }
3735

3736
        ctrl_file = strtok_r(NULL, ".", &buffer);
16,266✔
3737
        if (!ctrl_file) {
16,266✔
3738
                error = ECGINVAL;
606✔
3739
                goto fill_error;
606✔
3740
        }
3741

3742
        if (strcmp(ctrl_name, cg_mount_table[cg_index].name) == 0) {
15,660✔
3743
                error = cg_rd_ctrl_file(cg_mount_table[cg_index].name, cgrp->name,
4,838✔
3744
                                        ctrl_dir->d_name, &ctrl_value);
4,838✔
3745
                if (error || !ctrl_value)
4,838✔
3746
                        goto fill_error;
8✔
3747

3748
                if (cgroup_add_value_string(cgc, ctrl_dir->d_name, ctrl_value)) {
4,830✔
3749
                        error = ECGFAIL;
×
3750
                        goto fill_error;
×
3751
                }
3752
        }
3753
fill_error:
15,652✔
3754
        if (ctrl_value)
16,266✔
3755
                free(ctrl_value);
4,830✔
3756
        free(d_name);
16,266✔
3757

3758
        return error;
16,266✔
3759
}
3760

3761
/*
3762
 * cgroup_get_cgroup reads the cgroup data from the filesystem.
3763
 * struct cgroup has the name of the group to be populated
3764
 *
3765
 * return 0 on success.
3766
 */
3767
int cgroup_get_cgroup(struct cgroup *cgrp)
115✔
3768
{
3769
        char cgrp_ctrl_path[FILENAME_MAX];
3770
        struct dirent *ctrl_dir = NULL;
115✔
3771
        char mnt_path[FILENAME_MAX];
3772
        int initial_controller_cnt;
3773
        char *control_path = NULL;
115✔
3774
        int controller_cnt = 0;
115✔
3775
        DIR *dir = NULL;
115✔
3776
        int error;
3777
        int i, j;
3778
        int ret;
3779

3780
        if (!cgroup_initialized) {
115✔
3781
                /* ECGROUPNOTINITIALIZED */
3782
                return ECGROUPNOTINITIALIZED;
×
3783
        }
3784

3785
        if (!cgrp) {
115✔
3786
                /* ECGROUPNOTALLOWED */
3787
                return ECGROUPNOTALLOWED;
×
3788
        }
3789

3790
        initial_controller_cnt = cgrp->index;
115✔
3791

3792
        pthread_rwlock_rdlock(&cg_mount_table_lock);
115✔
3793
        for (i = 0; i < CG_CONTROLLER_MAX && cg_mount_table[i].name[0] != '\0'; i++) {
1,550✔
3794
                struct cgroup_controller *cgc;
3795
                struct stat stat_buffer;
3796
                int mnt_path_len;
3797

3798
                if (initial_controller_cnt > 0) {
1,437✔
3799
                        bool skip_this_controller = true;
45✔
3800

3801
                        /*
3802
                         * The user has specified a list of controllers they are interested
3803
                         * in.  Only operate on the specified controllers
3804
                         */
3805
                        for (j = 0; j < cgrp->index; j++) {
153✔
3806
                                if (strncmp(cg_mount_table[i].name, cgrp->controller[j]->name,
108✔
3807
                                            CONTROL_NAMELEN_MAX) == 0)
3808
                                        skip_this_controller = false;
12✔
3809
                        }
3810

3811
                        if (skip_this_controller)
45✔
3812
                                continue;
966✔
3813
                }
3814

3815
                if (!cg_build_path_locked(NULL, mnt_path, cg_mount_table[i].name))
1,404✔
3816
                        continue;
×
3817

3818
                mnt_path_len = strlen(mnt_path);
1,404✔
3819
                strncat(mnt_path, cgrp->name, FILENAME_MAX - mnt_path_len - 1);
1,404✔
3820
                mnt_path[sizeof(mnt_path) - 1] = '\0';
1,404✔
3821

3822
                if (access(mnt_path, F_OK))
1,404✔
3823
                        continue;
729✔
3824

3825
                if (!cg_build_path_locked(cgrp->name, cgrp_ctrl_path, cg_mount_table[i].name)) {
675✔
3826
                        /* This fails when the cgroup does not exist for that controller. */
3827
                        continue;
×
3828
                }
3829

3830
                /* Get the uid and gid information. */
3831
                if (cg_mount_table[i].version == CGROUP_V1) {
675✔
3832
                        ret = asprintf(&control_path, "%s/tasks", cgrp_ctrl_path);
293✔
3833

3834
                        if (ret < 0) {
293✔
3835
                                last_errno = errno;
×
3836
                                error = ECGOTHER;
×
3837
                                goto unlock_error;
2✔
3838
                        }
3839

3840
                        if (stat(control_path, &stat_buffer)) {
293✔
3841
                                last_errno = errno;
2✔
3842
                                free(control_path);
2✔
3843
                                error = ECGOTHER;
2✔
3844
                                goto unlock_error;
2✔
3845
                        }
3846

3847
                        cgrp->tasks_uid = stat_buffer.st_uid;
291✔
3848
                        cgrp->tasks_gid = stat_buffer.st_gid;
291✔
3849

3850
                        free(control_path);
291✔
3851
                } else { /* cgroup v2 */
3852
                        bool enabled;
3853

3854
                        error = cgroupv2_get_controllers(cgrp_ctrl_path, cg_mount_table[i].name,
382✔
3855
                                                         &enabled);
3856
                        if (error == ECGROUPNOTMOUNTED) {
382✔
3857
                                /*
3858
                                 * This controller isn't enabled.  Only hide it from the
3859
                                 * user if they've chosen to view all enabled controllers.
3860
                                 *
3861
                                 * If they've specified the controllers they're interested in
3862
                                 * and we've made it this far, then they are explicitly
3863
                                 * interested in this controller and we should not remove it.
3864
                                 */
3865
                                if (initial_controller_cnt == 0) {
208✔
3866
                                        controller_cnt++;
204✔
3867
                                        continue;
204✔
3868
                                }
3869
                        } else if (error) {
174✔
3870
                                goto unlock_error;
×
3871
                        }
3872
                }
3873

3874
                if (initial_controller_cnt)
469✔
3875
                        cgc = cgroup_get_controller(cgrp, cg_mount_table[i].name);
12✔
3876
                else
3877
                        cgc = cgroup_add_controller(cgrp, cg_mount_table[i].name);
457✔
3878
                if (!cgc) {
469✔
3879
                        error = ECGINVAL;
×
3880
                        goto unlock_error;
×
3881
                }
3882

3883
                dir = opendir(cgrp_ctrl_path);
469✔
3884
                if (!dir) {
469✔
3885
                        last_errno = errno;
×
3886
                        error = ECGOTHER;
×
3887
                        goto unlock_error;
×
3888
                }
3889

3890
                controller_cnt++;
469✔
3891

3892
                while ((ctrl_dir = readdir(dir)) != NULL) {
16,434✔
3893
                        /* Skip over non regular files */
3894
                        if (ctrl_dir->d_type != DT_REG)
15,965✔
3895
                                continue;
1,357✔
3896

3897
                        error = cgroup_fill_cgc(ctrl_dir, cgrp, cgc, i);
14,608✔
3898
                        for (j = 0; j < cgc->index; j++)
98,743✔
3899
                                cgc->values[j]->dirty = false;
84,135✔
3900

3901
                        if (error == ECGFAIL) {
14,608✔
3902
                                closedir(dir);
×
3903
                                goto unlock_error;
×
3904
                        }
3905
                }
3906
                closedir(dir);
469✔
3907

3908
                if (!strcmp(cgc->name, "memory")) {
469✔
3909
                        /*
3910
                         * Make sure that memory.limit_in_bytes is placed before
3911
                         * memory.memsw.limit_in_bytes in the list of values
3912
                         */
3913
                        int memsw_limit = -1;
109✔
3914
                        int mem_limit = -1;
109✔
3915

3916
                        for (j = 0; j < cgc->index; j++) {
2,637✔
3917
                                if (!strcmp(cgc->values[j]->name, "memory.memsw.limit_in_bytes"))
2,528✔
3918
                                        memsw_limit = j;
75✔
3919
                                else if (!strcmp(cgc->values[j]->name, "memory.limit_in_bytes"))
2,453✔
3920
                                        mem_limit = j;
75✔
3921
                        }
3922

3923
                        if (memsw_limit >= 0 && memsw_limit < mem_limit) {
109✔
UNCOV
3924
                                struct control_value *val = cgc->values[memsw_limit];
×
3925

UNCOV
3926
                                cgc->values[memsw_limit] = cgc->values[mem_limit];
×
UNCOV
3927
                                cgc->values[mem_limit] = val;
×
3928
                        }
3929
                }
3930
        }
3931

3932
        /*
3933
         * Check if the group really exists or not.  The cgrp->index controller count can't
3934
         * be used in this case because cgroup v2 allows controllers to be enabled/disabled in
3935
         * the subtree_control file.  Rather, cgroup_get_cgroup() tracks the number of possible
3936
         * controllers in the controller_cnt variable and uses that to determine if the cgroup
3937
         * exists or not.
3938
         */
3939
        if (!controller_cnt) {
113✔
3940
                error = ECGROUPNOTEXIST;
×
3941
                goto unlock_error;
×
3942
        }
3943

3944
        pthread_rwlock_unlock(&cg_mount_table_lock);
113✔
3945

3946
        return 0;
113✔
3947

3948
unlock_error:
2✔
3949
        pthread_rwlock_unlock(&cg_mount_table_lock);
2✔
3950
        /*
3951
         * XX: Need to figure out how to cleanup? Cleanup just the stuff
3952
         * we added, or the whole structure.
3953
         */
3954
        cgroup_free_controllers(cgrp);
2✔
3955
        cgrp = NULL;
2✔
3956

3957
        return error;
2✔
3958
}
3959

3960
/**
3961
 * cg_prepare_cgroup Process the selected rule. Prepare the cgroup structure
3962
 * which can be used to add the task to destination cgroup.
3963
 *
3964
 *  returns 0 on success.
3965
 */
3966
static int cg_prepare_cgroup(struct cgroup *cgrp, pid_t pid, const char *dest,
134✔
3967
                             const char * const controllers[])
3968
{
3969
        struct cgroup_controller *cptr = NULL;
134✔
3970
        const char *controller = NULL;
134✔
3971
        int ret = 0, i;
134✔
3972

3973
        /* Fill in cgroup details.  */
3974
        cgroup_dbg("Will move pid %d to cgroup '%s'\n", pid, dest);
134✔
3975

3976
        strncpy(cgrp->name, dest, FILENAME_MAX);
134✔
3977
        cgrp->name[FILENAME_MAX-1] = '\0';
134✔
3978

3979
        /* Scan all the controllers */
3980
        for (i = 0; i < CG_CONTROLLER_MAX; i++) {
326✔
3981
                int j = 0;
326✔
3982

3983
                if (!controllers[i])
326✔
3984
                        return 0;
134✔
3985
                controller = controllers[i];
192✔
3986

3987
                /* If first string is "*" that means all the mounted controllers. */
3988
                if (strcmp(controller, "*") == 0) {
192✔
3989
                        pthread_rwlock_rdlock(&cg_mount_table_lock);
×
3990

3991
                        for (j = 0; j < CG_CONTROLLER_MAX &&
×
3992
                                cg_mount_table[j].name[0] != '\0'; j++) {
×
3993
                                cgroup_dbg("Adding controller %s\n", cg_mount_table[j].name);
×
3994
                                cptr = cgroup_add_controller(cgrp, cg_mount_table[j].name);
×
3995
                                if (!cptr) {
×
3996
                                        cgroup_warn("adding controller '%s' failed\n",
×
3997
                                                    cg_mount_table[j].name);
3998
                                        pthread_rwlock_unlock(&cg_mount_table_lock);
×
3999
                                        cgroup_free_controllers(cgrp);
×
4000
                                        return ECGROUPNOTALLOWED;
×
4001
                                }
4002
                        }
4003
                        pthread_rwlock_unlock(&cg_mount_table_lock);
×
4004
                        return ret;
×
4005
                }
4006

4007
                /* It is individual controller names and not "*" */
4008
                cgroup_dbg("Adding controller %s\n", controller);
192✔
4009
                cptr = cgroup_add_controller(cgrp, controller);
192✔
4010
                if (!cptr) {
192✔
4011
                        cgroup_warn("adding controller '%s' failed\n", controller);
×
4012
                        cgroup_free_controllers(cgrp);
×
4013
                        return ECGROUPNOTALLOWED;
×
4014
                }
4015
        }
4016

4017
        return ret;
×
4018
}
4019

4020
/**
4021
 * Determines if the rule is a wildcard rule and if so, compares the wildcard
4022
 * rule against the new process.  If the new process matches the wildcard rule,
4023
 * then this function returns true. Otherwise it returns false.
4024
 *
4025
 *        @param rule_procname The procname field of the rule
4026
 *        @param procname The name of the new process
4027
 *        @return True if the procname matches the rule.  False otherwise
4028
 */
4029
STATIC bool cgroup_compare_wildcard_procname(const char * const rule_procname,
1,388✔
4030
                                             const char * const procname)
4031
{
4032
        size_t rule_strlen = strlen(rule_procname);
1,388✔
4033

4034
        if (rule_procname[rule_strlen - 1] != '*')
1,388✔
4035
                /* This rule does not end in a wildcard */
4036
                return false;
1,374✔
4037

4038
        /* Compare the two strings up to the asterisk */
4039
        if (strncmp(rule_procname, procname, rule_strlen - 1) != 0)
14✔
4040
                /* The strings did not match */
4041
                return false;
6✔
4042

4043
        /* All checks passed.  The wildcarded process matched this rule */
4044
        return true;
8✔
4045
}
4046

4047
static int cgroup_find_matching_destination(char *cgrp_list[], const char * const rule_dest,
28✔
4048
                                            int *matching_index)
4049
{
4050
        size_t rule_strlen = strlen(rule_dest);
28✔
4051
        int ret = -ENODATA;
28✔
4052
        int i;
4053

4054
        for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
68✔
4055
                if (cgrp_list[i] == NULL)
68✔
4056
                        break;
10✔
4057

4058
                if (rule_dest[rule_strlen - 1] == '/') {
58✔
4059
                        /*
4060
                         * Avoid a weird corner case where given a rule dest
4061
                         * like 'folder/', we _don't_ want to match 'folder1'
4062
                         */
4063
                        if (strlen(cgrp_list[i]) >= rule_strlen &&
14✔
4064
                            cgrp_list[i][rule_strlen - 1] != '/')
12✔
4065
                                continue;
6✔
4066

4067
                        /*
4068
                         * Strip off the '/' at the end of the rule, as
4069
                         * the destination from the cgrp_list will not
4070
                         * have a trailing '/'
4071
                         */
4072
                        rule_strlen--;
8✔
4073
                }
4074

4075
                if (strncmp(rule_dest, cgrp_list[i], rule_strlen) == 0) {
52✔
4076
                        *matching_index = i;
18✔
4077
                        ret = 0;
18✔
4078
                        break;
18✔
4079
                }
4080
        }
4081

4082
        return ret;
28✔
4083
}
4084

4085
static int cgroup_find_matching_controller(char * const *rule_controllers,
20✔
4086
                                           const char * const pid_controller, int *matching_index)
4087
{
4088
        int ret = -ENODATA;
20✔
4089
        int i;
4090

4091
        for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
22✔
4092
                if (rule_controllers[i] == NULL)
22✔
4093
                        break;
2✔
4094

4095
                if (strlen(rule_controllers[i]) != strlen(pid_controller))
20✔
4096
                        continue;
2✔
4097

4098
                if (strncmp(pid_controller, rule_controllers[i], strlen(pid_controller)) == 0) {
18✔
4099
                        *matching_index = i;
18✔
4100
                        ret = 0;
18✔
4101
                        break;
18✔
4102
                }
4103
        }
4104

4105
        return ret;
20✔
4106
}
4107

4108
static bool cgroup_is_rt_task(const pid_t pid)
28✔
4109
{
4110
        int sched_prio_min, sched_prio_max;
4111
        struct sched_param pid_param;
4112
        int ret;
4113

4114
        ret = sched_getparam(pid, &pid_param);
28✔
4115
        if (ret == -1) {
28✔
4116
                ret = ECGOTHER;
28✔
4117
                last_errno = errno;
28✔
4118
                return false;
28✔
4119
        }
4120

4121
        sched_prio_min = sched_get_priority_min(SCHED_RR);
×
4122
        sched_prio_max = sched_get_priority_max(SCHED_RR);
×
4123

4124
        if (pid_param.sched_priority >= sched_prio_min &&
×
4125
            pid_param.sched_priority <= sched_prio_max)
×
4126
                return true;
×
4127

4128
        return false;
×
4129
}
4130

4131
/**
4132
 * Evaluates if rule is an ignore rule and the pid/procname match this rule.
4133
 * If rule is an ignore rule and the pid/procname match this rule, then this
4134
 * function returns true.  Otherwise it returns false.
4135
 *
4136
 *        @param rule The rule being evaluated
4137
 *        @param pid PID of the process being compared
4138
 *        @param procname Process name of the process being compared
4139
 *        @return True if the rule is an ignore rule and this pid/procname
4140
 *                match the rule.  False otherwise
4141
 */
4142
STATIC bool cgroup_compare_ignore_rule(const struct cgroup_rule * const rule, pid_t pid,
1,445✔
4143
                                       const char * const procname)
4144
{
4145
        char *controller_list[MAX_MNT_ELEMENTS] = { '\0' };
1,445✔
4146
        char *cgrp_list[MAX_MNT_ELEMENTS] = { '\0' };
1,445✔
4147
        int rule_matching_controller_idx;
4148
        int cgrp_list_matching_idx = 0;
1,445✔
4149
        bool found_match = false;
1,445✔
4150
        char *token, *saveptr;
4151
        int ret, i;
4152

4153
        if (!rule->is_ignore)
1,445✔
4154
                /* Immediately return if the 'ignore' option is not set */
4155
                return false;
1,417✔
4156

4157
        /* If the rule is "ignore", move only non-rt tasks */
4158
        if (rule->is_ignore == CGRULE_OPT_IGNORE && cgroup_is_rt_task(pid) == true)
28✔
4159
                return false;
×
4160
        /* If the rule is "ignore_rt", move only non-rt tasks */
4161
        else if (rule->is_ignore == CGRULE_OPT_IGNORE_RT && cgroup_is_rt_task(pid) == false)
28✔
4162
                return false;
×
4163

4164
        /* If the rule is "ignore" and "ignore_rt", move all tasks */
4165

4166
        ret = cg_get_cgroups_from_proc_cgroups(pid, cgrp_list, controller_list,
28✔
4167
                                               MAX_MNT_ELEMENTS);
4168
        if (ret < 0)
28✔
4169
                goto out;
×
4170

4171
        if (strcmp(rule->destination, "*")) {
28✔
4172
                ret = cgroup_find_matching_destination(cgrp_list, rule->destination,
28✔
4173
                                                       &cgrp_list_matching_idx);
4174
                if (ret < 0)
28✔
4175
                        /* No cgroups matched */
4176
                        goto out;
10✔
4177
        }
4178

4179
        token = strtok_r(controller_list[cgrp_list_matching_idx], ",", &saveptr);
18✔
4180
        while (token != NULL) {
20✔
4181

4182
                ret = cgroup_find_matching_controller(rule->controllers, token,
20✔
4183
                                                      &rule_matching_controller_idx);
4184
                if (ret == 0)
20✔
4185
                        /* We found a matching controller */
4186
                        break;
18✔
4187

4188
                token = strtok_r(NULL, ",", &saveptr);
2✔
4189
        }
4190

4191
        if (!rule->procname) {
18✔
4192
                /*
4193
                 * The rule procname is empty, thus it's a wildcard and
4194
                 * all processes match.
4195
                 */
4196
                found_match = true;
10✔
4197
                goto out;
10✔
4198
        }
4199

4200
        if (!strcmp(rule->procname, procname)) {
8✔
4201
                found_match = true;
4✔
4202
                goto out;
4✔
4203
        }
4204

4205
        if (cgroup_compare_wildcard_procname(rule->procname, procname))
4✔
4206
                found_match = true;
2✔
4207

4208
out:
2✔
4209
        for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
476✔
4210
                if (controller_list[i])
448✔
4211
                        free(controller_list[i]);
72✔
4212
                if (cgrp_list[i])
448✔
4213
                        free(cgrp_list[i]);
72✔
4214
        }
4215

4216
        return found_match;
28✔
4217
}
4218

4219
static struct cgroup_rule *cgroup_find_matching_rule_uid_gid(uid_t uid, gid_t gid,
1,415✔
4220
                                                             struct cgroup_rule *rule)
4221
{
4222
        /* Temporary user data */
4223
        struct passwd *usr = NULL;
1,415✔
4224

4225
        /* Temporary group data */
4226
        struct group *grp = NULL;
1,415✔
4227

4228
        /* Temporary string pointer */
4229
        char *sp = NULL;
1,415✔
4230

4231
        /* Loop variable */
4232
        int i = 0;
1,415✔
4233
        int loglevel;
4234
        bool match_found = false;
1,415✔
4235

4236
        loglevel = cgroup_get_loglevel();
1,415✔
4237

4238
        while (rule) {
1,415✔
4239
                /* Skip "%" which indicates continuation of previous rule. */
4240
                if (rule->username[0] == '%') {
1,415✔
4241
                        rule = rule->next;
×
4242
                        continue;
×
4243
                }
4244
                /* The wildcard rule always matches. */
4245
                if ((rule->uid == CGRULE_WILD) && (rule->gid == CGRULE_WILD))
1,415✔
4246
                        return rule;
1,415✔
4247

4248
                /* This is the simple case of the UID matching. */
4249
                if (rule->uid == uid)
×
4250
                        return rule;
×
4251

4252
                /* This is the simple case of the GID matching. */
4253
                if (rule->gid == gid)
×
4254
                        return rule;
×
4255

4256
                /* If this is a group rule, the UID might be a member. */
4257
                if (rule->username[0] == '@') {
×
4258
                        /* Get the group data. */
4259
                        sp = &(rule->username[1]);
×
4260
                        grp = getgrnam(sp);
×
4261
                        if (!grp) {
×
4262
                                rule = rule->next;
×
4263
                                continue;
×
4264
                        }
4265

4266
                        /* Get the data for UID. */
4267
                        usr = getpwuid(uid);
×
4268
                        if (!usr) {
×
4269
                                rule = rule->next;
×
4270
                                continue;
×
4271
                        }
4272

4273
                        cgroup_dbg("User name: %s UID: %d Group name: %s GID: %d\n",
×
4274
                                   usr->pw_name, uid, grp->gr_name, grp->gr_gid);
4275
                        if (grp->gr_mem[0])
×
4276
                                cgroup_dbg("Group member(s):\n");
×
4277

4278
                        /* If UID is a member of group, we matched. */
4279
                        for (i = 0; grp->gr_mem[i]; i++) {
×
4280
                                if (!(strcmp(usr->pw_name, grp->gr_mem[i])))
×
4281
                                        match_found = true;
×
4282

4283
                                if (match_found && loglevel < CGROUP_LOG_DEBUG)
×
4284
                                        /*
4285
                                         * Only continue to run through the loop if debugging is
4286
                                         * enabled so that we can see all of the group members
4287
                                         */
4288
                                        break;
×
4289

4290
                                cgroup_dbg("\t%s\n", grp->gr_mem[i]);
×
4291
                        }
4292

4293
                        if (match_found)
×
4294
                                return rule;
×
4295
                }
4296

4297
                /* If we haven't matched, try the next rule. */
4298
                rule = rule->next;
×
4299
        }
4300

4301
        /* If we get here, no rules matched. */
4302
        return NULL;
×
4303
}
4304

4305
/**
4306
 * Finds the first rule in the cached list that matches the given UID, GID
4307
 * or PROCESS NAME, and returns a pointer to that rule.
4308
 * This function uses rl_lock.
4309
 *
4310
 * This function may NOT be thread safe.
4311
 *        @param uid The UID to match
4312
 *        @param gid The GID to match
4313
 *        @param procname The PROCESS NAME to match
4314
 *        @return Pointer to the first matching rule, or NULL if no match
4315
 * TODO: Determine thread-safeness and fix if not safe.
4316
 */
4317
static struct cgroup_rule *cgroup_find_matching_rule(uid_t uid, gid_t gid, pid_t pid,
1,415✔
4318
                                                     const char *procname)
4319
{
4320
        /* Return value */
4321
        struct cgroup_rule *ret = rl.head;
1,415✔
4322
        char *base = NULL;
1,415✔
4323

4324
        pthread_rwlock_wrlock(&rl_lock);
1,415✔
4325
        while (ret) {
2,785✔
4326
                ret = cgroup_find_matching_rule_uid_gid(uid, gid, ret);
1,415✔
4327
                if (!ret)
1,415✔
4328
                        break;
×
4329
                if (cgroup_compare_ignore_rule(ret, pid, procname))
1,415✔
4330
                        /*
4331
                         * This pid matched a rule that instructs the
4332
                         * cgrules daemon to ignore this process.
4333
                         */
4334
                        break;
×
4335
                if (ret->is_ignore) {
1,415✔
4336
                        /*
4337
                         * The rule currently being examined is an ignore
4338
                         * rule, but it didn't match this pid. Move on to
4339
                         * the next rule
4340
                         */
4341
                        ret = ret->next;
×
4342
                        continue;
×
4343
                }
4344
                if (!procname)
1,415✔
4345
                        /* If procname is NULL, return a rule matching UID or GID. */
4346
                        break;
×
4347
                if (!ret->procname)
1,415✔
4348
                        /* If no process name in a rule, that means wildcard */
4349
                        break;
×
4350
                if (!strcmp(ret->procname, procname))
1,415✔
4351
                        break;
45✔
4352

4353
                base = cgroup_basename(procname);
1,370✔
4354
                if (!strcmp(ret->procname, base))
1,370✔
4355
                        /* Check a rule of basename. */
4356
                        break;
×
4357
                if (cgroup_compare_wildcard_procname(ret->procname, procname))
1,370✔
4358
                        break;
×
4359
                ret = ret->next;
1,370✔
4360
                free(base);
1,370✔
4361
                base = NULL;
1,370✔
4362
        }
4363
        pthread_rwlock_unlock(&rl_lock);
1,415✔
4364

4365
        if (base)
1,415✔
4366
                free(base);
×
4367

4368
        return ret;
1,415✔
4369
}
4370

4371
/*
4372
 * Procedure the existence of cgroup "prefix" is in subsystem
4373
 * controller_name return 0 on success
4374
 */
4375
int cgroup_exist_in_subsystem(char *controller_name, char *prefix)
×
4376
{
4377
        char path[FILENAME_MAX];
4378
        char *ret_path;
4379
        DIR *dir;
4380
        int ret;
4381

4382
        pthread_rwlock_rdlock(&cg_mount_table_lock);
×
4383
        ret_path = cg_build_path_locked(prefix, path, controller_name);
×
4384
        pthread_rwlock_unlock(&cg_mount_table_lock);
×
4385
        if (!ret_path) {
×
4386
                ret = 1;
×
4387
                goto end;
×
4388
        }
4389

4390
        dir = opendir(path);
×
4391
        if (dir == NULL) {
×
4392
                /* cgroup in wanted subsystem does not exist */
4393
                ret = 1;
×
4394
        } else {
4395
                /* cgroup in wanted subsystem exists */
4396
                ret = 0;
×
4397
                closedir(dir);
×
4398
        }
4399
end:
×
4400
        return ret;
×
4401
}
4402

4403
/*
4404
 * Auxiliary function return a pointer to the string which is copy of
4405
 * input string and end with the slash
4406
 */
4407
char *cgroup_copy_with_slash(char *input)
×
4408
{
4409
        int len = strlen(input);
×
4410
        char *output;
4411

4412
        /* If input does not end with '/', allocate one more space for it */
4413
        if ((input[len-1]) != '/')
×
4414
                len = len+1;
×
4415

4416
        output = (char *)malloc(sizeof(char)*(len+1));
×
4417
        if (output == NULL)
×
4418
                return NULL;
×
4419

4420
        strcpy(output, input);
×
4421
        output[len-1] = '/';
×
4422
        output[len] = '\0';
×
4423

4424
        return output;
×
4425
}
4426

4427
/* Add controller to a group if it is not exists create it */
4428
static int add_controller(struct cgroup **pcgrp, char *cgrp_name,
×
4429
                          char controller_name[FILENAME_MAX])
4430
{
4431
        struct cgroup_controller *controller = NULL;
×
4432
        struct cgroup *cgrp = pcgrp[0];
×
4433
        int ret = 0;
×
4434

4435
        if  (cgrp == NULL) {
×
4436
                /* It is the first controller the cgrp have to be created */
4437
                cgrp = cgroup_new_cgroup(cgrp_name);
×
4438
                if (cgrp == NULL) {
×
4439
                        ret = ECGFAIL;
×
4440
                        goto end;
×
4441
                }
4442
                pcgrp[0] = cgrp;
×
4443
        }
4444

4445
        controller = cgroup_add_controller(cgrp, controller_name);
×
4446
        if (controller == NULL) {
×
4447
                cgroup_free(&cgrp);
×
4448
                ret = ECGFAIL;
×
4449
        }
4450
end:
×
4451
        return ret;
×
4452
}
4453

4454
/*
4455
 * Create control group based given template if the group already don't exist
4456
 * dest is template name with substitute variables tmp is used cgrules rule.
4457
 */
4458
static int cgroup_create_template_group(char *orig_group_name, struct cgroup_rule *tmp, int flags)
×
4459
{
4460

4461
        struct cgroup *template_group = NULL;
×
4462
        char *template_name = NULL;        /* Name of the template.              */
×
4463
        char *group_name = NULL;        /* Name of the group based on         */
×
4464
                                        /* template variables are substituted.*/
4465
        char *template_position;        /* Denotes directory in template      */
4466
                                        /* path which is investigated.        */
4467
        char *group_position;                /* Denotes directory in cgroup path   */
4468
                                        /* which is investigated.             */
4469

4470
        int ret = 0;
×
4471
        int exist;
4472
        int i;
4473

4474
        /* Template name and group name have to have '/' sign at the end */
4475
        template_name = cgroup_copy_with_slash(tmp->destination);
×
4476
        if (template_name == NULL) {
×
4477
                ret = ECGOTHER;
×
4478
                last_errno = errno;
×
4479
                goto end;
×
4480
        }
4481
        group_name = cgroup_copy_with_slash(orig_group_name);
×
4482
        if (group_name == NULL) {
×
4483
                ret = ECGOTHER;
×
4484
                last_errno = errno;
×
4485
                free(template_name);
×
4486
                template_name = NULL;
×
4487
                goto end;
×
4488
        }
4489

4490
        /* Set start positions */
4491
        template_position = strchr(template_name, '/');
×
4492
        group_position = strchr(group_name, '/');
×
4493

4494
        /*
4495
         * Go recursively through whole path to template group and create
4496
         * given directory if it does not exist yet
4497
         */
4498
        while ((group_position != NULL) && (template_position != NULL)) {
×
4499
                /* Set new subpath */
4500
                group_position[0] = '\0';
×
4501
                template_position[0] = '\0';
×
4502
                template_group = NULL;
×
4503

4504
                /* Test for which controllers wanted group does not exist */
4505
                i = 0;
×
4506
                while (tmp->controllers[i] != NULL) {
×
4507
                        exist = cgroup_exist_in_subsystem(tmp->controllers[i], group_name);
×
4508

4509
                        if (exist != 0) {
×
4510
                                /* The cgroup does not exist */
4511
                                ret = add_controller(&template_group, group_name,
×
4512
                                                     tmp->controllers[i]);
4513
                                if  (ret != 0)
×
4514
                                        goto while_end;
×
4515
                        }
4516
                        i++;
×
4517
                }
4518

4519
                if (template_group != NULL) {
×
4520
                        /*  New group have to be created */
4521
                        if (strcmp(group_name, template_name) == 0) {
×
4522
                                /* The prefix cgroup without template */
4523
                                ret = cgroup_create_cgroup(template_group, 0);
×
4524
                        } else {
4525
                                /* Use template to create relevant cgroup */
4526
                                ret = cgroup_config_create_template_group(template_group,
×
4527
                                                                          template_name, flags);
4528
                        }
4529

4530
                        if (ret != 0) {
×
4531
                                cgroup_free(&template_group);
×
4532
                                goto while_end;
×
4533
                        }
4534
                        cgroup_dbg("Group %s created - based on template %s\n", group_name,
×
4535
                                   template_name);
4536

4537
                        cgroup_free(&template_group);
×
4538
                }
4539
                template_position[0] = '/';
×
4540
                group_position[0] = '/';
×
4541
                template_position = strchr(++template_position, '/');
×
4542
                group_position = strchr(++group_position, '/');
×
4543
        }
4544

4545
while_end:
×
4546
        if ((template_position != NULL) && (template_position[0] == '\0'))
×
4547
                template_position[0] = '/';
×
4548
        if ((group_position != NULL) && (group_position[0] == '\0'))
×
4549
                group_position[0] = '/';
×
4550

4551
end:
×
4552
        if (group_name != NULL)
×
4553
                free(group_name);
×
4554
        if (template_name != NULL)
×
4555
                free(template_name);
×
4556

4557
        return ret;
×
4558
}
4559

4560
int cgroup_change_cgroup_flags(uid_t uid, gid_t gid, const char *procname, pid_t pid, int flags)
1,415✔
4561
{
4562
        /* Temporary pointer to a rule */
4563
        struct cgroup_rule *tmp = NULL;
1,415✔
4564

4565
        /* Temporary variables for destination substitution */
4566
        char newdest[FILENAME_MAX];
4567
        struct passwd *user_info;
4568
        struct group *group_info;
4569
        int available;
4570
        int written;
4571
        int i, j;
4572

4573
        /* Return codes */
4574
        int ret = 0;
1,415✔
4575

4576
        /* We need to check this before doing anything else! */
4577
        if (!cgroup_initialized) {
1,415✔
4578
                cgroup_warn("libcgroup is not initialized\n");
×
4579
                ret = ECGROUPNOTINITIALIZED;
×
4580
                goto finished;
×
4581
        }
4582

4583
        /*
4584
         * User had asked to find the matching rule (if one exist) in the
4585
         * cached rules but the list might be empty due to the inactive
4586
         * cgrulesengd. Lets emulate its behaviour of caching the rules by
4587
         * reloading the rules from the configuration file.
4588
         */
4589
        if ((flags & CGFLAG_USECACHE) && (rl.head == NULL)) {
1,415✔
4590
                cgroup_warn("no cached rules found, trying to reload from %s.\n",
×
4591
                            CGRULES_CONF_FILE);
4592
                ret = cgroup_reload_cached_rules();
×
4593
                if (ret != 0)
×
4594
                        goto finished;
×
4595
        }
4596

4597
        /*
4598
         * If the user did not ask for cached rules, we must parse the
4599
         * configuration to find a matching rule (if one exists).
4600
         * Else, we'll find the first match in the cached list (rl).
4601
         */
4602
        if (!(flags & CGFLAG_USECACHE)) {
1,415✔
4603
                cgroup_dbg("Not using cached rules for PID %d.\n", pid);
×
4604
                ret = cgroup_parse_rules(false, uid, gid, procname);
×
4605

4606
                /* The configuration file has an error!  We must exit now. */
4607
                if (ret != -1 && ret != 0) {
×
4608
                        cgroup_err("failed to parse the configuration rules\n");
×
4609
                        goto finished;
×
4610
                }
4611

4612
                /* We did not find a matching rule, so we're done. */
4613
                if (ret == 0) {
×
4614
                        cgroup_dbg("No rule found to match PID: %d, UID: %d, GID: %d\n",
×
4615
                                   pid, uid, gid);
4616
                        goto finished;
×
4617
                }
4618

4619
                /* Otherwise, we did match a rule and it's in trl. */
4620
                tmp = trl.head;
×
4621
        } else {
4622
                /* Find the first matching rule in the cached list. */
4623
                tmp = cgroup_find_matching_rule(uid, gid, pid, procname);
1,415✔
4624
                if (!tmp) {
1,415✔
4625
                        cgroup_dbg("No rule found to match PID: %d, UID: %d, GID: %d\n",
1,370✔
4626
                                   pid, uid, gid);
4627
                        ret = 0;
1,370✔
4628
                        goto finished;
1,370✔
4629
                }
4630
        }
4631
        cgroup_dbg("Found matching rule %s for PID: %d, UID: %d, GID: %d\n",
45✔
4632
                   tmp->username, pid, uid, gid);
4633

4634
        if (tmp->is_ignore) {
45✔
4635
                /*
4636
                 * This rule has instructed us that this pid is not to be
4637
                 * processed and should be ignored
4638
                 */
4639
                cgroup_dbg("Matching rule is an ignore rule\n");
×
4640
                ret = 0;
×
4641
                goto finished;
×
4642
        }
4643

4644
        /* If we are here, then we found a matching rule, so execute it. */
4645
        do {
4646
                cgroup_dbg("Executing rule %s for PID %d... ", tmp->username, pid);
45✔
4647

4648
                /* Destination substitutions */
4649
                for (j = i = 0; i < strlen(tmp->destination) &&
839✔
4650
                        (j < FILENAME_MAX - 2); ++i, ++j) {
794✔
4651
                        if (tmp->destination[i] == '%') {
794✔
4652
                                /* How many bytes did we write / error check */
4653
                                written = 0;
×
4654
                                /* How many bytes can we write */
4655
                                available = FILENAME_MAX - j - 2;
×
4656
                                /* Substitution */
4657
                                switch (tmp->destination[++i]) {
×
4658
                                case 'U':
×
4659
                                        written = snprintf(newdest+j, available, "%d", uid);
×
4660
                                        break;
×
4661
                                case 'u':
×
4662
                                        user_info = getpwuid(uid);
×
4663
                                        if (user_info) {
×
4664
                                                written = snprintf(newdest + j, available, "%s",
×
4665
                                                                   user_info->pw_name);
4666
                                        } else {
4667
                                                written = snprintf(newdest + j, available, "%d",
×
4668
                                                                   uid);
4669
                                        }
4670
                                        break;
×
4671
                                case 'G':
×
4672
                                        written = snprintf(newdest + j,        available, "%d", gid);
×
4673
                                        break;
×
4674
                                case 'g':
×
4675
                                        group_info = getgrgid(gid);
×
4676
                                        if (group_info) {
×
4677
                                                written = snprintf(newdest + j,        available, "%s",
×
4678
                                                                   group_info->gr_name);
4679
                                        } else {
4680
                                                written = snprintf(newdest + j,        available, "%d",
×
4681
                                                                   gid);
4682
                                        }
4683
                                        break;
×
4684
                                case 'P':
×
4685
                                        written = snprintf(newdest + j,        available, "%d", pid);
×
4686
                                        break;
×
4687
                                case 'p':
×
4688
                                        if (procname) {
×
4689
                                                written = snprintf(newdest + j,        available, "%s",
×
4690
                                                                   procname);
4691
                                        } else {
4692
                                                written = snprintf(newdest + j,        available, "%d",
×
4693
                                                                   pid);
4694
                                        }
4695
                                        break;
×
4696
                                }
4697
                                written = min(written, available);
×
4698
                                /*
4699
                                 * written<1 only when either error occurred
4700
                                 * during snprintf or if no substitution was made
4701
                                 * at all. In both cases, we want to just copy
4702
                                 * input string.
4703
                                 */
4704
                                if (written < 1) {
×
4705
                                        newdest[j] = '%';
×
4706
                                        if (available > 1)
×
4707
                                                newdest[++j] = tmp->destination[i];
×
4708
                                } else {
4709
                                        /*
4710
                                         * In next iteration, we will write just
4711
                                         * after the substitution, but j will get
4712
                                         * incremented in the meantime.
4713
                                         */
4714
                                        j += written - 1;
×
4715
                                }
4716
                        } else {
4717
                                if (tmp->destination[i] == '\\')
794✔
4718
                                        ++i;
×
4719
                                newdest[j] = tmp->destination[i];
794✔
4720
                        }
4721
                }
4722

4723
                newdest[j] = 0;
45✔
4724
                if (strcmp(newdest, tmp->destination) != 0) {
45✔
4725
                        /* Destination tag contains templates */
4726

4727
                        cgroup_dbg("control group %s is template\n", newdest);
×
4728
                        ret = cgroup_create_template_group(newdest, tmp, flags);
×
4729
                        if (ret) {
×
4730
                                cgroup_warn("failed to create cgroup based on template %s\n",
×
4731
                                            newdest);
4732
                                goto finished;
×
4733
                        }
4734
                }
4735

4736
                /* Apply the rule */
4737
                ret = cgroup_change_cgroup_path(newdest, pid,
45✔
4738
                                                (const char * const *)tmp->controllers);
45✔
4739
                if (ret) {
45✔
4740
                        cgroup_warn("failed to apply the rule. Error was: %d\n", ret);
×
4741
                        goto finished;
×
4742
                }
4743
                cgroup_dbg("OK!\n");
45✔
4744

4745
                /*
4746
                 * Now, check for multi-line rules.  As long as the "next"
4747
                 * rule starts with '%', it's actually part of the rule that
4748
                 * we just executed.
4749
                 */
4750
                tmp = tmp->next;
45✔
4751
        } while (tmp && (tmp->username[0] == '%'));
45✔
4752

4753
finished:
45✔
4754
        return ret;
1,415✔
4755
}
4756

4757
int cgroup_change_cgroup_uid_gid_flags(uid_t uid, gid_t gid, pid_t pid, int flags)
×
4758
{
4759
        return cgroup_change_cgroup_flags(uid, gid, NULL, pid, flags);
×
4760
}
4761

4762
/**
4763
 * Provides backwards-compatibility with older versions of the API.
4764
 * This function is deprecated, and cgroup_change_cgroup_uid_gid_flags()
4765
 * should be used instead.  In fact, this function simply calls the newer
4766
 * one with flags set to 0 (none).
4767
 *        @param uid The UID to match
4768
 *        @param gid The GID to match
4769
 *        @param pid The PID of the process to move
4770
 *        @return 0 on success, > 0 on error
4771
 */
4772
int cgroup_change_cgroup_uid_gid(uid_t uid, gid_t gid, pid_t pid)
×
4773
{
4774
        return cgroup_change_cgroup_uid_gid_flags(uid, gid, pid, 0);
×
4775
}
4776

4777
/**
4778
 * Changes the cgroup of a program based on the path provided.  In this case,
4779
 * the user must already know into which cgroup the task should be placed and
4780
 * no rules will be parsed.
4781
 *
4782
 *  returns 0 on success.
4783
 */
4784
int cgroup_change_cgroup_path(const char *dest, pid_t pid, const char *const controllers[])
134✔
4785
{
4786
        struct dirent *task_dir = NULL;
134✔
4787
        char path[FILENAME_MAX];
4788
        struct cgroup cgrp;
4789
        int nr, ret;
4790
        pid_t tid;
4791
        DIR *dir;
4792

4793
        if (!cgroup_initialized) {
134✔
4794
                cgroup_warn("libcgroup is not initialized\n");
×
4795
                return ECGROUPNOTINITIALIZED;
×
4796
        }
4797
        memset(&cgrp, 0, sizeof(struct cgroup));
134✔
4798

4799

4800
        if (is_cgroup_mode_unified() && !controllers) {
134✔
4801
                /*
4802
                 * Do not require the user to pass in an array of controller strings on
4803
                 * cgroup v2 systems.  The hierarchy will be the same regardless of
4804
                 * whether controllers are provided or not.
4805
                 */
4806
                strncpy(cgrp.name, dest, FILENAME_MAX);
×
4807
                cgrp.name[FILENAME_MAX-1] = '\0';
×
4808
        } else {
4809
                if (!controllers)
134✔
4810
                        return ECGINVAL;
×
4811

4812
                ret = cg_prepare_cgroup(&cgrp, pid, dest, controllers);
134✔
4813
                if (ret)
134✔
4814
                        return ret;
×
4815
        }
4816

4817
        /* Add process to cgroup */
4818
        ret = cgroup_attach_task_pid(&cgrp, pid);
134✔
4819
        if (ret) {
134✔
4820
                cgroup_warn("cgroup_attach_task_pid failed: %d\n", ret);
4✔
4821
                goto finished;
4✔
4822
        }
4823

4824
        /* Add all threads to cgroup */
4825
        snprintf(path, FILENAME_MAX, "/proc/%d/task/", pid);
130✔
4826
        dir = opendir(path);
130✔
4827
        if (!dir) {
130✔
4828
                last_errno = errno;
×
4829
                ret = ECGOTHER;
×
4830
                goto finished;
×
4831
        }
4832

4833
        while ((task_dir = readdir(dir)) != NULL) {
526✔
4834
                nr = sscanf(task_dir->d_name, "%i", &tid);
396✔
4835
                if (nr < 1)
396✔
4836
                        continue;
260✔
4837

4838
                if (tid == pid)
136✔
4839
                        continue;
130✔
4840

4841
                ret = cgroup_attach_task_pid(&cgrp, tid);
6✔
4842
                if (ret) {
6✔
4843
                        cgroup_warn("cgroup_attach_task_pid failed: %d\n", ret);
×
4844
                        break;
×
4845
                }
4846
        }
4847

4848
        closedir(dir);
130✔
4849

4850
finished:
134✔
4851
        cgroup_free_controllers(&cgrp);
134✔
4852

4853
        return ret;
134✔
4854
}
4855

4856
/**
4857
 * Changes the cgroup of all running PIDs based on the rules in the config file.
4858
 * If a rules exists for a PID, then the PID is placed in the correct group.
4859
 *
4860
 * This function may be called after creating new control groups to move
4861
 * running PIDs into the newly created control groups.
4862
 *        @return 0 on success, < 0 on error
4863
 */
4864
int cgroup_change_all_cgroups(void)
5✔
4865
{
4866
        struct dirent *pid_dir = NULL;
5✔
4867
        char *path = "/proc/";
5✔
4868
        DIR *dir;
4869

4870
        dir = opendir(path);
5✔
4871
        if (!dir)
5✔
4872
                return -ECGOTHER;
×
4873

4874
        while ((pid_dir = readdir(dir)) != NULL) {
1,410✔
4875
                int err, pid;
4876
                uid_t euid;
4877
                gid_t egid;
4878
                char *procname = NULL;
1,405✔
4879

4880
                err = sscanf(pid_dir->d_name, "%i", &pid);
1,405✔
4881
                if (err < 1)
1,405✔
4882
                        continue;
320✔
4883

4884
                err = cgroup_get_uid_gid_from_procfs(pid, &euid, &egid);
1,085✔
4885
                if (err)
1,085✔
4886
                        continue;
×
4887

4888
                err = cgroup_get_procname_from_procfs(pid, &procname);
1,085✔
4889
                if (err)
1,085✔
4890
                        continue;
×
4891

4892
                err = cgroup_change_cgroup_flags(euid, egid, procname, pid, CGFLAG_USECACHE);
1,085✔
4893
                if (err)
1,085✔
4894
                        cgroup_dbg("cgroup change pid %i failed\n", pid);
×
4895

4896
                free(procname);
1,085✔
4897
        }
4898

4899
        closedir(dir);
5✔
4900
        return 0;
5✔
4901
}
4902

4903
/**
4904
 * Print the cached rules table.  This function should be called only after
4905
 * first calling cgroup_parse_config(), but it will work with an empty rule
4906
 * list.
4907
 *        @param fp The file stream to print to
4908
 */
4909
void cgroup_print_rules_config(FILE *fp)
5✔
4910
{
4911
        /* Iterator */
4912
        struct cgroup_rule *itr = NULL;
5✔
4913

4914
        /* Loop variable */
4915
        int i = 0;
5✔
4916

4917
        pthread_rwlock_rdlock(&rl_lock);
5✔
4918

4919
        if (!(rl.head)) {
5✔
4920
                fprintf(fp, "The rules table is empty.\n\n");
×
4921
                pthread_rwlock_unlock(&rl_lock);
×
4922
                return;
×
4923
        }
4924

4925
        itr = rl.head;
5✔
4926
        while (itr) {
10✔
4927
                fprintf(fp, "Rule: %s", itr->username);
5✔
4928
                if (itr->procname)
5✔
4929
                        fprintf(fp, ":%s", itr->procname);
5✔
4930
                fprintf(fp, "\n");
5✔
4931

4932
                if (itr->uid == CGRULE_WILD)
5✔
4933
                        fprintf(fp, "  UID: any\n");
5✔
4934
                else if (itr->uid == CGRULE_INVALID)
×
4935
                        fprintf(fp, "  UID: N/A\n");
×
4936
                else
4937
                        fprintf(fp, "  UID: %d\n", itr->uid);
×
4938

4939
                if (itr->gid == CGRULE_WILD)
5✔
4940
                        fprintf(fp, "  GID: any\n");
5✔
4941
                else if (itr->gid == CGRULE_INVALID)
×
4942
                        fprintf(fp, "  GID: N/A\n");
×
4943
                else
4944
                        fprintf(fp, "  GID: %d\n", itr->gid);
×
4945

4946
                fprintf(fp, "  DEST: %s\n", itr->destination);
5✔
4947

4948
                fprintf(fp, "  CONTROLLERS:\n");
5✔
4949
                for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
85✔
4950
                        if (itr->controllers[i])
80✔
4951
                                fprintf(fp, "    %s\n", itr->controllers[i]);
5✔
4952
                }
4953
                fprintf(fp, "  OPTIONS:\n");
5✔
4954
                if (itr->is_ignore)
5✔
4955
                        fprintf(fp, "    IS_IGNORE: True\n");
×
4956
                else
4957
                        fprintf(fp, "    IS_IGNORE: False\n");
5✔
4958
                fprintf(fp, "\n");
5✔
4959
                itr = itr->next;
5✔
4960
        }
4961
        pthread_rwlock_unlock(&rl_lock);
5✔
4962
}
4963

4964
/**
4965
 * Reloads the rules list, using the given configuration file.
4966
 * This function is probably NOT thread safe (calls cgroup_parse_rules()).
4967
 *        @return 0 on success, > 0 on failure
4968
 */
4969
int cgroup_reload_cached_rules(void)
×
4970
{
4971
        /* Return codes */
4972
        int ret = 0;
×
4973

4974
        cgroup_dbg("Reloading cached rules from %s.\n", CGRULES_CONF_FILE);
×
4975
        ret = cgroup_parse_rules(true, CGRULE_INVALID, CGRULE_INVALID, NULL);
×
4976
        if (ret) {
×
4977
                cgroup_warn("error parsing configuration file '%s': %d\n", CGRULES_CONF_FILE, ret);
×
4978
                ret = ECGRULESPARSEFAIL;
×
4979
                goto finished;
×
4980
        }
4981

4982
        #ifdef CGROUP_DEBUG
4983
        cgroup_print_rules_config(stdout);
4984
        #endif
4985
finished:
×
4986
        return ret;
×
4987
}
4988

4989
/**
4990
 * Initializes the rules cache.
4991
 *        @return 0 on success, > 0 on error
4992
 */
4993
int cgroup_init_rules_cache(void)
5✔
4994
{
4995
        /* Return codes */
4996
        int ret = 0;
5✔
4997

4998
        /* Attempt to read the configuration file and cache the rules. */
4999
        ret = cgroup_parse_rules(true, CGRULE_INVALID, CGRULE_INVALID, NULL);
5✔
5000
        if (ret)
5✔
5001
                cgroup_dbg("Could not initialize rule cache, error was: %d\n", ret);
×
5002

5003
        return ret;
5✔
5004
}
5005

5006
/**
5007
 * cgroup_get_current_controller_path
5008
 * @pid: pid of the current process for which the path is to be determined
5009
 * @controller: name of the controller for which to determine current path
5010
 * @current_path: a pointer that is filled with the value of the current
5011
 *                path as seen in /proc/<pid>/cgroup
5012
 */
5013
int cgroup_get_current_controller_path(pid_t pid, const char *controller, char **current_path)
13✔
5014
{
5015
        enum cg_version_t version;
5016
        enum cg_setup_mode_t mode;
5017
        FILE *pid_cgrp_fd = NULL;
13✔
5018
        bool unified = false;
13✔
5019
        char *path = NULL;
13✔
5020
        int ret;
5021

5022
        if (!cgroup_initialized) {
13✔
5023
                cgroup_warn("libcgroup is not initialized\n");
×
5024
                return ECGROUPNOTINITIALIZED;
×
5025
        }
5026

5027
        if (!current_path)
13✔
5028
                return ECGOTHER;
×
5029

5030
        mode = cgroup_setup_mode();
13✔
5031
        if (mode == CGROUP_MODE_LEGACY && !controller)
13✔
5032
                return ECGOTHER;
×
5033

5034
        /*
5035
         * both unified/hybrid can have the controller mounted as
5036
         * cgroup v2 version.
5037
         */
5038
        if  (!controller) {
13✔
5039
                unified = true;
4✔
5040
        } else {
5041
                ret = cgroup_get_controller_version(controller, &version);
9✔
5042
                if (ret) {
9✔
5043
                        cgroup_warn("Failed to get version of the controller: %s\n", controller);
3✔
5044
                        ret = ECGINVAL;
3✔
5045
                        goto cleanup_path;
3✔
5046
                }
5047
                unified = (version == CGROUP_V2);
6✔
5048
        }
5049

5050
        ret = asprintf(&path, "/proc/%d/cgroup", pid);
10✔
5051
        if (ret <= 0) {
10✔
5052
                cgroup_warn("cannot allocate memory (/proc/pid/cgroup) ret %d\n", ret);
×
5053
                return ret;
×
5054
        }
5055

5056
        ret = ECGROUPNOTEXIST;
10✔
5057
        pid_cgrp_fd = fopen(path, "re");
10✔
5058
        if (!pid_cgrp_fd)
10✔
5059
                goto cleanup_path;
×
5060

5061
        /*
5062
         * Why do we grab the cg_mount_table_lock?, the reason is that the
5063
         * cgroup of a pid can change via the cgroup_attach_task_pid() call.
5064
         * To make sure, we return consistent and safe results, we acquire the
5065
         * lock upfront. We can optimize by acquiring and releasing
5066
         * the lock in the while loop, but that will be more expensive.
5067
         */
5068
        pthread_rwlock_rdlock(&cg_mount_table_lock);
10✔
5069
        while (!feof(pid_cgrp_fd)) {
78✔
5070
                char controllers[FILENAME_MAX];
5071
                char cgrp_path[FILENAME_MAX];
5072
                char *savedptr;
5073
                char *token;
5074
                int num;
5075

5076
                /*
5077
                 * with unified mode, the /proc/pid/cgroup the output is
5078
                 * similar to that of cgroup legacy and hybrid modes:
5079
                 * hierarchy-ID:controller-list:cgroup-path
5080
                 *
5081
                 * the difference is that in cgroup v2:
5082
                 * - hierarchy-ID is always 0 (one hierarchy allowed)
5083
                 * - controller-list is empty
5084
                 */
5085
                if (mode == CGROUP_MODE_UNIFIED || unified) {
78✔
5086
                        ret = fscanf(pid_cgrp_fd, "%d::%4096s\n", &num, cgrp_path);
43✔
5087
                        if (ret != 2) {
43✔
5088
                                /*
5089
                                 * we are interested only in unified format
5090
                                 * line, skip this line.
5091
                                 */
5092
                                if (unified) {
37✔
5093
                                        ret = fscanf(pid_cgrp_fd, "%*[^\n]\n");
37✔
5094
                                        if (ret == 0)
37✔
5095
                                                continue;
37✔
5096

5097
                                        if (ret == EOF) {
×
5098
                                                last_errno = errno;
×
5099
                                                ret = ECGEOF;
×
5100
                                                goto done;
10✔
5101
                                        }
5102
                                }
5103

5104
                                cgroup_warn("read failed for pid_cgroup_fd ret %d\n", ret);
×
5105
                                last_errno = errno;
×
5106
                                ret = ECGOTHER;
×
5107
                                goto done;
×
5108
                        }
5109

5110
                        /* check if the controller is enabled in cgroup v2 */
5111
                        if (controller) {
6✔
5112
                                ret = cgroupv2_controller_enabled(cgrp_path, controller);
2✔
5113
                                if (ret)
2✔
5114
                                        goto done;
1✔
5115
                        }
5116

5117
                        *current_path = strdup(cgrp_path);
5✔
5118
                        if (!*current_path) {
5✔
5119
                                last_errno = errno;
×
5120
                                ret = ECGOTHER;
×
5121
                                goto done;
×
5122
                        }
5123
                        ret = 0;
5✔
5124
                        goto done;
5✔
5125
                }
5126

5127
                /*
5128
                 * 4096 == FILENAME_MAX, keeping the coverity happy with precision
5129
                 * for the cgrp_path.
5130
                 */
5131
                ret = fscanf(pid_cgrp_fd, "%d:%[^:]:%4096s\n", &num, controllers, cgrp_path);
35✔
5132
                /*
5133
                 * Magic numbers like "3" seem to be integrating into my daily
5134
                 * life, I need some magic to help make them disappear :)
5135
                 */
5136
                if (ret != 3) {
35✔
5137
                        cgroup_warn("read failed for pid_cgrp_fd ret %d\n", ret);
×
5138
                        last_errno = errno;
×
5139
                        ret = ECGOTHER;
×
5140
                        goto done;
×
5141
                }
5142

5143
                token = strtok_r(controllers, ",", &savedptr);
35✔
5144
                while (token) {
69✔
5145
                        if (strncmp(controller, token, strlen(controller) + 1) == 0) {
38✔
5146
                                *current_path = strdup(cgrp_path);
4✔
5147
                                if (!*current_path) {
4✔
5148
                                        last_errno = errno;
×
5149
                                        ret = ECGOTHER;
×
5150
                                        goto done;
×
5151
                                }
5152
                                ret = 0;
4✔
5153
                                goto done;
4✔
5154
                        }
5155
                        token = strtok_r(NULL, ",", &savedptr);
34✔
5156
                }
5157
        }
5158

5159
done:
×
5160
        pthread_rwlock_unlock(&cg_mount_table_lock);
10✔
5161
        fclose(pid_cgrp_fd);
10✔
5162
cleanup_path:
13✔
5163
        free(path);
13✔
5164

5165
        return ret;
13✔
5166
}
5167

5168
const char *cgroup_strerror(int code)
51✔
5169
{
5170
        int idx = code % ECGROUPNOTCOMPILED;
51✔
5171

5172
        if (code == ECGOTHER) {
51✔
5173
#ifdef STRERROR_R_CHAR_P
5174
                return strerror_r(cgroup_get_last_errno(), errtext, MAXLEN);
34✔
5175
#else
5176
                return strerror_r(cgroup_get_last_errno(), errtext, sizeof(errtext)) ?
5177
                        "unknown error" : errtext;
5178
#endif
5179
        }
5180
        if (idx >= ARRAY_SIZE(cgroup_strerror_codes))
17✔
5181
                return "Invalid error code";
×
5182

5183
        return cgroup_strerror_codes[idx];
17✔
5184
}
5185

5186
/**
5187
 * Return last errno, which caused ECGOTHER error.
5188
 */
5189
int cgroup_get_last_errno(void)
34✔
5190
{
5191
        return last_errno;
34✔
5192
}
5193

5194
static int cg_walk_node(FTS *fts, FTSENT *ent, const int depth, struct cgroup_file_info *info,
11,020✔
5195
                        int dir)
5196
{
5197
        int ret = 0;
11,020✔
5198

5199
        if (!cgroup_initialized)
11,020✔
5200
                return ECGROUPNOTINITIALIZED;
×
5201

5202
        cgroup_dbg("seeing file %s\n", ent->fts_path);
11,020✔
5203

5204
        info->path = ent->fts_name;
11,020✔
5205
        info->parent = ent->fts_parent->fts_name;
11,020✔
5206
        info->full_path = ent->fts_path;
11,020✔
5207
        info->depth = ent->fts_level;
11,020✔
5208
        info->type = CGROUP_FILE_TYPE_OTHER;
11,020✔
5209

5210
        if (depth && (info->depth > depth))
11,020✔
5211
                return 0;
×
5212

5213
        switch (ent->fts_info) {
11,020✔
5214
        case FTS_DNR:
×
5215
        case FTS_ERR:
5216
                errno = ent->fts_errno;
×
5217
                break;
×
5218
        case FTS_D:
377✔
5219
                if (dir & CGROUP_WALK_TYPE_PRE_DIR)
377✔
5220
                        info->type = CGROUP_FILE_TYPE_DIR;
271✔
5221
                break;
377✔
5222
        case FTS_DC:
377✔
5223
        case FTS_NSOK:
5224
        case FTS_NS:
5225
        case FTS_DP:
5226
                if (dir & CGROUP_WALK_TYPE_POST_DIR)
377✔
5227
                        info->type = CGROUP_FILE_TYPE_DIR;
168✔
5228
                break;
377✔
5229
        case FTS_F:
10,266✔
5230
                info->type = CGROUP_FILE_TYPE_FILE;
10,266✔
5231
                break;
10,266✔
5232
        case FTS_DEFAULT:
×
5233
                break;
×
5234
        }
5235
        return ret;
11,020✔
5236
}
5237

5238
int cgroup_walk_tree_next(int depth, void **handle, struct cgroup_file_info *info, int base_level)
11,020✔
5239
{
5240
        struct cgroup_tree_handle *entry;
5241
        FTSENT *ent;
5242
        int ret = 0;
11,020✔
5243

5244
        if (!cgroup_initialized)
11,020✔
5245
                return ECGROUPNOTINITIALIZED;
×
5246

5247
        if (!handle)
11,020✔
5248
                return ECGINVAL;
×
5249

5250
        entry = (struct cgroup_tree_handle *) *handle;
11,020✔
5251

5252
        ent = fts_read(entry->fts);
11,020✔
5253
        if (!ent)
11,020✔
5254
                return ECGEOF;
104✔
5255
        if (!base_level && depth)
10,916✔
5256
                base_level = ent->fts_level + depth;
×
5257

5258
        ret = cg_walk_node(entry->fts, ent, base_level, info, entry->flags);
10,916✔
5259

5260
        *handle = entry;
10,916✔
5261
        return ret;
10,916✔
5262
}
5263

5264
int cgroup_walk_tree_end(void **handle)
104✔
5265
{
5266
        struct cgroup_tree_handle *entry;
5267

5268
        if (!cgroup_initialized)
104✔
5269
                return ECGROUPNOTINITIALIZED;
×
5270

5271
        if (!handle)
104✔
5272
                return ECGINVAL;
×
5273

5274
        entry = (struct cgroup_tree_handle *) *handle;
104✔
5275

5276
        fts_close(entry->fts);
104✔
5277
        free(entry);
104✔
5278
        *handle = NULL;
104✔
5279

5280
        return 0;
104✔
5281
}
5282

5283
/* TODO: Need to decide a better place to put this function. */
5284
int cgroup_walk_tree_begin(const char *controller, const char *base_path, int depth, void **handle,
104✔
5285
                           struct cgroup_file_info *info, int *base_level)
5286
{
5287
        struct cgroup_tree_handle *entry;
5288
        char full_path[FILENAME_MAX];
5289
        char *cg_path[2];
5290
        FTSENT *ent;
5291
        int ret = 0;
104✔
5292

5293
        if (!cgroup_initialized)
104✔
5294
                return ECGROUPNOTINITIALIZED;
×
5295

5296
        if (!handle)
104✔
5297
                return ECGINVAL;
×
5298

5299
        cgroup_dbg("path is %s\n", base_path);
104✔
5300

5301
        if (!cg_build_path(base_path, full_path, controller))
104✔
5302
                return ECGOTHER;
×
5303

5304
        entry = calloc(1, sizeof(struct cgroup_tree_handle));
104✔
5305

5306
        if (!entry) {
104✔
5307
                last_errno = errno;
×
5308
                *handle = NULL;
×
5309
                return ECGOTHER;
×
5310
        }
5311

5312
        entry->flags |= CGROUP_WALK_TYPE_PRE_DIR;
104✔
5313

5314
        *base_level = 0;
104✔
5315
        cg_path[0] = full_path;
104✔
5316
        cg_path[1] = NULL;
104✔
5317

5318
        entry->fts = fts_open(cg_path, FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT, NULL);
104✔
5319
        if (entry->fts == NULL) {
104✔
5320
                free(entry);
×
5321
                last_errno = errno;
×
5322
                *handle = NULL;
×
5323
                return ECGOTHER;
×
5324
        }
5325
        ent = fts_read(entry->fts);
104✔
5326
        if (!ent) {
104✔
5327
                cgroup_warn("fts_read failed\n");
×
5328
                fts_close(entry->fts);
×
5329
                free(entry);
×
5330
                *handle = NULL;
×
5331
                return ECGINVAL;
×
5332
        }
5333
        if (!*base_level && depth)
104✔
5334
                *base_level = ent->fts_level + depth;
×
5335

5336
        ret = cg_walk_node(entry->fts, ent, *base_level, info, entry->flags);
104✔
5337
        if (ret != 0) {
104✔
5338
                fts_close(entry->fts);
×
5339
                free(entry);
×
5340
                *handle = NULL;
×
5341
        } else {
5342
                *handle = entry;
104✔
5343
        }
5344
        return ret;
104✔
5345
}
5346

5347
int cgroup_walk_tree_set_flags(void **handle, int flags)
62✔
5348
{
5349
        struct cgroup_tree_handle *entry;
5350

5351
        if (!cgroup_initialized)
62✔
5352
                return ECGROUPNOTINITIALIZED;
×
5353

5354
        if (!handle)
62✔
5355
                return ECGINVAL;
×
5356

5357
        if ((flags & CGROUP_WALK_TYPE_PRE_DIR) &&
62✔
5358
            (flags & CGROUP_WALK_TYPE_POST_DIR))
×
5359
                return ECGINVAL;
×
5360

5361
        entry = (struct cgroup_tree_handle *) *handle;
62✔
5362
        entry->flags = flags;
62✔
5363

5364
        *handle = entry;
62✔
5365
        return 0;
62✔
5366
}
5367

5368
/*
5369
 * This parses a stat line which is in the form of (name value) pair
5370
 * separated by a space.
5371
 */
5372
static int cg_read_stat(FILE *fp, struct cgroup_stat *cgrp_stat)
×
5373
{
5374
        char *saveptr = NULL;
×
5375
        ssize_t read_bytes;
5376
        char *line = NULL;
×
5377
        size_t len = 0;
×
5378
        char *token;
5379
        int ret = 0;
×
5380

5381
        read_bytes = getline(&line, &len, fp);
×
5382
        if (read_bytes == -1) {
×
5383
                ret = ECGEOF;
×
5384
                goto out_free;
×
5385
        }
5386

5387
        token = strtok_r(line, " ", &saveptr);
×
5388
        if (!token) {
×
5389
                ret = ECGINVAL;
×
5390
                goto out_free;
×
5391
        }
5392
        strncpy(cgrp_stat->name, token, FILENAME_MAX - 1);
×
5393

5394
        token = strtok_r(NULL, " ", &saveptr);
×
5395
        if (!token) {
×
5396
                ret = ECGINVAL;
×
5397
                goto out_free;
×
5398
        }
5399
        strncpy(cgrp_stat->value, token, CG_VALUE_MAX - 1);
×
5400

5401
out_free:
×
5402
        free(line);
×
5403

5404
        return ret;
×
5405
}
5406

5407
int cgroup_read_value_end(void **handle)
325✔
5408
{
5409
        FILE *fp;
5410

5411
        if (!cgroup_initialized)
325✔
5412
                return ECGROUPNOTINITIALIZED;
×
5413

5414
        if (!handle)
325✔
5415
                return ECGINVAL;
×
5416

5417
        fp = (FILE *)*handle;
325✔
5418
        fclose(fp);
325✔
5419
        *handle = NULL;
325✔
5420

5421
        return 0;
325✔
5422
}
5423

5424
int cgroup_read_value_next(void **handle, char *buffer, int max)
465✔
5425
{
5426
        char *ret_c;
5427
        int ret = 0;
465✔
5428
        FILE *fp;
5429

5430
        if (!cgroup_initialized)
465✔
5431
                return ECGROUPNOTINITIALIZED;
×
5432

5433
        if (!buffer || !handle)
465✔
5434
                return ECGINVAL;
×
5435

5436
        fp = (FILE *)*handle;
465✔
5437
        ret_c = fgets(buffer, max, fp);
465✔
5438
        if (ret_c == NULL)
465✔
5439
                ret = ECGEOF;
308✔
5440

5441
        return ret;
465✔
5442
}
5443

5444
int cgroup_read_value_begin(const char * const controller, const char *path,
329✔
5445
                            const char * const name, void **handle, char *buffer, int max)
5446
{
5447
        char stat_file[FILENAME_MAX + sizeof(name)];
5448
        char stat_path[FILENAME_MAX];
5449
        char *ret_c = NULL;
329✔
5450
        int ret = 0;
329✔
5451
        FILE *fp;
5452

5453
        if (!cgroup_initialized)
329✔
5454
                return ECGROUPNOTINITIALIZED;
×
5455

5456
        if (!buffer || !handle)
329✔
5457
                return ECGINVAL;
×
5458

5459
        if (!cg_build_path(path, stat_path, controller))
329✔
5460
                return ECGOTHER;
×
5461

5462
        snprintf(stat_file, sizeof(stat_file), "%s/%s", stat_path, name);
329✔
5463
        fp = fopen(stat_file, "re");
329✔
5464
        if (!fp) {
329✔
5465
                cgroup_warn("fopen failed\n");
4✔
5466
                last_errno = errno;
4✔
5467
                *handle = NULL;
4✔
5468
                return ECGOTHER;
4✔
5469
        }
5470

5471
        ret_c = fgets(buffer, max, fp);
325✔
5472
        if (ret_c == NULL)
325✔
5473
                ret = ECGEOF;
7✔
5474

5475
        *handle = fp;
325✔
5476

5477
        return ret;
325✔
5478
}
5479

5480
int cgroup_read_stats_end(void **handle)
×
5481
{
5482
        FILE *fp;
5483

5484
        if (!cgroup_initialized)
×
5485
                return ECGROUPNOTINITIALIZED;
×
5486

5487
        if (!handle)
×
5488
                return ECGINVAL;
×
5489

5490
        fp = (FILE *)*handle;
×
5491
        if (fp == NULL)
×
5492
                return ECGINVAL;
×
5493

5494
        fclose(fp);
×
5495

5496
        return 0;
×
5497
}
5498

5499
int cgroup_read_stats_next(void **handle, struct cgroup_stat *cgrp_stat)
×
5500
{
5501
        FILE *fp;
5502
        int ret = 0;
×
5503

5504
        if (!cgroup_initialized)
×
5505
                return ECGROUPNOTINITIALIZED;
×
5506

5507
        if (!handle || !cgrp_stat)
×
5508
                return ECGINVAL;
×
5509

5510
        fp = (FILE *)*handle;
×
5511
        ret = cg_read_stat(fp, cgrp_stat);
×
5512
        *handle = fp;
×
5513

5514
        return ret;
×
5515
}
5516

5517
/* TODO: Need to decide a better place to put this function. */
5518
int cgroup_read_stats_begin(const char *controller, const char *path, void **handle,
×
5519
                            struct cgroup_stat *cgrp_stat)
5520
{
5521
        char stat_file[FILENAME_MAX + sizeof(".stat")];
5522
        char stat_path[FILENAME_MAX];
5523
        int ret = 0;
×
5524
        FILE *fp;
5525

5526
        if (!cgroup_initialized)
×
5527
                return ECGROUPNOTINITIALIZED;
×
5528

5529
        if (!cgrp_stat || !handle)
×
5530
                return ECGINVAL;
×
5531

5532
        if (!cg_build_path(path, stat_path, controller))
×
5533
                return ECGOTHER;
×
5534

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

5537
        fp = fopen(stat_file, "re");
×
5538
        if (!fp) {
×
5539
                cgroup_warn("fopen failed\n");
×
5540
                return ECGINVAL;
×
5541
        }
5542

5543
        ret = cg_read_stat(fp, cgrp_stat);
×
5544
        *handle = fp;
×
5545

5546
        return ret;
×
5547
}
5548

5549
int cgroup_get_task_end(void **handle)
×
5550
{
5551
        if (!cgroup_initialized)
×
5552
                return ECGROUPNOTINITIALIZED;
×
5553

5554
        if (!*handle)
×
5555
                return ECGINVAL;
×
5556

5557
        fclose((FILE *) *handle);
×
5558
        *handle = NULL;
×
5559

5560
        return 0;
×
5561
}
5562

5563
int cgroup_get_task_next(void **handle, pid_t *pid)
×
5564
{
5565
        int ret;
5566

5567
        if (!cgroup_initialized)
×
5568
                return ECGROUPNOTINITIALIZED;
×
5569

5570
        if (!handle)
×
5571
                return ECGINVAL;
×
5572

5573
        ret = fscanf((FILE *) *handle, "%u", pid);
×
5574

5575
        if (ret != 1) {
×
5576
                if (ret == EOF)
×
5577
                        return ECGEOF;
×
5578
                last_errno = errno;
×
5579
                return ECGOTHER;
×
5580
        }
5581

5582
        return 0;
×
5583
}
5584

5585
int cgroup_get_task_begin(const char *cgrp, const char *controller, void **handle, pid_t *pid)
×
5586
{
5587
        char path[FILENAME_MAX];
5588
        char *fullpath = NULL;
×
5589
        int ret = 0;
×
5590

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

5594
        if (!cg_build_path(cgrp, path, controller))
×
5595
                return ECGOTHER;
×
5596

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

5599
        if (ret < 0) {
×
5600
                last_errno = errno;
×
5601
                return ECGOTHER;
×
5602
        }
5603

5604
        *handle = (void *) fopen(fullpath, "re");
×
5605
        free(fullpath);
×
5606

5607
        if (!*handle) {
×
5608
                last_errno = errno;
×
5609
                return ECGOTHER;
×
5610
        }
5611
        ret = cgroup_get_task_next(handle, pid);
×
5612

5613
        return ret;
×
5614
}
5615

5616
int cgroup_get_controller_end(void **handle)
27✔
5617
{
5618
        int *pos = (int *) *handle;
27✔
5619

5620
        if (!cgroup_initialized)
27✔
5621
                return ECGROUPNOTINITIALIZED;
×
5622

5623
        if (!pos)
27✔
5624
                return ECGINVAL;
×
5625

5626
        free(pos);
27✔
5627
        *handle = NULL;
27✔
5628

5629
        return 0;
27✔
5630
}
5631

5632
int cgroup_get_controller_next(void **handle, struct cgroup_mount_point *info)
306✔
5633
{
5634
        int *pos = (int *) *handle;
306✔
5635
        int ret = 0;
306✔
5636

5637
        if (!cgroup_initialized)
306✔
5638
                return ECGROUPNOTINITIALIZED;
×
5639

5640
        if (!pos)
306✔
5641
                return ECGINVAL;
×
5642

5643
        if (!info)
306✔
5644
                return ECGINVAL;
×
5645

5646
        pthread_rwlock_rdlock(&cg_mount_table_lock);
306✔
5647

5648
        if (cg_mount_table[*pos].name[0] == '\0') {
306✔
5649
                ret = ECGEOF;
17✔
5650
                goto out_unlock;
17✔
5651
        }
5652

5653
        if (strncmp(cg_mount_table[*pos].name, CGRP_FILE_PREFIX, CONTROL_NAMELEN_MAX) == 0)
289✔
5654
                /*
5655
                 * For now, hide the "cgroup" pseudo-controller from the user.  This may be
5656
                 * worth revisiting in the future.
5657
                 */
5658
                (*pos)++;
13✔
5659

5660
        if (cg_mount_table[*pos].name[0] == '\0') {
289✔
5661
                ret = ECGEOF;
4✔
5662
                goto out_unlock;
4✔
5663
        }
5664

5665
        strncpy(info->name, cg_mount_table[*pos].name, FILENAME_MAX - 1);
285✔
5666
        info->name[FILENAME_MAX - 1] = '\0';
285✔
5667

5668
        strncpy(info->path, cg_mount_table[*pos].mount.path, FILENAME_MAX - 1);
285✔
5669
        info->path[FILENAME_MAX - 1] = '\0';
285✔
5670

5671
        (*pos)++;
285✔
5672
        *handle = pos;
285✔
5673

5674
out_unlock:
306✔
5675
        pthread_rwlock_unlock(&cg_mount_table_lock);
306✔
5676

5677
        return ret;
306✔
5678
}
5679

5680
int cgroup_get_controller_begin(void **handle, struct cgroup_mount_point *info)
27✔
5681
{
5682
        int *pos;
5683

5684
        if (!cgroup_initialized)
27✔
5685
                return ECGROUPNOTINITIALIZED;
×
5686

5687
        if (!info)
27✔
5688
                return ECGINVAL;
×
5689

5690
        pos = malloc(sizeof(int));
27✔
5691

5692
        if (!pos) {
27✔
5693
                last_errno = errno;
×
5694
                return ECGOTHER;
×
5695
        }
5696

5697
        *pos = 0;
27✔
5698

5699
        *handle = pos;
27✔
5700

5701
        return cgroup_get_controller_next(handle, info);
27✔
5702
}
5703

5704
/**
5705
 * Get process data (euid and egid) from /proc/<pid>/status file.
5706
 * @param pid: The process id
5707
 * @param euid: The uid of param pid
5708
 * @param egid: The gid of param pid
5709
 * @return 0 on success, > 0 on error.
5710
 */
5711
int cgroup_get_uid_gid_from_procfs(pid_t pid, uid_t *euid, gid_t *egid)
1,415✔
5712
{
5713
        char path[FILENAME_MAX];
5714
        uid_t ruid, suid, fsuid;
5715
        gid_t rgid, sgid, fsgid;
5716
        bool found_euid = false;
1,415✔
5717
        bool found_egid = false;
1,415✔
5718
        char buf[4092];
5719
        FILE *f;
5720

5721
        sprintf(path, "/proc/%d/status", pid);
1,415✔
5722
        f = fopen(path, "re");
1,415✔
5723
        if (!f)
1,415✔
5724
                return ECGROUPNOTEXIST;
×
5725

5726
        while (fgets(buf, sizeof(buf), f)) {
14,149✔
5727
                if (!strncmp(buf, "Uid:", 4)) {
14,149✔
5728
                        if (sscanf((buf + strlen("Uid:") + 1), "%d%d%d%d",
1,415✔
5729
                                    &ruid, euid, &suid, &fsuid) != 4)
5730
                                break;
×
5731
                        cgroup_dbg("Scanned proc values are %d %d %d %d\n",
1,415✔
5732
                                   ruid, *euid, suid, fsuid);
5733
                        found_euid = true;
1,415✔
5734
                } else if (!strncmp(buf, "Gid:", 4)) {
12,734✔
5735
                        if (sscanf((buf + strlen("Gid:") + 1), "%d%d%d%d",
1,415✔
5736
                                   &rgid, egid, &sgid, &fsgid) != 4)
5737
                                break;
×
5738
                        cgroup_dbg("Scanned proc values are %d %d %d %d\n",
1,415✔
5739
                                   rgid, *egid, sgid, fsgid);
5740
                        found_egid = true;
1,415✔
5741
                }
5742
                if (found_euid && found_egid)
14,149✔
5743
                        break;
1,415✔
5744
        }
5745
        fclose(f);
1,415✔
5746

5747
        if (!found_euid || !found_egid) {
1,415✔
5748
                /*
5749
                 * This method doesn't match the file format of
5750
                 * /proc/<pid>/status. The format has been changed and we
5751
                 * should catch up the change.
5752
                 */
5753
                cgroup_warn("invalid file format of /proc/%d/status\n", pid);
×
5754
                return ECGFAIL;
×
5755
        }
5756
        return 0;
1,415✔
5757
}
5758

5759
/**
5760
 * Given a pid, this function will return the controllers and cgroups that
5761
 * the pid is a member of. The caller is expected to allocate the
5762
 * controller_list[] and cgroup_list[] arrays as well as null each entry in
5763
 * the arrays.  This function will allocate the necessary memory for each
5764
 * string within the arrays.
5765
 *
5766
 *        @param pid The process id
5767
 *        @param cgrp_list[] An array of char pointers to hold the cgroups
5768
 *        @param controller_list[] An array of char pointers to hold the list
5769
 *               of controllers
5770
 *        @param list_len The size of the arrays
5771
 */
5772
STATIC int cg_get_cgroups_from_proc_cgroups(pid_t pid, char *cgrp_list[],
36✔
5773
                                            char *controller_list[], int list_len)
5774
{
5775
        char path[FILENAME_MAX];
5776
        char *stok_buff = NULL;
36✔
5777
        size_t buff_len;
5778
        char buf[4092];
5779
        int ret = 0;
36✔
5780
        int idx = 0;
36✔
5781
        FILE *f;
5782

5783
#ifdef UNIT_TEST
5784
        sprintf(path, "%s", TEST_PROC_PID_CGROUP_FILE);
36✔
5785
#else
5786
        sprintf(path, "/proc/%d/cgroup", pid);
×
5787
#endif
5788
        f = fopen(path, "re");
36✔
5789
        if (!f)
36✔
5790
                return ECGROUPNOTEXIST;
×
5791

5792
        while (fgets(buf, sizeof(buf), f)) {
142✔
5793
                /*
5794
                 * Each line in /proc/{pid}/cgroup is like the following:
5795
                 *
5796
                 * {cg#}:{controller}:{cgname}
5797
                 *
5798
                 * e.g.
5799
                 * 7:devices:/user.slice
5800
                 */
5801

5802
                /* Read in the cgroup number.  We don't care about it */
5803
                stok_buff = strtok(buf, ":");
108✔
5804
                /* Read in the controller name */
5805
                stok_buff = strtok(NULL, ":");
108✔
5806

5807
                /*
5808
                 * After this point, we have allocated memory.  If we return
5809
                 * an error code after this, it's up to us to free the memory
5810
                 * we allocated
5811
                 */
5812
                controller_list[idx] = strndup(stok_buff, strlen(stok_buff) + 1);
108✔
5813

5814
                /* Read in the cgroup name */
5815
                stok_buff = strtok(NULL, ":");
108✔
5816

5817
                if (stok_buff == NULL) {
108✔
5818
                        /*
5819
                         * An empty controller is reported on some kernels.
5820
                         * It may look like this:
5821
                         * 0::/user.slice/user-1000.slice/session-1.scope
5822
                         *
5823
                         * Ignore this controller and move on.  Note that we
5824
                         * need to free the controller list entry we made.
5825
                         */
5826
                        free(controller_list[idx]);
8✔
5827
                        controller_list[idx] = NULL;
8✔
5828
                        continue;
8✔
5829
                }
5830

5831
                buff_len = strlen(stok_buff);
100✔
5832
                if (stok_buff[buff_len - 1] == '\n')
100✔
5833
                        buff_len--; /* Don't copy the trailing newline char */
76✔
5834

5835
                /* Read in the cgroup name */
5836
                if (buff_len > 1) {
100✔
5837
                        /* Strip off the leading '/' for every cgroup but the root cgroup */
5838
                        cgrp_list[idx] = malloc(buff_len);
68✔
5839
                        snprintf(cgrp_list[idx], buff_len, "%s", &stok_buff[1]);
68✔
5840
                } else {
5841
                        /* Retain the leading '/' since we're in the root cgroup */
5842
                        cgrp_list[idx] = strndup(stok_buff, buff_len);
32✔
5843
                }
5844

5845
                idx++;
100✔
5846
                if (idx >= list_len) {
100✔
5847
                        cgroup_warn("Maximum mount elements reached. Consider increasing ");
2✔
5848
                        cgroup_cont("MAX_MNT_ELEMENTS\n");
2✔
5849
                        break;
2✔
5850
                }
5851
        }
5852
        fclose(f);
36✔
5853
        return ret;
36✔
5854
}
5855

5856
/**
5857
 * Get process name from /proc/<pid>/status file.
5858
 * @param pid: The process id
5859
 * @param pname_status : The process name
5860
 * @return 0 on success, > 0 on error.
5861
 */
5862
static int cg_get_procname_from_proc_status(pid_t pid, char **procname_status)
1,415✔
5863
{
5864
        char path[FILENAME_MAX];
5865
        int ret = ECGFAIL;
1,415✔
5866
        char buf[4092];
5867
        FILE *f;
5868
        int len;
5869

5870
        sprintf(path, "/proc/%d/status", pid);
1,415✔
5871
        f = fopen(path, "re");
1,415✔
5872
        if (!f)
1,415✔
5873
                return ECGROUPNOTEXIST;
×
5874

5875
        while (fgets(buf, sizeof(buf), f)) {
1,415✔
5876
                if (!strncmp(buf, "Name:", 5)) {
1,415✔
5877
                        len = strlen(buf);
1,415✔
5878
                        if (buf[len - 1] == '\n')
1,415✔
5879
                                buf[len - 1] = '\0';
1,415✔
5880
                        *procname_status = strdup(buf + strlen("Name:") + 1);
1,415✔
5881
                        if (*procname_status == NULL) {
1,415✔
5882
                                last_errno = errno;
×
5883
                                ret = ECGOTHER;
×
5884
                                break;
×
5885
                        }
5886
                        ret = 0;
1,415✔
5887
                        break;
1,415✔
5888
                }
5889
        }
5890
        fclose(f);
1,415✔
5891
        return ret;
1,415✔
5892
}
5893

5894
/**
5895
 * Get process name from /proc/<pid>/cmdline file.
5896
 * This function is mainly for getting a script name (shell, perl, etc).
5897
 * A script name is written into the second or later argument of
5898
 * /proc/<pid>/cmdline. This function gets each argument and
5899
 * compares it to a process name taken from /proc/<pid>/status.
5900
 * @param pid: The process id
5901
 * @param pname_status : The process name taken from /proc/<pid>/status
5902
 * @param pname_cmdline: The process name taken from /proc/<pid>/cmdline
5903
 * @return 0 on success, > 0 on error.
5904
 */
5905
static int cg_get_procname_from_proc_cmdline(pid_t pid, const char *pname_status,
167✔
5906
                                             char **pname_cmdline)
5907
{
5908
        char pid_cwd_path[FILENAME_MAX];
5909
        char pid_cmd_path[FILENAME_MAX];
5910
        char buf_pname[FILENAME_MAX];
5911
        char buf_cwd[FILENAME_MAX];
5912
        int ret = ECGFAIL;
167✔
5913
        int len = 0;
167✔
5914
        int c = 0;
167✔
5915
        FILE *f;
5916

5917
        memset(buf_cwd, '\0', sizeof(buf_cwd));
167✔
5918
        sprintf(pid_cwd_path, "/proc/%d/cwd", pid);
167✔
5919

5920
        if (readlink(pid_cwd_path, buf_cwd, sizeof(buf_cwd)) < 0)
167✔
5921
                return ECGROUPNOTEXIST;
×
5922

5923
        /* readlink doesn't append a null */
5924
        buf_cwd[FILENAME_MAX - 1] = '\0';
167✔
5925

5926
        sprintf(pid_cmd_path, "/proc/%d/cmdline", pid);
167✔
5927
        f = fopen(pid_cmd_path, "re");
167✔
5928
        if (!f)
167✔
5929
                return ECGROUPNOTEXIST;
×
5930

5931
        while (c != EOF) {
2,026✔
5932
                c = fgetc(f);
2,025✔
5933
                if ((c != EOF) && (c != '\0') && (len < FILENAME_MAX - 1)) {
2,025✔
5934
                        buf_pname[len] = c;
1,841✔
5935
                        len++;
1,841✔
5936
                        continue;
1,841✔
5937
                }
5938
                buf_pname[len] = '\0';
184✔
5939

5940
                if (len == FILENAME_MAX - 1)
184✔
5941
                        while ((c != EOF) && (c != '\0'))
×
5942
                                c = fgetc(f);
×
5943

5944
                /*
5945
                 * The taken process name from /proc/<pid>/status is
5946
                 * shortened to 15 characters if it is over. So the name
5947
                 * should be compared by its length.
5948
                 */
5949
                if (strncmp(pname_status, basename(buf_pname), TASK_COMM_LEN - 1)) {
184✔
5950
                        len = 0;
18✔
5951
                        continue;
18✔
5952
                }
5953

5954
                if (buf_pname[0] == '/') {
166✔
5955
                        *pname_cmdline = strdup(buf_pname);
90✔
5956
                        if (*pname_cmdline == NULL) {
90✔
5957
                                last_errno = errno;
×
5958
                                ret = ECGOTHER;
×
5959
                                break;
×
5960
                        }
5961
                        ret = 0;
90✔
5962
                        break;
90✔
5963
                }
5964

5965
                strcat(buf_cwd, "/");
76✔
5966
                strcat(buf_cwd, buf_pname);
76✔
5967
                if (!realpath(buf_cwd, pid_cmd_path)) {
76✔
5968
                        last_errno = errno;
74✔
5969
                        ret = ECGOTHER;
74✔
5970
                        break;
74✔
5971
                }
5972

5973
                *pname_cmdline = strdup(pid_cmd_path);
2✔
5974
                if (*pname_cmdline == NULL) {
2✔
5975
                        last_errno = errno;
×
5976
                        ret = ECGOTHER;
×
5977
                        break;
×
5978
                }
5979
                ret = 0;
2✔
5980
                break;
2✔
5981
        }
5982
        fclose(f);
167✔
5983
        return ret;
167✔
5984
}
5985

5986
/**
5987
 * Get a process name from /proc file system.
5988
 * This function allocates memory for a process name, writes a process
5989
 * name onto it. So a caller should free the memory when unusing it.
5990
 * @param pid: The process id
5991
 * @param procname: The process name
5992
 * @return 0 on success, > 0 on error.
5993
 */
5994
int cgroup_get_procname_from_procfs(pid_t pid, char **procname)
1,415✔
5995
{
5996
        char path[FILENAME_MAX];
5997
        char buf[FILENAME_MAX];
5998
        char *pname_cmdline;
5999
        char *pname_status;
6000
        int ret;
6001

6002
        ret = cg_get_procname_from_proc_status(pid, &pname_status);
1,415✔
6003
        if (ret)
1,415✔
6004
                return ret;
×
6005

6006
        /* Get the full patch of process name from /proc/<pid>/exe. */
6007
        memset(buf, '\0', sizeof(buf));
1,415✔
6008
        snprintf(path, FILENAME_MAX, "/proc/%d/exe", pid);
1,415✔
6009
        if (readlink(path, buf, sizeof(buf)) < 0) {
1,415✔
6010
                /*
6011
                 * readlink() fails if a kernel thread, and a process name
6012
                 * is taken from /proc/<pid>/status.
6013
                 */
6014
                *procname = pname_status;
625✔
6015
                return 0;
625✔
6016
        }
6017
        /* readlink doesn't append a null */
6018
        buf[FILENAME_MAX - 1] = '\0';
790✔
6019

6020
        if (!strncmp(pname_status, basename(buf), TASK_COMM_LEN - 1)) {
790✔
6021
                /*
6022
                 * The taken process name from /proc/<pid>/status is
6023
                 * shortened to 15 characters if it is over. So the name
6024
                 * should be compared by its length.
6025
                 */
6026
                free(pname_status);
623✔
6027
                *procname = strdup(buf);
623✔
6028
                if (*procname == NULL) {
623✔
6029
                        last_errno = errno;
×
6030
                        return ECGOTHER;
×
6031
                }
6032
                return 0;
623✔
6033
        }
6034

6035
        /*
6036
         * The above strncmp() is not 0 if a shell script, because
6037
         * /proc/<pid>/exe links a shell command (/bin/bash etc.) and the
6038
         * pname_status represents a shell script name. Then the full path
6039
         * of a shell script is taken from /proc/<pid>/cmdline.
6040
         */
6041
        ret = cg_get_procname_from_proc_cmdline(pid, pname_status, &pname_cmdline);
167✔
6042
        if (!ret) {
167✔
6043
                *procname = pname_cmdline;
92✔
6044
                free(pname_status);
92✔
6045
                return 0;
92✔
6046
        }
6047

6048
        /*
6049
         * The above strncmp() is not 0 also if executing a symbolic link,
6050
         * /proc/pid/exe points to real executable name then. Return it as
6051
         * the last resort.
6052
         */
6053
        free(pname_status);
75✔
6054
        *procname = strdup(buf);
75✔
6055
        if (*procname == NULL) {
75✔
6056
                last_errno = errno;
×
6057
                return ECGOTHER;
×
6058
        }
6059

6060
        return 0;
75✔
6061
}
6062

6063
int cgroup_register_unchanged_process(pid_t pid, int flags)
4✔
6064
{
6065
        char buff[sizeof(CGRULE_SUCCESS_STORE_PID)];
6066
        struct sockaddr_un addr;
6067
        size_t ret_len;
6068
        int ret = 1;
4✔
6069
        int sk;
6070

6071
        sk = socket(PF_UNIX, SOCK_STREAM, 0);
4✔
6072
        if (sk < 0)
4✔
6073
                return 1;
×
6074

6075
        memset(&addr, 0, sizeof(addr));
4✔
6076
        addr.sun_family = AF_UNIX;
4✔
6077
        strcpy(addr.sun_path, CGRULE_CGRED_SOCKET_PATH);
4✔
6078

6079
        if (connect(sk, (struct sockaddr *)&addr,
4✔
6080
            sizeof(addr.sun_family) + strlen(CGRULE_CGRED_SOCKET_PATH)) < 0) {
6081
                /* If the daemon does not work, this function returns 0 as success. */
6082
                ret = 0;
4✔
6083
                goto close;
4✔
6084
        }
6085
        if (write(sk, &pid, sizeof(pid)) < 0)
×
6086
                goto close;
×
6087

6088
        if (write(sk, &flags, sizeof(flags)) < 0)
×
6089
                goto close;
×
6090

6091
        ret_len = read(sk, buff, sizeof(buff));
×
6092
        if (ret_len != sizeof(buff))
×
6093
                goto close;
×
6094

6095
        if (strncmp(buff, CGRULE_SUCCESS_STORE_PID, sizeof(buff)))
×
6096
                goto close;
×
6097

6098
        ret = 0;
×
6099
close:
4✔
6100
        close(sk);
4✔
6101

6102
        return ret;
4✔
6103
}
6104

6105
int cgroup_get_subsys_mount_point(const char *controller, char **mount_point)
×
6106
{
6107
        int ret = ECGROUPNOTEXIST;
×
6108
        int i;
6109

6110
        if (!cgroup_initialized)
×
6111
                return ECGROUPNOTINITIALIZED;
×
6112

6113
        if (!controller)
×
6114
                return ECGINVAL;
×
6115

6116
        pthread_rwlock_rdlock(&cg_mount_table_lock);
×
6117
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
×
6118
                if (strncmp(cg_mount_table[i].name, controller, FILENAME_MAX))
×
6119
                        continue;
×
6120

6121
                *mount_point = strdup(cg_mount_table[i].mount.path);
×
6122

6123
                if (!*mount_point) {
×
6124
                        last_errno = errno;
×
6125
                        ret = ECGOTHER;
×
6126
                        goto out_exit;
×
6127
                }
6128

6129
                ret = 0;
×
6130
                break;
×
6131
        }
6132
out_exit:
×
6133
        pthread_rwlock_unlock(&cg_mount_table_lock);
×
6134

6135
        return ret;
×
6136
}
6137

6138
int cgroup_get_all_controller_end(void **handle)
472✔
6139
{
6140
        FILE *proc_cgrp = (FILE *) *handle;
472✔
6141

6142
        if (!proc_cgrp)
472✔
6143
                return ECGINVAL;
×
6144

6145
        fclose(proc_cgrp);
472✔
6146
        *handle = NULL;
472✔
6147

6148
        return 0;
472✔
6149
}
6150

6151
int cgroup_get_all_controller_next(void **handle, struct controller_data *info)
7,080✔
6152
{
6153
        FILE *proc_cgrp = (FILE *) *handle;
7,080✔
6154
        int hierarchy, num_cgrps, enabled;
6155
        char subsys_name[FILENAME_MAX];
6156
        int err = 0;
7,080✔
6157

6158
        if (!proc_cgrp)
7,080✔
6159
                return ECGINVAL;
×
6160

6161
        if (!info)
7,080✔
6162
                return ECGINVAL;
×
6163

6164
        /*
6165
         * check Linux Kernel sources/kernel/cgroup/cgroup.c cgroup_init_early(),
6166
         * MAX_CGROUP_TYPE_NAMELEN check for details on why 32 is used.
6167
         */
6168
        err = fscanf(proc_cgrp, "%32s %d %d %d\n", subsys_name, &hierarchy, &num_cgrps,
7,080✔
6169
                     &enabled);
6170

6171
        if (err != 4)
7,080✔
6172
                return ECGEOF;
472✔
6173

6174
        strncpy(info->name, subsys_name, FILENAME_MAX);
6,608✔
6175
        info->name[FILENAME_MAX-1] = '\0';
6,608✔
6176
        info->hierarchy = hierarchy;
6,608✔
6177
        info->num_cgroups = num_cgrps;
6,608✔
6178
        info->enabled = enabled;
6,608✔
6179

6180
        return 0;
6,608✔
6181
}
6182

6183
int cgroup_get_all_controller_begin(void **handle, struct controller_data *info)
472✔
6184
{
6185
        FILE *proc_cgroup = NULL;
472✔
6186
        char buf[FILENAME_MAX];
6187
        int ret;
6188

6189
        if (!info)
472✔
6190
                return ECGINVAL;
×
6191

6192
        proc_cgroup = fopen("/proc/cgroups", "re");
472✔
6193
        if (!proc_cgroup) {
472✔
6194
                last_errno = errno;
×
6195
                return ECGOTHER;
×
6196
        }
6197

6198
        if (!fgets(buf, FILENAME_MAX, proc_cgroup)) {
472✔
6199
                last_errno = errno;
×
6200
                fclose(proc_cgroup);
×
6201
                *handle = NULL;
×
6202
                return ECGOTHER;
×
6203
        }
6204
        *handle = proc_cgroup;
472✔
6205

6206
        ret = cgroup_get_all_controller_next(handle, info);
472✔
6207
        if (ret != 0) {
472✔
6208
                fclose(proc_cgroup);
×
6209
                *handle = NULL;
×
6210
        }
6211

6212
        return ret;
472✔
6213
}
6214

6215
static int pid_compare(const void *a, const void *b)
244✔
6216
{
6217
        const pid_t *pid1, *pid2;
6218

6219
        pid1 = (pid_t *) a;
244✔
6220
        pid2 = (pid_t *) b;
244✔
6221

6222
        return (*pid1 - *pid2);
244✔
6223
}
6224

6225
/* pids needs to be completely uninitialized so that we can set it up
6226
 *
6227
 * Caller must free up pids.
6228
 */
6229
static int read_pids(char *path, pid_t **pids, int *size)
18✔
6230
{
6231
        int tot_pids = 16;
18✔
6232
        pid_t *tmp_list;
6233
        FILE *pid_file;
6234
        int n = 0;
18✔
6235
        int err;
6236

6237
        pid_file = fopen(path, "r");
18✔
6238
        if (!pid_file) {
18✔
6239
                last_errno = errno;
×
6240
                *pids = NULL;
×
6241
                *size = 0;
×
6242
                if (errno == ENOENT)
×
6243
                        return ECGROUPUNSUPP;
×
6244
                else
6245
                        return ECGOTHER;
×
6246
        }
6247

6248
        /* Keep doubling the memory allocated if needed */
6249
        tmp_list = malloc(sizeof(pid_t) * tot_pids);
18✔
6250
        if (!tmp_list) {
18✔
6251
                last_errno = errno;
×
6252
                fclose(pid_file);
×
6253
                return ECGOTHER;
×
6254
        }
6255

6256
        while (!feof(pid_file)) {
42✔
6257
                while (!feof(pid_file) && n < tot_pids) {
154✔
6258
                        pid_t pid;
6259

6260
                        err = fscanf(pid_file, "%u", &pid);
148✔
6261
                        if (err == EOF)
148✔
6262
                                break;
18✔
6263
                        tmp_list[n] = pid;
130✔
6264
                        n++;
130✔
6265
                }
6266
                if (!feof(pid_file)) {
24✔
6267
                        pid_t *orig_list = tmp_list;
6✔
6268

6269
                        tot_pids *= 2;
6✔
6270
                        tmp_list = realloc(tmp_list, sizeof(pid_t) * tot_pids);
6✔
6271
                        if (!tmp_list) {
6✔
6272
                                last_errno = errno;
×
6273
                                fclose(pid_file);
×
6274
                                free(orig_list);
×
6275
                                *pids = NULL;
×
6276
                                *size = 0;
×
6277
                                return ECGOTHER;
×
6278
                        }
6279
                }
6280
        }
6281
        fclose(pid_file);
18✔
6282

6283
        *size = n;
18✔
6284
        qsort(tmp_list, n, sizeof(pid_t), &pid_compare);
18✔
6285
        *pids = tmp_list;
18✔
6286

6287
        return 0;
18✔
6288
}
6289

6290
int cgroup_get_procs(const char *name, const char *controller, pid_t **pids, int *size)
18✔
6291
{
6292
        char cgroup_path[FILENAME_MAX];
6293

6294
        cg_build_path(name, cgroup_path, controller);
18✔
6295
        strncat(cgroup_path, "/cgroup.procs", FILENAME_MAX-strlen(cgroup_path));
18✔
6296

6297
        return read_pids(cgroup_path, pids, size);
18✔
6298
}
6299

6300
int cgroup_get_threads(const char *name, const char *controller, pid_t **pids, int *size)
×
6301
{
6302
        char cgroup_path[FILENAME_MAX];
6303

6304
        cg_build_path(name, cgroup_path, controller);
×
6305
        strncat(cgroup_path, "/cgroup.threads", FILENAME_MAX-strlen(cgroup_path));
×
6306

6307
        return read_pids(cgroup_path, pids, size);
×
6308
}
6309

6310
int cgroup_dictionary_create(struct cgroup_dictionary **dict,
47✔
6311
                             int flags)
6312
{
6313
        if (!dict)
47✔
6314
                return ECGINVAL;
×
6315

6316
        *dict = (struct cgroup_dictionary *) calloc(1, sizeof(struct cgroup_dictionary));
47✔
6317
        if (!*dict) {
47✔
6318
                last_errno = errno;
×
6319
                return ECGOTHER;
×
6320
        }
6321
        (*dict)->flags = flags;
47✔
6322

6323
        return 0;
47✔
6324
}
6325

6326
int cgroup_dictionary_add(struct cgroup_dictionary *dict, const char *name, const char *value)
106✔
6327
{
6328
        struct cgroup_dictionary_item *it;
6329

6330
        if (!dict)
106✔
6331
                return ECGINVAL;
×
6332

6333
        it = (struct cgroup_dictionary_item *) malloc(sizeof(struct cgroup_dictionary_item));
106✔
6334
        if (!it) {
106✔
6335
                last_errno = errno;
×
6336
                return ECGOTHER;
×
6337
        }
6338

6339
        it->next = NULL;
106✔
6340
        it->name = name;
106✔
6341
        it->value = value;
106✔
6342

6343
        if (dict->tail) {
106✔
6344
                dict->tail->next = it;
59✔
6345
                dict->tail = it;
59✔
6346
        } else {
6347
                /* It is the first item */
6348
                dict->tail = it;
47✔
6349
                dict->head = it;
47✔
6350
        }
6351

6352
        return 0;
106✔
6353
}
6354

6355
int cgroup_dictionary_free(struct cgroup_dictionary *dict)
64✔
6356
{
6357
        struct cgroup_dictionary_item *it;
6358

6359
        if (!dict)
64✔
6360
                return ECGINVAL;
17✔
6361

6362
        it = dict->head;
47✔
6363
        while (it) {
153✔
6364
                struct cgroup_dictionary_item *del = it;
106✔
6365

6366
                it = it->next;
106✔
6367
                if (!(dict->flags & CG_DICT_DONT_FREE_ITEMS)) {
106✔
6368
                        free((void *)del->value);
106✔
6369
                        free((void *)del->name);
106✔
6370
                }
6371
                free(del);
106✔
6372
        }
6373
        free(dict);
47✔
6374

6375
        return 0;
47✔
6376
}
6377

6378
int cgroup_dictionary_iterator_begin(struct cgroup_dictionary *dict, void **handle,
47✔
6379
                                     const char **name, const char **value)
6380
{
6381
        struct cgroup_dictionary_iterator *iter;
6382

6383
        *handle = NULL;
47✔
6384

6385
        if (!dict)
47✔
6386
                return ECGINVAL;
×
6387

6388
        iter = (struct cgroup_dictionary_iterator *)
6389
                malloc(sizeof(struct cgroup_dictionary_iterator));
47✔
6390
        if (!iter) {
47✔
6391
                last_errno = errno;
×
6392
                return ECGOTHER;
×
6393
        }
6394

6395
        iter->item = dict->head;
47✔
6396
        *handle = iter;
47✔
6397

6398
        return cgroup_dictionary_iterator_next(handle, name, value);
47✔
6399
}
6400

6401
int cgroup_dictionary_iterator_next(void **handle, const char **name, const char **value)
153✔
6402
{
6403
        struct cgroup_dictionary_iterator *iter;
6404

6405
        if (!handle)
153✔
6406
                return ECGINVAL;
×
6407

6408
        iter = *handle;
153✔
6409

6410
        if (!iter)
153✔
6411
                return ECGINVAL;
×
6412

6413
        if (!iter->item)
153✔
6414
                return ECGEOF;
47✔
6415

6416
        *name = iter->item->name;
106✔
6417
        *value = iter->item->value;
106✔
6418
        iter->item = iter->item->next;
106✔
6419

6420
        return 0;
106✔
6421
}
6422

6423
void cgroup_dictionary_iterator_end(void **handle)
47✔
6424
{
6425
        if (!handle)
47✔
6426
                return;
×
6427

6428
        free(*handle);
47✔
6429
        *handle = NULL;
47✔
6430
}
6431

6432
int cgroup_get_subsys_mount_point_begin(const char *controller, void **handle, char *path)
3✔
6433
{
6434
        int i;
6435

6436
        if (!cgroup_initialized)
3✔
6437
                return ECGROUPNOTINITIALIZED;
×
6438
        if (!handle || !path || !controller)
3✔
6439
                return ECGINVAL;
×
6440

6441
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++)
38✔
6442
                if (strcmp(controller, cg_mount_table[i].name) == 0)
38✔
6443
                        break;
3✔
6444

6445
        if (cg_mount_table[i].name[0] == '\0') {
3✔
6446
                /* The controller is not mounted at all */
6447
                *handle = NULL;
×
6448
                *path = '\0';
×
6449
                return ECGEOF;
×
6450
        }
6451

6452
        /*
6453
         * 'handle' is pointer to struct cg_mount_point, which should be
6454
         * returned next.
6455
         */
6456
        *handle = cg_mount_table[i].mount.next;
3✔
6457
        strcpy(path, cg_mount_table[i].mount.path);
3✔
6458

6459
        return 0;
3✔
6460
}
6461

6462
int cgroup_get_subsys_mount_point_next(void **handle, char *path)
3✔
6463
{
6464
        struct cg_mount_point *it;
6465

6466
        if (!cgroup_initialized)
3✔
6467
                return ECGROUPNOTINITIALIZED;
×
6468

6469
        if (!handle || !path)
3✔
6470
                return ECGINVAL;
×
6471

6472
        it = *handle;
3✔
6473
        if (!it) {
3✔
6474
                *handle = NULL;
3✔
6475
                *path = '\0';
3✔
6476
                return ECGEOF;
3✔
6477
        }
6478

6479
        *handle = it->next;
×
6480
        strcpy(path, it->path);
×
6481

6482
        return 0;
×
6483
}
6484

6485
int cgroup_get_subsys_mount_point_end(void **handle)
3✔
6486
{
6487
        if (!cgroup_initialized)
3✔
6488
                return ECGROUPNOTINITIALIZED;
×
6489

6490
        if (!handle)
3✔
6491
                return ECGINVAL;
×
6492

6493
        *handle = NULL;
3✔
6494

6495
        return 0;
3✔
6496
}
6497

6498
int cgroup_get_controller_version(const char * const controller, enum cg_version_t * const version)
6,114✔
6499
{
6500
        int i;
6501

6502
        if (!version)
6,114✔
6503
                return ECGINVAL;
×
6504

6505
        if (!controller && strlen(cg_cgroup_v2_mount_path) > 0) {
6,114✔
6506
                *version = CGROUP_V2;
36✔
6507
                return 0;
36✔
6508
        }
6509

6510
        if (!controller)
6,078✔
6511
                return ECGINVAL;
×
6512

6513
        *version = CGROUP_UNK;
6,078✔
6514

6515
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
33,458✔
6516
                if (strncmp(cg_mount_table[i].name, controller,
33,451✔
6517
                            sizeof(cg_mount_table[i].name)) == 0) {
6518
                        *version = cg_mount_table[i].version;
6,071✔
6519
                        return 0;
6,071✔
6520
                }
6521
        }
6522

6523
        return ECGROUPNOTEXIST;
7✔
6524
}
6525

6526
static int search_and_append_mnt_path(struct cg_mount_point **mount_point, char *path)
54✔
6527
{
6528
        struct cg_mount_point *mnt_point, *mnt_tmp, *mnt_prev;
6529

6530
        mnt_tmp = *mount_point;
54✔
6531
        while (mnt_tmp) {
258✔
6532
                if (strcmp(mnt_tmp->path, path) == 0)
219✔
6533
                        return ECGVALUEEXISTS;
15✔
6534

6535
                mnt_prev = mnt_tmp;
204✔
6536
                mnt_tmp = mnt_tmp->next;
204✔
6537
        }
6538

6539
        mnt_point = malloc(sizeof(struct cg_mount_point));
39✔
6540
        if (mnt_point == NULL) {
39✔
6541
                last_errno = errno;
×
6542
                return ECGOTHER;
×
6543
        }
6544

6545
        strncpy(mnt_point->path, path, FILENAME_MAX - 1);
39✔
6546
        mnt_point->path[FILENAME_MAX - 1] = '\0';
39✔
6547

6548
        mnt_point->next = NULL;
39✔
6549

6550
        if (*mount_point == NULL)
39✔
6551
                *mount_point = mnt_point;
7✔
6552
        else
6553
                mnt_prev->next = mnt_point;
32✔
6554

6555
        return 0;
39✔
6556
}
6557

6558
/**
6559
 * List the mount paths, that matches the specified version
6560
 *
6561
 *        @param cgrp_version The cgroup type/version
6562
 *        @param mount_paths Holds the list of mount paths
6563
 *        @return 0 success and list of mounts paths in mount_paths
6564
 *                ECGOTHER on failure and mount_paths is NULL.
6565
 */
6566
int cgroup_list_mount_points(const enum cg_version_t cgrp_version, char ***mount_paths)
8✔
6567
{
6568
        struct cg_mount_point *mount_point, *mnt_tmp = NULL;
8✔
6569
        char **mnt_paths = NULL;
8✔
6570
        int i, idx = 0;
8✔
6571
        int ret;
6572

6573
        if (!cgroup_initialized)
8✔
6574
                return ECGROUPNOTINITIALIZED;
×
6575

6576
        if (cgrp_version != CGROUP_V1 && cgrp_version != CGROUP_V2)
8✔
6577
                return ECGINVAL;
×
6578

6579
        pthread_rwlock_rdlock(&cg_mount_table_lock);
8✔
6580

6581
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
112✔
6582
                if (cg_mount_table[i].version != cgrp_version)
104✔
6583
                        continue;
52✔
6584

6585
                mount_point = &cg_mount_table[i].mount;
52✔
6586
                while (mount_point) {
104✔
6587
                        ret = search_and_append_mnt_path(&mnt_tmp, mount_point->path);
52✔
6588
                        if (ret != 0 && ret != ECGVALUEEXISTS)
52✔
6589
                                goto err;
×
6590

6591
                        /* Avoid adding duplicate mount points */
6592
                        if (ret != ECGVALUEEXISTS)
52✔
6593
                                idx++;
37✔
6594

6595
                        mount_point = mount_point->next;
52✔
6596
                }
6597
        }
6598

6599
        /*
6600
         * Cgroup v2 can be mounted without any controller and these mount
6601
         * paths are not part of the cg_mount_table.  Check and append
6602
         * them to mnt_paths.
6603
         */
6604
        if (cgrp_version == CGROUP_V2 && cg_cgroup_v2_empty_mount_paths) {
8✔
6605
                mount_point = cg_cgroup_v2_empty_mount_paths;
2✔
6606
                while (mount_point) {
4✔
6607
                        ret = search_and_append_mnt_path(&mnt_tmp, mount_point->path);
2✔
6608
                        if (ret)
2✔
6609
                                goto err;
×
6610

6611
                        idx++;
2✔
6612
                        mount_point = mount_point->next;
2✔
6613
                }
6614
        }
6615

6616
        mnt_paths = malloc(sizeof(char *) * (idx + 1));
8✔
6617
        if (mnt_paths == NULL) {
8✔
6618
                last_errno = errno;
×
6619
                ret = ECGOTHER;
×
6620
                goto err;
×
6621
        }
6622

6623
        for (i = 0, mount_point = mnt_tmp;
46✔
6624
             mount_point;
3✔
6625
             mount_point = mount_point->next, i++) {
39✔
6626

6627
                mnt_paths[i] = strdup(mount_point->path);
39✔
6628
                if (mnt_paths[i] == NULL) {
39✔
6629
                        last_errno = errno;
×
6630
                        ret = ECGOTHER;
×
6631
                        goto err;
×
6632
                }
6633
        }
6634
        mnt_paths[i] = '\0';
8✔
6635

6636
        ret = 0;
8✔
6637
        *mount_paths = mnt_paths;
8✔
6638

6639
err:
8✔
6640
        pthread_rwlock_unlock(&cg_mount_table_lock);
8✔
6641

6642
        while (mnt_tmp) {
47✔
6643
                mount_point = mnt_tmp;
39✔
6644
                mnt_tmp = mnt_tmp->next;
39✔
6645
                free(mount_point);
39✔
6646
        }
6647

6648
        if (ret != 0 && mnt_paths) {
8✔
6649
                for (i = 0; i < idx; i++)
×
6650
                        free(mnt_paths[i]);
×
6651
                free(mnt_paths);
×
6652
                *mount_paths = NULL;
×
6653
        }
6654

6655
        return ret;
8✔
6656
}
6657

6658
const struct cgroup_library_version *cgroup_version(void)
8✔
6659
{
6660
        return &library_version;
8✔
6661
}
6662

6663
/**
6664
 * Finds the current cgroup setup mode (legacy/unified/hybrid).
6665
 * Returns unknown of failure and setup mode on success.
6666
 */
6667
enum cg_setup_mode_t cgroup_setup_mode(void)
471✔
6668
{
6669
#define CGROUP2_SUPER_MAGIC        0x63677270
6670
#define CGROUP_SUPER_MAGIC        0x27E0EB
6671

6672
        unsigned int cg_setup_mode_bitmask = 0U;
471✔
6673
        enum cg_setup_mode_t setup_mode;
6674
        struct statfs cgrp_buf;
6675
        int i, ret = 0;
471✔
6676

6677
        if (!cgroup_initialized)
471✔
6678
                return ECGROUPNOTINITIALIZED;
×
6679

6680
        setup_mode = CGROUP_MODE_UNK;
471✔
6681

6682
        pthread_rwlock_wrlock(&cg_mount_table_lock);
471✔
6683
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
7,110✔
6684
                ret = statfs(cg_mount_table[i].mount.path, &cgrp_buf);
6,639✔
6685
                if (ret) {
6,639✔
6686
                        setup_mode = CGROUP_MODE_UNK;
×
6687
                        cgroup_err("Failed to get stats of '%s'\n", cg_mount_table[i].mount.path);
×
6688
                        goto out;
×
6689
                }
6690

6691
                if (cgrp_buf.f_type == CGROUP2_SUPER_MAGIC)
6,639✔
6692
                        cg_setup_mode_bitmask |= (1U << 0);
1,192✔
6693
                else if (cgrp_buf.f_type == CGROUP_SUPER_MAGIC)
5,447✔
6694
                        cg_setup_mode_bitmask |= (1U << 1);
5,447✔
6695
        }
6696

6697
        if (cg_cgroup_v2_empty_mount_paths)
471✔
6698
                cg_setup_mode_bitmask |= (1U << 0);
78✔
6699

6700
        if (cg_setup_mode_bitmask & (1U << 0) && cg_setup_mode_bitmask & (1U << 1))
471✔
6701
                setup_mode = CGROUP_MODE_HYBRID;
413✔
6702
        else if (cg_setup_mode_bitmask & (1U << 0))
58✔
6703
                setup_mode = CGROUP_MODE_UNIFIED;
58✔
6704
        else if (cg_setup_mode_bitmask & (1U << 1))
×
6705
                setup_mode = CGROUP_MODE_LEGACY;
×
6706

6707
out:
×
6708
        pthread_rwlock_unlock(&cg_mount_table_lock);
471✔
6709
        return setup_mode;
471✔
6710
}
6711

6712
int cgroup_get_controller_count(struct cgroup *cgrp)
16✔
6713
{
6714
        if (!cgrp)
16✔
6715
                return -1;
×
6716

6717
        return cgrp->index;
16✔
6718
}
6719

6720
struct cgroup_controller *cgroup_get_controller_by_index(struct cgroup *cgrp, int index)
50✔
6721
{
6722
        if (!cgrp)
50✔
6723
                return NULL;
×
6724

6725
        if (index >= cgrp->index)
50✔
6726
                return NULL;
×
6727

6728
        return cgrp->controller[index];
50✔
6729
}
6730

6731
char *cgroup_get_controller_name(struct cgroup_controller *controller)
50✔
6732
{
6733
        if (!controller)
50✔
6734
                return NULL;
×
6735

6736
        return controller->name;
50✔
6737
}
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