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

drakenclimber / libcgroup / 20928558948

12 Jan 2026 05:21PM UTC coverage: 52.932% (-3.3%) from 56.221%
20928558948

push

github

drakenclimber
bootstrap: Update to googletest v1.15.0

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

5280 of 9975 relevant lines covered (52.93%)

563.4 hits per line

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

54.64
/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)
13,286✔
144
{
145
        if (owner == NO_UID_GID)
13,286✔
146
                owner = getuid();
12,614✔
147
        if (group == NO_UID_GID)
13,286✔
148
                group = getgid();
12,614✔
149

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

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

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

195
        while (1) {
13,286✔
196
                FTSENT *ent;
197

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

207
        return ret;
236✔
208
}
209

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

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

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

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

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

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

241
        close(fd);
388✔
242

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

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

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

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

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

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

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

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

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

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

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

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

394
        tmp_string = strdup(path);
220✔
395

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

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

401
        free(tmp_string);
220✔
402

403
        return base;
220✔
404
}
405

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

410
        pthread_rwlock_rdlock(&cg_mount_table_lock);
855✔
411

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

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

430
        pthread_rwlock_unlock(&cg_mount_table_lock);
×
431

432
        return 0;
×
433
}
434

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

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

459
        free(r);
×
460
}
461

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

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

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

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

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

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

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

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

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

512
        return itr;
1✔
513
}
514

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

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

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

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

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

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

560
        return ret;
×
561
}
562

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

569
        if (!rule || !field)
4✔
570
                return ECGINVAL;
×
571

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

576
        tmp = rule;
4✔
577

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

587
        if (quote_start) {
4✔
588
                if (!expect_quotes)
×
589
                        return ECGINVAL;
×
590

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

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

598
                quote_end = rule;
×
599
        }
600

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

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

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

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

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

624
        return len;
4✔
625
}
626

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

939
                newrule->next = NULL;
1✔
940

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

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

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

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

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

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

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

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

990
parsefail:
×
991
        ret = ECGRULESPARSEFAIL;
×
992

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

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

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

1042
        int ret;
1043

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

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

1054
        pthread_rwlock_wrlock(&rl_lock);
1✔
1055

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

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

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

1079
                return 0;
1✔
1080
        }
1081

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

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

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

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

1104
                        free(tmp);
×
1105

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

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

1125
        return ret;
×
1126
}
1127

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

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

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

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

1150
        it->next = mount;
×
1151

1152
        return 0;
×
1153
}
1154

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

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

1173
        return shared_mnt;
13,378✔
1174
}
1175

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

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

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

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

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

1194
        (*mnt_tbl_idx)++;
13,378✔
1195
}
13,378✔
1196

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

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

1215
                if (!mntopt)
5,096✔
1216
                        continue;
5,096✔
1217

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1290
out:
×
1291
        return ret;
364✔
1292
}
1293

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

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

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

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

1328
                ret = ECGEOF;
×
1329

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

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

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

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

1351
                goto out;
×
1352
        }
1353

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

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

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

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

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

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

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

1397
                        continue;
×
1398
                }
1399

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

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

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

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

1415
        return ret;
1,446✔
1416
}
1417

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

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

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

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

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

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

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

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

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

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

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

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

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

1504
        return ret;
×
1505
}
1506

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

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

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

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

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

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

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

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

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

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

1589
        return ret;
1,446✔
1590
}
1591

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

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

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

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

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

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

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

1638
                        continue;
360✔
1639
                }
1640

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

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

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

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

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

1671
err:
1,442✔
1672
        if (proc_mount)
1,446✔
1673
                fclose(proc_mount);
1,446✔
1674

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

1678
        return ret;
1,446✔
1679
}
1680

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

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

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

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

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

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

1709
        cgroup_initialized = 1;
1,442✔
1710

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

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

1719
        return ret;
1,446✔
1720
}
1721

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

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

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

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

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

1759
        return ret;
974✔
1760
}
1761

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

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

1774
        path[FILENAME_MAX-1] = '\0';
27,521✔
1775

1776
        return path;
27,521✔
1777
}
1778

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1889
                                cg_concat_path(tmp, name, path);
27,396✔
1890
                                free(tmp);
27,396✔
1891
                        }
1892
                        goto out;
28,059✔
1893
                }
1894
        }
1895
        path = NULL;
×
1896

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

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

1904
        return path;
28,184✔
1905
}
1906

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

1913
        return path;
1,837✔
1914
}
1915

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

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

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

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

1970
out:
561✔
1971
        if (fp)
561✔
1972
                fclose(fp);
435✔
1973

1974
        return err;
561✔
1975
}
1976

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

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

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

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

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

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

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

2014
        return err;
561✔
2015
}
2016

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

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

2029
        if (version != CGROUP_V2)
78✔
2030
                return 0;
×
2031

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

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

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

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

2052
        dname = dirname(parent);
72✔
2053

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

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

2064
        return error;
72✔
2065
}
2066

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

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

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

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

2118
        if (version == CGROUP_V2) {
×
2119

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

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

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

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

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

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

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

2147
        return ret;
×
2148
}
2149

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2327
done:
247✔
2328
        free(real_path);
490✔
2329

2330
        return ret;
490✔
2331
}
2332

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2543
        return error;
610✔
2544
}
2545

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

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

2567
        *enabled = false;
1,149✔
2568

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

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

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

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

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

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

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

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

2627
        return error;
1,149✔
2628
}
2629

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2729
        return 0;
8✔
2730
}
2731

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

2906
        return ret;
656✔
2907

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

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

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

2922
        return ret;
×
2923
}
2924

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

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

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

2943
        cgroup_free_controllers(dst);
417✔
2944

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

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

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

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

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

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

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

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

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

3001
        return error;
×
3002
}
3003

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

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

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

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

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

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

3041
                        dname = dirname(parent);
221✔
3042

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

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

3059
        base = strdup(path);
243✔
3060

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

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

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

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

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

3100
        return error;
244✔
3101
}
3102

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

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

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

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

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

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

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

3164
        return 0;
191✔
3165
}
3166

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

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

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

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

3210
        return ret;
188✔
3211
}
3212

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

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

3226
        pthread_rwlock_rdlock(&cg_mount_table_lock);
171✔
3227

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

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

3237
        pthread_rwlock_unlock(&cg_mount_table_lock);
171✔
3238

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

3264
        *parent = NULL;
193✔
3265

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3479
        return ECGOTHER;
×
3480
}
3481

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

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

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

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

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

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

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

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

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

3538
        cgroup_walk_tree_end(&handle);
21✔
3539

3540
        return ret;
21✔
3541
}
3542

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

3711
        return first_error;
192✔
3712
}
3713

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

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

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

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

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

3750
        fclose(ctrl_file);
3,369✔
3751

3752
        return 0;
3,369✔
3753
}
3754

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

3772
        d_name = strdup(ctrl_dir->d_name);
21,484✔
3773

3774
        if (!d_name) {
21,484✔
3775
                error = ECGOTHER;
×
3776
                cgroup_err("strdup failed to allocate memory: %s\n", strerror(errno));
×
3777
                goto fill_error;
×
3778
        }
3779

3780
        if (!strcmp(d_name, ".") || !strcmp(d_name, "..")) {
21,484✔
3781
                error = ECGINVAL;
×
3782
                goto fill_error;
×
3783
        }
3784

3785
        /*
3786
         * This part really needs to be optimized out. Probably use some
3787
         * sort of a flag, but this is fine for now.
3788
         */
3789
        cg_build_path_locked(cgrp->name, path, cg_mount_table[cg_index].name);
21,484✔
3790
        strncat(path, d_name, sizeof(path) - strlen(path) - 1);
21,484✔
3791

3792
        error = stat(path, &stat_buffer);
21,484✔
3793
        if (error) {
21,484✔
3794
                error = ECGFAIL;
×
3795
                goto fill_error;
×
3796
        }
3797

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

3811
        /* tmp_path would be pointing to the last six characters */
3812
        tmp_path = (char *)path + tmp_len;
21,484✔
3813

3814
        /*
3815
         * Checking to see, if this is actually a 'tasks' file We need to
3816
         * compare the last 6 bytes
3817
         */
3818
        if (strcmp(tmp_path, "/tasks")) {
21,484✔
3819
                cgrp->control_uid = stat_buffer.st_uid;
21,484✔
3820
                cgrp->control_gid = stat_buffer.st_gid;
21,484✔
3821
        }
3822

3823
        ctrl_name = strtok_r(d_name, ".", &buffer);
21,484✔
3824
        if (!ctrl_name) {
21,484✔
3825
                error = ECGFAIL;
×
3826
                goto fill_error;
×
3827
        }
3828

3829
        ctrl_file = strtok_r(NULL, ".", &buffer);
21,484✔
3830
        if (!ctrl_file) {
21,484✔
3831
                error = ECGINVAL;
×
3832
                goto fill_error;
×
3833
        }
3834

3835
        if (strcmp(ctrl_name, cg_mount_table[cg_index].name) == 0) {
21,484✔
3836
                error = cg_rd_ctrl_file(cg_mount_table[cg_index].name, cgrp->name,
3,379✔
3837
                                        ctrl_dir->d_name, &ctrl_value);
3,379✔
3838
                if (error || !ctrl_value)
3,379✔
3839
                        goto fill_error;
10✔
3840

3841
                if (cgroup_add_value_string(cgc, ctrl_dir->d_name, ctrl_value)) {
3,369✔
3842
                        error = ECGFAIL;
×
3843
                        goto fill_error;
×
3844
                }
3845
        }
3846
fill_error:
21,474✔
3847
        if (ctrl_value)
21,484✔
3848
                free(ctrl_value);
3,369✔
3849
        if (d_name)
21,484✔
3850
                free(d_name);
21,484✔
3851

3852
        return error;
21,484✔
3853
}
3854

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

3874
        if (!cgroup_initialized) {
71✔
3875
                /* ECGROUPNOTINITIALIZED */
3876
                return ECGROUPNOTINITIALIZED;
×
3877
        }
3878

3879
        if (!cgrp) {
71✔
3880
                /* ECGROUPNOTALLOWED */
3881
                return ECGROUPNOTALLOWED;
×
3882
        }
3883

3884
        initial_controller_cnt = cgrp->index;
71✔
3885

3886
        pthread_rwlock_rdlock(&cg_mount_table_lock);
71✔
3887
        for (i = 0; i < CG_CONTROLLER_MAX && cg_mount_table[i].name[0] != '\0'; i++) {
710✔
3888
                struct cgroup_controller *cgc;
3889
                struct stat stat_buffer;
3890
                int mnt_path_len;
3891

3892
                if (initial_controller_cnt > 0) {
639✔
3893
                        bool skip_this_controller = true;
54✔
3894

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

3905
                        if (skip_this_controller)
54✔
3906
                                continue;
312✔
3907
                }
3908

3909
                if (!cg_build_path_locked(NULL, mnt_path, cg_mount_table[i].name))
601✔
3910
                        continue;
×
3911

3912
                mnt_path_len = strlen(mnt_path);
601✔
3913
                strncat(mnt_path, cgrp->name, FILENAME_MAX - mnt_path_len - 1);
601✔
3914
                mnt_path[sizeof(mnt_path) - 1] = '\0';
601✔
3915

3916
                if (access(mnt_path, F_OK))
601✔
3917
                        continue;
×
3918

3919
                if (!cg_build_path_locked(cgrp->name, cgrp_ctrl_path, cg_mount_table[i].name)) {
601✔
3920
                        /* This fails when the cgroup does not exist for that controller. */
3921
                        continue;
×
3922
                }
3923

3924
                /* Get the uid and gid information. */
3925
                if (cg_mount_table[i].version == CGROUP_V1) {
601✔
3926
                        ret = asprintf(&control_path, "%s/tasks", cgrp_ctrl_path);
×
3927

3928
                        if (ret < 0) {
×
3929
                                last_errno = errno;
×
3930
                                error = ECGOTHER;
×
3931
                                goto unlock_error;
×
3932
                        }
3933

3934
                        if (stat(control_path, &stat_buffer)) {
×
3935
                                last_errno = errno;
×
3936
                                free(control_path);
×
3937
                                error = ECGOTHER;
×
3938
                                goto unlock_error;
×
3939
                        }
3940

3941
                        cgrp->tasks_uid = stat_buffer.st_uid;
×
3942
                        cgrp->tasks_gid = stat_buffer.st_gid;
×
3943

3944
                        free(control_path);
×
3945
                } else { /* cgroup v2 */
3946
                        bool enabled;
3947

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

3968
                if (initial_controller_cnt)
327✔
3969
                        cgc = cgroup_get_controller(cgrp, cg_mount_table[i].name);
16✔
3970
                else
3971
                        cgc = cgroup_add_controller(cgrp, cg_mount_table[i].name);
311✔
3972
                if (!cgc) {
327✔
3973
                        error = ECGINVAL;
×
3974
                        goto unlock_error;
×
3975
                }
3976

3977
                dir = opendir(cgrp_ctrl_path);
327✔
3978
                if (!dir) {
327✔
3979
                        last_errno = errno;
×
3980
                        error = ECGOTHER;
×
3981
                        goto unlock_error;
×
3982
                }
3983

3984
                controller_cnt++;
327✔
3985

3986
                while ((ctrl_dir = readdir(dir)) != NULL) {
20,266✔
3987
                        /* Skip over non regular files */
3988
                        if (ctrl_dir->d_type != DT_REG)
19,939✔
3989
                                continue;
899✔
3990

3991
                        error = cgroup_fill_cgc(ctrl_dir, cgrp, cgc, i);
19,040✔
3992
                        for (j = 0; j < cgc->index; j++)
111,599✔
3993
                                cgc->values[j]->dirty = false;
92,559✔
3994

3995
                        if (error == ECGFAIL) {
19,040✔
3996
                                closedir(dir);
×
3997
                                goto unlock_error;
×
3998
                        }
3999
                }
4000
                closedir(dir);
327✔
4001

4002
                if (!strcmp(cgc->name, "memory")) {
327✔
4003
                        /*
4004
                         * Make sure that memory.limit_in_bytes is placed before
4005
                         * memory.memsw.limit_in_bytes in the list of values
4006
                         */
4007
                        int memsw_limit = -1;
67✔
4008
                        int mem_limit = -1;
67✔
4009

4010
                        for (j = 0; j < cgc->index; j++) {
1,466✔
4011
                                if (!strcmp(cgc->values[j]->name, "memory.memsw.limit_in_bytes"))
1,399✔
4012
                                        memsw_limit = j;
×
4013
                                else if (!strcmp(cgc->values[j]->name, "memory.limit_in_bytes"))
1,399✔
4014
                                        mem_limit = j;
×
4015
                        }
4016

4017
                        if (memsw_limit >= 0 && memsw_limit < mem_limit) {
67✔
4018
                                struct control_value *val = cgc->values[memsw_limit];
×
4019

4020
                                cgc->values[memsw_limit] = cgc->values[mem_limit];
×
4021
                                cgc->values[mem_limit] = val;
×
4022
                        }
4023
                }
4024
        }
4025

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

4038
        pthread_rwlock_unlock(&cg_mount_table_lock);
71✔
4039

4040
        return 0;
71✔
4041

4042
unlock_error:
×
4043
        pthread_rwlock_unlock(&cg_mount_table_lock);
×
4044
        /*
4045
         * XX: Need to figure out how to cleanup? Cleanup just the stuff
4046
         * we added, or the whole structure.
4047
         */
4048
        cgroup_free_controllers(cgrp);
×
4049
        cgrp = NULL;
×
4050

4051
        return error;
×
4052
}
4053

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

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

4070
        strncpy(cgrp->name, dest, FILENAME_MAX);
47✔
4071
        cgrp->name[FILENAME_MAX-1] = '\0';
47✔
4072

4073
        /* Scan all the controllers */
4074
        for (i = 0; i < CG_CONTROLLER_MAX; i++) {
109✔
4075
                int j = 0;
109✔
4076

4077
                if (!controllers[i])
109✔
4078
                        return 0;
47✔
4079
                controller = controllers[i];
62✔
4080

4081
                /* If first string is "*" that means all the mounted controllers. */
4082
                if (strcmp(controller, "*") == 0) {
62✔
4083
                        pthread_rwlock_rdlock(&cg_mount_table_lock);
×
4084

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

4101
                /* It is individual controller names and not "*" */
4102
                cgroup_dbg("Adding controller %s\n", controller);
62✔
4103
                cptr = cgroup_add_controller(cgrp, controller);
62✔
4104
                if (!cptr) {
62✔
4105
                        cgroup_warn("adding controller '%s' failed\n", controller);
×
4106
                        cgroup_free_controllers(cgrp);
×
4107
                        return ECGROUPNOTALLOWED;
×
4108
                }
4109
        }
4110

4111
        return ret;
×
4112
}
4113

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

4128
        if (rule_procname[rule_strlen - 1] != '*')
220✔
4129
                /* This rule does not end in a wildcard */
4130
                return false;
220✔
4131

4132
        /* Compare the two strings up to the asterisk */
4133
        if (strncmp(rule_procname, procname, rule_strlen - 1) != 0)
×
4134
                /* The strings did not match */
4135
                return false;
×
4136

4137
        /* All checks passed.  The wildcarded process matched this rule */
4138
        return true;
×
4139
}
4140

4141
static int cgroup_find_matching_destination(char *cgrp_list[], const char * const rule_dest,
×
4142
                                            int *matching_index)
4143
{
4144
        size_t rule_strlen = strlen(rule_dest);
×
4145
        int ret = -ENODATA;
×
4146
        int i;
4147

4148
        for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
×
4149
                if (cgrp_list[i] == NULL)
×
4150
                        break;
×
4151

4152
                if (rule_dest[rule_strlen - 1] == '/') {
×
4153
                        /*
4154
                         * Avoid a weird corner case where given a rule dest
4155
                         * like 'folder/', we _don't_ want to match 'folder1'
4156
                         */
4157
                        if (strlen(cgrp_list[i]) >= rule_strlen &&
×
4158
                            cgrp_list[i][rule_strlen - 1] != '/')
×
4159
                                continue;
×
4160

4161
                        /*
4162
                         * Strip off the '/' at the end of the rule, as
4163
                         * the destination from the cgrp_list will not
4164
                         * have a trailing '/'
4165
                         */
4166
                        rule_strlen--;
×
4167
                }
4168

4169
                if (strncmp(rule_dest, cgrp_list[i], rule_strlen) == 0) {
×
4170
                        *matching_index = i;
×
4171
                        ret = 0;
×
4172
                        break;
×
4173
                }
4174
        }
4175

4176
        return ret;
×
4177
}
4178

4179
static int cgroup_find_matching_controller(char * const *rule_controllers,
×
4180
                                           const char * const pid_controller, int *matching_index)
4181
{
4182
        int ret = -ENODATA;
×
4183
        int i;
4184

4185
        for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
×
4186
                if (rule_controllers[i] == NULL)
×
4187
                        break;
×
4188

4189
                if (strlen(rule_controllers[i]) != strlen(pid_controller))
×
4190
                        continue;
×
4191

4192
                if (strncmp(pid_controller, rule_controllers[i], strlen(pid_controller)) == 0) {
×
4193
                        *matching_index = i;
×
4194
                        ret = 0;
×
4195
                        break;
×
4196
                }
4197
        }
4198

4199
        return ret;
×
4200
}
4201

4202
static bool cgroup_is_rt_task(const pid_t pid)
×
4203
{
4204
        int sched_prio_min, sched_prio_max;
4205
        struct sched_param pid_param;
4206
        int ret;
4207

4208
        ret = sched_getparam(pid, &pid_param);
×
4209
        if (ret == -1) {
×
4210
                ret = ECGOTHER;
×
4211
                last_errno = errno;
×
4212
                return false;
×
4213
        }
4214

4215
        sched_prio_min = sched_get_priority_min(SCHED_RR);
×
4216
        sched_prio_max = sched_get_priority_max(SCHED_RR);
×
4217

4218
        if (pid_param.sched_priority >= sched_prio_min &&
×
4219
            pid_param.sched_priority <= sched_prio_max)
×
4220
                return true;
×
4221

4222
        return false;
×
4223
}
4224

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

4247
        if (!rule->is_ignore)
221✔
4248
                /* Immediately return if the 'ignore' option is not set */
4249
                return false;
221✔
4250

4251
        /* If the rule is "ignore", move only non-rt tasks */
4252
        if (rule->is_ignore == CGRULE_OPT_IGNORE && cgroup_is_rt_task(pid) == true)
×
4253
                return false;
×
4254
        /* If the rule is "ignore_rt", move only non-rt tasks */
4255
        else if (rule->is_ignore == CGRULE_OPT_IGNORE_RT && cgroup_is_rt_task(pid) == false)
×
4256
                return false;
×
4257

4258
        /* If the rule is "ignore" and "ignore_rt", move all tasks */
4259

4260
        ret = cg_get_cgroups_from_proc_cgroups(pid, cgrp_list, controller_list,
×
4261
                                               MAX_MNT_ELEMENTS);
4262
        if (ret < 0)
×
4263
                goto out;
×
4264

4265
        if (strcmp(rule->destination, "*")) {
×
4266
                ret = cgroup_find_matching_destination(cgrp_list, rule->destination,
×
4267
                                                       &cgrp_list_matching_idx);
4268
                if (ret < 0)
×
4269
                        /* No cgroups matched */
4270
                        goto out;
×
4271
        }
4272

4273
        token = strtok_r(controller_list[cgrp_list_matching_idx], ",", &saveptr);
×
4274
        while (token != NULL) {
×
4275

4276
                ret = cgroup_find_matching_controller(rule->controllers, token,
×
4277
                                                      &rule_matching_controller_idx);
4278
                if (ret == 0)
×
4279
                        /* We found a matching controller */
4280
                        break;
×
4281

4282
                token = strtok_r(NULL, ",", &saveptr);
×
4283
        }
4284

4285
        if (!rule->procname) {
×
4286
                /*
4287
                 * The rule procname is empty, thus it's a wildcard and
4288
                 * all processes match.
4289
                 */
4290
                found_match = true;
×
4291
                goto out;
×
4292
        }
4293

4294
        if (!strcmp(rule->procname, procname)) {
×
4295
                found_match = true;
×
4296
                goto out;
×
4297
        }
4298

4299
        if (cgroup_compare_wildcard_procname(rule->procname, procname))
×
4300
                found_match = true;
×
4301

4302
out:
×
4303
        for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
×
4304
                if (controller_list[i])
×
4305
                        free(controller_list[i]);
×
4306
                if (cgrp_list[i])
×
4307
                        free(cgrp_list[i]);
×
4308
        }
4309

4310
        return found_match;
×
4311
}
4312

4313
static struct cgroup_rule *cgroup_find_matching_rule_uid_gid(uid_t uid, gid_t gid,
221✔
4314
                                                             struct cgroup_rule *rule)
4315
{
4316
        /* Temporary user data */
4317
        struct passwd *usr = NULL;
221✔
4318

4319
        /* Temporary group data */
4320
        struct group *grp = NULL;
221✔
4321

4322
        /* Temporary string pointer */
4323
        char *sp = NULL;
221✔
4324

4325
        /* Loop variable */
4326
        int i = 0;
221✔
4327
        int loglevel;
4328
        bool match_found = false;
221✔
4329

4330
        loglevel = cgroup_get_loglevel();
221✔
4331

4332
        while (rule) {
221✔
4333
                /* Skip "%" which indicates continuation of previous rule. */
4334
                if (rule->username[0] == '%') {
221✔
4335
                        rule = rule->next;
×
4336
                        continue;
×
4337
                }
4338
                /* The wildcard rule always matches. */
4339
                if ((rule->uid == CGRULE_WILD) && (rule->gid == CGRULE_WILD))
221✔
4340
                        return rule;
221✔
4341

4342
                /* This is the simple case of the UID matching. */
4343
                if (rule->uid == uid)
×
4344
                        return rule;
×
4345

4346
                /* This is the simple case of the GID matching. */
4347
                if (rule->gid == gid)
×
4348
                        return rule;
×
4349

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

4360
                        /* Get the data for UID. */
4361
                        usr = getpwuid(uid);
×
4362
                        if (!usr) {
×
4363
                                rule = rule->next;
×
4364
                                continue;
×
4365
                        }
4366

4367
                        cgroup_dbg("User name: %s UID: %d Group name: %s GID: %d\n",
×
4368
                                   usr->pw_name, uid, grp->gr_name, grp->gr_gid);
4369
                        if (grp->gr_mem[0])
×
4370
                                cgroup_dbg("Group member(s):\n");
×
4371

4372
                        /* If UID is a member of group, we matched. */
4373
                        for (i = 0; grp->gr_mem[i]; i++) {
×
4374
                                if (!(strcmp(usr->pw_name, grp->gr_mem[i])))
×
4375
                                        match_found = true;
×
4376

4377
                                if (match_found && loglevel < CGROUP_LOG_DEBUG)
×
4378
                                        /*
4379
                                         * Only continue to run through the loop if debugging is
4380
                                         * enabled so that we can see all of the group members
4381
                                         */
4382
                                        break;
×
4383

4384
                                cgroup_dbg("\t%s\n", grp->gr_mem[i]);
×
4385
                        }
4386

4387
                        if (match_found)
×
4388
                                return rule;
×
4389
                }
4390

4391
                /* If we haven't matched, try the next rule. */
4392
                rule = rule->next;
×
4393
        }
4394

4395
        /* If we get here, no rules matched. */
4396
        return NULL;
×
4397
}
4398

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

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

4447
                base = cgroup_basename(procname);
220✔
4448
                if (!strcmp(ret->procname, base))
220✔
4449
                        /* Check a rule of basename. */
4450
                        break;
×
4451
                if (cgroup_compare_wildcard_procname(ret->procname, procname))
220✔
4452
                        break;
×
4453
                ret = ret->next;
220✔
4454
                free(base);
220✔
4455
                base = NULL;
220✔
4456
        }
4457
        pthread_rwlock_unlock(&rl_lock);
221✔
4458

4459
        if (base)
221✔
4460
                free(base);
×
4461

4462
        return ret;
221✔
4463
}
4464

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

4476
        pthread_rwlock_rdlock(&cg_mount_table_lock);
×
4477
        ret_path = cg_build_path_locked(prefix, path, controller_name);
×
4478
        pthread_rwlock_unlock(&cg_mount_table_lock);
×
4479
        if (!ret_path) {
×
4480
                ret = 1;
×
4481
                goto end;
×
4482
        }
4483

4484
        dir = opendir(path);
×
4485
        if (dir == NULL) {
×
4486
                /* cgroup in wanted subsystem does not exist */
4487
                ret = 1;
×
4488
        } else {
4489
                /* cgroup in wanted subsystem exists */
4490
                ret = 0;
×
4491
                closedir(dir);
×
4492
        }
4493
end:
×
4494
        return ret;
×
4495
}
4496

4497
/*
4498
 * Auxiliary function return a pointer to the string which is copy of
4499
 * input string and end with the slash
4500
 */
4501
char *cgroup_copy_with_slash(char *input)
×
4502
{
4503
        int len = strlen(input);
×
4504
        char *output;
4505

4506
        /* If input does not end with '/', allocate one more space for it */
4507
        if ((input[len-1]) != '/')
×
4508
                len = len+1;
×
4509

4510
        output = (char *)malloc(sizeof(char)*(len+1));
×
4511
        if (output == NULL)
×
4512
                return NULL;
×
4513

4514
        strcpy(output, input);
×
4515
        output[len-1] = '/';
×
4516
        output[len] = '\0';
×
4517

4518
        return output;
×
4519
}
4520

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

4529
        if  (cgrp == NULL) {
×
4530
                /* It is the first controller the cgrp have to be created */
4531
                cgrp = cgroup_new_cgroup(cgrp_name);
×
4532
                if (cgrp == NULL) {
×
4533
                        ret = ECGFAIL;
×
4534
                        goto end;
×
4535
                }
4536
                pcgrp[0] = cgrp;
×
4537
        }
4538

4539
        controller = cgroup_add_controller(cgrp, controller_name);
×
4540
        if (controller == NULL) {
×
4541
                cgroup_free(&cgrp);
×
4542
                ret = ECGFAIL;
×
4543
        }
4544
end:
×
4545
        return ret;
×
4546
}
4547

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

4555
        struct cgroup *template_group = NULL;
×
4556
        char *template_name = NULL;        /* Name of the template.              */
×
4557
        char *group_name = NULL;        /* Name of the group based on         */
×
4558
                                        /* template variables are substituted.*/
4559
        char *template_position;        /* Denotes directory in template      */
4560
                                        /* path which is investigated.        */
4561
        char *group_position;                /* Denotes directory in cgroup path   */
4562
                                        /* which is investigated.             */
4563

4564
        int ret = 0;
×
4565
        int exist;
4566
        int i;
4567

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

4584
        /* Set start positions */
4585
        template_position = strchr(template_name, '/');
×
4586
        group_position = strchr(group_name, '/');
×
4587

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

4598
                /* Test for which controllers wanted group does not exist */
4599
                i = 0;
×
4600
                while (i < MAX_MNT_ELEMENTS && tmp->controllers[i] != NULL) {
×
4601
                        exist = cgroup_exist_in_subsystem(tmp->controllers[i], group_name);
×
4602

4603
                        if (exist != 0) {
×
4604
                                /* The cgroup does not exist */
4605
                                ret = add_controller(&template_group, group_name,
×
4606
                                                     tmp->controllers[i]);
4607
                                if  (ret != 0)
×
4608
                                        goto while_end;
×
4609
                        }
4610
                        i++;
×
4611
                }
4612

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

4624
                        if (ret != 0) {
×
4625
                                cgroup_free(&template_group);
×
4626
                                goto while_end;
×
4627
                        }
4628
                        cgroup_dbg("Group %s created - based on template %s\n", group_name,
×
4629
                                   template_name);
4630

4631
                        cgroup_free(&template_group);
×
4632
                }
4633
                template_position[0] = '/';
×
4634
                group_position[0] = '/';
×
4635
                template_position = strchr(++template_position, '/');
×
4636
                group_position = strchr(++group_position, '/');
×
4637
        }
4638

4639
while_end:
×
4640
        if ((template_position != NULL) && (template_position[0] == '\0'))
×
4641
                template_position[0] = '/';
×
4642
        if ((group_position != NULL) && (group_position[0] == '\0'))
×
4643
                group_position[0] = '/';
×
4644

4645
end:
×
4646
        if (group_name != NULL)
×
4647
                free(group_name);
×
4648
        if (template_name != NULL)
×
4649
                free(template_name);
×
4650

4651
        return ret;
×
4652
}
4653

4654
int cgroup_change_cgroup_flags(uid_t uid, gid_t gid, const char *procname, pid_t pid, int flags)
221✔
4655
{
4656
        /* Temporary pointer to a rule */
4657
        struct cgroup_rule *tmp = NULL;
221✔
4658

4659
        /* Temporary variables for destination substitution */
4660
        char newdest[FILENAME_MAX];
4661
        struct passwd *user_info;
4662
        struct group *group_info;
4663
        int available;
4664
        int written;
4665
        int i, j;
4666

4667
        /* Return codes */
4668
        int ret = 0;
221✔
4669

4670
        /* We need to check this before doing anything else! */
4671
        if (!cgroup_initialized) {
221✔
4672
                cgroup_warn("libcgroup is not initialized\n");
×
4673
                ret = ECGROUPNOTINITIALIZED;
×
4674
                goto finished;
×
4675
        }
4676

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

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

4700
                /* The configuration file has an error!  We must exit now. */
4701
                if (ret != -1 && ret != 0) {
×
4702
                        cgroup_err("failed to parse the configuration rules\n");
×
4703
                        goto finished;
×
4704
                }
4705

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

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

4728
        if (tmp->is_ignore) {
1✔
4729
                /*
4730
                 * This rule has instructed us that this pid is not to be
4731
                 * processed and should be ignored
4732
                 */
4733
                cgroup_dbg("Matching rule is an ignore rule\n");
×
4734
                ret = 0;
×
4735
                goto finished;
×
4736
        }
4737

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

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

4817
                newdest[j] = 0;
1✔
4818
                if (strcmp(newdest, tmp->destination) != 0) {
1✔
4819
                        /* Destination tag contains templates */
4820

4821
                        cgroup_dbg("control group %s is template\n", newdest);
×
4822
                        ret = cgroup_create_template_group(newdest, tmp, flags);
×
4823
                        if (ret) {
×
4824
                                cgroup_warn("failed to create cgroup based on template %s\n",
×
4825
                                            newdest);
4826
                                goto finished;
×
4827
                        }
4828
                }
4829

4830
                /* Apply the rule */
4831
                ret = cgroup_change_cgroup_path(newdest, pid,
1✔
4832
                                                (const char * const *)tmp->controllers);
1✔
4833
                if (ret) {
1✔
4834
                        cgroup_warn("failed to apply the rule. Error was: %d\n", ret);
×
4835
                        goto finished;
×
4836
                }
4837
                cgroup_dbg("OK!\n");
1✔
4838

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

4847
finished:
1✔
4848
        return ret;
221✔
4849
}
4850

4851
int cgroup_change_cgroup_uid_gid_flags(uid_t uid, gid_t gid, pid_t pid, int flags)
×
4852
{
4853
        return cgroup_change_cgroup_flags(uid, gid, NULL, pid, flags);
×
4854
}
4855

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

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

4887
        if (!cgroup_initialized) {
48✔
4888
                cgroup_warn("libcgroup is not initialized\n");
×
4889
                return ECGROUPNOTINITIALIZED;
×
4890
        }
4891
        memset(&cgrp, 0, sizeof(struct cgroup));
48✔
4892

4893

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

4906
                ret = cg_prepare_cgroup(&cgrp, pid, dest, controllers);
47✔
4907
                if (ret)
47✔
4908
                        return ret;
×
4909
        }
4910

4911
        /* Add process to cgroup */
4912
        ret = cgroup_attach_task_pid(&cgrp, pid);
48✔
4913
        if (ret) {
48✔
4914
                cgroup_warn("cgroup_attach_task_pid failed: %d\n", ret);
4✔
4915
                goto finished;
4✔
4916
        }
4917

4918
        /* Add all threads to cgroup */
4919
        snprintf(path, FILENAME_MAX, "/proc/%d/task/", pid);
44✔
4920
        dir = opendir(path);
44✔
4921
        if (!dir) {
44✔
4922
                last_errno = errno;
×
4923
                ret = ECGOTHER;
×
4924
                goto finished;
×
4925
        }
4926

4927
        while ((task_dir = readdir(dir)) != NULL) {
182✔
4928
                nr = sscanf(task_dir->d_name, "%i", &tid);
138✔
4929
                if (nr < 1)
138✔
4930
                        continue;
88✔
4931

4932
                if (tid == pid)
50✔
4933
                        continue;
44✔
4934

4935
                ret = cgroup_attach_task_pid(&cgrp, tid);
6✔
4936
                if (ret) {
6✔
4937
                        cgroup_warn("cgroup_attach_task_pid failed: %d\n", ret);
×
4938
                        break;
×
4939
                }
4940
        }
4941

4942
        closedir(dir);
44✔
4943

4944
finished:
48✔
4945
        cgroup_free_controllers(&cgrp);
48✔
4946

4947
        return ret;
48✔
4948
}
4949

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

4964
        dir = opendir(path);
1✔
4965
        if (!dir)
1✔
4966
                return -ECGOTHER;
×
4967

4968
        while ((pid_dir = readdir(dir)) != NULL) {
250✔
4969
                int err, pid;
4970
                uid_t euid;
4971
                gid_t egid;
4972
                char *procname = NULL;
249✔
4973

4974
                err = sscanf(pid_dir->d_name, "%i", &pid);
249✔
4975
                if (err < 1)
249✔
4976
                        continue;
65✔
4977

4978
                err = cgroup_get_uid_gid_from_procfs(pid, &euid, &egid);
184✔
4979
                if (err)
184✔
4980
                        continue;
×
4981

4982
                err = cgroup_get_procname_from_procfs(pid, &procname);
184✔
4983
                if (err)
184✔
4984
                        continue;
×
4985

4986
                err = cgroup_change_cgroup_flags(euid, egid, procname, pid, CGFLAG_USECACHE);
184✔
4987
                if (err)
184✔
4988
                        cgroup_dbg("cgroup change pid %i failed\n", pid);
×
4989

4990
                free(procname);
184✔
4991
        }
4992

4993
        closedir(dir);
1✔
4994
        return 0;
1✔
4995
}
4996

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

5008
        /* Loop variable */
5009
        int i = 0;
1✔
5010

5011
        pthread_rwlock_rdlock(&rl_lock);
1✔
5012

5013
        if (!(rl.head)) {
1✔
5014
                fprintf(fp, "The rules table is empty.\n\n");
×
5015
                pthread_rwlock_unlock(&rl_lock);
×
5016
                return;
×
5017
        }
5018

5019
        itr = rl.head;
1✔
5020
        while (itr) {
2✔
5021
                fprintf(fp, "Rule: %s", itr->username);
1✔
5022
                if (itr->procname)
1✔
5023
                        fprintf(fp, ":%s", itr->procname);
1✔
5024
                fprintf(fp, "\n");
1✔
5025

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

5033
                if (itr->gid == CGRULE_WILD)
1✔
5034
                        fprintf(fp, "  GID: any\n");
1✔
5035
                else if (itr->gid == CGRULE_INVALID)
×
5036
                        fprintf(fp, "  GID: N/A\n");
×
5037
                else
5038
                        fprintf(fp, "  GID: %d\n", itr->gid);
×
5039

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

5042
                fprintf(fp, "  CONTROLLERS:\n");
1✔
5043
                for (i = 0; i < MAX_MNT_ELEMENTS; i++) {
18✔
5044
                        if (itr->controllers[i])
17✔
5045
                                fprintf(fp, "    %s\n", itr->controllers[i]);
1✔
5046
                }
5047
                fprintf(fp, "  OPTIONS:\n");
1✔
5048
                if (itr->is_ignore)
1✔
5049
                        fprintf(fp, "    IS_IGNORE: True\n");
×
5050
                else
5051
                        fprintf(fp, "    IS_IGNORE: False\n");
1✔
5052
                fprintf(fp, "\n");
1✔
5053
                itr = itr->next;
1✔
5054
        }
5055
        pthread_rwlock_unlock(&rl_lock);
1✔
5056
}
5057

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

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

5076
        #ifdef CGROUP_DEBUG
5077
        cgroup_print_rules_config(stdout);
5078
        #endif
5079
finished:
×
5080
        return ret;
×
5081
}
5082

5083
/**
5084
 * Initializes the rules cache.
5085
 *        @return 0 on success, > 0 on error
5086
 */
5087
int cgroup_init_rules_cache(void)
1✔
5088
{
5089
        /* Return codes */
5090
        int ret = 0;
1✔
5091

5092
        /* Attempt to read the configuration file and cache the rules. */
5093
        ret = cgroup_parse_rules(true, CGRULE_INVALID, CGRULE_INVALID, NULL);
1✔
5094
        if (ret)
1✔
5095
                cgroup_dbg("Could not initialize rule cache, error was: %d\n", ret);
×
5096

5097
        return ret;
1✔
5098
}
5099

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

5116
        if (!cgroup_initialized) {
7✔
5117
                cgroup_warn("libcgroup is not initialized\n");
×
5118
                return ECGROUPNOTINITIALIZED;
×
5119
        }
5120

5121
        if (!current_path)
7✔
5122
                return ECGOTHER;
×
5123

5124
        mode = cgroup_setup_mode();
7✔
5125
        if (mode == CGROUP_MODE_LEGACY && !controller)
7✔
5126
                return ECGOTHER;
×
5127

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

5144
        ret = asprintf(&path, "/proc/%d/cgroup", pid);
6✔
5145
        if (ret <= 0) {
6✔
5146
                cgroup_warn("cannot allocate memory (/proc/pid/cgroup) ret %d\n", ret);
×
5147
                return ret;
×
5148
        }
5149

5150
        ret = ECGROUPNOTEXIST;
6✔
5151
        pid_cgrp_fd = fopen(path, "re");
6✔
5152
        if (!pid_cgrp_fd)
6✔
5153
                goto cleanup_path;
×
5154

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

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

5191
                                        if (ret == EOF) {
×
5192
                                                last_errno = errno;
×
5193
                                                ret = ECGEOF;
×
5194
                                                goto done;
6✔
5195
                                        }
5196
                                }
5197

5198
                                cgroup_warn("read failed for pid_cgroup_fd ret %d\n", ret);
×
5199
                                last_errno = errno;
×
5200
                                ret = ECGOTHER;
×
5201
                                goto done;
×
5202
                        }
5203

5204
                        /* check if the controller is enabled in cgroup v2 */
5205
                        if (controller) {
6✔
5206
                                ret = cgroupv2_controller_enabled(cgrp_path, controller);
2✔
5207
                                if (ret)
2✔
5208
                                        goto done;
1✔
5209
                        }
5210

5211
                        *current_path = strdup(cgrp_path);
5✔
5212
                        if (!*current_path) {
5✔
5213
                                last_errno = errno;
×
5214
                                ret = ECGOTHER;
×
5215
                                goto done;
×
5216
                        }
5217
                        ret = 0;
5✔
5218
                        goto done;
5✔
5219
                }
5220

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

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

5253
done:
×
5254
        pthread_rwlock_unlock(&cg_mount_table_lock);
6✔
5255
        fclose(pid_cgrp_fd);
6✔
5256
cleanup_path:
7✔
5257
        free(path);
7✔
5258

5259
        return ret;
7✔
5260
}
5261

5262
const char *cgroup_strerror(int code)
48✔
5263
{
5264
        int idx = code % ECGROUPNOTCOMPILED;
48✔
5265

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

5277
        return cgroup_strerror_codes[idx];
28✔
5278
}
5279

5280
/**
5281
 * Return last errno, which caused ECGOTHER error.
5282
 */
5283
int cgroup_get_last_errno(void)
20✔
5284
{
5285
        return last_errno;
20✔
5286
}
5287

5288
static int cg_walk_node(FTS *fts, FTSENT *ent, const int depth, struct cgroup_file_info *info,
8,467✔
5289
                        int dir)
5290
{
5291
        int ret = 0;
8,467✔
5292

5293
        if (!cgroup_initialized)
8,467✔
5294
                return ECGROUPNOTINITIALIZED;
×
5295

5296
        cgroup_dbg("seeing file %s\n", ent->fts_path);
8,467✔
5297

5298
        info->path = ent->fts_name;
8,467✔
5299
        info->parent = ent->fts_parent->fts_name;
8,467✔
5300
        info->full_path = ent->fts_path;
8,467✔
5301
        info->depth = ent->fts_level;
8,467✔
5302
        info->type = CGROUP_FILE_TYPE_OTHER;
8,467✔
5303

5304
        if (depth && (info->depth > depth))
8,467✔
5305
                return 0;
×
5306

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

5332
int cgroup_walk_tree_next(int depth, void **handle, struct cgroup_file_info *info, int base_level)
8,467✔
5333
{
5334
        struct cgroup_tree_handle *entry;
5335
        FTSENT *ent;
5336
        int ret = 0;
8,467✔
5337

5338
        if (!cgroup_initialized)
8,467✔
5339
                return ECGROUPNOTINITIALIZED;
×
5340

5341
        if (!handle)
8,467✔
5342
                return ECGINVAL;
×
5343

5344
        entry = (struct cgroup_tree_handle *) *handle;
8,467✔
5345

5346
        ent = fts_read(entry->fts);
8,467✔
5347
        if (!ent)
8,467✔
5348
                return ECGEOF;
41✔
5349
        if (!base_level && depth)
8,426✔
5350
                base_level = ent->fts_level + depth;
×
5351

5352
        ret = cg_walk_node(entry->fts, ent, base_level, info, entry->flags);
8,426✔
5353

5354
        *handle = entry;
8,426✔
5355
        return ret;
8,426✔
5356
}
5357

5358
int cgroup_walk_tree_end(void **handle)
41✔
5359
{
5360
        struct cgroup_tree_handle *entry;
5361

5362
        if (!cgroup_initialized)
41✔
5363
                return ECGROUPNOTINITIALIZED;
×
5364

5365
        if (!handle)
41✔
5366
                return ECGINVAL;
×
5367

5368
        entry = (struct cgroup_tree_handle *) *handle;
41✔
5369

5370
        fts_close(entry->fts);
41✔
5371
        free(entry);
41✔
5372
        *handle = NULL;
41✔
5373

5374
        return 0;
41✔
5375
}
5376

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

5387
        if (!cgroup_initialized)
41✔
5388
                return ECGROUPNOTINITIALIZED;
×
5389

5390
        if (!handle)
41✔
5391
                return ECGINVAL;
×
5392

5393
        cgroup_dbg("path is %s\n", base_path);
41✔
5394

5395
        if (!cg_build_path(base_path, full_path, controller))
41✔
5396
                return ECGOTHER;
×
5397

5398
        entry = calloc(1, sizeof(struct cgroup_tree_handle));
41✔
5399

5400
        if (!entry) {
41✔
5401
                last_errno = errno;
×
5402
                *handle = NULL;
×
5403
                return ECGOTHER;
×
5404
        }
5405

5406
        entry->flags |= CGROUP_WALK_TYPE_PRE_DIR;
41✔
5407

5408
        *base_level = 0;
41✔
5409
        cg_path[0] = full_path;
41✔
5410
        cg_path[1] = NULL;
41✔
5411

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

5430
        ret = cg_walk_node(entry->fts, ent, *base_level, info, entry->flags);
41✔
5431
        if (ret != 0) {
41✔
5432
                fts_close(entry->fts);
×
5433
                free(entry);
×
5434
                *handle = NULL;
×
5435
        } else {
5436
                *handle = entry;
41✔
5437
        }
5438
        return ret;
41✔
5439
}
5440

5441
int cgroup_walk_tree_set_flags(void **handle, int flags)
27✔
5442
{
5443
        struct cgroup_tree_handle *entry;
5444

5445
        if (!cgroup_initialized)
27✔
5446
                return ECGROUPNOTINITIALIZED;
×
5447

5448
        if (!handle)
27✔
5449
                return ECGINVAL;
×
5450

5451
        if ((flags & CGROUP_WALK_TYPE_PRE_DIR) &&
27✔
5452
            (flags & CGROUP_WALK_TYPE_POST_DIR))
×
5453
                return ECGINVAL;
×
5454

5455
        entry = (struct cgroup_tree_handle *) *handle;
27✔
5456
        entry->flags = flags;
27✔
5457

5458
        *handle = entry;
27✔
5459
        return 0;
27✔
5460
}
5461

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

5475
        read_bytes = getline(&line, &len, fp);
×
5476
        if (read_bytes == -1) {
×
5477
                ret = ECGEOF;
×
5478
                goto out_free;
×
5479
        }
5480

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

5488
        token = strtok_r(NULL, " ", &saveptr);
×
5489
        if (!token) {
×
5490
                ret = ECGINVAL;
×
5491
                goto out_free;
×
5492
        }
5493
        strncpy(cgrp_stat->value, token, CG_VALUE_MAX - 1);
×
5494

5495
out_free:
×
5496
        free(line);
×
5497

5498
        return ret;
×
5499
}
5500

5501
int cgroup_read_value_end(void **handle)
219✔
5502
{
5503
        FILE *fp;
5504

5505
        if (!cgroup_initialized)
219✔
5506
                return ECGROUPNOTINITIALIZED;
×
5507

5508
        if (!handle)
219✔
5509
                return ECGINVAL;
×
5510

5511
        fp = (FILE *)*handle;
219✔
5512
        fclose(fp);
219✔
5513
        *handle = NULL;
219✔
5514

5515
        return 0;
219✔
5516
}
5517

5518
int cgroup_read_value_next(void **handle, char *buffer, int max)
291✔
5519
{
5520
        char *ret_c;
5521
        int ret = 0;
291✔
5522
        FILE *fp;
5523

5524
        if (!cgroup_initialized)
291✔
5525
                return ECGROUPNOTINITIALIZED;
×
5526

5527
        if (!buffer || !handle)
291✔
5528
                return ECGINVAL;
×
5529

5530
        fp = (FILE *)*handle;
291✔
5531
        ret_c = fgets(buffer, max, fp);
291✔
5532
        if (ret_c == NULL)
291✔
5533
                ret = ECGEOF;
181✔
5534

5535
        return ret;
291✔
5536
}
5537

5538
int cgroup_read_value_begin(const char * const controller, const char *path,
222✔
5539
                            const char * const name, void **handle, char *buffer, int max)
5540
{
5541
        char stat_file[FILENAME_MAX + sizeof(name)];
5542
        char stat_path[FILENAME_MAX];
5543
        char *ret_c = NULL;
222✔
5544
        int ret = 0;
222✔
5545
        FILE *fp;
5546

5547
        if (!cgroup_initialized)
222✔
5548
                return ECGROUPNOTINITIALIZED;
×
5549

5550
        if (!buffer || !handle)
222✔
5551
                return ECGINVAL;
×
5552

5553
        if (!cg_build_path(path, stat_path, controller))
222✔
5554
                return ECGOTHER;
×
5555

5556
        snprintf(stat_file, sizeof(stat_file), "%s/%s", stat_path, name);
222✔
5557
        fp = fopen(stat_file, "re");
222✔
5558
        if (!fp) {
222✔
5559
                cgroup_warn("fopen failed\n");
3✔
5560
                last_errno = errno;
3✔
5561
                *handle = NULL;
3✔
5562
                return ECGOTHER;
3✔
5563
        }
5564

5565
        ret_c = fgets(buffer, max, fp);
219✔
5566
        if (ret_c == NULL)
219✔
5567
                ret = ECGEOF;
8✔
5568

5569
        *handle = fp;
219✔
5570

5571
        return ret;
219✔
5572
}
5573

5574
int cgroup_read_stats_end(void **handle)
×
5575
{
5576
        FILE *fp;
5577

5578
        if (!cgroup_initialized)
×
5579
                return ECGROUPNOTINITIALIZED;
×
5580

5581
        if (!handle)
×
5582
                return ECGINVAL;
×
5583

5584
        fp = (FILE *)*handle;
×
5585
        if (fp == NULL)
×
5586
                return ECGINVAL;
×
5587

5588
        fclose(fp);
×
5589

5590
        return 0;
×
5591
}
5592

5593
int cgroup_read_stats_next(void **handle, struct cgroup_stat *cgrp_stat)
×
5594
{
5595
        FILE *fp;
5596
        int ret = 0;
×
5597

5598
        if (!cgroup_initialized)
×
5599
                return ECGROUPNOTINITIALIZED;
×
5600

5601
        if (!handle || !cgrp_stat)
×
5602
                return ECGINVAL;
×
5603

5604
        fp = (FILE *)*handle;
×
5605
        ret = cg_read_stat(fp, cgrp_stat);
×
5606
        *handle = fp;
×
5607

5608
        return ret;
×
5609
}
5610

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

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

5623
        if (!cgrp_stat || !handle)
×
5624
                return ECGINVAL;
×
5625

5626
        if (!cg_build_path(path, stat_path, controller))
×
5627
                return ECGOTHER;
×
5628

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

5631
        fp = fopen(stat_file, "re");
×
5632
        if (!fp) {
×
5633
                cgroup_warn("fopen failed\n");
×
5634
                return ECGINVAL;
×
5635
        }
5636

5637
        ret = cg_read_stat(fp, cgrp_stat);
×
5638
        *handle = fp;
×
5639

5640
        return ret;
×
5641
}
5642

5643
int cgroup_get_task_end(void **handle)
×
5644
{
5645
        if (!cgroup_initialized)
×
5646
                return ECGROUPNOTINITIALIZED;
×
5647

5648
        if (!*handle)
×
5649
                return ECGINVAL;
×
5650

5651
        fclose((FILE *) *handle);
×
5652
        *handle = NULL;
×
5653

5654
        return 0;
×
5655
}
5656

5657
int cgroup_get_task_next(void **handle, pid_t *pid)
×
5658
{
5659
        int ret;
5660

5661
        if (!cgroup_initialized)
×
5662
                return ECGROUPNOTINITIALIZED;
×
5663

5664
        if (!handle)
×
5665
                return ECGINVAL;
×
5666

5667
        ret = fscanf((FILE *) *handle, "%u", pid);
×
5668

5669
        if (ret != 1) {
×
5670
                if (ret == EOF)
×
5671
                        return ECGEOF;
×
5672
                last_errno = errno;
×
5673
                return ECGOTHER;
×
5674
        }
5675

5676
        return 0;
×
5677
}
5678

5679
int cgroup_get_task_begin(const char *cgrp, const char *controller, void **handle, pid_t *pid)
×
5680
{
5681
        char path[FILENAME_MAX];
5682
        char *fullpath = NULL;
×
5683
        int ret = 0;
×
5684

5685
        if (!cgroup_initialized)
×
5686
                return ECGROUPNOTINITIALIZED;
×
5687

5688
        if (!cg_build_path(cgrp, path, controller))
×
5689
                return ECGOTHER;
×
5690

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

5693
        if (ret < 0) {
×
5694
                last_errno = errno;
×
5695
                return ECGOTHER;
×
5696
        }
5697

5698
        *handle = (void *) fopen(fullpath, "re");
×
5699
        free(fullpath);
×
5700

5701
        if (!*handle) {
×
5702
                last_errno = errno;
×
5703
                return ECGOTHER;
×
5704
        }
5705
        ret = cgroup_get_task_next(handle, pid);
×
5706

5707
        return ret;
×
5708
}
5709

5710
int cgroup_get_controller_end(void **handle)
8✔
5711
{
5712
        int *pos = (int *) *handle;
8✔
5713

5714
        if (!cgroup_initialized)
8✔
5715
                return ECGROUPNOTINITIALIZED;
×
5716

5717
        if (!pos)
8✔
5718
                return ECGINVAL;
×
5719

5720
        free(pos);
8✔
5721
        *handle = NULL;
8✔
5722

5723
        return 0;
8✔
5724
}
5725

5726
int cgroup_get_controller_next(void **handle, struct cgroup_mount_point *info)
72✔
5727
{
5728
        int *pos = (int *) *handle;
72✔
5729
        int ret = 0;
72✔
5730

5731
        if (!cgroup_initialized)
72✔
5732
                return ECGROUPNOTINITIALIZED;
×
5733

5734
        if (!pos)
72✔
5735
                return ECGINVAL;
×
5736

5737
        if (!info)
72✔
5738
                return ECGINVAL;
×
5739

5740
        pthread_rwlock_rdlock(&cg_mount_table_lock);
72✔
5741

5742
        if (cg_mount_table[*pos].name[0] == '\0') {
72✔
5743
                ret = ECGEOF;
×
5744
                goto out_unlock;
×
5745
        }
5746

5747
        if (strncmp(cg_mount_table[*pos].name, CGRP_FILE_PREFIX, CONTROL_NAMELEN_MAX) == 0)
72✔
5748
                /*
5749
                 * For now, hide the "cgroup" pseudo-controller from the user.  This may be
5750
                 * worth revisiting in the future.
5751
                 */
5752
                (*pos)++;
8✔
5753

5754
        if (cg_mount_table[*pos].name[0] == '\0') {
72✔
5755
                ret = ECGEOF;
8✔
5756
                goto out_unlock;
8✔
5757
        }
5758

5759
        strncpy(info->name, cg_mount_table[*pos].name, FILENAME_MAX - 1);
64✔
5760
        info->name[FILENAME_MAX - 1] = '\0';
64✔
5761

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

5765
        (*pos)++;
64✔
5766
        *handle = pos;
64✔
5767

5768
out_unlock:
72✔
5769
        pthread_rwlock_unlock(&cg_mount_table_lock);
72✔
5770

5771
        return ret;
72✔
5772
}
5773

5774
int cgroup_get_controller_begin(void **handle, struct cgroup_mount_point *info)
8✔
5775
{
5776
        int *pos;
5777

5778
        if (!cgroup_initialized)
8✔
5779
                return ECGROUPNOTINITIALIZED;
×
5780

5781
        if (!info)
8✔
5782
                return ECGINVAL;
×
5783

5784
        pos = malloc(sizeof(int));
8✔
5785

5786
        if (!pos) {
8✔
5787
                last_errno = errno;
×
5788
                return ECGOTHER;
×
5789
        }
5790

5791
        *pos = 0;
8✔
5792

5793
        *handle = pos;
8✔
5794

5795
        return cgroup_get_controller_next(handle, info);
8✔
5796
}
5797

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

5815
        sprintf(path, "/proc/%d/status", pid);
221✔
5816
        f = fopen(path, "re");
221✔
5817
        if (!f)
221✔
5818
                return ECGROUPNOTEXIST;
×
5819

5820
        while (fgets(buf, sizeof(buf), f)) {
2,210✔
5821
                if (!strncmp(buf, "Uid:", 4)) {
2,210✔
5822
                        if (sscanf((buf + strlen("Uid:") + 1), "%d%d%d%d",
221✔
5823
                                    &ruid, euid, &suid, &fsuid) != 4)
5824
                                break;
×
5825
                        cgroup_dbg("Scanned proc values are %d %d %d %d\n",
221✔
5826
                                   ruid, *euid, suid, fsuid);
5827
                        found_euid = true;
221✔
5828
                } else if (!strncmp(buf, "Gid:", 4)) {
1,989✔
5829
                        if (sscanf((buf + strlen("Gid:") + 1), "%d%d%d%d",
221✔
5830
                                   &rgid, egid, &sgid, &fsgid) != 4)
5831
                                break;
×
5832
                        cgroup_dbg("Scanned proc values are %d %d %d %d\n",
221✔
5833
                                   rgid, *egid, sgid, fsgid);
5834
                        found_egid = true;
221✔
5835
                }
5836
                if (found_euid && found_egid)
2,210✔
5837
                        break;
221✔
5838
        }
5839
        fclose(f);
221✔
5840

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

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

5877
#ifdef UNIT_TEST
5878
        sprintf(path, "%s", TEST_PROC_PID_CGROUP_FILE);
×
5879
#else
5880
        sprintf(path, "/proc/%d/cgroup", pid);
×
5881
#endif
5882
        f = fopen(path, "re");
×
5883
        if (!f)
×
5884
                return ECGROUPNOTEXIST;
×
5885

5886
        while (fgets(buf, sizeof(buf), f)) {
×
5887
                /*
5888
                 * Each line in /proc/{pid}/cgroup is like the following:
5889
                 *
5890
                 * {cg#}:{controller}:{cgname}
5891
                 *
5892
                 * e.g.
5893
                 * 7:devices:/user.slice
5894
                 */
5895

5896
                /* Read in the cgroup number.  We don't care about it */
5897
                stok_buff = strtok(buf, ":");
×
5898
                /* Read in the controller name */
5899
                stok_buff = strtok(NULL, ":");
×
5900

5901
                /*
5902
                 * After this point, we have allocated memory.  If we return
5903
                 * an error code after this, it's up to us to free the memory
5904
                 * we allocated
5905
                 */
5906
                controller_list[idx] = strndup(stok_buff, strlen(stok_buff) + 1);
×
5907

5908
                /* Read in the cgroup name */
5909
                stok_buff = strtok(NULL, ":");
×
5910

5911
                if (stok_buff == NULL) {
×
5912
                        /*
5913
                         * An empty controller is reported on some kernels.
5914
                         * It may look like this:
5915
                         * 0::/user.slice/user-1000.slice/session-1.scope
5916
                         *
5917
                         * Ignore this controller and move on.  Note that we
5918
                         * need to free the controller list entry we made.
5919
                         */
5920
                        free(controller_list[idx]);
×
5921
                        controller_list[idx] = NULL;
×
5922
                        continue;
×
5923
                }
5924

5925
                buff_len = strlen(stok_buff);
×
5926
                if (stok_buff[buff_len - 1] == '\n')
×
5927
                        buff_len--; /* Don't copy the trailing newline char */
×
5928

5929
                /* Read in the cgroup name */
5930
                if (buff_len > 1) {
×
5931
                        /* Strip off the leading '/' for every cgroup but the root cgroup */
5932
                        cgrp_list[idx] = malloc(buff_len);
×
5933
                        if (!cgrp_list[idx]) {
×
5934
                                cgroup_err("malloc failed: %s\n", strerror(errno));
×
5935
                                fclose(f);
×
5936
                                return ECGOTHER;
×
5937
                        }
5938
                        snprintf(cgrp_list[idx], buff_len, "%s", &stok_buff[1]);
×
5939
                } else {
5940
                        /* Retain the leading '/' since we're in the root cgroup */
5941
                        cgrp_list[idx] = strndup(stok_buff, buff_len);
×
5942
                }
5943

5944
                idx++;
×
5945
                if (idx >= list_len) {
×
5946
                        cgroup_warn("Maximum mount elements reached. Consider increasing ");
×
5947
                        cgroup_cont("MAX_MNT_ELEMENTS\n");
×
5948
                        break;
×
5949
                }
5950
        }
5951
        fclose(f);
×
5952
        return ret;
×
5953
}
5954

5955
/**
5956
 * Get process name from /proc/<pid>/status file.
5957
 * @param pid: The process id
5958
 * @param pname_status : The process name
5959
 * @return 0 on success, > 0 on error.
5960
 */
5961
static int cg_get_procname_from_proc_status(pid_t pid, char **procname_status)
221✔
5962
{
5963
        char path[FILENAME_MAX];
5964
        int ret = ECGFAIL;
221✔
5965
        char buf[4092];
5966
        FILE *f;
5967
        int len;
5968

5969
        sprintf(path, "/proc/%d/status", pid);
221✔
5970
        f = fopen(path, "re");
221✔
5971
        if (!f)
221✔
5972
                return ECGROUPNOTEXIST;
×
5973

5974
        while (fgets(buf, sizeof(buf), f)) {
221✔
5975
                if (!strncmp(buf, "Name:", 5)) {
221✔
5976
                        len = strlen(buf);
221✔
5977
                        if (buf[len - 1] == '\n')
221✔
5978
                                buf[len - 1] = '\0';
221✔
5979
                        *procname_status = strdup(buf + strlen("Name:") + 1);
221✔
5980
                        if (*procname_status == NULL) {
221✔
5981
                                last_errno = errno;
×
5982
                                ret = ECGOTHER;
×
5983
                                break;
×
5984
                        }
5985
                        ret = 0;
221✔
5986
                        break;
221✔
5987
                }
5988
        }
5989
        fclose(f);
221✔
5990
        return ret;
221✔
5991
}
5992

5993
/**
5994
 * Get process name from /proc/<pid>/cmdline file.
5995
 * This function is mainly for getting a script name (shell, perl, etc).
5996
 * A script name is written into the second or later argument of
5997
 * /proc/<pid>/cmdline. This function gets each argument and
5998
 * compares it to a process name taken from /proc/<pid>/status.
5999
 * @param pid: The process id
6000
 * @param pname_status : The process name taken from /proc/<pid>/status
6001
 * @param pname_cmdline: The process name taken from /proc/<pid>/cmdline
6002
 * @return 0 on success, > 0 on error.
6003
 */
6004
static int cg_get_procname_from_proc_cmdline(pid_t pid, const char *pname_status,
16✔
6005
                                             char **pname_cmdline)
6006
{
6007
        char pid_cwd_path[FILENAME_MAX];
6008
        char pid_cmd_path[FILENAME_MAX];
6009
        char buf_pname[FILENAME_MAX];
6010
        char buf_cwd[FILENAME_MAX];
6011
        int ret = ECGFAIL;
16✔
6012
        int len = 0;
16✔
6013
        int c = 0;
16✔
6014
        FILE *f;
6015

6016
        memset(buf_cwd, '\0', sizeof(buf_cwd));
16✔
6017
        sprintf(pid_cwd_path, "/proc/%d/cwd", pid);
16✔
6018

6019
        if (readlink(pid_cwd_path, buf_cwd, sizeof(buf_cwd)) < 0)
16✔
6020
                return ECGROUPNOTEXIST;
×
6021

6022
        /* readlink doesn't append a null */
6023
        buf_cwd[FILENAME_MAX - 1] = '\0';
16✔
6024

6025
        sprintf(pid_cmd_path, "/proc/%d/cmdline", pid);
16✔
6026
        f = fopen(pid_cmd_path, "re");
16✔
6027
        if (!f)
16✔
6028
                return ECGROUPNOTEXIST;
×
6029

6030
        while (c != EOF) {
246✔
6031
                c = fgetc(f);
245✔
6032
                if ((c != EOF) && (c != '\0') && (len < FILENAME_MAX - 1)) {
245✔
6033
                        buf_pname[len] = c;
226✔
6034
                        len++;
226✔
6035
                        continue;
226✔
6036
                }
6037
                buf_pname[len] = '\0';
19✔
6038

6039
                if (len == FILENAME_MAX - 1)
19✔
6040
                        while ((c != EOF) && (c != '\0'))
×
6041
                                c = fgetc(f);
×
6042

6043
                /*
6044
                 * The taken process name from /proc/<pid>/status is
6045
                 * shortened to 15 characters if it is over. So the name
6046
                 * should be compared by its length.
6047
                 */
6048
                if (strncmp(pname_status, basename(buf_pname), TASK_COMM_LEN - 1)) {
19✔
6049
                        len = 0;
4✔
6050
                        continue;
4✔
6051
                }
6052

6053
                if (buf_pname[0] == '/') {
15✔
6054
                        *pname_cmdline = strdup(buf_pname);
9✔
6055
                        if (*pname_cmdline == NULL) {
9✔
6056
                                last_errno = errno;
×
6057
                                ret = ECGOTHER;
×
6058
                                break;
×
6059
                        }
6060
                        ret = 0;
9✔
6061
                        break;
9✔
6062
                }
6063

6064
                strcat(buf_cwd, "/");
6✔
6065
                strcat(buf_cwd, buf_pname);
6✔
6066
                if (!realpath(buf_cwd, pid_cmd_path)) {
6✔
6067
                        last_errno = errno;
6✔
6068
                        ret = ECGOTHER;
6✔
6069
                        break;
6✔
6070
                }
6071

6072
                *pname_cmdline = strdup(pid_cmd_path);
×
6073
                if (*pname_cmdline == NULL) {
×
6074
                        last_errno = errno;
×
6075
                        ret = ECGOTHER;
×
6076
                        break;
×
6077
                }
6078
                ret = 0;
×
6079
                break;
×
6080
        }
6081
        fclose(f);
16✔
6082
        return ret;
16✔
6083
}
6084

6085
/**
6086
 * Get a process name from /proc file system.
6087
 * This function allocates memory for a process name, writes a process
6088
 * name onto it. So a caller should free the memory when unusing it.
6089
 * @param pid: The process id
6090
 * @param procname: The process name
6091
 * @return 0 on success, > 0 on error.
6092
 */
6093
int cgroup_get_procname_from_procfs(pid_t pid, char **procname)
221✔
6094
{
6095
        char path[FILENAME_MAX];
6096
        char buf[FILENAME_MAX];
6097
        char *pname_cmdline;
6098
        char *pname_status;
6099
        int ret;
6100

6101
        ret = cg_get_procname_from_proc_status(pid, &pname_status);
221✔
6102
        if (ret)
221✔
6103
                return ret;
×
6104

6105
        /* Get the full patch of process name from /proc/<pid>/exe. */
6106
        memset(buf, '\0', sizeof(buf));
221✔
6107
        snprintf(path, FILENAME_MAX, "/proc/%d/exe", pid);
221✔
6108
        if (readlink(path, buf, sizeof(buf)) < 0) {
221✔
6109
                /*
6110
                 * readlink() fails if a kernel thread, and a process name
6111
                 * is taken from /proc/<pid>/status.
6112
                 */
6113
                *procname = pname_status;
132✔
6114
                return 0;
132✔
6115
        }
6116
        /* readlink doesn't append a null */
6117
        buf[FILENAME_MAX - 1] = '\0';
89✔
6118

6119
        if (!strncmp(pname_status, basename(buf), TASK_COMM_LEN - 1)) {
89✔
6120
                /*
6121
                 * The taken process name from /proc/<pid>/status is
6122
                 * shortened to 15 characters if it is over. So the name
6123
                 * should be compared by its length.
6124
                 */
6125
                free(pname_status);
73✔
6126
                *procname = strdup(buf);
73✔
6127
                if (*procname == NULL) {
73✔
6128
                        last_errno = errno;
×
6129
                        return ECGOTHER;
×
6130
                }
6131
                return 0;
73✔
6132
        }
6133

6134
        /*
6135
         * The above strncmp() is not 0 if a shell script, because
6136
         * /proc/<pid>/exe links a shell command (/bin/bash etc.) and the
6137
         * pname_status represents a shell script name. Then the full path
6138
         * of a shell script is taken from /proc/<pid>/cmdline.
6139
         */
6140
        ret = cg_get_procname_from_proc_cmdline(pid, pname_status, &pname_cmdline);
16✔
6141
        if (!ret) {
16✔
6142
                *procname = pname_cmdline;
9✔
6143
                free(pname_status);
9✔
6144
                return 0;
9✔
6145
        }
6146

6147
        /*
6148
         * The above strncmp() is not 0 also if executing a symbolic link,
6149
         * /proc/pid/exe points to real executable name then. Return it as
6150
         * the last resort.
6151
         */
6152
        free(pname_status);
7✔
6153
        *procname = strdup(buf);
7✔
6154
        if (*procname == NULL) {
7✔
6155
                last_errno = errno;
×
6156
                return ECGOTHER;
×
6157
        }
6158

6159
        return 0;
7✔
6160
}
6161

6162
int cgroup_register_unchanged_process(pid_t pid, int flags)
4✔
6163
{
6164
        char buff[sizeof(CGRULE_SUCCESS_STORE_PID)];
6165
        struct sockaddr_un addr;
6166
        size_t ret_len;
6167
        int ret = 1;
4✔
6168
        int sk;
6169

6170
        sk = socket(PF_UNIX, SOCK_STREAM, 0);
4✔
6171
        if (sk < 0)
4✔
6172
                return 1;
×
6173

6174
        memset(&addr, 0, sizeof(addr));
4✔
6175
        addr.sun_family = AF_UNIX;
4✔
6176
        strcpy(addr.sun_path, CGRULE_CGRED_SOCKET_PATH);
4✔
6177

6178
        if (connect(sk, (struct sockaddr *)&addr,
4✔
6179
            sizeof(addr.sun_family) + strlen(CGRULE_CGRED_SOCKET_PATH)) < 0) {
6180
                /* If the daemon does not work, this function returns 0 as success. */
6181
                ret = 0;
4✔
6182
                goto close;
4✔
6183
        }
6184
        if (write(sk, &pid, sizeof(pid)) < 0)
×
6185
                goto close;
×
6186

6187
        if (write(sk, &flags, sizeof(flags)) < 0)
×
6188
                goto close;
×
6189

6190
        ret_len = read(sk, buff, sizeof(buff));
×
6191
        if (ret_len != sizeof(buff))
×
6192
                goto close;
×
6193

6194
        if (strncmp(buff, CGRULE_SUCCESS_STORE_PID, sizeof(buff)))
×
6195
                goto close;
×
6196

6197
        ret = 0;
×
6198
close:
4✔
6199
        close(sk);
4✔
6200

6201
        return ret;
4✔
6202
}
6203

6204
int cgroup_get_subsys_mount_point(const char *controller, char **mount_point)
×
6205
{
6206
        int ret = ECGROUPNOTEXIST;
×
6207
        int i;
6208

6209
        if (!cgroup_initialized)
×
6210
                return ECGROUPNOTINITIALIZED;
×
6211

6212
        if (!controller)
×
6213
                return ECGINVAL;
×
6214

6215
        pthread_rwlock_rdlock(&cg_mount_table_lock);
×
6216
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
×
6217
                if (strncmp(cg_mount_table[i].name, controller, FILENAME_MAX))
×
6218
                        continue;
×
6219

6220
                *mount_point = strdup(cg_mount_table[i].mount.path);
×
6221

6222
                if (!*mount_point) {
×
6223
                        last_errno = errno;
×
6224
                        ret = ECGOTHER;
×
6225
                        goto out_exit;
×
6226
                }
6227

6228
                ret = 0;
×
6229
                break;
×
6230
        }
6231
out_exit:
×
6232
        pthread_rwlock_unlock(&cg_mount_table_lock);
×
6233

6234
        return ret;
×
6235
}
6236

6237
int cgroup_get_all_controller_end(void **handle)
180✔
6238
{
6239
        FILE *proc_cgrp = (FILE *) *handle;
180✔
6240

6241
        if (!proc_cgrp)
180✔
6242
                return ECGINVAL;
×
6243

6244
        fclose(proc_cgrp);
180✔
6245
        *handle = NULL;
180✔
6246

6247
        return 0;
180✔
6248
}
6249

6250
int cgroup_get_all_controller_next(void **handle, struct controller_data *info)
2,700✔
6251
{
6252
        FILE *proc_cgrp = (FILE *) *handle;
2,700✔
6253
        int hierarchy, num_cgrps, enabled;
6254
        char subsys_name[FILENAME_MAX];
6255
        int err = 0;
2,700✔
6256

6257
        if (!proc_cgrp)
2,700✔
6258
                return ECGINVAL;
×
6259

6260
        if (!info)
2,700✔
6261
                return ECGINVAL;
×
6262

6263
        /*
6264
         * check Linux Kernel sources/kernel/cgroup/cgroup.c cgroup_init_early(),
6265
         * MAX_CGROUP_TYPE_NAMELEN check for details on why 32 is used.
6266
         */
6267
        err = fscanf(proc_cgrp, "%32s %d %d %d\n", subsys_name, &hierarchy, &num_cgrps,
2,700✔
6268
                     &enabled);
6269

6270
        if (err != 4)
2,700✔
6271
                return ECGEOF;
180✔
6272

6273
        strncpy(info->name, subsys_name, FILENAME_MAX);
2,520✔
6274
        info->name[FILENAME_MAX-1] = '\0';
2,520✔
6275
        info->hierarchy = hierarchy;
2,520✔
6276
        info->num_cgroups = num_cgrps;
2,520✔
6277
        info->enabled = enabled;
2,520✔
6278

6279
        return 0;
2,520✔
6280
}
6281

6282
int cgroup_get_all_controller_begin(void **handle, struct controller_data *info)
180✔
6283
{
6284
        FILE *proc_cgroup = NULL;
180✔
6285
        char buf[FILENAME_MAX];
6286
        int ret;
6287

6288
        if (!info)
180✔
6289
                return ECGINVAL;
×
6290

6291
        proc_cgroup = fopen("/proc/cgroups", "re");
180✔
6292
        if (!proc_cgroup) {
180✔
6293
                last_errno = errno;
×
6294
                return ECGOTHER;
×
6295
        }
6296

6297
        if (!fgets(buf, FILENAME_MAX, proc_cgroup)) {
180✔
6298
                last_errno = errno;
×
6299
                fclose(proc_cgroup);
×
6300
                *handle = NULL;
×
6301
                return ECGOTHER;
×
6302
        }
6303
        *handle = proc_cgroup;
180✔
6304

6305
        ret = cgroup_get_all_controller_next(handle, info);
180✔
6306
        if (ret != 0) {
180✔
6307
                fclose(proc_cgroup);
×
6308
                *handle = NULL;
×
6309
        }
6310

6311
        return ret;
180✔
6312
}
6313

6314
static int pid_compare(const void *a, const void *b)
84✔
6315
{
6316
        const pid_t *pid1, *pid2;
6317

6318
        pid1 = (pid_t *) a;
84✔
6319
        pid2 = (pid_t *) b;
84✔
6320

6321
        return (*pid1 - *pid2);
84✔
6322
}
6323

6324
/* pids needs to be completely uninitialized so that we can set it up
6325
 *
6326
 * Caller must free up pids.
6327
 */
6328
static int read_pids(char *path, pid_t **pids, int *size)
10✔
6329
{
6330
        int tot_pids = 16;
10✔
6331
        pid_t *tmp_list;
6332
        FILE *pid_file;
6333
        int n = 0;
10✔
6334
        int err;
6335

6336
        pid_file = fopen(path, "r");
10✔
6337
        if (!pid_file) {
10✔
6338
                last_errno = errno;
×
6339
                *pids = NULL;
×
6340
                *size = 0;
×
6341
                if (errno == ENOENT)
×
6342
                        return ECGROUPUNSUPP;
×
6343
                else
6344
                        return ECGOTHER;
×
6345
        }
6346

6347
        /* Keep doubling the memory allocated if needed */
6348
        tmp_list = malloc(sizeof(pid_t) * tot_pids);
10✔
6349
        if (!tmp_list) {
10✔
6350
                last_errno = errno;
×
6351
                fclose(pid_file);
×
6352
                return ECGOTHER;
×
6353
        }
6354

6355
        while (!feof(pid_file)) {
22✔
6356
                while (!feof(pid_file) && n < tot_pids) {
62✔
6357
                        pid_t pid;
6358

6359
                        err = fscanf(pid_file, "%u", &pid);
60✔
6360
                        if (err == EOF)
60✔
6361
                                break;
10✔
6362
                        tmp_list[n] = pid;
50✔
6363
                        n++;
50✔
6364
                }
6365
                if (!feof(pid_file)) {
12✔
6366
                        pid_t *orig_list = tmp_list;
2✔
6367

6368
                        tot_pids *= 2;
2✔
6369
                        tmp_list = realloc(tmp_list, sizeof(pid_t) * tot_pids);
2✔
6370
                        if (!tmp_list) {
2✔
6371
                                last_errno = errno;
×
6372
                                fclose(pid_file);
×
6373
                                free(orig_list);
×
6374
                                *pids = NULL;
×
6375
                                *size = 0;
×
6376
                                return ECGOTHER;
×
6377
                        }
6378
                }
6379
        }
6380
        fclose(pid_file);
10✔
6381

6382
        *size = n;
10✔
6383
        qsort(tmp_list, n, sizeof(pid_t), &pid_compare);
10✔
6384
        *pids = tmp_list;
10✔
6385

6386
        return 0;
10✔
6387
}
6388

6389
int cgroup_get_procs(const char *name, const char *controller, pid_t **pids, int *size)
10✔
6390
{
6391
        char cgroup_path[FILENAME_MAX];
6392

6393
        cg_build_path(name, cgroup_path, controller);
10✔
6394
        strncat(cgroup_path, "/cgroup.procs", FILENAME_MAX-strlen(cgroup_path));
10✔
6395

6396
        return read_pids(cgroup_path, pids, size);
10✔
6397
}
6398

6399
int cgroup_get_threads(const char *name, const char *controller, pid_t **pids, int *size)
×
6400
{
6401
        char cgroup_path[FILENAME_MAX];
6402

6403
        cg_build_path(name, cgroup_path, controller);
×
6404
        strncat(cgroup_path, "/cgroup.threads", FILENAME_MAX-strlen(cgroup_path));
×
6405

6406
        return read_pids(cgroup_path, pids, size);
×
6407
}
6408

6409
int cgroup_dictionary_create(struct cgroup_dictionary **dict,
12✔
6410
                             int flags)
6411
{
6412
        if (!dict)
12✔
6413
                return ECGINVAL;
×
6414

6415
        *dict = (struct cgroup_dictionary *) calloc(1, sizeof(struct cgroup_dictionary));
12✔
6416
        if (!*dict) {
12✔
6417
                last_errno = errno;
×
6418
                return ECGOTHER;
×
6419
        }
6420
        (*dict)->flags = flags;
12✔
6421

6422
        return 0;
12✔
6423
}
6424

6425
int cgroup_dictionary_add(struct cgroup_dictionary *dict, const char *name, const char *value)
24✔
6426
{
6427
        struct cgroup_dictionary_item *it;
6428

6429
        if (!dict)
24✔
6430
                return ECGINVAL;
×
6431

6432
        it = (struct cgroup_dictionary_item *) malloc(sizeof(struct cgroup_dictionary_item));
24✔
6433
        if (!it) {
24✔
6434
                last_errno = errno;
×
6435
                return ECGOTHER;
×
6436
        }
6437

6438
        it->next = NULL;
24✔
6439
        it->name = name;
24✔
6440
        it->value = value;
24✔
6441

6442
        if (dict->tail) {
24✔
6443
                dict->tail->next = it;
12✔
6444
                dict->tail = it;
12✔
6445
        } else {
6446
                /* It is the first item */
6447
                dict->tail = it;
12✔
6448
                dict->head = it;
12✔
6449
        }
6450

6451
        return 0;
24✔
6452
}
6453

6454
int cgroup_dictionary_free(struct cgroup_dictionary *dict)
16✔
6455
{
6456
        struct cgroup_dictionary_item *it;
6457

6458
        if (!dict)
16✔
6459
                return ECGINVAL;
4✔
6460

6461
        it = dict->head;
12✔
6462
        while (it) {
36✔
6463
                struct cgroup_dictionary_item *del = it;
24✔
6464

6465
                it = it->next;
24✔
6466
                if (!(dict->flags & CG_DICT_DONT_FREE_ITEMS)) {
24✔
6467
                        free((void *)del->value);
24✔
6468
                        free((void *)del->name);
24✔
6469
                }
6470
                free(del);
24✔
6471
        }
6472
        free(dict);
12✔
6473

6474
        return 0;
12✔
6475
}
6476

6477
int cgroup_dictionary_iterator_begin(struct cgroup_dictionary *dict, void **handle,
12✔
6478
                                     const char **name, const char **value)
6479
{
6480
        struct cgroup_dictionary_iterator *iter;
6481

6482
        *handle = NULL;
12✔
6483

6484
        if (!dict)
12✔
6485
                return ECGINVAL;
×
6486

6487
        iter = (struct cgroup_dictionary_iterator *)
6488
                malloc(sizeof(struct cgroup_dictionary_iterator));
12✔
6489
        if (!iter) {
12✔
6490
                last_errno = errno;
×
6491
                return ECGOTHER;
×
6492
        }
6493

6494
        iter->item = dict->head;
12✔
6495
        *handle = iter;
12✔
6496

6497
        return cgroup_dictionary_iterator_next(handle, name, value);
12✔
6498
}
6499

6500
int cgroup_dictionary_iterator_next(void **handle, const char **name, const char **value)
36✔
6501
{
6502
        struct cgroup_dictionary_iterator *iter;
6503

6504
        if (!handle)
36✔
6505
                return ECGINVAL;
×
6506

6507
        iter = *handle;
36✔
6508

6509
        if (!iter)
36✔
6510
                return ECGINVAL;
×
6511

6512
        if (!iter->item)
36✔
6513
                return ECGEOF;
12✔
6514

6515
        *name = iter->item->name;
24✔
6516
        *value = iter->item->value;
24✔
6517
        iter->item = iter->item->next;
24✔
6518

6519
        return 0;
24✔
6520
}
6521

6522
void cgroup_dictionary_iterator_end(void **handle)
12✔
6523
{
6524
        if (!handle)
12✔
6525
                return;
×
6526

6527
        free(*handle);
12✔
6528
        *handle = NULL;
12✔
6529
}
6530

6531
int cgroup_get_subsys_mount_point_begin(const char *controller, void **handle, char *path)
×
6532
{
6533
        int i;
6534

6535
        if (!cgroup_initialized)
×
6536
                return ECGROUPNOTINITIALIZED;
×
6537
        if (!handle || !path || !controller)
×
6538
                return ECGINVAL;
×
6539

6540
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++)
×
6541
                if (strcmp(controller, cg_mount_table[i].name) == 0)
×
6542
                        break;
×
6543

6544
        if (cg_mount_table[i].name[0] == '\0') {
×
6545
                /* The controller is not mounted at all */
6546
                *handle = NULL;
×
6547
                *path = '\0';
×
6548
                return ECGEOF;
×
6549
        }
6550

6551
        /*
6552
         * 'handle' is pointer to struct cg_mount_point, which should be
6553
         * returned next.
6554
         */
6555
        *handle = cg_mount_table[i].mount.next;
×
6556
        strcpy(path, cg_mount_table[i].mount.path);
×
6557

6558
        return 0;
×
6559
}
6560

6561
int cgroup_get_subsys_mount_point_next(void **handle, char *path)
×
6562
{
6563
        struct cg_mount_point *it;
6564

6565
        if (!cgroup_initialized)
×
6566
                return ECGROUPNOTINITIALIZED;
×
6567

6568
        if (!handle || !path)
×
6569
                return ECGINVAL;
×
6570

6571
        it = *handle;
×
6572
        if (!it) {
×
6573
                *handle = NULL;
×
6574
                *path = '\0';
×
6575
                return ECGEOF;
×
6576
        }
6577

6578
        *handle = it->next;
×
6579
        strcpy(path, it->path);
×
6580

6581
        return 0;
×
6582
}
6583

6584
int cgroup_get_subsys_mount_point_end(void **handle)
×
6585
{
6586
        if (!cgroup_initialized)
×
6587
                return ECGROUPNOTINITIALIZED;
×
6588

6589
        if (!handle)
×
6590
                return ECGINVAL;
×
6591

6592
        *handle = NULL;
×
6593

6594
        return 0;
×
6595
}
6596

6597
int cgroup_get_controller_version(const char * const controller, enum cg_version_t * const version)
3,189✔
6598
{
6599
        int i;
6600

6601
        if (!version)
3,189✔
6602
                return ECGINVAL;
×
6603

6604
        if (!controller && strlen(cg_cgroup_v2_mount_path) > 0) {
3,189✔
6605
                *version = CGROUP_V2;
56✔
6606
                return 0;
56✔
6607
        }
6608

6609
        if (!controller)
3,133✔
6610
                return ECGINVAL;
×
6611

6612
        *version = CGROUP_UNK;
3,133✔
6613

6614
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
8,974✔
6615
                if (strncmp(cg_mount_table[i].name, controller,
8,971✔
6616
                            sizeof(cg_mount_table[i].name)) == 0) {
6617
                        *version = cg_mount_table[i].version;
3,130✔
6618
                        return 0;
3,130✔
6619
                }
6620
        }
6621

6622
        return ECGROUPNOTEXIST;
3✔
6623
}
6624

6625
static int search_and_append_mnt_path(struct cg_mount_point **mount_point, char *path)
18✔
6626
{
6627
        struct cg_mount_point *mnt_point, *mnt_tmp, *mnt_prev;
6628

6629
        mnt_tmp = *mount_point;
18✔
6630
        while (mnt_tmp) {
18✔
6631
                if (strcmp(mnt_tmp->path, path) == 0)
16✔
6632
                        return ECGVALUEEXISTS;
16✔
6633

6634
                mnt_prev = mnt_tmp;
×
6635
                mnt_tmp = mnt_tmp->next;
×
6636
        }
6637

6638
        mnt_point = malloc(sizeof(struct cg_mount_point));
2✔
6639
        if (mnt_point == NULL) {
2✔
6640
                last_errno = errno;
×
6641
                return ECGOTHER;
×
6642
        }
6643

6644
        strncpy(mnt_point->path, path, FILENAME_MAX - 1);
2✔
6645
        mnt_point->path[FILENAME_MAX - 1] = '\0';
2✔
6646

6647
        mnt_point->next = NULL;
2✔
6648

6649
        if (*mount_point == NULL)
2✔
6650
                *mount_point = mnt_point;
2✔
6651
        else
6652
                mnt_prev->next = mnt_point;
×
6653

6654
        return 0;
2✔
6655
}
6656

6657
/**
6658
 * List the mount paths, that matches the specified version
6659
 *
6660
 *        @param cgrp_version The cgroup type/version
6661
 *        @param mount_paths Holds the list of mount paths
6662
 *        @return 0 success and list of mounts paths in mount_paths
6663
 *                ECGOTHER on failure and mount_paths is NULL.
6664
 */
6665
int cgroup_list_mount_points(const enum cg_version_t cgrp_version, char ***mount_paths)
4✔
6666
{
6667
        struct cg_mount_point *mount_point, *mnt_tmp = NULL;
4✔
6668
        char **mnt_paths = NULL;
4✔
6669
        int i, idx = 0;
4✔
6670
        int ret;
6671

6672
        if (!cgroup_initialized)
4✔
6673
                return ECGROUPNOTINITIALIZED;
×
6674

6675
        if (cgrp_version != CGROUP_V1 && cgrp_version != CGROUP_V2)
4✔
6676
                return ECGINVAL;
×
6677

6678
        pthread_rwlock_rdlock(&cg_mount_table_lock);
4✔
6679

6680
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
40✔
6681
                if (cg_mount_table[i].version != cgrp_version)
36✔
6682
                        continue;
18✔
6683

6684
                mount_point = &cg_mount_table[i].mount;
18✔
6685
                while (mount_point) {
36✔
6686
                        ret = search_and_append_mnt_path(&mnt_tmp, mount_point->path);
18✔
6687
                        if (ret != 0 && ret != ECGVALUEEXISTS)
18✔
6688
                                goto err;
×
6689

6690
                        /* Avoid adding duplicate mount points */
6691
                        if (ret != ECGVALUEEXISTS)
18✔
6692
                                idx++;
2✔
6693

6694
                        mount_point = mount_point->next;
18✔
6695
                }
6696
        }
6697

6698
        /*
6699
         * Cgroup v2 can be mounted without any controller and these mount
6700
         * paths are not part of the cg_mount_table.  Check and append
6701
         * them to mnt_paths.
6702
         */
6703
        if (cgrp_version == CGROUP_V2 && cg_cgroup_v2_empty_mount_paths) {
4✔
6704
                mount_point = cg_cgroup_v2_empty_mount_paths;
×
6705
                while (mount_point) {
×
6706
                        ret = search_and_append_mnt_path(&mnt_tmp, mount_point->path);
×
6707
                        if (ret)
×
6708
                                goto err;
×
6709

6710
                        idx++;
×
6711
                        mount_point = mount_point->next;
×
6712
                }
6713
        }
6714

6715
        mnt_paths = malloc(sizeof(char *) * (idx + 1));
4✔
6716
        if (mnt_paths == NULL) {
4✔
6717
                last_errno = errno;
×
6718
                ret = ECGOTHER;
×
6719
                goto err;
×
6720
        }
6721

6722
        for (i = 0, mount_point = mnt_tmp;
4✔
6723
             mount_point;
6✔
6724
             mount_point = mount_point->next, i++) {
2✔
6725

6726
                mnt_paths[i] = strdup(mount_point->path);
2✔
6727
                if (mnt_paths[i] == NULL) {
2✔
6728
                        last_errno = errno;
×
6729
                        ret = ECGOTHER;
×
6730
                        goto err;
×
6731
                }
6732
        }
6733
        mnt_paths[i] = NULL;
4✔
6734

6735
        ret = 0;
4✔
6736
        *mount_paths = mnt_paths;
4✔
6737

6738
err:
4✔
6739
        pthread_rwlock_unlock(&cg_mount_table_lock);
4✔
6740

6741
        while (mnt_tmp) {
6✔
6742
                mount_point = mnt_tmp;
2✔
6743
                mnt_tmp = mnt_tmp->next;
2✔
6744
                free(mount_point);
2✔
6745
        }
6746

6747
        if (ret != 0 && mnt_paths) {
4✔
6748
                for (i = 0; i < idx; i++)
×
6749
                        free(mnt_paths[i]);
×
6750
                free(mnt_paths);
×
6751
                *mount_paths = NULL;
×
6752
        }
6753

6754
        return ret;
4✔
6755
}
6756

6757
const struct cgroup_library_version *cgroup_version(void)
4✔
6758
{
6759
        return &library_version;
4✔
6760
}
6761

6762
/**
6763
 * Finds the current cgroup setup mode (legacy/unified/hybrid).
6764
 * Returns unknown of failure and setup mode on success.
6765
 */
6766
enum cg_setup_mode_t cgroup_setup_mode(void)
195✔
6767
{
6768
#define CGROUP2_SUPER_MAGIC        0x63677270
6769
#define CGROUP_SUPER_MAGIC        0x27E0EB
6770

6771
        unsigned int cg_setup_mode_bitmask = 0U;
195✔
6772
        enum cg_setup_mode_t setup_mode;
6773
        struct statfs cgrp_buf;
6774
        int i, ret = 0;
195✔
6775

6776
        if (!cgroup_initialized)
195✔
6777
                return ECGROUPNOTINITIALIZED;
×
6778

6779
        setup_mode = CGROUP_MODE_UNK;
195✔
6780

6781
        pthread_rwlock_wrlock(&cg_mount_table_lock);
195✔
6782
        for (i = 0; cg_mount_table[i].name[0] != '\0'; i++) {
1,950✔
6783
                ret = statfs(cg_mount_table[i].mount.path, &cgrp_buf);
1,755✔
6784
                if (ret) {
1,755✔
6785
                        setup_mode = CGROUP_MODE_UNK;
×
6786
                        cgroup_err("Failed to get stats of '%s'\n", cg_mount_table[i].mount.path);
×
6787
                        goto out;
×
6788
                }
6789

6790
                if (cgrp_buf.f_type == CGROUP2_SUPER_MAGIC)
1,755✔
6791
                        cg_setup_mode_bitmask |= (1U << 0);
1,755✔
6792
                else if (cgrp_buf.f_type == CGROUP_SUPER_MAGIC)
×
6793
                        cg_setup_mode_bitmask |= (1U << 1);
×
6794
        }
6795

6796
        if (cg_cgroup_v2_empty_mount_paths)
195✔
6797
                cg_setup_mode_bitmask |= (1U << 0);
×
6798

6799
        if (cg_setup_mode_bitmask & (1U << 0) && cg_setup_mode_bitmask & (1U << 1))
195✔
6800
                setup_mode = CGROUP_MODE_HYBRID;
×
6801
        else if (cg_setup_mode_bitmask & (1U << 0))
195✔
6802
                setup_mode = CGROUP_MODE_UNIFIED;
195✔
6803
        else if (cg_setup_mode_bitmask & (1U << 1))
×
6804
                setup_mode = CGROUP_MODE_LEGACY;
×
6805

6806
out:
×
6807
        pthread_rwlock_unlock(&cg_mount_table_lock);
195✔
6808
        return setup_mode;
195✔
6809
}
6810

6811
int cgroup_get_controller_count(struct cgroup *cgrp)
25✔
6812
{
6813
        if (!cgrp)
25✔
6814
                return -1;
×
6815

6816
        return cgrp->index;
25✔
6817
}
6818

6819
struct cgroup_controller *cgroup_get_controller_by_index(struct cgroup *cgrp, int index)
83✔
6820
{
6821
        if (!cgrp)
83✔
6822
                return NULL;
×
6823

6824
        if (index >= cgrp->index)
83✔
6825
                return NULL;
×
6826

6827
        return cgrp->controller[index];
83✔
6828
}
6829

6830
char *cgroup_get_controller_name(struct cgroup_controller *controller)
83✔
6831
{
6832
        if (!controller)
83✔
6833
                return NULL;
×
6834

6835
        return controller->name;
83✔
6836
}
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