Advertisement
rexonf

Untitled

Apr 1st, 2024
98
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 8.56 KB | None | 0 0
  1. #include "precompile.hxx"
  2.  
  3. #define LIBNAME   executeAsync
  4. #define STR(s) #s
  5. #define LIBSNAME STR(LIBNAME)
  6. #define MAKELUAOPEN(name) luaopen_ ## name
  7. #define LUAOPEN(name) MAKELUAOPEN(name)
  8.  
  9. static const char* metatableKey = LIBSNAME" Metatable Key";
  10.  
  11. int pushError (lua_State* L) {
  12.   int err = errno; // calls to Lua may modify errno
  13.   lua_pushnil (L);
  14.   lua_pushstring (L, strerror (err));
  15.   return 2;
  16. }
  17.  
  18. #define nil NULL
  19. struct FdsAndId {std::array<int, 2> fds; int id;};
  20. class Jobs {
  21.   fd_set readfds;
  22.  
  23.   std::map <pid_t, FdsAndId> PidMap;
  24. public:
  25.   Jobs () {
  26.     ++jobQueueCount;
  27.     miId = jobQueueCount;
  28.   }
  29.  
  30.   int execute (lua_State* L, const char* cmdline, int id) {
  31.     int pipeOut [2];
  32.     int pipeErr [2];
  33.     pid_t pidChild;
  34.     if (pipe2 (pipeOut, O_NONBLOCK) == -1) return pushError (L);
  35.     if (pipe2 (pipeErr, O_NONBLOCK) == -1) return pushError (L);
  36.     pidChild = fork();
  37.     if (pidChild == -1) return pushError (L);
  38.  
  39.     constexpr int readEnd = 0;
  40.     constexpr int writeEnd = 1;
  41.     if (pidChild == 0) {    /* Child writes to pipe */
  42.       close (pipeOut [readEnd]);
  43.       dup2 (pipeOut [writeEnd], STDOUT_FILENO);
  44.       // Check `man stdout` for more info
  45.       close (pipeErr [readEnd]);
  46.       dup2 (pipeErr [writeEnd], STDERR_FILENO);
  47.       // Check `man stdout` for more info
  48.       close (pipeOut [writeEnd]);
  49.       close (pipeErr [writeEnd]);
  50.       execlp ("sh", "sh", "-c", cmdline, (char*) NULL);
  51.       return 0;
  52.     } else { /* Parent */
  53.       close (pipeOut [writeEnd]);
  54.       close (pipeErr [writeEnd]);
  55.       PidMap [pidChild] = {{pipeOut[readEnd], pipeErr[readEnd]}, id};
  56.       lua_pushboolean (L, 1);
  57.       lua_pushinteger (L, pidChild);
  58.       lua_pushinteger (L, PidMap.size ());
  59.       return 3;
  60.     }
  61.   } // end Jobs::execute
  62.  
  63. #define addMainReturn(index) lua_rawseti (L, -2, index)
  64. #define addSubReturn(index) lua_rawseti (L, -2, index)
  65.  
  66.   int wait (lua_State* L) {
  67.     if (! PidMap.size ()) {
  68.       lua_createtable (L, 1, 0); // main return
  69.       lua_createtable (L, 3, 0); // sub return
  70.       lua_pushinteger (L, 0); addSubReturn (1);
  71.       lua_pushinteger (L, -1); addSubReturn (2);
  72.       lua_pushinteger (L, -1); addSubReturn (3);
  73.       addMainReturn (1);
  74.       return 1; // lua: {{0, -1, -1}}
  75.     }
  76.  
  77.     /* Initialize readfds, before the select() call. */
  78.     FD_ZERO (&readfds);
  79.     int maxfds = 0;
  80.     for (const auto &i: PidMap) {
  81.       for (const auto& j: i.second.fds) {
  82.         FD_SET (j, &readfds);
  83.         maxfds = std::max (maxfds, j);
  84.       }
  85.     }
  86.     int r = select (maxfds+1, &readfds, NULL, nil, NULL);
  87.     if (r == -1) {
  88.       /* Handle error */
  89.       const char* err = 0;
  90.       switch (errno) {
  91.       case EBADF:
  92.         err = "One or more of the file descriptor sets specified a file"
  93.           " descriptor that is not a valid open file descriptor.";
  94.         break;
  95.       case EINTR:
  96.         err = "The function was interrupted while blocked waiting for any"
  97.           " of the selected descriptors to become ready and before the"
  98.           " timeout interval expired.";
  99.         break;
  100.       case EINVAL:
  101.         err = "The nfds argument is less than 0 or greater than FD_SETSIZE."
  102.           " OR One of the specified file descriptors refers to a STREAM"
  103.           " or multiplexer that is linked (directly or indirectly)"
  104.           " downstream from a multiplexer.";
  105.         break;
  106.       }
  107.       lua_pushnil (L);
  108.       lua_pushstring (L, err);
  109.       return 2;
  110.     } // end error
  111.  
  112.     lua_createtable (L, r, 0); // main return
  113.     int indexInReturnTable = 1;
  114.     int countSet = 0;
  115.     for (auto ii = PidMap.begin (); ii != PidMap.end ();) {
  116.       auto& i = *ii;
  117.       auto next = ii;
  118.       ++next;
  119.       const std::array<int, 2>& fds = i.second.fds;
  120.       for (int j = 0; j < fds.size (); ++j) {
  121.         int fd = fds [j];
  122.         if (FD_ISSET (fd, &readfds)) {
  123.           ++countSet;
  124.           FILE* fp = fdopen (fd, "r");
  125.           size_t n = ~((size_t)0);
  126.           size_t rlen = LUAL_BUFFERSIZE;  /* try to read that much each time */
  127.           size_t nr;  /* number of chars actually read */
  128.           luaL_Buffer b;
  129.           luaL_buffinit(L, &b);
  130.           do {
  131.             char *p = luaL_prepbuffer(&b);
  132.             if (rlen > n) rlen = n;  /* cannot read more than asked */
  133.             nr = fread(p, sizeof(char), rlen, fp);
  134.             luaL_addsize(&b, nr);
  135.             n -= nr;  /* still have to read `n' chars */
  136.           } while (n > 0 && nr == rlen);  /* until end of count or eof */
  137.           luaL_pushresult(&b);  /* close buffer */
  138.  
  139.           int id = i.second.id;
  140.           if (lua_objlen(L, -1) > 0) { // no point in returning empty string
  141.             lua_createtable (L, 3, 0); // sub return
  142.             lua_insert (L, -2); // buffered string comes to the top
  143.             addSubReturn (1);
  144.             lua_pushinteger (L, id);  addSubReturn (2);
  145.             lua_pushinteger (L, j+1); addSubReturn (3);
  146.             addMainReturn (indexInReturnTable++);
  147.           } else {
  148.             lua_pop (L, 1); // the empty string
  149.           }
  150.          
  151.           if (nr < rlen) {
  152.             if (feof (fp) && j == 0) { // child closed its stdout
  153.               /* this is kinda wrong, should wait for SIGCHILD instead, but will do
  154.                for now */
  155.               PidMap.erase (ii);
  156.               lua_createtable (L, 3, 0); // sub return
  157.               lua_pushinteger (L, PidMap.size ()); addSubReturn (1);
  158.               lua_pushinteger (L, id); addSubReturn (2);
  159.               int exitCode;
  160.               waitpid (i.first, &exitCode, 0);
  161.               lua_pushinteger (L, exitCode); addSubReturn (3);
  162.               addMainReturn (indexInReturnTable++);
  163.             }
  164.           }
  165.         }
  166.       } // end for (int j = 0; j < fds.size (); ++j)
  167.       ii = next;
  168.     } // end for (auto ii = PidMap.begin (); ii != PidMap.end ();)
  169.  
  170.     if (countSet) return 1; // main return
  171.     else {
  172.       lua_pop (L, 1); // pop main return table
  173.       lua_pushnil (L);
  174.       lua_pushstring (L, "select triggered but no fd is FD_ISSET!");
  175.       return 2;
  176.     }
  177.   } // end ::wait
  178. private:
  179.   int miId;
  180.   static int jobQueueCount;
  181. };
  182. int Jobs::jobQueueCount = 0;
  183.  
  184. Jobs *check_self (lua_State *L, int index = 1) {
  185.   Jobs *p = (Jobs *) lua_touserdata(L, index);
  186.   if (p != NULL) {  /* value is a userdata? */
  187.     if (lua_getmetatable(L, index)) {  /* does it have a metatable? */
  188.       /* get supposed metatable */
  189.       lua_pushlightuserdata (L, &metatableKey);
  190.       lua_rawget (L, LUA_REGISTRYINDEX);
  191.       if (lua_rawequal(L, -1, -2)) {  /* does it have the correct mt? */
  192.         lua_pop(L, 2);  /* remove both metatables */
  193.         return p;
  194.       }
  195.     }
  196.   }
  197.   luaL_typerror(L, index, LIBSNAME);  /* else error */
  198.   return NULL;  /* to avoid warnings */
  199. }
  200.  
  201. extern "C" {
  202.  
  203.   static int new_jobs (lua_State *L) {
  204.     new (lua_newuserdata (L, sizeof (Jobs))) Jobs ();
  205.  
  206.     lua_pushlightuserdata(L, &metatableKey);
  207.     lua_rawget(L, LUA_REGISTRYINDEX);
  208.     lua_setmetatable (L, -2);
  209.  
  210.     return 1;
  211.   }
  212.  
  213.   static int execute (lua_State *L) {
  214.     Jobs *self = check_self (L);
  215.  
  216.     // app: string, args: string, id: int
  217.     const char* cmdline = luaL_checkstring (L, 2);
  218.     int id = luaL_checkint (L, 3);
  219.     return self->execute (L, cmdline, id);
  220.   }
  221.  
  222.   // returns number of remaining process, id of finished process, exit code of
  223.   // finished process, read handle of output of finished process
  224.   static int waitJob (lua_State *L) {
  225.     Jobs *self = check_self (L);
  226.  
  227.     return self->wait (L);
  228.   }
  229.  
  230.   static int gc (lua_State *L) { // '__gc'
  231.     Jobs* self = check_self (L);
  232.     self->~Jobs ();
  233.     return 0;
  234.   }
  235.  
  236.   static const struct luaL_Reg Jobs_methods [] = {
  237.     {"__gc", gc},
  238.     {NULL, NULL},
  239.   };
  240.  
  241.   EXPORT int LUAOPEN(LIBNAME) (lua_State *L) {
  242.     lua_pushlightuserdata (L, &metatableKey); // key-in-registry
  243.     lua_newtable (L); // metatable-userdata
  244.     luaL_register (L, NULL, Jobs_methods);
  245.  
  246.     const struct luaL_Reg Jobs_functions [] = {
  247.       {"execute",  execute },
  248.       {"wait",     waitJob },
  249.       {NULL,       NULL},
  250.     };
  251.     lua_newtable (L); // this will be __index
  252.     luaL_register (L, NULL, Jobs_functions);
  253.     lua_setfield (L, -2, "__index");
  254.  
  255.     // registry [key-in-registry] = metatable-userdata
  256.     lua_settable (L, LUA_REGISTRYINDEX);
  257.  
  258.     lua_newtable (L); // class table, to return
  259.  
  260.     // metatable for class table
  261.     // allow call syntax:
  262.     lua_newtable (L);
  263.     lua_pushcfunction (L, new_jobs);
  264.     lua_setfield (L, -2, "__call");
  265.  
  266.     lua_setmetatable (L, -2);
  267.  
  268.     return 1;
  269.   }
  270. } // extern "C"
  271.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement