webmcp

view libraries/extos/extos.c @ 351:b1748c6c3c89

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

Impressum / About Us