webmcp
view libraries/extos/extos.c @ 61:382f042d99a3
Changed version number to v1.1.3
| author | jbe | 
|---|---|
| date | Tue Apr 03 00:25:33 2012 +0200 (2012-04-03) | 
| parents | 9fdfb27f8e67 | 
| children | 3d43a5cf17c1 | 
 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 int luaopen_extos(lua_State *L) {
   332   {
   333     struct timespec tp;
   334     if (clock_gettime(CLOCK_MONOTONIC, &tp)) {
   335       return luaL_error(L, "Could not access monotonic hires time.");
   336     }
   337     extos_monotonic_start_time = tp.tv_sec + 0.000000001 * tp.tv_nsec;
   338   }
   339   lua_getglobal(L, "os");
   340   lua_pushcfunction(L, extos_pfilter);
   341   lua_setfield(L, -2, "pfilter");
   342   lua_pushcfunction(L, extos_listdir);
   343   lua_setfield(L, -2, "listdir");
   344   lua_pushcfunction(L, extos_crypt);
   345   lua_setfield(L, -2, "crypt");
   346   lua_pushcfunction(L, extos_hires_time);
   347   lua_setfield(L, -2, "hires_time");
   348   lua_pushcfunction(L, extos_monotonic_hires_time);
   349   lua_setfield(L, -2, "monotonic_hires_time");
   350   return 0;
   351 }
