The Pedigree Project 0.1
executable-file-contract-test/main.c
1/* Copyright (c) 2026, Pedigree Developers. */
2#define _GNU_SOURCE
3#include <fcntl.h>
4#include <poll.h>
5#include <signal.h>
6#include <stdlib.h>
7#include <string.h>
8#include <time.h>
9#include <unistd.h>
10
11#include "contract.h"
12#include <sys/stat.h>
13#include <sys/wait.h>
14
15size_t ef_page;
16char ef_directory[PATH_MAX];
17char ef_self[PATH_MAX];
18int ef_ext2;
19
20int ef_create(const char* name, char path[PATH_MAX]) {
21 snprintf(path, PATH_MAX, "%s/%s", ef_directory, name);
22 int fd = open(path, O_CREAT | O_EXCL | O_RDWR | O_CLOEXEC, 0700);
23 if (fd < 0)
24 return -1;
25 unsigned char* bytes = malloc(3 * ef_page);
26 if (bytes)
27 memset(bytes, 0x31, 3 * ef_page);
28 if (!bytes || write(fd, bytes, 3 * ef_page) != (ssize_t)(3 * ef_page)) {
29 free(bytes);
30 close(fd);
31 unlink(path);
32 return -1;
33 }
34 free(bytes);
35 return fd;
36}
37
38int ef_copy(const char* source, const char* destination) {
39 int failed = 0;
40 int input = open(source, O_RDONLY | O_CLOEXEC);
41 int output = open(destination, O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0700);
42 CHECK(input >= 0 && output >= 0);
43 char bytes[8192];
44 ssize_t count;
45 while ((count = read(input, bytes, sizeof(bytes))) > 0) {
46 ssize_t done = 0;
47 while (done < count) {
48 ssize_t amount = write(output, bytes + done, count - done);
49 CHECK(amount > 0);
50 done += amount;
51 }
52 }
53 CHECK(count == 0);
54out:
55 if (input >= 0)
56 close(input);
57 if (output >= 0)
58 close(output);
59 return failed;
60}
61
62int ef_receive(int fd, char expected) {
63 struct pollfd item = {.fd = fd, .events = POLLIN};
64 int result;
65 do {
66 result = poll(&item, 1, 5000);
67 } while (result < 0 && errno == EINTR);
68 char byte = 0;
69 return result > 0 && read(fd, &byte, 1) == 1 && byte == expected ? 0 : -1;
70}
71
72int ef_send(int fd, char value) {
73 return write(fd, &value, 1) == 1 ? 0 : -1;
74}
75
76int ef_reap(pid_t child) {
77 for (int n = 0; n < 1000; ++n) {
78 int status;
79 pid_t result = waitpid(child, &status, WNOHANG);
80 if (result == child)
81 return WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
82 if (result < 0 && errno != EINTR)
83 return -1;
84 struct timespec pause = {0, 5000000};
85 nanosleep(&pause, NULL);
86 }
87 kill(child, SIGKILL);
88 while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
89 }
90 return -1;
91}
92
93int main(int argc, char** argv) {
94 setvbuf(stdout, NULL, _IONBF, 0);
95 alarm(60);
96 if (argc == 4 && !strcmp(argv[1], "--load"))
97 return ef_load(argv[2], atoi(argv[3]));
98 if (argc > 2) {
99 fprintf(stderr, "usage: %s [/tmp|/]\n", argv[0]);
100 return 2;
101 }
102 int failed = 0;
103 const char* base = argc == 2 ? argv[1] : "/tmp";
104 ef_ext2 = strcmp(base, "/tmp") != 0;
105 long page = sysconf(_SC_PAGESIZE);
106 CHECK(page > 0);
107 ef_page = (size_t)page;
108 ssize_t count = readlink("/proc/self/exe", ef_self, sizeof(ef_self) - 1);
109 CHECK(count > 0 && (size_t)count < sizeof(ef_self) - 1);
110 ef_self[count] = 0;
111 snprintf(ef_directory, sizeof(ef_directory), "%s/executable-files-XXXXXX", base);
112 CHECK(mkdtemp(ef_directory));
113 printf("EXECUTABLE-FILE-CONTRACT: BEGIN %s\n", base);
114 CHECK(!ef_mutations());
115 puts("EXECUTABLE-FILE-CONTRACT: PASS mutation-denial");
116 CHECK(!ef_lifetime());
117 puts("EXECUTABLE-FILE-CONTRACT: PASS fork-split-lifetime");
118 CHECK(!ef_conflicts());
119 puts("EXECUTABLE-FILE-CONTRACT: PASS shared-conflicts");
120 CHECK(!ef_ordinary());
121 puts("EXECUTABLE-FILE-CONTRACT: PASS ordinary-private");
122 CHECK(!ef_library());
123 puts("EXECUTABLE-FILE-CONTRACT: PASS library-replacement");
124out:
125 if (*ef_directory)
126 rmdir(ef_directory);
127 puts(failed ? "EXECUTABLE-FILE-CONTRACT: END FAIL" : "EXECUTABLE-FILE-CONTRACT: END PASS");
128 return failed;
129}