blob: a938b6c8b55a22d5792a29b3dbce24b18b90fe91 [file] [log] [blame]
Roman Gushchin84092dbc2018-05-11 19:03:49 +01001/* SPDX-License-Identifier: GPL-2.0 */
2
3#define _GNU_SOURCE
4
5#include <errno.h>
6#include <fcntl.h>
7#include <linux/limits.h>
8#include <signal.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/stat.h>
13#include <sys/types.h>
14#include <sys/wait.h>
15#include <unistd.h>
16
17#include "cgroup_util.h"
18
19static ssize_t read_text(const char *path, char *buf, size_t max_len)
20{
21 ssize_t len;
22 int fd;
23
24 fd = open(path, O_RDONLY);
25 if (fd < 0)
26 return fd;
27
28 len = read(fd, buf, max_len - 1);
29 if (len < 0)
30 goto out;
31
32 buf[len] = 0;
33out:
34 close(fd);
35 return len;
36}
37
38static ssize_t write_text(const char *path, char *buf, size_t len)
39{
40 int fd;
41
42 fd = open(path, O_WRONLY | O_APPEND);
43 if (fd < 0)
44 return fd;
45
46 len = write(fd, buf, len);
47 if (len < 0) {
48 close(fd);
49 return len;
50 }
51
52 close(fd);
53
54 return len;
55}
56
57char *cg_name(const char *root, const char *name)
58{
59 size_t len = strlen(root) + strlen(name) + 2;
60 char *ret = malloc(len);
61
62 if (name)
63 snprintf(ret, len, "%s/%s", root, name);
64
65 return ret;
66}
67
68char *cg_name_indexed(const char *root, const char *name, int index)
69{
70 size_t len = strlen(root) + strlen(name) + 10;
71 char *ret = malloc(len);
72
73 if (name)
74 snprintf(ret, len, "%s/%s_%d", root, name, index);
75
76 return ret;
77}
78
79int cg_read(const char *cgroup, const char *control, char *buf, size_t len)
80{
81 char path[PATH_MAX];
82
83 snprintf(path, sizeof(path), "%s/%s", cgroup, control);
84
85 if (read_text(path, buf, len) >= 0)
86 return 0;
87
88 return -1;
89}
90
91int cg_read_strcmp(const char *cgroup, const char *control,
92 const char *expected)
93{
94 size_t size = strlen(expected) + 1;
95 char *buf;
96
97 buf = malloc(size);
98 if (!buf)
99 return -1;
100
101 if (cg_read(cgroup, control, buf, size))
102 return -1;
103
104 return strcmp(expected, buf);
105}
106
107int cg_read_strstr(const char *cgroup, const char *control, const char *needle)
108{
109 char buf[PAGE_SIZE];
110
111 if (cg_read(cgroup, control, buf, sizeof(buf)))
112 return -1;
113
114 return strstr(buf, needle) ? 0 : -1;
115}
116
117long cg_read_long(const char *cgroup, const char *control)
118{
119 char buf[128];
120
121 if (cg_read(cgroup, control, buf, sizeof(buf)))
122 return -1;
123
124 return atol(buf);
125}
126
127long cg_read_key_long(const char *cgroup, const char *control, const char *key)
128{
129 char buf[PAGE_SIZE];
130 char *ptr;
131
132 if (cg_read(cgroup, control, buf, sizeof(buf)))
133 return -1;
134
135 ptr = strstr(buf, key);
136 if (!ptr)
137 return -1;
138
139 return atol(ptr + strlen(key));
140}
141
142int cg_write(const char *cgroup, const char *control, char *buf)
143{
144 char path[PATH_MAX];
145 size_t len = strlen(buf);
146
147 snprintf(path, sizeof(path), "%s/%s", cgroup, control);
148
149 if (write_text(path, buf, len) == len)
150 return 0;
151
152 return -1;
153}
154
155int cg_find_unified_root(char *root, size_t len)
156{
157 char buf[10 * PAGE_SIZE];
158 char *fs, *mount, *type;
159 const char delim[] = "\n\t ";
160
161 if (read_text("/proc/self/mounts", buf, sizeof(buf)) <= 0)
162 return -1;
163
164 /*
165 * Example:
166 * cgroup /sys/fs/cgroup cgroup2 rw,seclabel,noexec,relatime 0 0
167 */
168 for (fs = strtok(buf, delim); fs; fs = strtok(NULL, delim)) {
169 mount = strtok(NULL, delim);
170 type = strtok(NULL, delim);
171 strtok(NULL, delim);
172 strtok(NULL, delim);
173 strtok(NULL, delim);
174
175 if (strcmp(fs, "cgroup") == 0 &&
176 strcmp(type, "cgroup2") == 0) {
177 strncpy(root, mount, len);
178 return 0;
179 }
180 }
181
182 return -1;
183}
184
185int cg_create(const char *cgroup)
186{
187 return mkdir(cgroup, 0644);
188}
189
190static int cg_killall(const char *cgroup)
191{
192 char buf[PAGE_SIZE];
193 char *ptr = buf;
194
195 if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf)))
196 return -1;
197
198 while (ptr < buf + sizeof(buf)) {
199 int pid = strtol(ptr, &ptr, 10);
200
201 if (pid == 0)
202 break;
203 if (*ptr)
204 ptr++;
205 else
206 break;
207 if (kill(pid, SIGKILL))
208 return -1;
209 }
210
211 return 0;
212}
213
214int cg_destroy(const char *cgroup)
215{
216 int ret;
217
218retry:
219 ret = rmdir(cgroup);
220 if (ret && errno == EBUSY) {
221 ret = cg_killall(cgroup);
222 if (ret)
223 return ret;
224 usleep(100);
225 goto retry;
226 }
227
228 if (ret && errno == ENOENT)
229 ret = 0;
230
231 return ret;
232}
233
234int cg_run(const char *cgroup,
235 int (*fn)(const char *cgroup, void *arg),
236 void *arg)
237{
238 int pid, retcode;
239
240 pid = fork();
241 if (pid < 0) {
242 return pid;
243 } else if (pid == 0) {
244 char buf[64];
245
246 snprintf(buf, sizeof(buf), "%d", getpid());
247 if (cg_write(cgroup, "cgroup.procs", buf))
248 exit(EXIT_FAILURE);
249 exit(fn(cgroup, arg));
250 } else {
251 waitpid(pid, &retcode, 0);
252 if (WIFEXITED(retcode))
253 return WEXITSTATUS(retcode);
254 else
255 return -1;
256 }
257}
258
259int cg_run_nowait(const char *cgroup,
260 int (*fn)(const char *cgroup, void *arg),
261 void *arg)
262{
263 int pid;
264
265 pid = fork();
266 if (pid == 0) {
267 char buf[64];
268
269 snprintf(buf, sizeof(buf), "%d", getpid());
270 if (cg_write(cgroup, "cgroup.procs", buf))
271 exit(EXIT_FAILURE);
272 exit(fn(cgroup, arg));
273 }
274
275 return pid;
276}
277
278int get_temp_fd(void)
279{
280 return open(".", O_TMPFILE | O_RDWR | O_EXCL);
281}
282
283int alloc_pagecache(int fd, size_t size)
284{
285 char buf[PAGE_SIZE];
286 struct stat st;
287 int i;
288
289 if (fstat(fd, &st))
290 goto cleanup;
291
292 size += st.st_size;
293
294 if (ftruncate(fd, size))
295 goto cleanup;
296
297 for (i = 0; i < size; i += sizeof(buf))
298 read(fd, buf, sizeof(buf));
299
300 return 0;
301
302cleanup:
303 return -1;
304}
305
306int alloc_anon(const char *cgroup, void *arg)
307{
308 size_t size = (unsigned long)arg;
309 char *buf, *ptr;
310
311 buf = malloc(size);
312 for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
313 *ptr = 0;
314
315 free(buf);
316 return 0;
317}