webmcp

view libraries/extos/extos.c @ 270:aedd13009ddc

Proper handling of 404's and mime types for static file delivery
author jbe
date Fri Mar 20 16:38:37 2015 +0100 (2015-03-20)
parents 593413f317f4
children 34bf5f7abe0d
line source
1 #include <lua.h>
2 #include <lauxlib.h>
3 #include <dirent.h>
4 #include <time.h>
5 #include <unistd.h>
6 #include <sys/types.h>
7 #include <sys/wait.h>
8 #include <signal.h>
9 #include <errno.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <fcntl.h>
13 #include <poll.h>
14 #include <stdlib.h>
16 #define EXTOS_MAX_ERRLEN 80
17 #define EXTOS_EXEC_MAX_ARGS 64
19 static lua_Number extos_monotonic_start_time;
21 static int extos_pfilter(lua_State *L) {
22 int i, result, exit_status, status_pipe_len;
23 const char *in_buf;
24 size_t in_len;
25 size_t in_pos = 0;
26 const char *filename;
27 const char *args[EXTOS_EXEC_MAX_ARGS+2];
28 int pipe_status[2];
29 int pipe_in[2];
30 int pipe_out[2];
31 int pipe_err[2];
32 pid_t child;
33 char status_buf[1];
34 char *out_buf = NULL;
35 size_t out_len = 1024;
36 size_t out_pos = 0;
37 char *err_buf = NULL;
38 size_t err_len = 1024;
39 size_t err_pos = 0;
40 void *old_sigpipe_action;
41 struct pollfd fds[3];
42 int in_closed = 0;
43 int out_closed = 0;
44 int err_closed = 0;
45 void *newptr;
46 char errmsg[EXTOS_MAX_ERRLEN+1];
47 in_buf = luaL_optlstring(L, 1, "", &in_len);
48 filename = luaL_checkstring(L, 2);
49 args[0] = filename;
50 for (i = 0; i < EXTOS_EXEC_MAX_ARGS; i++) {
51 if (lua_isnoneornil(L, 3+i)) break;
52 else args[i+1] = luaL_checkstring(L, 3+i);
53 }
54 if (!lua_isnoneornil(L, 3+i)) {
55 return luaL_error(L, "Too many arguments for pfilter call.");
56 }
57 args[i+1] = 0;
58 // status pipe for internal communication
59 if (pipe(pipe_status) < 0) {
60 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
61 goto extos_pfilter_error_A0;
62 }
63 // stdin
64 if (pipe(pipe_in) < 0) {
65 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
66 goto extos_pfilter_error_A1;
67 }
68 if (in_len) {
69 do result = fcntl(pipe_in[1], F_SETFL, O_NONBLOCK); while (result < 0 && errno == EINTR);
70 } else {
71 do result = close(pipe_in[1]); while (result < 0 && errno == EINTR);
72 in_closed = 1;
73 }
74 if (result < 0) {
75 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
76 goto extos_pfilter_error_A2;
77 }
78 // stdout
79 if (pipe(pipe_out) < 0) {
80 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
81 goto extos_pfilter_error_A2;
82 }
83 do result = fcntl(pipe_out[0], F_SETFL, O_NONBLOCK); while (result < 0 && errno == EINTR);
84 if (result < 0) {
85 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
86 goto extos_pfilter_error_A3;
87 }
88 // stderr
89 if (pipe(pipe_err) < 0) {
90 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
91 goto extos_pfilter_error_A3;
92 }
93 do result = fcntl(pipe_err[0], F_SETFL, O_NONBLOCK); while (result < 0 && errno == EINTR);
94 if (result < 0) {
95 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
96 goto extos_pfilter_error_A4;
97 }
98 // fork
99 child = fork();
100 if (child < 0) {
101 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
102 goto extos_pfilter_error_A4;
103 }
104 // skip error handling
105 goto extos_pfilter_success_A;
106 // error handling
107 extos_pfilter_error_A4:
108 do result = close(pipe_err[0]); while (result < 0 && errno == EINTR);
109 do result = close(pipe_err[1]); while (result < 0 && errno == EINTR);
110 extos_pfilter_error_A3:
111 do result = close(pipe_out[0]); while (result < 0 && errno == EINTR);
112 do result = close(pipe_out[1]); while (result < 0 && errno == EINTR);
113 extos_pfilter_error_A2:
114 do result = close(pipe_in[0]); while (result < 0 && errno == EINTR);
115 do result = close(pipe_in[1]); while (result < 0 && errno == EINTR);
116 extos_pfilter_error_A1:
117 do result = close(pipe_status[0]); while (result < 0 && errno == EINTR);
118 do result = close(pipe_status[1]); while (result < 0 && errno == EINTR);
119 extos_pfilter_error_A0:
120 return luaL_error(L, "Unexpected error in pfilter: %s", errmsg);
121 // end of error handling
122 extos_pfilter_success_A:
123 if (child) { // parent
124 old_sigpipe_action = signal(SIGPIPE, SIG_IGN);
125 do result = close(pipe_status[1]); while (result < 0 && errno == EINTR);
126 if (result < 0) goto extos_pfilter_error_B;
127 do result = close(pipe_in[0]); while (result < 0 && errno == EINTR);
128 if (result < 0) goto extos_pfilter_error_B;
129 do result = close(pipe_out[1]); while (result < 0 && errno == EINTR);
130 if (result < 0) goto extos_pfilter_error_B;
131 do result = close(pipe_err[1]); while (result < 0 && errno == EINTR);
132 if (result < 0) goto extos_pfilter_error_B;
133 out_buf = malloc(out_len * sizeof(char));
134 if (!out_buf) goto extos_pfilter_error_B;
135 err_buf = malloc(err_len * sizeof(char));
136 if (!err_buf) goto extos_pfilter_error_B;
137 while (!in_closed || !out_closed || !err_closed) {
138 i = 0;
139 if (!in_closed) {
140 fds[i].fd = pipe_in[1];
141 fds[i].events = POLLOUT;
142 i++;
143 }
144 if (!out_closed) {
145 fds[i].fd = pipe_out[0];
146 fds[i].events = POLLIN;
147 i++;
148 }
149 if (!err_closed) {
150 fds[i].fd = pipe_err[0];
151 fds[i].events = POLLIN;
152 i++;
153 }
154 do result = poll(fds, i, -1); while (result < 0 && errno == EINTR);
155 if (result < 0) goto extos_pfilter_error_B;
156 if (!in_closed) {
157 do result = write(pipe_in[1], in_buf+in_pos, in_len-in_pos); while (result < 0 && errno == EINTR);
158 if (result < 0) {
159 if (errno == EPIPE) {
160 do result = close(pipe_in[1]); while (result < 0 && errno == EINTR);
161 in_closed = 1;
162 } else if (errno != EAGAIN) {
163 goto extos_pfilter_error_B;
164 }
165 } else {
166 in_pos += result;
167 if (in_pos == in_len) {
168 do result = close(pipe_in[1]); while (result < 0 && errno == EINTR);
169 in_closed = 1;
170 }
171 }
172 }
173 if (!out_closed) {
174 do result = read(pipe_out[0], out_buf+out_pos, out_len-out_pos); while (result < 0 && errno == EINTR);
175 if (result < 0) {
176 if (errno != EAGAIN) goto extos_pfilter_error_B;
177 } else if (result == 0) {
178 do result = close(pipe_out[0]); while (result < 0 && errno == EINTR);
179 out_closed = 1;
180 } else {
181 out_pos += result;
182 if (out_pos == out_len) {
183 out_len *= 2;
184 newptr = realloc(out_buf, out_len * sizeof(char));
185 if (!newptr) goto extos_pfilter_error_B;
186 out_buf = newptr;
187 }
188 }
189 }
190 if (!err_closed) {
191 do result = read(pipe_err[0], err_buf+err_pos, err_len-err_pos); while (result < 0 && errno == EINTR);
192 if (result < 0) {
193 if (errno != EAGAIN) goto extos_pfilter_error_B;
194 } else if (result == 0) {
195 do result = close(pipe_err[0]); while (result < 0 && errno == EINTR);
196 err_closed = 1;
197 } else {
198 err_pos += result;
199 if (err_pos == err_len) {
200 err_len *= 2;
201 newptr = realloc(err_buf, err_len * sizeof(char));
202 if (!newptr) goto extos_pfilter_error_B;
203 err_buf = newptr;
204 }
205 }
206 }
207 }
208 lua_pushlstring(L, out_buf, out_pos);
209 free(out_buf);
210 out_buf = NULL;
211 lua_pushlstring(L, err_buf, err_pos);
212 free(err_buf);
213 err_buf = NULL;
214 do result = waitpid(child, &exit_status, 0); while (result < 0 && errno == EINTR);
215 child = 0;
216 if (result < 0) goto extos_pfilter_error_B;
217 do status_pipe_len = read(pipe_status[0], status_buf, 1); while (status_pipe_len < 0 && errno == EINTR);
218 if (status_pipe_len < 0) goto extos_pfilter_error_B;
219 do result = close(pipe_status[0]); while (result < 0 && errno == EINTR);
220 signal(SIGPIPE, old_sigpipe_action);
221 if (status_pipe_len == 0) {
222 if (WIFEXITED(exit_status)) lua_pushinteger(L, WEXITSTATUS(exit_status));
223 else lua_pushinteger(L, -WTERMSIG(exit_status));
224 return 3;
225 } else if (status_buf[0] == 0) {
226 return luaL_error(L, "Error in pfilter while reopening standard file descriptors in child process.");
227 } else {
228 strerror_r(status_buf[0], errmsg, EXTOS_MAX_ERRLEN+1);
229 lua_pushnil(L);
230 lua_pushfstring(L, "Could not execute \"%s\": %s", filename, errmsg);
231 return 2;
232 }
233 extos_pfilter_error_B:
234 signal(SIGPIPE, old_sigpipe_action);
235 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
236 if (out_buf) free(out_buf);
237 if (err_buf) free(err_buf);
238 if (!in_closed) {
239 do result = close(pipe_in[1]); while (result < 0 && errno == EINTR);
240 }
241 if (!out_closed) {
242 do result = close(pipe_out[0]); while (result < 0 && errno == EINTR);
243 }
244 if (!err_closed) {
245 do result = close(pipe_err[0]); while (result < 0 && errno == EINTR);
246 }
247 if (child) do result = waitpid(child, &exit_status, 0); while (result < 0 && errno == EINTR);
248 return luaL_error(L, "Unexpected error in pfilter: %s", errmsg);
249 } else { // child
250 do result = close(pipe_status[0]); while (result < 0 && errno == EINTR);
251 do result = close(pipe_in[1]); while (result < 0 && errno == EINTR);
252 do result = close(pipe_out[0]); while (result < 0 && errno == EINTR);
253 do result = close(0); while (result < 0 && errno == EINTR);
254 do result = close(1); while (result < 0 && errno == EINTR);
255 do result = close(2); while (result < 0 && errno == EINTR);
256 do result = dup(pipe_in[0]); while (result < 0 && errno == EINTR);
257 if (result != 0) goto extos_pfilter_error_fd_remapping;
258 do result = dup(pipe_out[1]); while (result < 0 && errno == EINTR);
259 if (result != 1) goto extos_pfilter_error_fd_remapping;
260 do result = dup(pipe_err[1]); while (result < 0 && errno == EINTR);
261 if (result != 2) goto extos_pfilter_error_fd_remapping;
262 execvp(filename, args);
263 status_buf[0] = errno;
264 do result = write(pipe_status[1], status_buf, 1); while (result < 0 && errno == EINTR);
265 _exit(0);
266 extos_pfilter_error_fd_remapping:
267 status_buf[0] = 0;
268 do result = write(pipe_status[1], status_buf, 1); while (result < 0 && errno == EINTR);
269 _exit(0);
270 }
271 }
273 static int extos_listdir(lua_State *L) {
274 DIR *dir;
275 int i = 1;
276 struct dirent entry_buffer;
277 struct dirent *entry;
278 dir = opendir(luaL_checkstring(L, 1));
279 if (!dir) {
280 lua_pushnil(L);
281 lua_pushliteral(L, "Could not list directory.");
282 return 2;
283 }
284 lua_settop(L, 0);
285 lua_newtable(L); // 1
286 while (1) {
287 readdir_r(dir, &entry_buffer, &entry);
288 if (!entry) break;
289 // Linux doesn't have d_namlen
290 //lua_pushlstring(L, entry->d_name, entry->d_namlen);
291 lua_pushstring(L, entry->d_name);
292 lua_rawseti(L, 1, i++);
293 }
294 closedir(dir);
295 return 1;
296 }
298 static int extos_crypt(lua_State *L) {
299 char *key;
300 char *salt;
301 char *result;
302 key = luaL_checkstring(L, 1);
303 salt = luaL_checkstring(L, 2);
304 result = crypt(key, salt); // TODO: Call not thread safe
305 if (result) lua_pushstring(L, result);
306 else lua_pushnil(L);
307 return 1;
308 }
310 static int extos_hires_time(lua_State *L) {
311 struct timespec tp;
312 if (clock_gettime(CLOCK_REALTIME, &tp)) {
313 return luaL_error(L, "Could not access CLOCK_REALTIME.");
314 }
315 lua_pushnumber(L, tp.tv_sec + 0.000000001 * tp.tv_nsec);
316 return 1;
317 }
319 // returns time in seconds since loading the library
320 static int extos_monotonic_hires_time(lua_State *L) {
321 struct timespec tp;
322 if (clock_gettime(CLOCK_MONOTONIC, &tp)) {
323 return luaL_error(L, "Could not access CLOCK_MONOTONIC.");
324 }
325 lua_pushnumber(L,
326 tp.tv_sec + 0.000000001 * tp.tv_nsec - extos_monotonic_start_time
327 );
328 return 1;
329 }
331 static const struct luaL_Reg extos_module_functions[] = {
332 {"pfilter", extos_pfilter},
333 {"listdir", extos_listdir},
334 {"crypt", extos_crypt},
335 {"hires_time", extos_hires_time},
336 {"monotonic_hires_time", extos_monotonic_hires_time},
337 {NULL, NULL}
338 };
340 int luaopen_extos(lua_State *L) {
341 {
342 struct timespec tp;
343 if (clock_gettime(CLOCK_MONOTONIC, &tp)) {
344 return luaL_error(L, "Could not access monotonic hires time.");
345 }
346 extos_monotonic_start_time = tp.tv_sec + 0.000000001 * tp.tv_nsec;
347 }
348 #if LUA_VERSION_NUM >= 502
349 lua_newtable(L);
350 luaL_setfuncs(L, extos_module_functions, 0);
351 #else
352 luaL_register(L, lua_tostring(L, 1), extos_module_functions);
353 #endif
354 return 1;
355 }

Impressum / About Us