webmcp

view libraries/extos/extos.c @ 560:75204c64cc5f

Use CRLF instead of LF for terminating raw header
author jbe
date Tue Jun 09 13:27:58 2020 +0200 (11 months ago)
parents 59d2aaa6df07
children
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/stat.h>
8 #include <sys/wait.h>
9 #include <signal.h>
10 #include <errno.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <fcntl.h>
14 #include <poll.h>
15 #include <stdlib.h>
17 #define EXTOS_MAX_ERRLEN 80
18 #define EXTOS_EXEC_MAX_ARGS 64
20 #define EXTOS_STRERROR_R_MSG "Error detail unavailable due to noncompliant strerror_r() implementation"
22 static lua_Number extos_monotonic_start_time;
24 static int extos_pfilter(lua_State *L) {
25 int i, result, exit_status, status_pipe_len;
26 const char *in_buf;
27 size_t in_len;
28 size_t in_pos = 0;
29 const char *filename;
30 const char *args[EXTOS_EXEC_MAX_ARGS+2];
31 int pipe_status[2];
32 int pipe_in[2];
33 int pipe_out[2];
34 int pipe_err[2];
35 pid_t child;
36 char status_buf[1];
37 char *out_buf = NULL;
38 size_t out_len = 1024;
39 size_t out_pos = 0;
40 char *err_buf = NULL;
41 size_t err_len = 1024;
42 size_t err_pos = 0;
43 void *old_sigpipe_action;
44 struct pollfd fds[3];
45 int in_closed = 0;
46 int out_closed = 0;
47 int err_closed = 0;
48 void *newptr;
49 char errmsg[EXTOS_MAX_ERRLEN+1] = EXTOS_STRERROR_R_MSG;
50 in_buf = luaL_optlstring(L, 1, "", &in_len);
51 filename = luaL_checkstring(L, 2);
52 args[0] = filename;
53 for (i = 0; i < EXTOS_EXEC_MAX_ARGS; i++) {
54 if (lua_isnoneornil(L, 3+i)) break;
55 else args[i+1] = luaL_checkstring(L, 3+i);
56 }
57 if (!lua_isnoneornil(L, 3+i)) {
58 return luaL_error(L, "Too many arguments for pfilter call.");
59 }
60 args[i+1] = 0;
61 // status pipe for internal communication
62 if (pipe(pipe_status) < 0) {
63 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
64 goto extos_pfilter_error_A0;
65 }
66 // stdin
67 if (pipe(pipe_in) < 0) {
68 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
69 goto extos_pfilter_error_A1;
70 }
71 if (in_len) {
72 do result = fcntl(pipe_in[1], F_SETFL, O_NONBLOCK); while (result < 0 && errno == EINTR);
73 } else {
74 do result = close(pipe_in[1]); while (result < 0 && errno == EINTR);
75 in_closed = 1;
76 }
77 if (result < 0) {
78 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
79 goto extos_pfilter_error_A2;
80 }
81 // stdout
82 if (pipe(pipe_out) < 0) {
83 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
84 goto extos_pfilter_error_A2;
85 }
86 do result = fcntl(pipe_out[0], F_SETFL, O_NONBLOCK); while (result < 0 && errno == EINTR);
87 if (result < 0) {
88 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
89 goto extos_pfilter_error_A3;
90 }
91 // stderr
92 if (pipe(pipe_err) < 0) {
93 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
94 goto extos_pfilter_error_A3;
95 }
96 do result = fcntl(pipe_err[0], F_SETFL, O_NONBLOCK); while (result < 0 && errno == EINTR);
97 if (result < 0) {
98 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
99 goto extos_pfilter_error_A4;
100 }
101 // fork
102 child = fork();
103 if (child < 0) {
104 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
105 goto extos_pfilter_error_A4;
106 }
107 // skip error handling
108 goto extos_pfilter_success_A;
109 // error handling
110 extos_pfilter_error_A4:
111 do result = close(pipe_err[0]); while (result < 0 && errno == EINTR);
112 do result = close(pipe_err[1]); while (result < 0 && errno == EINTR);
113 extos_pfilter_error_A3:
114 do result = close(pipe_out[0]); while (result < 0 && errno == EINTR);
115 do result = close(pipe_out[1]); while (result < 0 && errno == EINTR);
116 extos_pfilter_error_A2:
117 do result = close(pipe_in[0]); while (result < 0 && errno == EINTR);
118 do result = close(pipe_in[1]); while (result < 0 && errno == EINTR);
119 extos_pfilter_error_A1:
120 do result = close(pipe_status[0]); while (result < 0 && errno == EINTR);
121 do result = close(pipe_status[1]); while (result < 0 && errno == EINTR);
122 extos_pfilter_error_A0:
123 return luaL_error(L, "Unexpected error in pfilter: %s", errmsg);
124 // end of error handling
125 extos_pfilter_success_A:
126 if (child) { // parent
127 old_sigpipe_action = signal(SIGPIPE, SIG_IGN);
128 do result = close(pipe_status[1]); while (result < 0 && errno == EINTR);
129 if (result < 0) goto extos_pfilter_error_B;
130 do result = close(pipe_in[0]); while (result < 0 && errno == EINTR);
131 if (result < 0) goto extos_pfilter_error_B;
132 do result = close(pipe_out[1]); while (result < 0 && errno == EINTR);
133 if (result < 0) goto extos_pfilter_error_B;
134 do result = close(pipe_err[1]); while (result < 0 && errno == EINTR);
135 if (result < 0) goto extos_pfilter_error_B;
136 out_buf = malloc(out_len * sizeof(char));
137 if (!out_buf) goto extos_pfilter_error_B;
138 err_buf = malloc(err_len * sizeof(char));
139 if (!err_buf) goto extos_pfilter_error_B;
140 while (!in_closed || !out_closed || !err_closed) {
141 i = 0;
142 if (!in_closed) {
143 fds[i].fd = pipe_in[1];
144 fds[i].events = POLLOUT;
145 i++;
146 }
147 if (!out_closed) {
148 fds[i].fd = pipe_out[0];
149 fds[i].events = POLLIN;
150 i++;
151 }
152 if (!err_closed) {
153 fds[i].fd = pipe_err[0];
154 fds[i].events = POLLIN;
155 i++;
156 }
157 do result = poll(fds, i, -1); while (result < 0 && errno == EINTR);
158 if (result < 0) goto extos_pfilter_error_B;
159 if (!in_closed) {
160 do result = write(pipe_in[1], in_buf+in_pos, in_len-in_pos); while (result < 0 && errno == EINTR);
161 if (result < 0) {
162 if (errno == EPIPE) {
163 do result = close(pipe_in[1]); while (result < 0 && errno == EINTR);
164 in_closed = 1;
165 } else if (errno != EAGAIN) {
166 goto extos_pfilter_error_B;
167 }
168 } else {
169 in_pos += result;
170 if (in_pos == in_len) {
171 do result = close(pipe_in[1]); while (result < 0 && errno == EINTR);
172 in_closed = 1;
173 }
174 }
175 }
176 if (!out_closed) {
177 do result = read(pipe_out[0], out_buf+out_pos, out_len-out_pos); while (result < 0 && errno == EINTR);
178 if (result < 0) {
179 if (errno != EAGAIN) goto extos_pfilter_error_B;
180 } else if (result == 0) {
181 do result = close(pipe_out[0]); while (result < 0 && errno == EINTR);
182 out_closed = 1;
183 } else {
184 out_pos += result;
185 if (out_pos == out_len) {
186 out_len *= 2;
187 newptr = realloc(out_buf, out_len * sizeof(char));
188 if (!newptr) goto extos_pfilter_error_B;
189 out_buf = newptr;
190 }
191 }
192 }
193 if (!err_closed) {
194 do result = read(pipe_err[0], err_buf+err_pos, err_len-err_pos); while (result < 0 && errno == EINTR);
195 if (result < 0) {
196 if (errno != EAGAIN) goto extos_pfilter_error_B;
197 } else if (result == 0) {
198 do result = close(pipe_err[0]); while (result < 0 && errno == EINTR);
199 err_closed = 1;
200 } else {
201 err_pos += result;
202 if (err_pos == err_len) {
203 err_len *= 2;
204 newptr = realloc(err_buf, err_len * sizeof(char));
205 if (!newptr) goto extos_pfilter_error_B;
206 err_buf = newptr;
207 }
208 }
209 }
210 }
211 lua_pushlstring(L, out_buf, out_pos);
212 free(out_buf);
213 out_buf = NULL;
214 lua_pushlstring(L, err_buf, err_pos);
215 free(err_buf);
216 err_buf = NULL;
217 do result = waitpid(child, &exit_status, 0); while (result < 0 && errno == EINTR);
218 child = 0;
219 if (result < 0) goto extos_pfilter_error_B;
220 do status_pipe_len = read(pipe_status[0], status_buf, 1); while (status_pipe_len < 0 && errno == EINTR);
221 if (status_pipe_len < 0) goto extos_pfilter_error_B;
222 do result = close(pipe_status[0]); while (result < 0 && errno == EINTR);
223 signal(SIGPIPE, old_sigpipe_action);
224 if (status_pipe_len == 0) {
225 if (WIFEXITED(exit_status)) lua_pushinteger(L, WEXITSTATUS(exit_status));
226 else lua_pushinteger(L, -WTERMSIG(exit_status));
227 return 3;
228 } else if (status_buf[0] == 0) {
229 return luaL_error(L, "Error in pfilter while reopening standard file descriptors in child process.");
230 } else {
231 strerror_r(status_buf[0], errmsg, EXTOS_MAX_ERRLEN+1);
232 lua_pushnil(L);
233 lua_pushfstring(L, "Could not execute \"%s\": %s", filename, errmsg);
234 return 2;
235 }
236 extos_pfilter_error_B:
237 signal(SIGPIPE, old_sigpipe_action);
238 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
239 if (out_buf) free(out_buf);
240 if (err_buf) free(err_buf);
241 if (!in_closed) {
242 do result = close(pipe_in[1]); while (result < 0 && errno == EINTR);
243 }
244 if (!out_closed) {
245 do result = close(pipe_out[0]); while (result < 0 && errno == EINTR);
246 }
247 if (!err_closed) {
248 do result = close(pipe_err[0]); while (result < 0 && errno == EINTR);
249 }
250 if (child) do result = waitpid(child, &exit_status, 0); while (result < 0 && errno == EINTR);
251 return luaL_error(L, "Unexpected error in pfilter: %s", errmsg);
252 } else { // child
253 do result = close(pipe_status[0]); while (result < 0 && errno == EINTR);
254 do result = close(pipe_in[1]); while (result < 0 && errno == EINTR);
255 do result = close(pipe_out[0]); while (result < 0 && errno == EINTR);
256 do result = close(0); while (result < 0 && errno == EINTR);
257 do result = close(1); while (result < 0 && errno == EINTR);
258 do result = close(2); while (result < 0 && errno == EINTR);
259 do result = dup(pipe_in[0]); while (result < 0 && errno == EINTR);
260 if (result != 0) goto extos_pfilter_error_fd_remapping;
261 do result = dup(pipe_out[1]); while (result < 0 && errno == EINTR);
262 if (result != 1) goto extos_pfilter_error_fd_remapping;
263 do result = dup(pipe_err[1]); while (result < 0 && errno == EINTR);
264 if (result != 2) goto extos_pfilter_error_fd_remapping;
265 execvp(filename, (char *const *)args); // coercion to suppress compiler warning
266 status_buf[0] = errno;
267 do result = write(pipe_status[1], status_buf, 1); while (result < 0 && errno == EINTR);
268 _exit(0);
269 extos_pfilter_error_fd_remapping:
270 status_buf[0] = 0;
271 do result = write(pipe_status[1], status_buf, 1); while (result < 0 && errno == EINTR);
272 _exit(0);
273 }
274 }
276 static int extos_listdir(lua_State *L) {
277 DIR *dir;
278 int i = 1;
279 struct dirent entry_buffer;
280 struct dirent *entry;
281 dir = opendir(luaL_checkstring(L, 1));
282 if (!dir) {
283 lua_pushnil(L);
284 lua_pushliteral(L, "Could not list directory.");
285 return 2;
286 }
287 lua_settop(L, 0);
288 lua_newtable(L); // 1
289 while (1) {
290 readdir_r(dir, &entry_buffer, &entry);
291 if (!entry) break;
292 // Linux doesn't have d_namlen
293 //lua_pushlstring(L, entry->d_name, entry->d_namlen);
294 lua_pushstring(L, entry->d_name);
295 lua_rawseti(L, 1, i++);
296 }
297 closedir(dir);
298 return 1;
299 }
301 #define EXTOS_STAT_FOLLOW -1
302 #define EXTOS_STAT_NOFOLLOW -2
304 static int extos_stat_impl(lua_State *L, int fd) {
305 struct stat sb;
306 if (fd < 0) {
307 const char *filename;
308 filename = luaL_checkstring(L, 1);
309 if (fd == EXTOS_STAT_FOLLOW ? stat(filename, &sb) : lstat(filename, &sb)) {
310 char errmsg[EXTOS_MAX_ERRLEN+1] = EXTOS_STRERROR_R_MSG;
311 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
312 if (errno == ENOENT) lua_pushboolean(L, 0);
313 else lua_pushnil(L);
314 lua_pushfstring(L, "Could not get file stats for \"%s\": %s", filename, errmsg);
315 return 2;
316 }
317 } else {
318 if (fstat(fd, &sb)) {
319 char errmsg[EXTOS_MAX_ERRLEN+1] = EXTOS_STRERROR_R_MSG;
320 strerror_r(errno, errmsg, EXTOS_MAX_ERRLEN+1);
321 lua_pushnil(L);
322 lua_pushfstring(L, "Could not get file stats for open file: %s", errmsg);
323 return 2;
324 }
325 }
326 lua_createtable(L, 0, 19);
327 lua_pushinteger(L, sb.st_dev);
328 lua_setfield(L, -2, "dev");
329 lua_pushinteger(L, sb.st_ino);
330 lua_setfield(L, -2, "ino");
331 lua_pushinteger(L, sb.st_nlink);
332 lua_setfield(L, -2, "nlink");
333 lua_pushinteger(L, sb.st_atime);
334 lua_setfield(L, -2, "atime");
335 lua_pushinteger(L, sb.st_mtime);
336 lua_setfield(L, -2, "mtime");
337 lua_pushinteger(L, sb.st_ctime);
338 lua_setfield(L, -2, "ctime");
339 lua_pushinteger(L, sb.st_size);
340 lua_setfield(L, -2, "size");
341 lua_pushinteger(L, sb.st_blksize);
342 lua_setfield(L, -2, "blksize");
343 lua_pushinteger(L, sb.st_blocks);
344 lua_setfield(L, -2, "blocks");
345 lua_pushinteger(L, sb.st_uid);
346 lua_setfield(L, -2, "uid");
347 lua_pushinteger(L, sb.st_gid);
348 lua_setfield(L, -2, "gid");
349 lua_pushinteger(L, sb.st_mode);
350 lua_setfield(L, -2, "mode");
351 lua_pushboolean(L, S_ISBLK(sb.st_mode));
352 lua_setfield(L, -2, "isblk");
353 lua_pushboolean(L, S_ISCHR(sb.st_mode));
354 lua_setfield(L, -2, "ischr");
355 lua_pushboolean(L, S_ISDIR(sb.st_mode));
356 lua_setfield(L, -2, "isdir");
357 lua_pushboolean(L, S_ISFIFO(sb.st_mode));
358 lua_setfield(L, -2, "isfifo");
359 lua_pushboolean(L, S_ISLNK(sb.st_mode));
360 lua_setfield(L, -2, "islnk");
361 lua_pushboolean(L, S_ISREG(sb.st_mode));
362 lua_setfield(L, -2, "isreg");
363 lua_pushboolean(L, S_ISSOCK(sb.st_mode));
364 lua_setfield(L, -2, "issock");
365 return 1;
366 }
368 static int extos_stat(lua_State *L) {
369 return extos_stat_impl(L, EXTOS_STAT_FOLLOW);
370 }
372 static int extos_lstat(lua_State *L) {
373 return extos_stat_impl(L, EXTOS_STAT_NOFOLLOW);
374 }
376 static int extos_fstat(lua_State *L) {
377 luaL_Stream *stream;
378 stream = luaL_checkudata(L, 1, LUA_FILEHANDLE);
379 if (!stream->closef) luaL_error(L, "attempt to use a closed file");
380 return extos_stat_impl(L, fileno(stream->f));
381 }
383 static int extos_crypt(lua_State *L) {
384 const char *key;
385 const char *salt;
386 char *result;
387 key = luaL_checkstring(L, 1);
388 salt = luaL_checkstring(L, 2);
389 result = crypt(key, salt); // TODO: Call not thread safe
390 if (result) lua_pushstring(L, result);
391 else lua_pushnil(L);
392 return 1;
393 }
395 static int extos_hires_time(lua_State *L) {
396 struct timespec tp;
397 if (clock_gettime(CLOCK_REALTIME, &tp)) {
398 return luaL_error(L, "Could not access CLOCK_REALTIME.");
399 }
400 lua_pushnumber(L, tp.tv_sec + tp.tv_nsec / 1.0e9);
401 return 1;
402 }
404 // returns time in seconds since loading the library
405 static int extos_monotonic_hires_time(lua_State *L) {
406 struct timespec tp;
407 if (clock_gettime(CLOCK_MONOTONIC, &tp)) {
408 return luaL_error(L, "Could not access CLOCK_MONOTONIC.");
409 }
410 lua_pushnumber(L,
411 tp.tv_sec + tp.tv_nsec / 1.0e9 - extos_monotonic_start_time
412 );
413 return 1;
414 }
416 static const struct luaL_Reg extos_module_functions[] = {
417 {"pfilter", extos_pfilter},
418 {"listdir", extos_listdir},
419 {"stat", extos_stat},
420 {"lstat", extos_lstat},
421 {"fstat", extos_fstat},
422 {"crypt", extos_crypt},
423 {"hires_time", extos_hires_time},
424 {"monotonic_hires_time", extos_monotonic_hires_time},
425 {NULL, NULL}
426 };
428 int luaopen_extos(lua_State *L) {
429 {
430 struct timespec tp;
431 if (clock_gettime(CLOCK_MONOTONIC, &tp)) {
432 return luaL_error(L, "Could not access monotonic hires time.");
433 }
434 extos_monotonic_start_time = tp.tv_sec + tp.tv_nsec / 1.0e9;
435 }
436 #if LUA_VERSION_NUM >= 502
437 lua_newtable(L);
438 luaL_setfuncs(L, extos_module_functions, 0);
439 #else
440 luaL_register(L, lua_tostring(L, 1), extos_module_functions);
441 #endif
442 return 1;
443 }

Impressum / About Us