162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright © 2018 Alexey Dobriyan <adobriyan@gmail.com>
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Permission to use, copy, modify, and distribute this software for any
562306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above
662306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
962306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1062306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1162306a36Sopenharmony_ci * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1262306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1362306a36Sopenharmony_ci * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1462306a36Sopenharmony_ci * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1562306a36Sopenharmony_ci */
1662306a36Sopenharmony_ci// Test
1762306a36Sopenharmony_ci// 1) read and lseek on every file in /proc
1862306a36Sopenharmony_ci// 2) readlink of every symlink in /proc
1962306a36Sopenharmony_ci// 3) recursively (1) + (2) for every directory in /proc
2062306a36Sopenharmony_ci// 4) write to /proc/*/clear_refs and /proc/*/task/*/clear_refs
2162306a36Sopenharmony_ci// 5) write to /proc/sysrq-trigger
2262306a36Sopenharmony_ci#undef NDEBUG
2362306a36Sopenharmony_ci#include <assert.h>
2462306a36Sopenharmony_ci#include <errno.h>
2562306a36Sopenharmony_ci#include <sys/types.h>
2662306a36Sopenharmony_ci#include <dirent.h>
2762306a36Sopenharmony_ci#include <stdbool.h>
2862306a36Sopenharmony_ci#include <stdlib.h>
2962306a36Sopenharmony_ci#include <stdio.h>
3062306a36Sopenharmony_ci#include <string.h>
3162306a36Sopenharmony_ci#include <sys/stat.h>
3262306a36Sopenharmony_ci#include <sys/vfs.h>
3362306a36Sopenharmony_ci#include <fcntl.h>
3462306a36Sopenharmony_ci#include <unistd.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#include "proc.h"
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void f_reg(DIR *d, const char *filename)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	char buf[4096];
4162306a36Sopenharmony_ci	int fd;
4262306a36Sopenharmony_ci	ssize_t rv;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	/* read from /proc/kmsg can block */
4562306a36Sopenharmony_ci	fd = openat(dirfd(d), filename, O_RDONLY|O_NONBLOCK);
4662306a36Sopenharmony_ci	if (fd == -1)
4762306a36Sopenharmony_ci		return;
4862306a36Sopenharmony_ci	/* struct proc_ops::proc_lseek is mandatory if file is seekable. */
4962306a36Sopenharmony_ci	(void)lseek(fd, 0, SEEK_SET);
5062306a36Sopenharmony_ci	rv = read(fd, buf, sizeof(buf));
5162306a36Sopenharmony_ci	assert((0 <= rv && rv <= sizeof(buf)) || rv == -1);
5262306a36Sopenharmony_ci	close(fd);
5362306a36Sopenharmony_ci}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic void f_reg_write(DIR *d, const char *filename, const char *buf, size_t len)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	int fd;
5862306a36Sopenharmony_ci	ssize_t rv;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	fd = openat(dirfd(d), filename, O_WRONLY);
6162306a36Sopenharmony_ci	if (fd == -1)
6262306a36Sopenharmony_ci		return;
6362306a36Sopenharmony_ci	rv = write(fd, buf, len);
6462306a36Sopenharmony_ci	assert((0 <= rv && rv <= len) || rv == -1);
6562306a36Sopenharmony_ci	close(fd);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic void f_lnk(DIR *d, const char *filename)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	char buf[4096];
7162306a36Sopenharmony_ci	ssize_t rv;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	rv = readlinkat(dirfd(d), filename, buf, sizeof(buf));
7462306a36Sopenharmony_ci	assert((0 <= rv && rv <= sizeof(buf)) || rv == -1);
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic void f(DIR *d, unsigned int level)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct dirent *de;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	de = xreaddir(d);
8262306a36Sopenharmony_ci	assert(de->d_type == DT_DIR);
8362306a36Sopenharmony_ci	assert(streq(de->d_name, "."));
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	de = xreaddir(d);
8662306a36Sopenharmony_ci	assert(de->d_type == DT_DIR);
8762306a36Sopenharmony_ci	assert(streq(de->d_name, ".."));
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	while ((de = xreaddir(d))) {
9062306a36Sopenharmony_ci		assert(!streq(de->d_name, "."));
9162306a36Sopenharmony_ci		assert(!streq(de->d_name, ".."));
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		switch (de->d_type) {
9462306a36Sopenharmony_ci			DIR *dd;
9562306a36Sopenharmony_ci			int fd;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci		case DT_REG:
9862306a36Sopenharmony_ci			if (level == 0 && streq(de->d_name, "sysrq-trigger")) {
9962306a36Sopenharmony_ci				f_reg_write(d, de->d_name, "h", 1);
10062306a36Sopenharmony_ci			} else if (level == 1 && streq(de->d_name, "clear_refs")) {
10162306a36Sopenharmony_ci				f_reg_write(d, de->d_name, "1", 1);
10262306a36Sopenharmony_ci			} else if (level == 3 && streq(de->d_name, "clear_refs")) {
10362306a36Sopenharmony_ci				f_reg_write(d, de->d_name, "1", 1);
10462306a36Sopenharmony_ci			} else {
10562306a36Sopenharmony_ci				f_reg(d, de->d_name);
10662306a36Sopenharmony_ci			}
10762306a36Sopenharmony_ci			break;
10862306a36Sopenharmony_ci		case DT_DIR:
10962306a36Sopenharmony_ci			fd = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY);
11062306a36Sopenharmony_ci			if (fd == -1)
11162306a36Sopenharmony_ci				continue;
11262306a36Sopenharmony_ci			dd = fdopendir(fd);
11362306a36Sopenharmony_ci			if (!dd)
11462306a36Sopenharmony_ci				continue;
11562306a36Sopenharmony_ci			f(dd, level + 1);
11662306a36Sopenharmony_ci			closedir(dd);
11762306a36Sopenharmony_ci			break;
11862306a36Sopenharmony_ci		case DT_LNK:
11962306a36Sopenharmony_ci			f_lnk(d, de->d_name);
12062306a36Sopenharmony_ci			break;
12162306a36Sopenharmony_ci		default:
12262306a36Sopenharmony_ci			assert(0);
12362306a36Sopenharmony_ci		}
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ciint main(void)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	DIR *d;
13062306a36Sopenharmony_ci	struct statfs sfs;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	d = opendir("/proc");
13362306a36Sopenharmony_ci	if (!d)
13462306a36Sopenharmony_ci		return 4;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	/* Ensure /proc is proc. */
13762306a36Sopenharmony_ci	if (fstatfs(dirfd(d), &sfs) == -1) {
13862306a36Sopenharmony_ci		return 1;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci	if (sfs.f_type != 0x9fa0) {
14162306a36Sopenharmony_ci		fprintf(stderr, "error: unexpected f_type %lx\n", (long)sfs.f_type);
14262306a36Sopenharmony_ci		return 2;
14362306a36Sopenharmony_ci	}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	f(d, 0);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	return 0;
14862306a36Sopenharmony_ci}
149