test/wsgidav.c

branch
dav-2
changeset 902
06fa328989ee
equal deleted inserted replaced
901:271103d11d55 902:06fa328989ee
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2026 Olaf Wintermann. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "wsgidav.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33
34 //ifndef _WIN32
35
36 #include <unistd.h>
37 #include <sys/fcntl.h>
38
39 #include <spawn.h>
40 #include <sys/wait.h>
41 #include <sys/stat.h>
42
43 #include <cx/string.h>
44 #include <cx/buffer.h>
45 #include <cx/streams.h>
46 #include <cx/printf.h>
47
48
49 // -------------------------- Process Utils --------------------------------
50
51 typedef struct {
52 pid_t pid;
53 int in;
54 int out;
55 int err;
56 } Process;
57
58 static Process process_spawn(const char *exec, char **args) {
59 Process proc = { 0 };
60
61 int pin[2];
62 int pout[2];
63 int perr[2];
64
65 // child process stdin/stdout pipes
66 if(pipe(pin)) {
67 perror("pipe");
68 return proc;
69 }
70 if(pipe(pout)) {
71 perror("pipe");
72 close(pin[0]);
73 close(pin[1]);
74 }
75 if(pipe(perr)) {
76 perror("pipe");
77 close(pin[0]);
78 close(pin[1]);
79 close(pout[0]);
80 close(pout[1]);
81 }
82
83 posix_spawn_file_actions_t actions;
84 posix_spawn_file_actions_init(&actions);
85 posix_spawn_file_actions_adddup2(&actions, pin[0], 0);
86 posix_spawn_file_actions_adddup2(&actions, pout[1], 1);
87 posix_spawn_file_actions_adddup2(&actions, perr[1], 2);
88
89 // child environment variables
90 char *path = getenv("PATH");
91 if(!path) {
92 path = "/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin";
93 }
94
95 cxmutstr env_path = cx_asprintf("PATH=%s", path);
96
97 char *env[10];
98 env[0] = "LC_ALL=C";
99 env[1] = env_path.ptr;
100 env[2] = NULL; // array must be null-terminated
101
102 // execute posix_spawn to create a child process
103 pid_t child;
104 int ret = posix_spawn(
105 &child,
106 exec,
107 &actions,
108 NULL, // attributes
109 args,
110 env);
111 posix_spawn_file_actions_destroy(&actions);
112
113 close(pin[0]);
114 close(pout[1]);
115 close(perr[1]);
116
117 if(ret) {
118 // posix spawn failed
119 perror("posix_spawn");
120 // close remaining pipe fds
121 close(pin[1]);
122 close(pout[0]);
123 close(perr[0]);
124 } else {
125 proc.pid = child;
126 proc.in = pin[1];
127 proc.out = pout[0];
128 proc.err = perr[0];
129 }
130
131 return proc;
132 }
133
134 int process_close(Process *p) {
135 if(p->pid == 0) {
136 return -1;
137 }
138 close(p->in);
139 close(p->out);
140 int status = -1;
141 waitpid(p->pid, &status, 0);
142 return status;
143 }
144
145 size_t process_read(void *buf, size_t size, size_t n, Process *p) {
146 ssize_t r = read(p->out, buf, size*n);
147 return r > 0 ? r : 0;
148 }
149
150 size_t process_err_read(void *buf, size_t size, size_t n, Process *p) {
151 ssize_t r = read(p->err, buf, size*n);
152 return r > 0 ? r : 0;
153 }
154
155 static cxmutstr read_str(Process *p, cx_read_func readf) {
156 if(p->pid == 0) {
157 return CX_NULLSTR;
158 }
159 CxBuffer buffer;
160 cxBufferInit(&buffer, NULL, NULL, 1024, CX_BUFFER_AUTO_EXTEND);
161 cx_stream_copy(p, &buffer, readf, (cx_write_func)cxBufferWrite);
162 cxBufferTerminate(&buffer);
163 return cx_mutstrn(buffer.space, buffer.size);
164 }
165
166 cxmutstr process_read_string(Process *p) {
167 return read_str(p, (cx_read_func)process_read);
168 }
169
170 cxmutstr process_err_read_string(Process *p) {
171 return read_str(p, (cx_read_func)process_err_read);
172 }
173
174
175 // ------------------------------ wsgidav exec -----------------------------
176
177 static char *wsgidav_exec_path;
178 static Process wsgidav_process;
179 static int runtest_root_dir = 0;
180
181 int wsgidav_is_available(void) {
182 // check if the wsgidava binary is available
183 char *args[] = {
184 "which",
185 "wsgidav",
186 NULL
187 };
188 Process p = process_spawn("/usr/bin/which", args);
189 if(p.pid == 0) {
190 return 0;
191 }
192
193 cxmutstr path = process_read_string(&p);
194 int status = process_close(&p);
195 if(status == 0) {
196 wsgidav_exec_path = cx_strdup(cx_strtrim(path)).ptr;
197 }
198
199 free(path.ptr);
200
201 if(status != 0) {
202 return 0;
203 }
204
205 // check if the wsgidav config file is available
206 struct stat s;
207 if(!stat("test/wsgidav/wsgidav.yaml", &s)) {
208 runtest_root_dir = 1; // dav root directory
209 } else {
210 if(!stat("../test/wsgidav/wsgidav.yaml", &s)) {
211 runtest_root_dir = 2; // build directory
212 } else {
213 fprintf(stderr, "Error: wsgidav found, but the config file wsgidav.yaml was not found\nTry running the tests from the dav root directory\n");
214 return 0;
215 }
216 }
217
218 if(runtest_root_dir == 1) {
219 if(!stat("build", &s)) {
220 if(!S_ISDIR(s.st_mode)) {
221 fprintf(stderr, "Error: build is not a directory: wsgidav disabled\n");
222 return 0;
223 }
224 } else {
225 fprintf(stderr, "Error: build directory not found: wsgidav disabled\n");
226 return 0;
227 }
228 }
229
230 return 1;
231 }
232
233 int wsgidav_start(void) {
234 if(!wsgidav_exec_path) {
235 return 1; // wsgidav_start called but wsgidav_is_available return 0
236 }
237
238 if(runtest_root_dir == 1) {
239 if(chdir("build")) {
240 perror("chdir");
241 return 1;
242 }
243 }
244
245 // delete previous testrepo directory
246 char *args1[] = { "rm", "-Rf", "testrepo", NULL };
247 Process p_rm = process_spawn("/bin/rm", args1);
248 cxmutstr err = process_err_read_string(&p_rm);
249 if(process_close(&p_rm) != 0) {
250 cxmutstr s = cx_strtrim(err);
251 fprintf(stderr, "rm failed: %.*s\n", (int)s.length, s.ptr);
252 return 1;
253 }
254 free(err.ptr);
255
256 // copy testrepo to the build directory
257 char *args2[] = { "cp", "-R", "../test/wsgidav/testrepo", "testrepo", NULL };
258 Process p_cp = process_spawn("/bin/cp", args2);
259 err = process_err_read_string(&p_cp);
260 if(process_close(&p_cp) != 0) {
261 cxmutstr s = cx_strtrim(err);
262 fprintf(stderr, "cp failed: %.*s\n", (int)s.length, s.ptr);
263 return 1;
264 }
265 free(err.ptr);
266
267 // start wsgidav
268 char *args3[] = { "wsgidav", "-c", "../test/wsgidav/wsgidav.yaml", NULL };
269 wsgidav_process = process_spawn(wsgidav_exec_path, args3);
270
271 // TODO: parse log or try to connect to localhost
272 sleep(1);
273
274 return 0;
275 }
276
277 int wsgidav_stop(void) {
278 if(wsgidav_process.pid == 0) {
279 return 0;
280 }
281 kill(wsgidav_process.pid, SIGKILL);
282 (void)process_close(&wsgidav_process);
283 //if(status > 2) {
284 // printf("wsgidav exit: %d\n", status);
285 //}
286 return 0;
287 }
288
289 /*
290 #else
291
292 int wsgidav_is_available(void) {
293 return 0;
294 }
295
296 int wsgidav_start(void) {
297 return 1;
298 }
299
300 int wsgidav_stop(void) {
301 return 1;
302 }
303
304 #endif
305
306 */

mercurial