17#define CHECK(expression) \
19 if (!(expression)) { \
20 fprintf(stderr, "ACCOUNTING-CONTRACT: FAIL line=%d errno=%d\n", __LINE__, errno); \
26static int wait_for(pid_t child,
int expected) {
28 while (waitpid(child, &status, 0) == -1) {
32 return status == expected;
35static int record_for(
int fd, pid_t pid,
struct acct_v3* result) {
37 if (fstat(fd, &metadata) || metadata.st_size < 0 || metadata.st_size %
sizeof(*result))
40 for (off_t offset = 0; offset < metadata.st_size; offset +=
sizeof(*result)) {
41 struct acct_v3 record;
42 if (pread(fd, &record,
sizeof(record), offset) !=
sizeof(record) ||
43 record.ac_version != (3 | ACCT_BYTEORDER))
45 if (record.ac_pid == (
unsigned)pid) {
53static pid_t child_exit(
int code) {
60static void* thread_exit(
void* argument) {
64int main(
int argc,
char** argv) {
65 if (argc > 1 && !strcmp(argv[1],
"--exec-child"))
67 int failed = 0, first = -1, second = -1;
68 char directory[256], first_path[300], second_path[300], missing[300];
69 const char* base = argc > 1 ? argv[1] :
"/tests";
70 snprintf(directory,
sizeof(directory),
"%s/accounting-contract.XXXXXX", base);
71 first_path[0] = second_path[0] = missing[0] = 0;
73 CHECK(geteuid() == 0 && acct(NULL) == 0);
74 CHECK(mkdtemp(directory));
75 snprintf(first_path,
sizeof(first_path),
"%s/first", directory);
76 snprintf(second_path,
sizeof(second_path),
"%s/second", directory);
77 snprintf(missing,
sizeof(missing),
"%s/missing", directory);
78 first = open(first_path, O_CREAT | O_EXCL | O_RDWR, 0600);
79 second = open(second_path, O_CREAT | O_EXCL | O_RDWR, 0600);
80 CHECK(first >= 0 && second >= 0);
82 CHECK(acct(
"") == -1 && errno == ENOENT);
84 CHECK(acct(directory) == -1 && errno == EACCES);
86 CHECK(acct(missing) == -1 && errno == ENOENT);
87 CHECK(acct(first_path) == 0);
89 CHECK(acct(missing) == -1 && errno == ENOENT);
91 time_t before = time(NULL);
95 struct timespec pause = {.tv_nsec = 80000000};
96 nanosleep(&pause, NULL);
97 volatile unsigned sum = 0;
98 for (
unsigned i = 0; i < 5000000; ++i)
102 CHECK(wait_for(child, 23 << 8));
103 struct acct_v3 record;
104 CHECK(record_for(first, child, &record) == 1);
105 CHECK(record.ac_ppid == (
unsigned)getpid() && record.ac_uid == (
unsigned)getuid() &&
106 record.ac_gid == (
unsigned)getgid() && record.ac_exitcode == (23 << 8));
107 CHECK((record.ac_flag & AFORK) && !(record.ac_flag & AXSIG) && record.ac_etime >= 5 &&
108 record.ac_btime >= (
unsigned)before && record.ac_btime <= (
unsigned)time(NULL));
109 puts(
"ACCOUNTING-CONTRACT: PASS record-before-wait");
114 struct timespec pause = {.tv_nsec = 80000000};
115 nanosleep(&pause, NULL);
116 execl(
"/usr/bin/accounting-contract-test",
"accounting-contract-test",
"--exec-child", NULL);
119 CHECK(wait_for(child, 42 << 8));
120 CHECK(record_for(first, child, &record) == 1 && !(record.ac_flag & AFORK) &&
121 record.ac_exitcode == (42 << 8) && record.ac_etime >= 5 &&
122 !strncmp(record.ac_comm,
"accounting-cont", 15));
123 puts(
"ACCOUNTING-CONTRACT: PASS exec-lifetime");
128 pthread_t threads[3];
129 for (
unsigned i = 0; i < 3; ++i)
130 if (pthread_create(&threads[i], NULL, thread_exit, NULL))
132 for (
unsigned i = 0; i < 3; ++i)
133 if (pthread_join(threads[i], NULL))
135 if (record_for(first, getpid(), &record) != 0)
139 CHECK(wait_for(child, 24 << 8));
140 CHECK(record_for(first, child, &record) == 1 && record.ac_exitcode == (24 << 8));
141 puts(
"ACCOUNTING-CONTRACT: PASS final-process-exit");
146 if (setgid(65534) || setuid(65534))
149 if (acct(NULL) != -1 || errno != EPERM)
152 if (acct(first_path) != -1 || errno != EPERM)
156 CHECK(wait_for(child, 0));
157 CHECK(record_for(first, child, &record) == 1 && record.ac_uid == 65534 && record.ac_gid == 65534);
158 puts(
"ACCOUNTING-CONTRACT: PASS credentials");
163 kill(getpid(), SIGTERM);
166 CHECK(wait_for(child, SIGTERM));
167 CHECK(record_for(first, child, &record) == 1 && (record.ac_flag & AXSIG) &&
168 record.ac_exitcode == SIGTERM);
171 for (
unsigned i = 0; i < 8; ++i) {
172 children[i] = child_exit(i);
173 CHECK(children[i] > 0);
175 for (
unsigned i = 0; i < 8; ++i) {
176 CHECK(wait_for(children[i], i << 8));
177 CHECK(record_for(first, children[i], &record) == 1 && record.ac_exitcode == (i << 8));
179 CHECK(unlink(first_path) == 0);
180 child = child_exit(17);
181 CHECK(child > 0 && wait_for(child, 17 << 8));
182 CHECK(record_for(first, child, &record) == 1);
183 puts(
"ACCOUNTING-CONTRACT: PASS concurrent-and-unlinked");
185 struct stat old_first, old_second, current;
186 CHECK(acct(second_path) == 0 && fstat(first, &old_first) == 0);
187 child = child_exit(19);
188 CHECK(child > 0 && wait_for(child, 19 << 8));
189 CHECK(record_for(second, child, &record) == 1 && record_for(first, child, &record) == 0);
190 CHECK(fstat(first, ¤t) == 0 && current.st_size == old_first.st_size);
191 CHECK(acct(NULL) == 0 && fstat(second, &old_second) == 0);
192 child = child_exit(21);
193 CHECK(child > 0 && wait_for(child, 21 << 8));
194 CHECK(fstat(second, ¤t) == 0 && current.st_size == old_second.st_size);
195 CHECK(fsync(second) == 0);
196 puts(
"ACCOUNTING-CONTRACT: PASS switch-and-disable");
209 puts(failed ?
"ACCOUNTING-CONTRACT: END FAIL" :
"ACCOUNTING-CONTRACT: END PASS");