• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/procd/jail/cgroups.c

  1 /*
  2  * Copyright (C) 2020 Daniel Golle <daniel@makrotopia.org>
  3  *
  4  * This program is free software; you can redistribute it and/or modify
  5  * it under the terms of the GNU Lesser General Public License version 2.1
  6  * as published by the Free Software Foundation
  7  *
  8  * This program is distributed in the hope that it will be useful,
  9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 11  * GNU General Public License for more details.
 12  *
 13  * reads unified cgroup config as proposed in
 14  * https://github.com/opencontainers/runtime-spec/pull/1040
 15  * attempt conversion from cgroup1 -> cgroup2
 16  * https://github.com/containers/crun/blob/0.14.1/crun.1.md#cgroup-v2
 17  *
 18  * ToDo:
 19  *  - convert cgroup1 net_prio and net_cls to eBPF program
 20  *  - rdma (anyone?) intelrdt (anyone?)
 21  */
 22 
 23 #define _GNU_SOURCE
 24 
 25 #include <errno.h>
 26 #include <fcntl.h>
 27 #include <limits.h>
 28 #include <poll.h>
 29 #include <stdlib.h>
 30 #include <stdio.h>
 31 #include <string.h>
 32 #include <sys/stat.h>
 33 #include <sys/mman.h>
 34 #include <time.h>
 35 #include <unistd.h>
 36 #include <libgen.h>
 37 #include <inttypes.h>
 38 
 39 #include <libubox/avl.h>
 40 #include <libubox/avl-cmp.h>
 41 #include <libubox/blobmsg.h>
 42 #include <libubox/list.h>
 43 #include <libubox/utils.h>
 44 
 45 #include "log.h"
 46 #include "cgroups.h"
 47 #include "cgroups-bpf.h"
 48 
 49 #define CGROUP_ROOT "/sys/fs/cgroup/"
 50 #define CGROUP_IO_WEIGHT_MAX 10000
 51 #define CGROUP_DRAIN_TIMEOUT_MSEC 1000
 52 
 53 struct cgval {
 54         struct avl_node avl;
 55         char *val;
 56 };
 57 
 58 struct avl_tree cgvals;
 59 static char *cgroup_path;
 60 static bool initialized;
 61 static bool destroyed;
 62 
 63 void cgroups_prepare(void) {
 64         initialized = false;
 65         destroyed = false;
 66 }
 67 
 68 void cgroups_init(const char *p) {
 69         avl_init(&cgvals, avl_strcmp, false, NULL);
 70         cgroup_path = strdup(p);
 71         initialized = true;
 72 }
 73 
 74 static void cgroups_set(const char *key, const char *val)
 75 {
 76         struct cgval *valp;
 77 
 78         valp = avl_find_element(&cgvals, key, valp, avl);
 79         if (!valp) {
 80                 valp = malloc(sizeof(struct cgval));
 81                 if (!valp)
 82                         exit(ENOMEM);
 83 
 84                 valp->avl.key = strdup(key);
 85                 avl_insert(&cgvals, &valp->avl);
 86         } else {
 87                 DEBUG("overwriting previous cgroup2 assignment %s=\"%s\"!\n", key, valp->val);
 88                 free(valp->val);
 89         }
 90 
 91         valp->val = strdup(val);
 92 }
 93 
 94 void cgroups_free(void)
 95 {
 96         struct cgval *valp, *tmp;
 97 
 98         if (initialized) {
 99                 avl_remove_all_elements(&cgvals, valp, avl, tmp) {
100                         free((void *)(valp->avl.key));
101                         free(valp->val);
102                         free(valp);
103                 }
104                 free(cgroup_path);
105                 cgroup_path = NULL;
106                 initialized = false;
107         }
108 }
109 
110 static int cgroups_write_attr(const char *attr, const char *val, size_t vlen)
111 {
112         char *ent;
113         int fd, ret = 0;
114         size_t len;
115 
116         if (!cgroup_path)
117                 return -ENODEV;
118 
119         len = strlen(cgroup_path) + 1 + strlen(attr) + 1;
120         ent = malloc(len);
121         if (!ent)
122                 return -ENOMEM;
123 
124         snprintf(ent, len, "%s/%s", cgroup_path, attr);
125         fd = open(ent, O_WRONLY);
126         if (fd < 0) {
127                 ret = -errno;
128                 free(ent);
129                 return ret;
130         }
131 
132         if (write(fd, val, vlen) < 0)
133                 ret = -errno;
134 
135         close(fd);
136         free(ent);
137         return ret;
138 }
139 
140 int cgroups_kill_all(void)
141 {
142         return cgroups_write_attr("cgroup.kill", "1", 1);
143 }
144 
145 int cgroups_set_frozen(bool frozen)
146 {
147         return cgroups_write_attr("cgroup.freeze", frozen ? "1" : "", 1);
148 }
149 
150 int cgroups_reclaim(int64_t bytes, int32_t swappiness)
151 {
152         char val[64];
153         int len, ret;
154         int attempt;
155 
156         if (bytes < 0)
157                 return -EINVAL;
158 
159         if (swappiness >= 0)
160                 len = snprintf(val, sizeof(val), "%" PRId64 " swappiness=%" PRId32,
161                                bytes, swappiness);
162         else
163                 len = snprintf(val, sizeof(val), "%" PRId64, bytes);
164         if (len < 0 || len >= (int)sizeof(val))
165                 return -EINVAL;
166 
167         for (attempt = 0; attempt < 2; ++attempt) {
168                 ret = cgroups_write_attr("memory.reclaim", val, len);
169                 if (ret != -EINTR)
170                         break;
171         }
172         if (ret == -ENOENT)
173                 ret = -ENODEV;
174         return ret;
175 }
176 
177 int cgroups_attach_pid(pid_t pid)
178 {
179         char *ent;
180         int fd, ret = 0;
181         size_t len;
182 
183         if (!cgroup_path)
184                 return -ENODEV;
185 
186         len = strlen(cgroup_path) + strlen("/cgroup.procs") + 1;
187         ent = malloc(len);
188         if (!ent)
189                 return -ENOMEM;
190 
191         snprintf(ent, len, "%s/cgroup.procs", cgroup_path);
192         fd = open(ent, O_WRONLY);
193         if (fd < 0) {
194                 ret = -errno;
195                 free(ent);
196                 return ret;
197         }
198 
199         if (dprintf(fd, "%d", pid) < 0)
200                 ret = -errno;
201 
202         close(fd);
203         free(ent);
204         return ret;
205 }
206 
207 static void cgroups_compute_subtree_control(char *out, size_t outlen)
208 {
209         struct cgval *valp;
210         bool cpuset = false,
211              cpu = false,
212              hugetlb = false,
213              io = false,
214              memory = false,
215              pids = false,
216              rdma = false;
217         char *p;
218 
219         out[0] = '\0';
220 
221         avl_for_each_element(&cgvals, valp, avl) {
222                 const char *ent = (const char *)valp->avl.key;
223 
224                 if (!strncmp("cpuset.", ent, 7))
225                         cpuset = true;
226                 else if (!strncmp("cpu.", ent, 4))
227                         cpu = true;
228                 else if (!strncmp("hugetlb.", ent, 8))
229                         hugetlb = true;
230                 else if (!strncmp("io.", ent, 3))
231                         io = true;
232                 else if (!strncmp("memory.", ent, 7))
233                         memory = true;
234                 else if (!strncmp("pids.", ent, 5))
235                         pids = true;
236                 else if (!strncmp("rdma.", ent, 5))
237                         rdma = true;
238         }
239 
240         if (cpuset)
241                 strncat(out, "+cpuset ", outlen - strlen(out) - 1);
242 
243         if (cpu)
244                 strncat(out, "+cpu ", outlen - strlen(out) - 1);
245 
246         if (hugetlb)
247                 strncat(out, "+hugetlb ", outlen - strlen(out) - 1);
248 
249         if (io)
250                 strncat(out, "+io ", outlen - strlen(out) - 1);
251 
252         if (memory)
253                 strncat(out, "+memory ", outlen - strlen(out) - 1);
254 
255         if (pids)
256                 strncat(out, "+pids ", outlen - strlen(out) - 1);
257 
258         if (rdma)
259                 strncat(out, "+rdma ", outlen - strlen(out) - 1);
260 
261         p = strchr(out, '\0');
262         if (p > out && p[-1] == ' ')
263                 p[-1] = '\0';
264 }
265 
266 static bool cgroups_populated(int fd)
267 {
268         char buf[128];
269         ssize_t len;
270         char *val;
271 
272         if (lseek(fd, 0, SEEK_SET) < 0)
273                 return false;
274 
275         len = read(fd, buf, sizeof(buf) - 1);
276         if (len <= 0)
277                 return false;
278 
279         buf[len] = '\0';
280         val = strstr(buf, "populated ");
281         if (!val)
282                 return false;
283 
284         return val[strlen("populated ")] == '1';
285 }
286 
287 static void cgroups_wait_empty(void)
288 {
289         struct timespec deadline, now;
290         struct pollfd pfd;
291         int remaining;
292         size_t len;
293         char *ent;
294         int fd;
295 
296         len = strlen(cgroup_path) + strlen("/cgroup.events") + 1;
297         ent = malloc(len);
298         if (!ent)
299                 return;
300 
301         snprintf(ent, len, "%s/cgroup.events", cgroup_path);
302         fd = open(ent, O_RDONLY);
303         free(ent);
304         if (fd < 0)
305                 return;
306 
307         clock_gettime(CLOCK_MONOTONIC, &deadline);
308         deadline.tv_sec += CGROUP_DRAIN_TIMEOUT_MSEC / 1000;
309 
310         pfd.fd = fd;
311         pfd.events = POLLPRI;
312 
313         while (cgroups_populated(fd)) {
314                 clock_gettime(CLOCK_MONOTONIC, &now);
315                 remaining = (deadline.tv_sec - now.tv_sec) * 1000 +
316                             (deadline.tv_nsec - now.tv_nsec) / 1000000;
317                 if (remaining <= 0)
318                         break;
319 
320                 if (poll(&pfd, 1, remaining) < 0 && errno != EINTR)
321                         break;
322         }
323 
324         close(fd);
325 }
326 
327 void cgroups_destroy(void)
328 {
329         char *sep;
330 
331         destroyed = true;
332 
333         if (!cgroup_path)
334                 return;
335 
336         cgroups_kill_all();
337         cgroups_wait_empty();
338 
339         if (rmdir(cgroup_path) && errno == EBUSY)
340                 ERROR("failed to remove busy cgroup %s\n", cgroup_path);
341 
342         sep = strrchr(cgroup_path, '/');
343         if (sep && sep != cgroup_path) {
344                 *sep = '\0';
345                 (void)rmdir(cgroup_path);
346                 *sep = '/';
347         }
348 }
349 
350 bool cgroups_destroyed(void)
351 {
352         return destroyed;
353 }
354 
355 void cgroups_create(void)
356 {
357         char subtree_control[64] = { 0 };
358         char *cdir, *ent;
359         size_t maxlen;
360         int fd;
361 
362         if (!cgroup_path)
363                 return;
364 
365         DEBUG("creating cgroup %s\n", cgroup_path);
366         mkdir_p(cgroup_path, 0700);
367 
368         cgroups_compute_subtree_control(subtree_control, sizeof(subtree_control));
369         if (!subtree_control[0]) {
370                 DEBUG("no cgroup controllers requested, skipping subtree_control walk\n");
371                 return;
372         }
373 
374         maxlen = strlen(cgroup_path) + strlen("/cgroup.subtree_control") + 1;
375         ent = malloc(maxlen);
376         if (!ent)
377                 exit(ENOMEM);
378 
379         DEBUG("recursively applying cgroup.subtree_control = \"%s\"\n", subtree_control);
380         cdir = &cgroup_path[strlen(CGROUP_ROOT) - 2];
381         while ((cdir = strchr(cdir + 1, '/'))) {
382                 *cdir = '\0';
383                 snprintf(ent, maxlen, "%s/cgroup.subtree_control", cgroup_path);
384                 DEBUG(" * %s\n", ent);
385                 if ((fd = open(ent, O_WRONLY)) < 0) {
386                         ERROR("can't open %s: %m\n", ent);
387                         *cdir = '/';
388                         continue;
389                 }
390                 if (write(fd, subtree_control, strlen(subtree_control)) == -1)
391                         ERROR("can't write to %s: %m\n", ent);
392                 close(fd);
393                 *cdir = '/';
394         }
395         free(ent);
396 }
397 
398 /* a swap limit narrows nothing on a kernel built without swap */
399 static bool cgroups_attr_vacuous(const char *key)
400 {
401         if (strcmp(key, "memory.swap.max"))
402                 return false;
403 
404         return access("/proc/swaps", F_OK) != 0;
405 }
406 
407 int cgroups_configure(void)
408 {
409         struct cgval *valp;
410         size_t maxlen = 0;
411         int fd, dirfd;
412         int ret = 0;
413         char *ent;
414 
415         if (!cgroup_path)
416                 return 0;
417 
418         avl_for_each_element(&cgvals, valp, avl) {
419                 size_t klen = strlen((char *)valp->avl.key);
420 
421                 if (klen > maxlen)
422                         maxlen = klen;
423         }
424         maxlen += strlen(cgroup_path) + 2;
425 
426         ent = malloc(maxlen);
427         if (!ent)
428                 exit(ENOMEM);
429 
430         avl_for_each_element(&cgvals, valp, avl) {
431                 DEBUG("applying cgroup2 %s=\"%s\"\n", (char *)valp->avl.key, valp->val);
432                 snprintf(ent, maxlen, "%s/%s", cgroup_path, (char *)valp->avl.key);
433                 fd = open(ent, O_WRONLY);
434                 if (fd < 0) {
435                         if (cgroups_attr_vacuous((char *)valp->avl.key)) {
436                                 INFO("%s is unavailable here, \"%s\" narrows nothing\n",
437                                      ent, valp->val);
438                                 continue;
439                         }
440                         ERROR("can't open %s: %m\n", ent);
441                         jail_cgroup_refused((char *)valp->avl.key);
442                         ret = ENOTSUP;
443                         break;
444                 }
445                 if (dprintf(fd, "%s", valp->val) < 0) {
446                         ERROR("can't write to %s: %m\n", ent);
447                         close(fd);
448                         if (cgroups_attr_vacuous((char *)valp->avl.key))
449                                 continue;
450 
451                         jail_cgroup_refused((char *)valp->avl.key);
452                         ret = ENOTSUP;
453                         break;
454                 }
455                 close(fd);
456         }
457         free(ent);
458 
459         if (ret)
460                 return ret;
461 
462         dirfd = open(cgroup_path, O_DIRECTORY);
463         if (dirfd < 0) {
464                 ERROR("can't open %s: %m\n", cgroup_path);
465         } else {
466                 attach_cgroups_ebpf(dirfd);
467                 close(dirfd);
468         }
469 
470         return 0;
471 }
472 
473 int cgroups_apply(pid_t pid)
474 {
475         int ret;
476 
477         cgroups_create();
478 
479         ret = cgroups_configure();
480         if (ret)
481                 return ret;
482 
483         cgroups_attach_pid(pid);
484 
485         return 0;
486 }
487 
488 enum {
489         OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_MAJOR,
490         OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_MINOR,
491         OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_WEIGHT,
492         OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_LEAFWEIGHT,
493         __OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_MAX,
494 };
495 
496 static const struct blobmsg_policy oci_linux_cgroups_blockio_weightdevice_policy[] = {
497         [OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_MAJOR] = { "major", BLOBMSG_CAST_INT64 },
498         [OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_MINOR] = { "minor", BLOBMSG_CAST_INT64 },
499         [OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_WEIGHT] = { "weight", BLOBMSG_TYPE_INT32 },
500         [OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_LEAFWEIGHT] = { "leafWeight", BLOBMSG_TYPE_INT32 },
501 };
502 
503 enum {
504         OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MAJOR,
505         OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MINOR,
506         OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_RATE,
507         __OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MAX,
508 };
509 
510 static const struct blobmsg_policy oci_linux_cgroups_blockio_throttledevice_policy[] = {
511         [OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MAJOR] = { "major", BLOBMSG_CAST_INT64 },
512         [OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MINOR] = { "minor", BLOBMSG_CAST_INT64 },
513         [OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_RATE] = { "rate", BLOBMSG_CAST_INT64 },
514 };
515 
516 enum {
517         OCI_LINUX_CGROUPS_BLOCKIO_WEIGHT,
518         OCI_LINUX_CGROUPS_BLOCKIO_LEAFWEIGHT,
519         OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE,
520         OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEREADBPSDEVICE,
521         OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEWRITEBPSDEVICE,
522         OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEREADIOPSDEVICE,
523         OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEWRITEIOPSDEVICE,
524         __OCI_LINUX_CGROUPS_BLOCKIO_MAX,
525 };
526 
527 static const struct blobmsg_policy oci_linux_cgroups_blockio_policy[] = {
528         [OCI_LINUX_CGROUPS_BLOCKIO_WEIGHT] = { "weight", BLOBMSG_TYPE_INT32 },
529         [OCI_LINUX_CGROUPS_BLOCKIO_LEAFWEIGHT] = { "leafWeight", BLOBMSG_TYPE_INT32 },
530         [OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE] = { "weightDevice", BLOBMSG_TYPE_ARRAY },
531         [OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEREADBPSDEVICE] = { "throttleReadBpsDevice", BLOBMSG_TYPE_ARRAY },
532         [OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEWRITEBPSDEVICE] = { "throttleWriteBpsDevice", BLOBMSG_TYPE_ARRAY },
533         [OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEREADIOPSDEVICE] = { "throttleReadIOPSDevice", BLOBMSG_TYPE_ARRAY },
534         [OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEWRITEIOPSDEVICE] = { "throttleWriteIOPSDevice", BLOBMSG_TYPE_ARRAY },
535 };
536 
537 struct posix_dev {
538         uint64_t major;
539         uint64_t minor;
540 };
541 
542 struct iomax_line {
543         struct avl_node avl;
544         struct posix_dev dev;
545         uint64_t rbps;
546         uint64_t wbps;
547         uint64_t riops;
548         uint64_t wiops;
549 };
550 
551 static int avl_devcmp(const void *k1, const void *k2, void *ptr)
552 {
553         struct posix_dev *d1 = (struct posix_dev *)k1, *d2 = (struct posix_dev *)k2;
554 
555         if (d1->major < d2->major)
556                 return -1;
557 
558         if (d1->major > d2->major)
559                 return 1;
560 
561         if (d1->minor < d2->minor)
562                 return -1;
563 
564         if (d1->minor > d2->minor)
565                 return 1;
566 
567         return 0;
568 }
569 
570 static struct iomax_line *get_iomax_line(struct avl_tree *iomax, uint64_t major, uint64_t minor)
571 {
572         struct iomax_line *l;
573         struct posix_dev d;
574         d.major = major;
575         d.minor = minor;
576         l = avl_find_element(iomax, &d, l, avl);
577         if (!l) {
578                 l = malloc(sizeof(struct iomax_line));
579                 if (!l)
580                         exit(ENOMEM);
581 
582                 l->dev.major = d.major;
583                 l->dev.minor = d.minor;
584                 l->avl.key = &l->dev;
585                 l->rbps = -1;
586                 l->wbps = -1;
587                 l->riops = -1;
588                 l->wiops = -1;
589                 avl_insert(iomax, &l->avl);
590         }
591 
592         return l;
593 }
594 
595 static int parseOCIlinuxcgroups_legacy_blockio(struct blob_attr *msg)
596 {
597         struct blob_attr *tb[__OCI_LINUX_CGROUPS_BLOCKIO_MAX],
598                          *tbwd[__OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_MAX],
599                          *tbtd[__OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MAX],
600                          *cur;
601         int rem;
602         int weight = -1;
603         size_t numweightstrs = 0, numiomaxstrs = 0, strtotlen = 1;
604         char **weightstrs = NULL, **iomaxstrs = NULL, **curstr;
605         char *weightstr, *iomaxstr;
606         struct avl_tree iomax;
607         struct iomax_line *curiomax, *tmp;
608 
609         blobmsg_parse(oci_linux_cgroups_blockio_policy, __OCI_LINUX_CGROUPS_BLOCKIO_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
610 
611         if (tb[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHT]) {
612                 weight = blobmsg_get_u32(tb[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHT]);
613                 ++numweightstrs;
614         }
615 
616         if (weight > CGROUP_IO_WEIGHT_MAX)
617                 return ERANGE;
618 
619         if (tb[OCI_LINUX_CGROUPS_BLOCKIO_LEAFWEIGHT])
620                 return jail_unsupported(tb[OCI_LINUX_CGROUPS_BLOCKIO_LEAFWEIGHT]);
621 
622         blobmsg_for_each_attr(cur, tb[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE], rem)
623                 ++numweightstrs;
624 
625         weightstrs = calloc(numweightstrs + 1, sizeof(char *));
626         if (!weightstrs)
627                 exit(ENOMEM);
628 
629         numweightstrs = 0;
630 
631         if (weight > -1)
632                 if (asprintf(&weightstrs[numweightstrs++], "default %d", weight) < 0)
633                         return ENOMEM;
634 
635         blobmsg_for_each_attr(cur, tb[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE], rem) {
636                 uint64_t major, minor;
637                 int devweight = weight;
638 
639                 blobmsg_parse(oci_linux_cgroups_blockio_weightdevice_policy, __OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_MAX, tbwd, blobmsg_data(cur), blobmsg_len(cur));
640                 if (!tbwd[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_MAJOR] ||
641                     !tbwd[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_MINOR])
642                         return ENODATA;
643 
644                 if (!tbwd[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_WEIGHT] &&
645                     !tbwd[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_LEAFWEIGHT])
646                         return ENODATA;
647 
648                 if (tbwd[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_WEIGHT])
649                         devweight = blobmsg_get_u32(tbwd[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_WEIGHT]);
650 
651                 if (devweight > CGROUP_IO_WEIGHT_MAX)
652                         return ERANGE;
653 
654                 if (tbwd[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_LEAFWEIGHT])
655                         return jail_unsupported(
656                                 tbwd[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_LEAFWEIGHT]);
657 
658                 major = blobmsg_cast_u64(tbwd[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_MAJOR]);
659                 minor = blobmsg_cast_u64(tbwd[OCI_LINUX_CGROUPS_BLOCKIO_WEIGHTDEVICE_MINOR]);
660 
661                 if (asprintf(&weightstrs[numweightstrs++], "%" PRIu64 ":%" PRIu64 " %u", major, minor, devweight) < 0)
662                         return ENOMEM;
663         }
664 
665         if (numweightstrs) {
666                 curstr = weightstrs;
667                 while (*curstr)
668                         strtotlen += strlen(*(curstr++)) + 1;
669 
670                 weightstr = calloc(strtotlen, sizeof(char));
671                 if (!weightstr)
672                         exit(ENOMEM);
673 
674                 curstr = weightstrs;
675                 while (*curstr) {
676                         strcat(weightstr, *curstr);
677                         strcat(weightstr, "\n");
678                         free(*(curstr++));
679                 }
680 
681                 cgroups_set("io.bfq.weight", weightstr);
682                 free(weightstr);
683         };
684 
685         free(weightstrs);
686 
687         avl_init(&iomax, avl_devcmp, false, NULL);
688 
689         blobmsg_for_each_attr(cur, tb[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEREADBPSDEVICE], rem) {
690                 struct iomax_line *l;
691 
692                 blobmsg_parse(oci_linux_cgroups_blockio_throttledevice_policy, __OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MAX, tbtd, blobmsg_data(cur), blobmsg_len(cur));
693 
694                 if (!tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MAJOR] ||
695                     !tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MINOR] ||
696                     !tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_RATE])
697                         return ENODATA;
698 
699                 l = get_iomax_line(&iomax,
700                                    blobmsg_cast_u64(tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MAJOR]),
701                                    blobmsg_cast_u64(tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MINOR]));
702 
703                 l->rbps = blobmsg_cast_u64(tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_RATE]);
704         }
705 
706         blobmsg_for_each_attr(cur, tb[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEWRITEBPSDEVICE], rem) {
707                 struct iomax_line *l;
708 
709                 blobmsg_parse(oci_linux_cgroups_blockio_throttledevice_policy, __OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MAX, tbtd, blobmsg_data(cur), blobmsg_len(cur));
710 
711                 if (!tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MAJOR] ||
712                     !tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MINOR] ||
713                     !tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_RATE])
714                         return ENODATA;
715 
716                 l = get_iomax_line(&iomax,
717                                    blobmsg_cast_u64(tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MAJOR]),
718                                    blobmsg_cast_u64(tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MINOR]));
719 
720                 l->wbps = blobmsg_cast_u64(tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_RATE]);
721         }
722 
723         blobmsg_for_each_attr(cur, tb[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEREADIOPSDEVICE], rem) {
724                 struct iomax_line *l;
725 
726                 blobmsg_parse(oci_linux_cgroups_blockio_throttledevice_policy, __OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MAX, tbtd, blobmsg_data(cur), blobmsg_len(cur));
727 
728                 if (!tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MAJOR] ||
729                     !tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MINOR] ||
730                     !tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_RATE])
731                         return ENODATA;
732 
733                 l = get_iomax_line(&iomax,
734                                    blobmsg_cast_u64(tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MAJOR]),
735                                    blobmsg_cast_u64(tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MINOR]));
736 
737                 l->riops = blobmsg_cast_u64(tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_RATE]);
738         }
739 
740         blobmsg_for_each_attr(cur, tb[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEWRITEIOPSDEVICE], rem) {
741                 struct iomax_line *l;
742 
743                 blobmsg_parse(oci_linux_cgroups_blockio_throttledevice_policy, __OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MAX, tbtd, blobmsg_data(cur), blobmsg_len(cur));
744 
745                 if (!tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MAJOR] ||
746                     !tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MINOR] ||
747                     !tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_RATE])
748                         return ENODATA;
749 
750                 l = get_iomax_line(&iomax,
751                                    blobmsg_cast_u64(tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MAJOR]),
752                                    blobmsg_cast_u64(tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_MINOR]));
753 
754                 l->wiops = blobmsg_cast_u64(tbtd[OCI_LINUX_CGROUPS_BLOCKIO_THROTTLEDEVICE_RATE]);
755         }
756 
757         avl_for_each_element(&iomax, curiomax, avl)
758                 ++numiomaxstrs;
759 
760         if (!numiomaxstrs)
761                 return 0;
762 
763         iomaxstrs = calloc(numiomaxstrs + 1, sizeof(char *));
764         if (!iomaxstrs)
765                 exit(ENOMEM);
766 
767         numiomaxstrs = 0;
768 
769         avl_for_each_element(&iomax, curiomax, avl) {
770                 char iomaxlstr[160];
771                 char lstr[32];
772 
773                 sprintf(iomaxlstr, "%" PRIu64 ":%" PRIu64 " ", curiomax->dev.major, curiomax->dev.minor);
774 
775                 if (curiomax->rbps != -1) {
776                         sprintf(lstr, "rbps=%" PRIu64 " ", curiomax->rbps);
777                         strcat(iomaxlstr, lstr);
778                 }
779                 if (curiomax->wbps != -1) {
780                         sprintf(lstr, "wbps=%" PRIu64 " ", curiomax->wbps);
781                         strcat(iomaxlstr, lstr);
782                 }
783                 if (curiomax->riops != -1) {
784                         sprintf(lstr, "riops=%" PRIu64 " ", curiomax->riops);
785                         strcat(iomaxlstr, lstr);
786                 }
787                 if (curiomax->wiops != -1) {
788                         sprintf(lstr, "wiops=%" PRIu64 " ", curiomax->wiops);
789                         strcat(iomaxlstr, lstr);
790                 }
791 
792                 iomaxstrs[numiomaxstrs++] = strdup(iomaxlstr);
793         }
794 
795         avl_for_each_element_safe(&iomax, curiomax, avl, tmp) {
796                 avl_delete(&iomax, &curiomax->avl);
797                 free(curiomax);
798         }
799 
800         strtotlen = 1; /* 1 accounts for \0 at end of string */
801         if (numiomaxstrs) {
802                 curstr = iomaxstrs;
803                 while (*curstr)
804                         strtotlen += strlen(*(curstr++)) + 1; /* +1 accounts for \n at end of line */
805 
806                 iomaxstr = calloc(strtotlen, sizeof(char));
807                 if (!iomaxstr)
808                         exit(ENOMEM);
809 
810                 curstr = iomaxstrs;
811 
812                 while (*curstr) {
813                         strcat(iomaxstr, *curstr);
814                         strcat(iomaxstr, "\n");
815                         free(*(curstr++));
816                 }
817 
818                 cgroups_set("io.max", iomaxstr);
819                 free(iomaxstr);
820         };
821 
822         free(iomaxstrs);
823 
824         return 0;
825 }
826 
827 
828 enum {
829         OCI_LINUX_CGROUPS_CPU_SHARES,
830         OCI_LINUX_CGROUPS_CPU_PERIOD,
831         OCI_LINUX_CGROUPS_CPU_QUOTA,
832         OCI_LINUX_CGROUPS_CPU_BURST,
833         OCI_LINUX_CGROUPS_CPU_IDLE,
834         OCI_LINUX_CGROUPS_CPU_REALTIMERUNTIME,
835         OCI_LINUX_CGROUPS_CPU_REALTIMEPERIOD,
836         OCI_LINUX_CGROUPS_CPU_CPUS,
837         OCI_LINUX_CGROUPS_CPU_MEMS,
838         __OCI_LINUX_CGROUPS_CPU_MAX,
839 };
840 
841 static const struct blobmsg_policy oci_linux_cgroups_cpu_policy[] = {
842         [OCI_LINUX_CGROUPS_CPU_SHARES] = { "shares", BLOBMSG_CAST_INT64 },
843         [OCI_LINUX_CGROUPS_CPU_PERIOD] = { "period", BLOBMSG_CAST_INT64 },
844         [OCI_LINUX_CGROUPS_CPU_QUOTA] = { "quota", BLOBMSG_CAST_INT64 }, /* signed int64! */
845         [OCI_LINUX_CGROUPS_CPU_BURST] = { "burst", BLOBMSG_CAST_INT64 },
846         [OCI_LINUX_CGROUPS_CPU_IDLE] = { "idle", BLOBMSG_CAST_INT64 },
847         [OCI_LINUX_CGROUPS_CPU_REALTIMEPERIOD] = { "realtimePeriod", BLOBMSG_CAST_INT64 },
848         [OCI_LINUX_CGROUPS_CPU_REALTIMERUNTIME] = { "realtimeRuntime", BLOBMSG_CAST_INT64 },
849         [OCI_LINUX_CGROUPS_CPU_CPUS] = { "cpus", BLOBMSG_TYPE_STRING },
850         [OCI_LINUX_CGROUPS_CPU_MEMS] = { "mems", BLOBMSG_TYPE_STRING },
851 };
852 
853 static int parseOCIlinuxcgroups_legacy_cpu(struct blob_attr *msg)
854 {
855         struct blob_attr *tb[__OCI_LINUX_CGROUPS_CPU_MAX];
856         uint64_t shares, period = 0;
857         int64_t quota = -2; /* unset */
858         char tmp[32] = { 0 };
859 
860         blobmsg_parse(oci_linux_cgroups_cpu_policy, __OCI_LINUX_CGROUPS_CPU_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
861 
862         /* no equivalent in cgroup2 */
863         if (tb[OCI_LINUX_CGROUPS_CPU_REALTIMEPERIOD])
864                 return jail_unsupported(tb[OCI_LINUX_CGROUPS_CPU_REALTIMEPERIOD]);
865 
866         if (tb[OCI_LINUX_CGROUPS_CPU_REALTIMERUNTIME])
867                 return jail_unsupported(tb[OCI_LINUX_CGROUPS_CPU_REALTIMERUNTIME]);
868 
869         if (tb[OCI_LINUX_CGROUPS_CPU_SHARES]) {
870                 shares = blobmsg_cast_u64(tb[OCI_LINUX_CGROUPS_CPU_SHARES]);
871                 if ((shares < 2) || (shares > 262144))
872                         return ERANGE;
873 
874                 snprintf(tmp, sizeof(tmp), "%" PRIu64, (((uint64_t)1) + ((shares - 2) * 9999) / 262142));
875                 cgroups_set("cpu.weight", tmp);
876                 tmp[0] = '\0';
877         }
878 
879         if (tb[OCI_LINUX_CGROUPS_CPU_QUOTA])
880                 quota = blobmsg_cast_s64(tb[OCI_LINUX_CGROUPS_CPU_QUOTA]);
881 
882         if (tb[OCI_LINUX_CGROUPS_CPU_PERIOD])
883                 period = blobmsg_cast_u64(tb[OCI_LINUX_CGROUPS_CPU_PERIOD]);
884 
885         if (period) {
886                 if (quota >= 0)
887                         snprintf(tmp, sizeof(tmp), "%" PRId64 " %" PRIu64 , quota, period);
888                 else
889                         snprintf(tmp, sizeof(tmp), "max %" PRIu64, period); /* assume default */
890         } else if (quota >= 0) {
891                 snprintf(tmp, sizeof(tmp), "%" PRId64, quota);
892         } else if (quota == -1) {
893                 strcpy(tmp, "max");
894         }
895 
896         if (tmp[0])
897                 cgroups_set("cpu.max", tmp);
898 
899         if (tb[OCI_LINUX_CGROUPS_CPU_BURST]) {
900                 snprintf(tmp, sizeof(tmp), "%" PRIu64,
901                          blobmsg_cast_u64(tb[OCI_LINUX_CGROUPS_CPU_BURST]));
902                 cgroups_set("cpu.max.burst", tmp);
903         }
904 
905         if (tb[OCI_LINUX_CGROUPS_CPU_IDLE]) {
906                 snprintf(tmp, sizeof(tmp), "%" PRId64,
907                          blobmsg_cast_s64(tb[OCI_LINUX_CGROUPS_CPU_IDLE]));
908                 cgroups_set("cpu.idle", tmp);
909         }
910 
911         if (tb[OCI_LINUX_CGROUPS_CPU_CPUS])
912                 cgroups_set("cpuset.cpus", blobmsg_get_string(tb[OCI_LINUX_CGROUPS_CPU_CPUS]));
913 
914         if (tb[OCI_LINUX_CGROUPS_CPU_MEMS])
915                 cgroups_set("cpuset.mems", blobmsg_get_string(tb[OCI_LINUX_CGROUPS_CPU_MEMS]));
916 
917         return 0;
918 }
919 
920 
921 enum {
922         OCI_LINUX_CGROUPS_MEMORY_LIMIT,
923         OCI_LINUX_CGROUPS_MEMORY_RESERVATION,
924         OCI_LINUX_CGROUPS_MEMORY_SWAP,
925         OCI_LINUX_CGROUPS_MEMORY_KERNEL,
926         OCI_LINUX_CGROUPS_MEMORY_KERNELTCP,
927         OCI_LINUX_CGROUPS_MEMORY_SWAPPINESS,
928         OCI_LINUX_CGROUPS_MEMORY_DISABLEOOMKILLER,
929         OCI_LINUX_CGROUPS_MEMORY_USEHIERARCHY,
930         OCI_LINUX_CGROUPS_MEMORY_CHECKBEFOREUPDATE,
931         __OCI_LINUX_CGROUPS_MEMORY_MAX,
932 };
933 
934 static const struct blobmsg_policy oci_linux_cgroups_memory_policy[] = {
935         [OCI_LINUX_CGROUPS_MEMORY_LIMIT] = { "limit", BLOBMSG_CAST_INT64 }, /* signed int64! */
936         [OCI_LINUX_CGROUPS_MEMORY_RESERVATION] = { "reservation", BLOBMSG_CAST_INT64 }, /* signed int64! */
937         [OCI_LINUX_CGROUPS_MEMORY_SWAP] = { "swap", BLOBMSG_CAST_INT64 }, /* signed int64! */
938         [OCI_LINUX_CGROUPS_MEMORY_KERNEL] = { "kernel", BLOBMSG_CAST_INT64 }, /* signed int64! ignored */
939         [OCI_LINUX_CGROUPS_MEMORY_KERNELTCP] = { "kernelTCP", BLOBMSG_CAST_INT64 }, /* signed int64! ignored */
940         [OCI_LINUX_CGROUPS_MEMORY_SWAPPINESS] = { "swappiness", BLOBMSG_CAST_INT64 },
941         [OCI_LINUX_CGROUPS_MEMORY_DISABLEOOMKILLER] = { "disableOOMKiller", BLOBMSG_TYPE_BOOL },
942         [OCI_LINUX_CGROUPS_MEMORY_USEHIERARCHY] = { "useHierarchy", BLOBMSG_TYPE_BOOL },
943         [OCI_LINUX_CGROUPS_MEMORY_CHECKBEFOREUPDATE] = { "checkBeforeUpdate", BLOBMSG_TYPE_BOOL },
944 };
945 
946 static int64_t read_int64_file(const char *path)
947 {
948         char buf[32];
949         char *end;
950         int64_t v;
951         int fd;
952         ssize_t n;
953 
954         fd = open(path, O_RDONLY | O_CLOEXEC);
955         if (fd < 0)
956                 return -1;
957         do {
958                 n = read(fd, buf, sizeof(buf) - 1);
959         } while (n < 0 && errno == EINTR);
960         close(fd);
961         if (n <= 0)
962                 return -1;
963         buf[n] = '\0';
964         v = strtoll(buf, &end, 10);
965         if (end == buf || (*end != '\0' && *end != '\n'))
966                 return -1;
967         return v;
968 }
969 
970 int64_t cgroups_read_int64(const char *attr)
971 {
972         char path[PATH_MAX];
973 
974         if (!cgroup_path)
975                 return -1;
976 
977         snprintf(path, sizeof(path), "%s/%s", cgroup_path, attr);
978         return read_int64_file(path);
979 }
980 
981 int cgroups_open_attr(const char *attr)
982 {
983         char path[PATH_MAX];
984 
985         if (!cgroup_path)
986                 return -1;
987 
988         snprintf(path, sizeof(path), "%s/%s", cgroup_path, attr);
989         return open(path, O_RDONLY | O_CLOEXEC);
990 }
991 
992 int cgroups_open_dir(void)
993 {
994         if (!cgroup_path)
995                 return -1;
996         return open(cgroup_path, O_PATH | O_DIRECTORY | O_CLOEXEC);
997 }
998 
999 void cgroups_set_memory_limit(int64_t bytes)
1000 {
1001         char tmp[32];
1002 
1003         snprintf(tmp, sizeof(tmp), "%" PRId64, bytes);
1004         cgroups_set("memory.max", tmp);
1005 }
1006 
1007 static int parseOCIlinuxcgroups_legacy_memory(struct blob_attr *msg, bool is_update)
1008 {
1009         struct blob_attr *tb[__OCI_LINUX_CGROUPS_MEMORY_MAX];
1010         char tmp[32] = { 0 };
1011         int64_t limit = -1, swap, reservation;
1012 
1013         blobmsg_parse(oci_linux_cgroups_memory_policy, __OCI_LINUX_CGROUPS_MEMORY_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
1014 
1015         /*
1016          * not all properties of the OCI memory section can be mapped to cgroup2
1017          * kernel memory accounting is always enabled and included in the set
1018          *   memory limit, hence these options can be ignored
1019          * disableOOMKiller could be emulated using oom_score_adj + seccomp eBPF
1020          *   preventing self-upgrade (but allow downgrade)
1021          *
1022          * see also https://github.com/opencontainers/runtime-spec/issues/1005
1023          */
1024         if (tb[OCI_LINUX_CGROUPS_MEMORY_SWAPPINESS])
1025                 return jail_unsupported(tb[OCI_LINUX_CGROUPS_MEMORY_SWAPPINESS]);
1026 
1027         if (tb[OCI_LINUX_CGROUPS_MEMORY_DISABLEOOMKILLER])
1028                 return jail_unsupported(tb[OCI_LINUX_CGROUPS_MEMORY_DISABLEOOMKILLER]);
1029 
1030         if (tb[OCI_LINUX_CGROUPS_MEMORY_USEHIERARCHY])
1031                 return jail_unsupported(tb[OCI_LINUX_CGROUPS_MEMORY_USEHIERARCHY]);
1032 
1033         if (is_update && tb[OCI_LINUX_CGROUPS_MEMORY_CHECKBEFOREUPDATE] &&
1034             blobmsg_get_bool(tb[OCI_LINUX_CGROUPS_MEMORY_CHECKBEFOREUPDATE])) {
1035                 char path[PATH_MAX];
1036                 int64_t current;
1037 
1038                 snprintf(path, sizeof(path), "%s/memory.current", cgroup_path);
1039                 current = read_int64_file(path);
1040                 if (current < 0) {
1041                         ERROR("memory.checkBeforeUpdate: cannot read %s: %m\n", path);
1042                         return EIO;
1043                 }
1044 
1045                 if (tb[OCI_LINUX_CGROUPS_MEMORY_LIMIT]) {
1046                         int64_t new_limit = blobmsg_cast_s64(tb[OCI_LINUX_CGROUPS_MEMORY_LIMIT]);
1047                         if (new_limit != -1 && new_limit < current) {
1048                                 ERROR("memory.checkBeforeUpdate: new limit %" PRId64
1049                                       " < current usage %" PRId64 "\n", new_limit, current);
1050                                 return EBUSY;
1051                         }
1052                 }
1053                 if (tb[OCI_LINUX_CGROUPS_MEMORY_RESERVATION]) {
1054                         int64_t new_res = blobmsg_cast_s64(tb[OCI_LINUX_CGROUPS_MEMORY_RESERVATION]);
1055                         if (new_res != -1 && new_res < current) {
1056                                 ERROR("memory.checkBeforeUpdate: new reservation %" PRId64
1057                                       " < current usage %" PRId64 "\n", new_res, current);
1058                                 return EBUSY;
1059                         }
1060                 }
1061         }
1062 
1063         if (tb[OCI_LINUX_CGROUPS_MEMORY_LIMIT]) {
1064                 limit = blobmsg_cast_s64(tb[OCI_LINUX_CGROUPS_MEMORY_LIMIT]);
1065                 if (limit == -1)
1066                         strcpy(tmp, "max");
1067                 else
1068                         snprintf(tmp, sizeof(tmp), "%" PRId64, limit);
1069 
1070                 cgroups_set("memory.max", tmp);
1071         }
1072 
1073         if (tb[OCI_LINUX_CGROUPS_MEMORY_RESERVATION]) {
1074                 reservation = blobmsg_cast_s64(tb[OCI_LINUX_CGROUPS_MEMORY_RESERVATION]);
1075 
1076                 if (reservation == -1)
1077                         strcpy(tmp, "max");
1078                 else
1079                         snprintf(tmp, sizeof(tmp), "%" PRId64, reservation);
1080 
1081                 cgroups_set("memory.low", tmp);
1082         }
1083 
1084         /* OCI 'swap' acounts for memory+swap */
1085         if (tb[OCI_LINUX_CGROUPS_MEMORY_SWAP]) {
1086                 swap = blobmsg_cast_s64(tb[OCI_LINUX_CGROUPS_MEMORY_SWAP]);
1087 
1088                 if (swap == -1)
1089                         strcpy(tmp, "max");
1090                 else if (limit == -1 || (limit < swap))
1091                         snprintf(tmp, sizeof(tmp), "%" PRId64, swap);
1092                 else
1093                         snprintf(tmp, sizeof(tmp), "%" PRId64, limit - swap);
1094 
1095                 cgroups_set("memory.swap.max", tmp);
1096         }
1097 
1098         return 0;
1099 }
1100 
1101 
1102 enum {
1103         OCI_LINUX_CGROUPS_PIDS_LIMIT,
1104         __OCI_LINUX_CGROUPS_PIDS_MAX,
1105 };
1106 
1107 static const struct blobmsg_policy oci_linux_cgroups_pids_policy[] = {
1108         [OCI_LINUX_CGROUPS_PIDS_LIMIT] = { "limit", BLOBMSG_CAST_INT64 },
1109 };
1110 
1111 static int parseOCIlinuxcgroups_legacy_pids(struct blob_attr *msg)
1112 {
1113         struct blob_attr *tb[__OCI_LINUX_CGROUPS_MEMORY_MAX];
1114         char tmp[32] = { 0 };
1115         int64_t limit;
1116 
1117         blobmsg_parse(oci_linux_cgroups_pids_policy, __OCI_LINUX_CGROUPS_PIDS_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
1118 
1119         if (!tb[OCI_LINUX_CGROUPS_PIDS_LIMIT])
1120                 return 0;
1121 
1122         limit = blobmsg_cast_s64(tb[OCI_LINUX_CGROUPS_PIDS_LIMIT]);
1123         if (limit < 0)
1124                 strcpy(tmp, "max");
1125         else
1126                 snprintf(tmp, sizeof(tmp), "%" PRId64, limit);
1127 
1128         cgroups_set("pids.max", tmp);
1129 
1130         return 0;
1131 }
1132 
1133 static int parseOCIlinuxcgroups_unified(struct blob_attr *msg)
1134 {
1135         struct blob_attr *cur;
1136         int rem;
1137 
1138         blobmsg_for_each_attr(cur, msg, rem) {
1139                 if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING)
1140                         return EINVAL;
1141 
1142                 /* restrict keys */
1143                 if (strchr(blobmsg_name(cur), '/') ||
1144                     !strcmp(blobmsg_name(cur), "cgroup.subtree_control") ||
1145                     !strcmp(blobmsg_name(cur), "cgroup.procs") ||
1146                     !strcmp(blobmsg_name(cur), "cgroup.threads") ||
1147                     !strcmp(blobmsg_name(cur), "cgroup.freeze"))
1148                         return EINVAL;
1149 
1150                 cgroups_set(blobmsg_name(cur), blobmsg_get_string(cur));
1151         }
1152 
1153         return 0;
1154 }
1155 
1156 enum {
1157         OCI_LINUX_CGROUPS_BLOCKIO,
1158         OCI_LINUX_CGROUPS_CPU,
1159         OCI_LINUX_CGROUPS_DEVICES,
1160         OCI_LINUX_CGROUPS_HUGEPAGELIMITS,
1161         OCI_LINUX_CGROUPS_INTELRDT,
1162         OCI_LINUX_CGROUPS_MEMORY,
1163         OCI_LINUX_CGROUPS_NETWORK,
1164         OCI_LINUX_CGROUPS_PIDS,
1165         OCI_LINUX_CGROUPS_RDMA,
1166         OCI_LINUX_CGROUPS_UNIFIED,
1167         __OCI_LINUX_CGROUPS_MAX,
1168 };
1169 
1170 static const struct blobmsg_policy oci_linux_cgroups_policy[] = {
1171         [OCI_LINUX_CGROUPS_BLOCKIO] = { "blockIO", BLOBMSG_TYPE_TABLE },
1172         [OCI_LINUX_CGROUPS_CPU] = { "cpu", BLOBMSG_TYPE_TABLE },
1173         [OCI_LINUX_CGROUPS_DEVICES] = { "devices", BLOBMSG_TYPE_ARRAY },
1174         [OCI_LINUX_CGROUPS_HUGEPAGELIMITS] = { "hugepageLimits", BLOBMSG_TYPE_ARRAY },
1175         [OCI_LINUX_CGROUPS_INTELRDT] = { "intelRdt", BLOBMSG_TYPE_TABLE },
1176         [OCI_LINUX_CGROUPS_MEMORY] = { "memory", BLOBMSG_TYPE_TABLE },
1177         [OCI_LINUX_CGROUPS_NETWORK] = { "network", BLOBMSG_TYPE_TABLE },
1178         [OCI_LINUX_CGROUPS_PIDS] = { "pids", BLOBMSG_TYPE_TABLE },
1179         [OCI_LINUX_CGROUPS_RDMA] = { "rdma", BLOBMSG_TYPE_TABLE },
1180         [OCI_LINUX_CGROUPS_UNIFIED] = { "unified", BLOBMSG_TYPE_TABLE },
1181 };
1182 
1183 int parseOCIlinuxcgroups(struct blob_attr *msg, bool is_update)
1184 {
1185         struct blob_attr *tb[__OCI_LINUX_CGROUPS_MAX];
1186         int ret;
1187 
1188         blobmsg_parse(oci_linux_cgroups_policy, __OCI_LINUX_CGROUPS_MAX, tb, blobmsg_data(msg), blobmsg_len(msg));
1189 
1190         if (tb[OCI_LINUX_CGROUPS_HUGEPAGELIMITS])
1191                 return jail_unsupported(tb[OCI_LINUX_CGROUPS_HUGEPAGELIMITS]);
1192 
1193         if (tb[OCI_LINUX_CGROUPS_INTELRDT])
1194                 return jail_unsupported(tb[OCI_LINUX_CGROUPS_INTELRDT]);
1195 
1196         if (tb[OCI_LINUX_CGROUPS_NETWORK])
1197                 return jail_unsupported(tb[OCI_LINUX_CGROUPS_NETWORK]);
1198 
1199         if (tb[OCI_LINUX_CGROUPS_RDMA])
1200                 return jail_unsupported(tb[OCI_LINUX_CGROUPS_RDMA]);
1201 
1202         if (tb[OCI_LINUX_CGROUPS_BLOCKIO]) {
1203                 ret = parseOCIlinuxcgroups_legacy_blockio(tb[OCI_LINUX_CGROUPS_BLOCKIO]);
1204                 if (ret)
1205                         return ret;
1206         }
1207 
1208         if (tb[OCI_LINUX_CGROUPS_CPU]) {
1209                 ret = parseOCIlinuxcgroups_legacy_cpu(tb[OCI_LINUX_CGROUPS_CPU]);
1210                 if (ret)
1211                         return ret;
1212         }
1213 
1214         if (tb[OCI_LINUX_CGROUPS_DEVICES]) {
1215                 ret = parseOCIlinuxcgroups_devices(tb[OCI_LINUX_CGROUPS_DEVICES]);
1216                 if (ret)
1217                         return ret;
1218         }
1219 
1220         if (tb[OCI_LINUX_CGROUPS_MEMORY]) {
1221                 ret = parseOCIlinuxcgroups_legacy_memory(tb[OCI_LINUX_CGROUPS_MEMORY], is_update);
1222                 if (ret)
1223                         return ret;
1224         }
1225 
1226         if (tb[OCI_LINUX_CGROUPS_PIDS]) {
1227                 ret = parseOCIlinuxcgroups_legacy_pids(tb[OCI_LINUX_CGROUPS_PIDS]);
1228                 if (ret)
1229                         return ret;
1230         }
1231 
1232         if (tb[OCI_LINUX_CGROUPS_UNIFIED]) {
1233                 ret = parseOCIlinuxcgroups_unified(tb[OCI_LINUX_CGROUPS_UNIFIED]);
1234                 if (ret)
1235                         return ret;
1236         }
1237 
1238         return 0;
1239 }
1240 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt