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