1#include <assert.h>
2#include <errno.h>
3#include <fcntl.h>
4#include <limits.h>
5#include <spawn.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <unistd.h>
10
11#include <sys/stat.h>
12#include <sys/types.h>
13#include <sys/wait.h>
14
15#define TESTDIR "/bar/baz"
16#define TESTPATH "/foo/bar/test"
17#define SUBTEST "./test sub"
18
19extern char **environ;
20
21void test_spawn(void) {
22 pid_t pid;
23 int ret;
24 posix_spawn_file_actions_t file_actions;
25 char *argv[] = {"true", NULL};
26
27 assert(posix_spawn_file_actions_init(&file_actions) == 0);
28
29 ret = posix_spawn(&pid, TESTPATH, &file_actions, NULL, argv, environ);
30
31 assert(ret == 0);
32 assert(waitpid(pid, NULL, 0) != -1);
33}
34
35void test_execv(void) {
36 char *argv[] = {"true", NULL};
37 assert(execv(TESTPATH, argv) == 0);
38}
39
40void test_system(void) {
41 assert(system(TESTPATH) == 0);
42}
43
44void test_subprocess(void) {
45 assert(system(SUBTEST) == 0);
46}
47
48void test_stat_with_null_path(void) {
49 // This checks whether the compiler optimizes away the null pointer check
50 // on the path passed to stat(). If that's the case, the following code
51 // should segfault.
52 struct stat buf;
53#pragma GCC diagnostic push
54#pragma GCC diagnostic ignored "-Wnonnull"
55 stat(NULL, &buf);
56#pragma GCC diagnostic pop
57}
58
59void assert_mktemp_path(
60 const char * orig_prefix,
61 const char * orig_suffix,
62 const char * updated
63) {
64 // prefix unchanged
65 assert(strncmp(updated, orig_prefix, strlen(orig_prefix)) == 0);
66 // wildcards replaced
67 assert(strcmp(updated + strlen(orig_prefix), "XXXXXX") != 0);
68 // suffix unchanged
69 assert(strcmp(updated + strlen(orig_prefix) + 6, orig_suffix) == 0);
70}
71
72int main(int argc, char *argv[])
73{
74 FILE *testfp;
75 int testfd;
76 struct stat testsb;
77#ifdef __GLIBC__
78 struct stat64 testsb64;
79#endif
80#if defined(__linux__) && defined(STATX_TYPE)
81 struct statx testsbx;
82#endif
83 char buf[PATH_MAX];
84
85 testfp = fopen(TESTPATH, "r");
86 assert(testfp != NULL);
87 fclose(testfp);
88
89 testfd = open(TESTPATH, O_RDONLY);
90 assert(testfd != -1);
91 close(testfd);
92
93 assert(access(TESTPATH, X_OK) == 0);
94
95 // On EOVERFLOW checks below: when TESTPATH lands on a filesystem
96 // that requires 64-bit inode values (like btrfs used for a while)
97 // it will fail on 32-bit systems.
98
99 assert(stat(TESTPATH, &testsb) != -1 || errno == EOVERFLOW);
100#ifdef __GLIBC__
101 assert(stat64(TESTPATH, &testsb64) != -1);
102#endif
103 assert(fstatat(123, TESTPATH, &testsb, 0) != -1 || errno == EOVERFLOW);
104#ifdef __GLIBC__
105 assert(fstatat64(123, TESTPATH, &testsb64, 0) != -1);
106#endif
107#if defined(__linux__) && defined(STATX_TYPE)
108 assert(statx(123, TESTPATH, 0, STATX_ALL, &testsbx) != -1);
109#endif
110
111 assert(getcwd(buf, PATH_MAX) != NULL);
112 assert(chdir(TESTDIR) == 0);
113 assert(chdir(buf) == 0);
114
115 assert(mkdir(TESTDIR "/dir-mkdir", 0777) == 0);
116 assert(unlink(TESTDIR "/dir-mkdir") == -1); // it's a directory!
117#ifndef __APPLE__
118 assert(errno == EISDIR);
119#endif
120 assert(rmdir(TESTDIR "/dir-mkdir") == 0);
121 assert(unlink(TESTDIR "/dir-mkdir") == -1);
122 assert(errno == ENOENT);
123
124 assert(mkdirat(123, TESTDIR "/dir-mkdirat", 0777) == 0);
125 assert(unlinkat(123, TESTDIR "/dir-mkdirat", 0) == -1); // it's a directory!
126#ifndef __APPLE__
127 assert(errno == EISDIR);
128#endif
129 assert(unlinkat(123, TESTDIR "/dir-mkdirat", AT_REMOVEDIR) == 0);
130
131 strncpy(buf, TESTDIR "/tempXXXXXX", PATH_MAX);
132 testfd = mkstemp(buf);
133 assert(testfd > 0);
134 assert_mktemp_path(TESTDIR "/temp", "", buf);
135 close(testfd);
136
137 strncpy(buf, TESTDIR "/tempXXXXXX", PATH_MAX);
138 testfd = mkostemp(buf, 0);
139 assert(testfd > 0);
140 assert_mktemp_path(TESTDIR "/temp", "", buf);
141 close(testfd);
142
143 strncpy(buf, TESTDIR "/tempXXXXXX.test", PATH_MAX);
144 testfd = mkstemps(buf, strlen(".test"));
145 assert(testfd > 0);
146 assert_mktemp_path(TESTDIR "/temp", ".test", buf);
147 close(testfd);
148
149 strncpy(buf, TESTDIR "/tempXXXXXX.test", PATH_MAX);
150 testfd = mkostemps(buf, strlen(".test"), 0);
151 assert(testfd > 0);
152 assert_mktemp_path(TESTDIR "/temp", ".test", buf);
153 close(testfd);
154
155 strncpy(buf, TESTDIR "/tempXXXXXX", PATH_MAX);
156 assert(mkdtemp(buf) == buf);
157 assert_mktemp_path(TESTDIR "/temp", "", buf);
158
159 strncpy(buf, TESTDIR "/tempXXXXXX", PATH_MAX);
160 assert(mktemp(buf) == buf);
161 assert_mktemp_path(TESTDIR "/temp", "", buf);
162
163 test_spawn();
164 test_system();
165 test_stat_with_null_path();
166
167 // Only run subprocess if no arguments are given
168 // as the subprocess will be called without argument
169 // otherwise we will have infinite recursion
170 if (argc == 1) {
171 test_subprocess();
172 }
173
174 test_execv();
175
176 /* If all goes well, this is never reached because test_execv() replaces
177 * the current process.
178 */
179 return 0;
180}