14#include <pedigree/log.h>
17#include <sys/syscall.h>
19#define CHECK(expression) \
21 if (!(expression)) { \
22 fprintf(stderr, "SYSTEM-STATUS-CONTRACT: FAIL line=%d errno=%d\n", __LINE__, errno); \
27static int memory_status(
void) {
28 int fd = open(
"/proc/meminfo", O_RDONLY);
30 unsigned char guarded[16];
31 memset(guarded, 0xa5,
sizeof(guarded));
32 CHECK(pread(fd, guarded + 1, 1, 0) == 1 && guarded[1] ==
'M');
33 CHECK(guarded[0] == 0xa5 && guarded[2] == 0xa5);
34 CHECK(pread(fd, guarded + 1, 5, 4) == 5 && !memcmp(guarded + 1,
"otal:", 5));
35 CHECK(guarded[0] == 0xa5 && guarded[6] == 0xa5);
36 CHECK(pread(fd, guarded,
sizeof(guarded), LLONG_MAX -
sizeof(guarded)) == 0);
39 ssize_t bytes = pread(fd, contents,
sizeof(contents) - 1, 0);
42 unsigned long long total = 0, freeKb = 0;
43 CHECK(sscanf(contents,
"MemTotal: %llu kB\nMemFree: %llu kB", &total, &freeKb) == 2);
44 CHECK(total > 0 && freeKb <= total);
45 CHECK(close(fd) == 0);
46 puts(
"SYSTEM-STATUS-CONTRACT: MEMORY PASS");
50static ssize_t read_status_file(
const char* path,
char* contents,
size_t capacity) {
51 int fd = open(path, O_RDONLY);
54 ssize_t bytes = read(fd, contents, capacity - 1);
63static unsigned long long metrics_yields(
const char* contents,
size_t* rows) {
64 unsigned long long total = 0;
66 for (
const char* line = contents; line && *line; line = strchr(line,
'\n')) {
71 unsigned long long value;
72 if (sscanf(line,
"pedigree_scheduler_yields_total{cpu=\"%u\"} %llu", &cpu, &value) == 2) {
80static int metrics_status(
void) {
81 int fd = open(
"/proc/metrics", O_RDONLY);
84 CHECK(!fstat(fd, &status) && status.st_size > 0 && status.st_size < 1024 * 1024);
85 CHECK((status.st_mode & 0444) == 0444 && !(status.st_mode & 0222));
86 const size_t length = (size_t)status.st_size;
87 char* expected = malloc(length + 1);
88 char* contents = malloc(length + 1);
89 CHECK(expected && contents);
90 CHECK(pread(fd, expected, length, 0) == (ssize_t)length);
92 CHECK(length >= 6 && !strcmp(expected + length - 6,
"# EOF\n"));
93 CHECK(strstr(expected,
"# TYPE pedigree_metrics_enabled gauge\n"));
94 CHECK(strstr(expected,
"# TYPE pedigree_cpus gauge\n"));
95 CHECK(strstr(expected,
"# TYPE pedigree_uptime_seconds gauge\n"));
96 CHECK(strstr(expected,
"# TYPE pedigree_memory_managed_bytes gauge\n"));
97 CHECK(strstr(expected,
"# TYPE pedigree_memory_free_bytes gauge\n"));
98 CHECK(strstr(expected,
"# TYPE pedigree_threads gauge\n"));
99 CHECK(strstr(expected,
"# TYPE pedigree_runnable_threads gauge\n"));
100 unsigned long long managed = 0, free_bytes = 0, threads = 0, runnable = 0;
101 const char* managed_line = strstr(expected,
"\npedigree_memory_managed_bytes ");
102 const char* free_line = strstr(expected,
"\npedigree_memory_free_bytes ");
103 const char* threads_line = strstr(expected,
"\npedigree_threads ");
104 const char* runnable_line = strstr(expected,
"\npedigree_runnable_threads ");
105 CHECK(managed_line &&
106 sscanf(managed_line,
"\npedigree_memory_managed_bytes %llu", &managed) == 1);
107 CHECK(free_line && sscanf(free_line,
"\npedigree_memory_free_bytes %llu", &free_bytes) == 1);
108 CHECK(threads_line && sscanf(threads_line,
"\npedigree_threads %llu", &threads) == 1);
109 CHECK(runnable_line && sscanf(runnable_line,
"\npedigree_runnable_threads %llu", &runnable) == 1);
110 CHECK(managed > 0 && free_bytes <= managed && threads > 0 && runnable <= threads);
111 const char* enabled_line = strstr(expected,
"\npedigree_metrics_enabled ");
113 CHECK(enabled_line && sscanf(enabled_line,
"\npedigree_metrics_enabled %u", &enabled) == 1 &&
119 while (offset < length) {
120 size_t chunk = (offset % 11) + 1;
121 if (chunk > length - offset) {
122 chunk = length - offset;
125 CHECK(read(offset & 1 ? alias : fd, contents + offset, chunk) == (ssize_t)chunk);
128 contents[length] = 0;
129 CHECK(!memcmp(contents, expected, length) && read(alias, contents, 1) == 0);
130 CHECK(pread(fd, contents, 1, LLONG_MAX - 1) == 0);
133 const unsigned long long before = metrics_yields(expected, &before_rows);
136 const char* cpu_line = strstr(expected,
"\npedigree_cpus ");
137 CHECK(cpu_line && sscanf(cpu_line,
"\npedigree_cpus %lu", &cpus) == 1 && cpus > 0 &&
138 before_rows == cpus);
139 CHECK(strstr(expected,
"# TYPE pedigree_scheduler_yields_total counter\n"));
140 CHECK(strstr(expected,
"# TYPE pedigree_spinlock_acquires_total counter\n"));
141 CHECK(strstr(expected,
"# TYPE pedigree_physical_page_allocations_total counter\n"));
142 CHECK(strstr(expected,
"# TYPE pedigree_wait_queue_waits_total counter\n"));
143 CHECK(strstr(expected,
"# TYPE pedigree_network_rx_packets_total counter\n"));
144 CHECK(strstr(expected,
"# TYPE pedigree_file_read_bytes_total counter\n"));
145 CHECK(strstr(expected,
"policy=\"no_irq\""));
147 CHECK(!before_rows && !strstr(expected,
"_total"));
150 for (
unsigned i = 0; i < 16; ++i) {
151 CHECK(sched_yield() == 0);
153 int next = open(
"/proc/metrics", O_RDONLY);
154 CHECK(next >= 0 && !fstat(next, &status) && status.st_size > 0 && status.st_size < 1024 * 1024);
155 char* later = malloc((
size_t)status.st_size + 1);
156 CHECK(later && read(next, later, status.st_size) == status.st_size);
157 later[status.st_size] = 0;
160 CHECK(metrics_yields(later, &after_rows) > before && after_rows == before_rows);
162 CHECK(strstr(later,
"\npedigree_metrics_enabled 0\n") && !strstr(later,
"_total"));
164 CHECK(close(next) == 0);
167 CHECK(lseek(alias, 0, SEEK_SET) == 0);
168 CHECK(read(fd, contents, length) == (ssize_t)length && !memcmp(contents, expected, length));
169 CHECK(close(fd) == 0 && lseek(alias, 0, SEEK_SET) == 0);
170 CHECK(read(alias, contents, length) == (ssize_t)length && !memcmp(contents, expected, length));
171 CHECK(close(alias) == 0);
176 const uid_t saved_uid = geteuid();
178 CHECK(seteuid(65534) == 0);
181 int writable = open(
"/proc/metrics", O_WRONLY);
182 const int open_error = errno;
184 CHECK(seteuid(saved_uid) == 0);
186 CHECK(writable == -1 && open_error == EACCES);
187 puts(
"SYSTEM-STATUS-CONTRACT: METRICS PASS");
191static int process_status(
void) {
193 unsigned long long uptime = 0, uptime_hundredths = 0, idle = 0, idle_hundredths = 0;
194 CHECK(read_status_file(
"/proc/uptime", contents,
sizeof(contents)) > 0);
195 CHECK(sscanf(contents,
"%llu.%llu %llu.%llu", &uptime, &uptime_hundredths, &idle,
196 &idle_hundredths) == 4);
197 CHECK(uptime_hundredths < 100 && idle_hundredths < 100);
199 CHECK(read_status_file(
"/proc/stat", contents,
sizeof(contents)) > 0);
200 unsigned long long user = 0, kernel = 0, idle_ticks = 0;
201 CHECK(sscanf(contents,
"cpu %llu %*u %llu %llu", &user, &kernel, &idle_ticks) == 3);
202 CHECK(strstr(contents,
"\nbtime ") && strstr(contents,
"\nprocs_running "));
204 CHECK(read_status_file(
"/proc/loadavg", contents,
sizeof(contents)) > 0);
205 unsigned long long running = 0, tasks = 0, last_pid = 0;
206 CHECK(sscanf(contents,
"%*u.%*u %*u.%*u %*u.%*u %llu/%llu %llu", &running, &tasks, &last_pid) ==
208 CHECK(tasks > 0 && running <= tasks && last_pid >= (
unsigned long long)getpid());
210 CHECK(read_status_file(
"/proc/cpuinfo", contents,
sizeof(contents)) > 0);
211 CHECK(strstr(contents,
"processor\t: 0") && strstr(contents,
"vendor_id\t: Pedigree"));
212 CHECK(read_status_file(
"/proc/partitions", contents,
sizeof(contents)) > 0);
213 CHECK(strstr(contents,
"major minor") && strstr(contents,
"disk"));
215 CHECK(read_status_file(
"/proc/self/status", contents,
sizeof(contents)) > 0);
217 snprintf(pid_line,
sizeof(pid_line),
"Pid:\t%ld\n", (
long)getpid());
218 CHECK(strstr(contents,
"Name:\t") && strstr(contents, pid_line) && strstr(contents,
"VmRSS:\t"));
220 CHECK(read_status_file(
"/proc/self/stat", contents,
sizeof(contents)) > 0);
221 unsigned long long stat_pid = 0;
222 CHECK(sscanf(contents,
"%llu (", &stat_pid) == 1 && stat_pid == (
unsigned long long)getpid());
223 char* stat_field = strrchr(contents,
')');
225 size_t stat_fields = 2;
226 while (*++stat_field) {
227 while (*stat_field ==
' ' || *stat_field ==
'\n')
232 while (*stat_field && *stat_field !=
' ' && *stat_field !=
'\n')
236 CHECK(stat_fields == 52);
238 CHECK(read_status_file(
"/proc/self/statm", contents,
sizeof(contents)) > 0);
239 unsigned long long size = 0, resident = 0, shared = 0;
240 CHECK(sscanf(contents,
"%llu %llu %llu", &size, &resident, &shared) == 3);
241 CHECK(resident <= size && shared <= resident);
243 ssize_t command_bytes = read_status_file(
"/proc/self/cmdline", contents,
sizeof(contents));
244 CHECK(command_bytes > 1 && contents[command_bytes - 1] == 0);
245 CHECK(strstr(contents,
"system-status-contract-test"));
247 struct stat expected, proc_path;
248 CHECK(!stat(
".", &expected) && !stat(
"/proc/self/cwd", &proc_path));
249 CHECK(expected.st_dev == proc_path.st_dev && expected.st_ino == proc_path.st_ino);
250 CHECK(!stat(
"/", &expected) && !stat(
"/proc/self/root", &proc_path));
251 CHECK(expected.st_dev == proc_path.st_dev && expected.st_ino == proc_path.st_ino);
252 char executable[PATH_MAX] = {};
253 ssize_t executable_length = readlink(
"/proc/self/exe", executable,
sizeof(executable) - 1);
254 CHECK(executable_length > 0 && strstr(executable,
"system-status-contract-test"));
255 puts(
"SYSTEM-STATUS-CONTRACT: PROCFS PASS");
259static int first_entry(
const char* path,
char* name,
size_t capacity) {
260 DIR* directory = opendir(path);
263 struct dirent* entry;
265 while ((entry = readdir(directory))) {
266 if (!strcmp(entry->d_name,
".") || !strcmp(entry->d_name,
".."))
268 snprintf(name, capacity,
"%s", entry->d_name);
276static int sysfs_status(
void) {
278 CHECK(!stat(
"/sys/class", &status) && S_ISDIR(status.st_mode));
279 CHECK(!stat(
"/sys/bus/pci/devices", &status) && S_ISDIR(status.st_mode));
280 CHECK(!stat(
"/sys/firmware/efi", &status) && S_ISDIR(status.st_mode));
282 char entry[128], path[256], contents[512];
283 CHECK(first_entry(
"/sys/bus/pci/devices", entry,
sizeof(entry)) == 1);
284 snprintf(path,
sizeof(path),
"/sys/bus/pci/devices/%s/vendor", entry);
285 CHECK(read_status_file(path, contents,
sizeof(contents)) > 0 && !strncmp(contents,
"0x", 2));
286 snprintf(path,
sizeof(path),
"/sys/bus/pci/devices/%s/device", entry);
287 CHECK(read_status_file(path, contents,
sizeof(contents)) > 0 && !strncmp(contents,
"0x", 2));
289 int block = first_entry(
"/sys/class/block", entry,
sizeof(entry));
292 snprintf(path,
sizeof(path),
"/sys/class/block/%s/size", entry);
293 CHECK(read_status_file(path, contents,
sizeof(contents)) > 0);
296 int network = first_entry(
"/sys/class/net", entry,
sizeof(entry));
299 snprintf(path,
sizeof(path),
"/sys/class/net/%s/operstate", entry);
300 CHECK(read_status_file(path, contents,
sizeof(contents)) > 0);
302 puts(
"SYSTEM-STATUS-CONTRACT: SYSFS PASS");
306static int device_status(
void) {
308 CHECK(!stat(
"/dev/disk/by-uuid", &status) && S_ISDIR(status.st_mode));
309 CHECK(!stat(
"/dev/disk/by-label", &status) && S_ISDIR(status.st_mode));
310 CHECK(!stat(
"/dev/disk/by-partuuid", &status) && S_ISDIR(status.st_mode));
311 CHECK(!stat(
"/dev/disk/by-partlabel", &status) && S_ISDIR(status.st_mode));
313 char entry[128], path[256];
314 CHECK(first_entry(
"/dev/disk/by-uuid", entry,
sizeof(entry)) == 1);
315 snprintf(path,
sizeof(path),
"/dev/disk/by-uuid/%s", entry);
316 char target[64] = {};
317 ssize_t length = readlink(path, target,
sizeof(target) - 1);
318 CHECK(length > 0 && !strncmp(target,
"/dev/block/", 11));
319 CHECK(!stat(path, &status) && S_ISBLK(status.st_mode));
320 CHECK(!stat(
"/dev/disk/by-label/pedigree", &status) && S_ISBLK(status.st_mode));
322 unsigned char byte = 0xa5;
323 int fd = open(
"/dev/full", O_RDWR);
325 CHECK(read(fd, &
byte, 1) == 1 &&
byte == 0);
327 CHECK(write(fd,
"x", 1) == -1 && errno == ENOSPC);
328 CHECK(close(fd) == 0);
330 memset(target, 0,
sizeof(target));
331 length = readlink(
"/dev/stdin", target,
sizeof(target) - 1);
332 CHECK(length > 0 && !strcmp(target,
"/proc/self/fd/0"));
333 length = readlink(
"/dev/stdout", target,
sizeof(target) - 1);
334 CHECK(length > 0 && !strcmp(target,
"/proc/self/fd/1"));
335 length = readlink(
"/dev/stderr", target,
sizeof(target) - 1);
336 CHECK(length > 0 && !strcmp(target,
"/proc/self/fd/2"));
338 fd = open(
"/dev/shm/system-status-contract", O_CREAT | O_EXCL | O_RDWR, 0600);
339 CHECK(fd >= 0 && write(fd,
"shm", 3) == 3 && close(fd) == 0);
340 CHECK(unlink(
"/dev/shm/system-status-contract") == 0);
341 fd = open(
"/run/lock/system-status-contract", O_CREAT | O_EXCL | O_RDWR, 0600);
342 CHECK(fd >= 0 && close(fd) == 0);
343 CHECK(unlink(
"/run/lock/system-status-contract") == 0);
344 puts(
"SYSTEM-STATUS-CONTRACT: DEVFS PASS");
348static int network_status(
void) {
349 int fd = open(
"/proc/net/interfaces", O_RDONLY);
352 ssize_t bytes = read(fd, contents,
sizeof(contents) - 1);
355 CHECK(strstr(contents,
" Device:") || !strcmp(contents,
"No network devices.\n"));
356 if (strstr(contents,
" Device:")) {
357 CHECK(strstr(contents,
" MAC:") && strstr(contents,
" IPv4:") &&
358 strstr(contents,
" Netmask:") && strstr(contents,
" Gateway:"));
360 CHECK(close(fd) == 0);
361 fd = open(
"/proc/net/dev", O_RDONLY);
363 bytes = read(fd, contents,
sizeof(contents) - 1);
366 CHECK(strstr(contents,
"Inter-") && strstr(contents,
"Transmit"));
367 CHECK(close(fd) == 0);
368 fd = open(
"/proc/resolv.conf", O_RDONLY);
370 bytes = read(fd, contents,
sizeof(contents) - 1);
373 CHECK(!bytes || strstr(contents,
"nameserver "));
374 CHECK(close(fd) == 0);
375 puts(
"SYSTEM-STATUS-CONTRACT: NETWORK PASS");
379static int kernel_log(
void) {
380 CHECK(syscall(SYS_syslog, 0, NULL, 0) == 0);
381 CHECK(syscall(SYS_syslog, 1, NULL, 0) == 0);
382 long capacity = syscall(SYS_syslog, 10, NULL, 0);
383 CHECK(capacity > 0 && capacity < INT_MAX);
384 CHECK(syscall(SYS_syslog, 3, NULL, 0) == 0);
386 CHECK(syscall(SYS_syslog, 3, NULL, -1) == -1 && errno == EINVAL);
388 CHECK(syscall(SYS_syslog, 3, (
void*)(uintptr_t)1, 1) == -1 && errno == EFAULT);
390 CHECK(syscall(SYS_syslog, 42, NULL, 0) == -1 && errno == EINVAL);
391 void* readonly = mmap(NULL, 4096, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
392 CHECK(readonly != MAP_FAILED);
394 CHECK(syscall(SYS_syslog, 3, readonly, 1) == -1 && errno == EFAULT);
395 CHECK(munmap(readonly, 4096) == 0);
398 snprintf(marker,
sizeof(marker),
"SYSTEM-STATUS-KLOG-%ld", (
long)getpid());
399 CHECK(pedigree_log(LOG_NOTICE,
"%s", marker) == 0);
400 char* buffer = malloc((
size_t)capacity + 2);
402 for (
int pass = 0; pass < 3; ++pass) {
403 memset(buffer, 0xa5, (
size_t)capacity + 2);
404 long count = syscall(SYS_syslog, 3, buffer + 1, capacity);
405 CHECK(count > 0 && count <= capacity);
406 CHECK((
unsigned char)buffer[0] == 0xa5 && (
unsigned char)buffer[capacity + 1] == 0xa5);
407 buffer[count + 1] = 0;
408 CHECK(strstr(buffer + 1, marker));
411 CHECK(syscall(SYS_syslog, 4, buffer + 1, capacity) == -1 && errno == ENOSYS);
413 CHECK(syscall(SYS_syslog, 5, NULL, 0) == -1 && errno == ENOSYS);
416 unsigned char tail[3] = {0xa5, 0xa5, 0xa5};
417 CHECK(syscall(SYS_syslog, 3, tail + 1, 1) == 1 && tail[1] ==
'\n');
418 CHECK(tail[0] == 0xa5 && tail[2] == 0xa5);
420 puts(
"SYSTEM-STATUS-CONTRACT: KERNEL-LOG PASS");
425 if (memory_status() || process_status() || metrics_status() || sysfs_status() ||
426 device_status() || network_status() || kernel_log())
428 puts(
"SYSTEM-STATUS-CONTRACT: PASS");