a sandboxed Lua backend for FastCGI
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

183 lines
4.1 KiB

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <stdint.h>
  4. #include <string.h>
  5. #include <errno.h>
  6. #include <time.h>
  7. #include <fcgi_config.h>
  8. #include <fcgiapp.h>
  9. #include <lua5.1/lua.h>
  10. #include <pthread.h>
  11. #include "lua.h"
  12. #include "config.h"
  13. #include "lua-fastcgi.h"
  14. static char *http_status_strings[] = {
  15. [200] = "OK",
  16. [403] = "Forbidden",
  17. [404] = "Not Found",
  18. [500] = "Internal Server Error"
  19. };
  20. #define senderror(status_code,error_string) \
  21. if(!state.committed){ \
  22. FCGX_FPrintF(request.out, "Status: %d %s\r\n", status_code, http_status_strings[status_code]); \
  23. FCGX_FPrintF(request.out, "Content-Type: %s\r\n\r\n", config->content_type); \
  24. state.committed = 1; \
  25. } \
  26. FCGX_PutS(error_string, state.response);
  27. #ifdef DEBUG
  28. static void printcfg(LF_config *cfg)
  29. {
  30. printf("Listen: %s\n", cfg->listen);
  31. printf("Backlog: %d\n", cfg->backlog);
  32. printf("Threads: %d\n", cfg->threads);
  33. printf("Sandbox: %d\n", cfg->sandbox);
  34. printf("Max Memory: %zu\n", cfg->mem_max);
  35. printf("Max Output: %zu\n", cfg->output_max);
  36. printf("CPU usec: %lu\n", cfg->cpu_usec);
  37. printf("CPU sec: %lu\n", cfg->cpu_sec);
  38. printf("Default Content Type: %s\n", cfg->content_type);
  39. printf("\n");
  40. }
  41. static void printvars(FCGX_Request *request)
  42. {
  43. for(int i=0; request->envp[i] != NULL; i++){
  44. printf("%s\n", request->envp[i]);
  45. }
  46. printf("\n");
  47. }
  48. #endif
  49. void *thread_run(void *arg)
  50. {
  51. LF_params *params = arg;
  52. LF_config *config = params->config;
  53. LF_limits *limits = LF_newlimits();
  54. LF_state state;
  55. lua_State *l;
  56. FCGX_Request request;
  57. FCGX_InitRequest(&request, params->socket, 0);
  58. for(;;){
  59. LF_setlimits(
  60. limits, config->mem_max, config->output_max,
  61. config->cpu_sec, config->cpu_usec
  62. );
  63. l = LF_newstate(config->sandbox, config->content_type);
  64. if(FCGX_Accept_r(&request)){
  65. printf("FCGX_Accept_r() failure\n");
  66. LF_closestate(l);
  67. continue;
  68. }
  69. #ifdef DEBUG
  70. printvars(&request);
  71. struct timespec rstart, rend;
  72. clock_gettime(CLOCK_MONOTONIC, &rstart);
  73. #endif
  74. LF_parserequest(l, &request, &state);
  75. #ifdef DEBUG
  76. clock_gettime(CLOCK_MONOTONIC, &rend);
  77. // Assumes the request returns in less than a second (which it should)
  78. printf("Request parsed in %luns\n", (rend.tv_nsec-rstart.tv_nsec));
  79. #endif
  80. LF_enablelimits(l, limits);
  81. switch(LF_loadscript(l)){
  82. case 0:
  83. if(lua_pcall(l, 0, 0, 0)){
  84. if(lua_isstring(l, -1)){
  85. senderror(500, lua_tostring(l, -1));
  86. } else {
  87. senderror(500, "unspecified lua error");
  88. }
  89. } else if(!state.committed){
  90. senderror(200, "");
  91. }
  92. break;
  93. case LF_ERRACCESS: senderror(403, "access denied"); break;
  94. case LF_ERRMEMORY: senderror(500, "not enough memory"); break;
  95. case LF_ERRNOTFOUND:
  96. printf("404\n");
  97. senderror(404, "no such file or directory");
  98. break;
  99. case LF_ERRSYNTAX: senderror(500, lua_tostring(l, -1)); break;
  100. case LF_ERRBYTECODE: senderror(403, "compiled bytecode not supported"); break;
  101. case LF_ERRNOPATH: senderror(500, "SCRIPT_FILENAME not provided"); break;
  102. case LF_ERRNONAME: senderror(500, "SCRIPT_NAME not provided"); break;
  103. }
  104. FCGX_Finish_r(&request);
  105. LF_closestate(l);
  106. }
  107. }
  108. int main()
  109. {
  110. if(FCGX_Init() != 0){
  111. printf("FCGX_Init() failure\n");
  112. exit(EXIT_FAILURE);
  113. }
  114. LF_config *config = LF_createconfig();
  115. if(config == NULL){
  116. printf("LF_createconfig(): memory allocation error\n");
  117. exit(EXIT_FAILURE);
  118. }
  119. if(LF_loadconfig(config, "./lua-fastcgi.lua")){
  120. printf("Error loading lua-fastcgi.lua\n");
  121. }
  122. #ifdef DEBUG
  123. printcfg(config);
  124. #endif
  125. int socket = FCGX_OpenSocket(config->listen, config->backlog);
  126. if(socket < 0){
  127. printf("FCGX_OpenSocket() failure: could not open %s\n", config->listen);
  128. exit(EXIT_FAILURE);
  129. }
  130. LF_params *params = malloc(sizeof(LF_params));
  131. params->socket = socket;
  132. params->config = config;
  133. if(config->threads == 1){
  134. thread_run(params);
  135. } else {
  136. pthread_attr_t attr;
  137. pthread_attr_init(&attr);
  138. pthread_t threads[config->threads];
  139. for(int i=0; i < config->threads; i++){
  140. int r = pthread_create(&threads[i], &attr, &thread_run, params);
  141. if(r){
  142. printf("Thread creation error: %d\n", r);
  143. exit(EXIT_FAILURE);
  144. }
  145. }
  146. pthread_join(threads[0], NULL);
  147. }
  148. return 0;
  149. }