1 /* 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 #include "../clone3/clone3_selftests.h"
19 
20 static ssize_t read_text(const char *path, char *buf, size_t max_len)
21 {
22 	ssize_t len;
23 	int fd;
24 
25 	fd = open(path, O_RDONLY);
26 	if (fd < 0)
27 		return fd;
28 
29 	len = read(fd, buf, max_len - 1);
30 	if (len < 0)
31 		goto out;
32 
33 	buf[len] = 0;
34 out:
35 	close(fd);
36 	return len;
37 }
38 
39 static ssize_t write_text(const char *path, char *buf, ssize_t len)
40 {
41 	int fd;
42 
43 	fd = open(path, O_WRONLY | O_APPEND);
44 	if (fd < 0)
45 		return fd;
46 
47 	len = write(fd, buf, len);
48 	if (len < 0) {
49 		close(fd);
50 		return len;
51 	}
52 
53 	close(fd);
54 
55 	return len;
56 }
57 
58 char *cg_name(const char *root, const char *name)
59 {
60 	size_t len = strlen(root) + strlen(name) + 2;
61 	char *ret = malloc(len);
62 
63 	snprintf(ret, len, "%s/%s", root, name);
64 
65 	return ret;
66 }
67 
68 char *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 	snprintf(ret, len, "%s/%s_%d", root, name, index);
74 
75 	return ret;
76 }
77 
78 char *cg_control(const char *cgroup, const char *control)
79 {
80 	size_t len = strlen(cgroup) + strlen(control) + 2;
81 	char *ret = malloc(len);
82 
83 	snprintf(ret, len, "%s/%s", cgroup, control);
84 
85 	return ret;
86 }
87 
88 int cg_read(const char *cgroup, const char *control, char *buf, size_t len)
89 {
90 	char path[PATH_MAX];
91 
92 	snprintf(path, sizeof(path), "%s/%s", cgroup, control);
93 
94 	if (read_text(path, buf, len) >= 0)
95 		return 0;
96 
97 	return -1;
98 }
99 
100 int cg_read_strcmp(const char *cgroup, const char *control,
101 		   const char *expected)
102 {
103 	size_t size;
104 	char *buf;
105 	int ret;
106 
107 	/* Handle the case of comparing against empty string */
108 	if (!expected)
109 		size = 32;
110 	else
111 		size = strlen(expected) + 1;
112 
113 	buf = malloc(size);
114 	if (!buf)
115 		return -1;
116 
117 	if (cg_read(cgroup, control, buf, size)) {
118 		free(buf);
119 		return -1;
120 	}
121 
122 	ret = strcmp(expected, buf);
123 	free(buf);
124 	return ret;
125 }
126 
127 int cg_read_strstr(const char *cgroup, const char *control, const char *needle)
128 {
129 	char buf[PAGE_SIZE];
130 
131 	if (cg_read(cgroup, control, buf, sizeof(buf)))
132 		return -1;
133 
134 	return strstr(buf, needle) ? 0 : -1;
135 }
136 
137 long cg_read_long(const char *cgroup, const char *control)
138 {
139 	char buf[128];
140 
141 	if (cg_read(cgroup, control, buf, sizeof(buf)))
142 		return -1;
143 
144 	return atol(buf);
145 }
146 
147 long cg_read_key_long(const char *cgroup, const char *control, const char *key)
148 {
149 	char buf[PAGE_SIZE];
150 	char *ptr;
151 
152 	if (cg_read(cgroup, control, buf, sizeof(buf)))
153 		return -1;
154 
155 	ptr = strstr(buf, key);
156 	if (!ptr)
157 		return -1;
158 
159 	return atol(ptr + strlen(key));
160 }
161 
162 long cg_read_lc(const char *cgroup, const char *control)
163 {
164 	char buf[PAGE_SIZE];
165 	const char delim[] = "\n";
166 	char *line;
167 	long cnt = 0;
168 
169 	if (cg_read(cgroup, control, buf, sizeof(buf)))
170 		return -1;
171 
172 	for (line = strtok(buf, delim); line; line = strtok(NULL, delim))
173 		cnt++;
174 
175 	return cnt;
176 }
177 
178 int cg_write(const char *cgroup, const char *control, char *buf)
179 {
180 	char path[PATH_MAX];
181 	ssize_t len = strlen(buf);
182 
183 	snprintf(path, sizeof(path), "%s/%s", cgroup, control);
184 
185 	if (write_text(path, buf, len) == len)
186 		return 0;
187 
188 	return -1;
189 }
190 
191 int cg_find_unified_root(char *root, size_t len)
192 {
193 	char buf[10 * PAGE_SIZE];
194 	char *fs, *mount, *type;
195 	const char delim[] = "\n\t ";
196 
197 	if (read_text("/proc/self/mounts", buf, sizeof(buf)) <= 0)
198 		return -1;
199 
200 	/*
201 	 * Example:
202 	 * cgroup /sys/fs/cgroup cgroup2 rw,seclabel,noexec,relatime 0 0
203 	 */
204 	for (fs = strtok(buf, delim); fs; fs = strtok(NULL, delim)) {
205 		mount = strtok(NULL, delim);
206 		type = strtok(NULL, delim);
207 		strtok(NULL, delim);
208 		strtok(NULL, delim);
209 		strtok(NULL, delim);
210 
211 		if (strcmp(type, "cgroup2") == 0) {
212 			strncpy(root, mount, len);
213 			return 0;
214 		}
215 	}
216 
217 	return -1;
218 }
219 
220 int cg_create(const char *cgroup)
221 {
222 	return mkdir(cgroup, 0644);
223 }
224 
225 int cg_wait_for_proc_count(const char *cgroup, int count)
226 {
227 	char buf[10 * PAGE_SIZE] = {0};
228 	int attempts;
229 	char *ptr;
230 
231 	for (attempts = 10; attempts >= 0; attempts--) {
232 		int nr = 0;
233 
234 		if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf)))
235 			break;
236 
237 		for (ptr = buf; *ptr; ptr++)
238 			if (*ptr == '\n')
239 				nr++;
240 
241 		if (nr >= count)
242 			return 0;
243 
244 		usleep(100000);
245 	}
246 
247 	return -1;
248 }
249 
250 int cg_killall(const char *cgroup)
251 {
252 	char buf[PAGE_SIZE];
253 	char *ptr = buf;
254 
255 	if (cg_read(cgroup, "cgroup.procs", buf, sizeof(buf)))
256 		return -1;
257 
258 	while (ptr < buf + sizeof(buf)) {
259 		int pid = strtol(ptr, &ptr, 10);
260 
261 		if (pid == 0)
262 			break;
263 		if (*ptr)
264 			ptr++;
265 		else
266 			break;
267 		if (kill(pid, SIGKILL))
268 			return -1;
269 	}
270 
271 	return 0;
272 }
273 
274 int cg_destroy(const char *cgroup)
275 {
276 	int ret;
277 
278 retry:
279 	ret = rmdir(cgroup);
280 	if (ret && errno == EBUSY) {
281 		cg_killall(cgroup);
282 		usleep(100);
283 		goto retry;
284 	}
285 
286 	if (ret && errno == ENOENT)
287 		ret = 0;
288 
289 	return ret;
290 }
291 
292 int cg_enter(const char *cgroup, int pid)
293 {
294 	char pidbuf[64];
295 
296 	snprintf(pidbuf, sizeof(pidbuf), "%d", pid);
297 	return cg_write(cgroup, "cgroup.procs", pidbuf);
298 }
299 
300 int cg_enter_current(const char *cgroup)
301 {
302 	return cg_write(cgroup, "cgroup.procs", "0");
303 }
304 
305 int cg_enter_current_thread(const char *cgroup)
306 {
307 	return cg_write(cgroup, "cgroup.threads", "0");
308 }
309 
310 int cg_run(const char *cgroup,
311 	   int (*fn)(const char *cgroup, void *arg),
312 	   void *arg)
313 {
314 	int pid, retcode;
315 
316 	pid = fork();
317 	if (pid < 0) {
318 		return pid;
319 	} else if (pid == 0) {
320 		char buf[64];
321 
322 		snprintf(buf, sizeof(buf), "%d", getpid());
323 		if (cg_write(cgroup, "cgroup.procs", buf))
324 			exit(EXIT_FAILURE);
325 		exit(fn(cgroup, arg));
326 	} else {
327 		waitpid(pid, &retcode, 0);
328 		if (WIFEXITED(retcode))
329 			return WEXITSTATUS(retcode);
330 		else
331 			return -1;
332 	}
333 }
334 
335 pid_t clone_into_cgroup(int cgroup_fd)
336 {
337 #ifdef CLONE_ARGS_SIZE_VER2
338 	pid_t pid;
339 
340 	struct clone_args args = {
341 		.flags = CLONE_INTO_CGROUP,
342 		.exit_signal = SIGCHLD,
343 		.cgroup = cgroup_fd,
344 	};
345 
346 	pid = sys_clone3(&args, sizeof(struct clone_args));
347 	/*
348 	 * Verify that this is a genuine test failure:
349 	 * ENOSYS -> clone3() not available
350 	 * E2BIG  -> CLONE_INTO_CGROUP not available
351 	 */
352 	if (pid < 0 && (errno == ENOSYS || errno == E2BIG))
353 		goto pretend_enosys;
354 
355 	return pid;
356 
357 pretend_enosys:
358 #endif
359 	errno = ENOSYS;
360 	return -ENOSYS;
361 }
362 
363 int clone_reap(pid_t pid, int options)
364 {
365 	int ret;
366 	siginfo_t info = {
367 		.si_signo = 0,
368 	};
369 
370 again:
371 	ret = waitid(P_PID, pid, &info, options | __WALL | __WNOTHREAD);
372 	if (ret < 0) {
373 		if (errno == EINTR)
374 			goto again;
375 		return -1;
376 	}
377 
378 	if (options & WEXITED) {
379 		if (WIFEXITED(info.si_status))
380 			return WEXITSTATUS(info.si_status);
381 	}
382 
383 	if (options & WSTOPPED) {
384 		if (WIFSTOPPED(info.si_status))
385 			return WSTOPSIG(info.si_status);
386 	}
387 
388 	if (options & WCONTINUED) {
389 		if (WIFCONTINUED(info.si_status))
390 			return 0;
391 	}
392 
393 	return -1;
394 }
395 
396 int dirfd_open_opath(const char *dir)
397 {
398 	return open(dir, O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW | O_PATH);
399 }
400 
401 #define close_prot_errno(fd)                                                   \
402 	if (fd >= 0) {                                                         \
403 		int _e_ = errno;                                               \
404 		close(fd);                                                     \
405 		errno = _e_;                                                   \
406 	}
407 
408 static int clone_into_cgroup_run_nowait(const char *cgroup,
409 					int (*fn)(const char *cgroup, void *arg),
410 					void *arg)
411 {
412 	int cgroup_fd;
413 	pid_t pid;
414 
415 	cgroup_fd =  dirfd_open_opath(cgroup);
416 	if (cgroup_fd < 0)
417 		return -1;
418 
419 	pid = clone_into_cgroup(cgroup_fd);
420 	close_prot_errno(cgroup_fd);
421 	if (pid == 0)
422 		exit(fn(cgroup, arg));
423 
424 	return pid;
425 }
426 
427 int cg_run_nowait(const char *cgroup,
428 		  int (*fn)(const char *cgroup, void *arg),
429 		  void *arg)
430 {
431 	int pid;
432 
433 	pid = clone_into_cgroup_run_nowait(cgroup, fn, arg);
434 	if (pid > 0)
435 		return pid;
436 
437 	/* Genuine test failure. */
438 	if (pid < 0 && errno != ENOSYS)
439 		return -1;
440 
441 	pid = fork();
442 	if (pid == 0) {
443 		char buf[64];
444 
445 		snprintf(buf, sizeof(buf), "%d", getpid());
446 		if (cg_write(cgroup, "cgroup.procs", buf))
447 			exit(EXIT_FAILURE);
448 		exit(fn(cgroup, arg));
449 	}
450 
451 	return pid;
452 }
453 
454 int get_temp_fd(void)
455 {
456 	return open(".", O_TMPFILE | O_RDWR | O_EXCL);
457 }
458 
459 int alloc_pagecache(int fd, size_t size)
460 {
461 	char buf[PAGE_SIZE];
462 	struct stat st;
463 	int i;
464 
465 	if (fstat(fd, &st))
466 		goto cleanup;
467 
468 	size += st.st_size;
469 
470 	if (ftruncate(fd, size))
471 		goto cleanup;
472 
473 	for (i = 0; i < size; i += sizeof(buf))
474 		read(fd, buf, sizeof(buf));
475 
476 	return 0;
477 
478 cleanup:
479 	return -1;
480 }
481 
482 int alloc_anon(const char *cgroup, void *arg)
483 {
484 	size_t size = (unsigned long)arg;
485 	char *buf, *ptr;
486 
487 	buf = malloc(size);
488 	for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
489 		*ptr = 0;
490 
491 	free(buf);
492 	return 0;
493 }
494 
495 int is_swap_enabled(void)
496 {
497 	char buf[PAGE_SIZE];
498 	const char delim[] = "\n";
499 	int cnt = 0;
500 	char *line;
501 
502 	if (read_text("/proc/swaps", buf, sizeof(buf)) <= 0)
503 		return -1;
504 
505 	for (line = strtok(buf, delim); line; line = strtok(NULL, delim))
506 		cnt++;
507 
508 	return cnt > 1;
509 }
510 
511 int set_oom_adj_score(int pid, int score)
512 {
513 	char path[PATH_MAX];
514 	int fd, len;
515 
516 	sprintf(path, "/proc/%d/oom_score_adj", pid);
517 
518 	fd = open(path, O_WRONLY | O_APPEND);
519 	if (fd < 0)
520 		return fd;
521 
522 	len = dprintf(fd, "%d", score);
523 	if (len < 0) {
524 		close(fd);
525 		return len;
526 	}
527 
528 	close(fd);
529 	return 0;
530 }
531 
532 ssize_t proc_read_text(int pid, bool thread, const char *item, char *buf, size_t size)
533 {
534 	char path[PATH_MAX];
535 
536 	if (!pid)
537 		snprintf(path, sizeof(path), "/proc/%s/%s",
538 			 thread ? "thread-self" : "self", item);
539 	else
540 		snprintf(path, sizeof(path), "/proc/%d/%s", pid, item);
541 
542 	return read_text(path, buf, size);
543 }
544 
545 int proc_read_strstr(int pid, bool thread, const char *item, const char *needle)
546 {
547 	char buf[PAGE_SIZE];
548 
549 	if (proc_read_text(pid, thread, item, buf, sizeof(buf)) < 0)
550 		return -1;
551 
552 	return strstr(buf, needle) ? 0 : -1;
553 }
554 
555 int clone_into_cgroup_run_wait(const char *cgroup)
556 {
557 	int cgroup_fd;
558 	pid_t pid;
559 
560 	cgroup_fd =  dirfd_open_opath(cgroup);
561 	if (cgroup_fd < 0)
562 		return -1;
563 
564 	pid = clone_into_cgroup(cgroup_fd);
565 	close_prot_errno(cgroup_fd);
566 	if (pid < 0)
567 		return -1;
568 
569 	if (pid == 0)
570 		exit(EXIT_SUCCESS);
571 
572 	/*
573 	 * We don't care whether this fails. We only care whether the initial
574 	 * clone succeeded.
575 	 */
576 	(void)clone_reap(pid, WEXITED);
577 	return 0;
578 }
579