PageRenderTime 42ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/liquidfeedback/webmcp/libraries/extos/extos.c

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