FastCGI application to run cgi applications https://redmine.lighttpd.net/projects/fcgi-cgi
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.
 
 
 
 
 
 

625 lines
17 KiB

  1. #ifdef HAVE_CONFIG_H
  2. #include "config.h"
  3. #endif
  4. #include "fastcgi.h"
  5. #include <unistd.h>
  6. #include <errno.h>
  7. #include <stdlib.h>
  8. #include <fcntl.h>
  9. #include <string.h>
  10. #include <sys/stat.h>
  11. #define MAX_BUFFER_SIZE (64*1024)
  12. #define CONST_STR_LEN(x) (x), sizeof(x) - 1
  13. #define GSTR_LEN(x) (x) ? (x)->str : "", (x) ? (x)->len : 0
  14. #define UNUSED(x) ((void)(x))
  15. /* #define ERROR(...) g_printerr(G_STRLOC " (" G_STRFUNC "): " __VA_ARGS__) */
  16. #define __STR(x) #x
  17. #define ERROR(...) g_printerr("fcgi-cgi.c:" G_STRINGIFY(__LINE__) ": " __VA_ARGS__)
  18. #define PACKAGE_DESC (PACKAGE_NAME " v" PACKAGE_VERSION " - FastCGI application to run normal cgi applications")
  19. struct fcgi_cgi_server;
  20. typedef struct fcgi_cgi_server fcgi_cgi_server;
  21. struct fcgi_cgi_child;
  22. typedef struct fcgi_cgi_child fcgi_cgi_child;
  23. struct fcgi_cgi_server {
  24. struct ev_loop *loop;
  25. fastcgi_server *fsrv;
  26. GPtrArray *aborted_pending_childs;
  27. ev_signal
  28. sig_w_INT,
  29. sig_w_TERM,
  30. sig_w_HUP;
  31. };
  32. struct fcgi_cgi_child {
  33. fcgi_cgi_server *srv;
  34. fastcgi_connection *fcon;
  35. gint aborted_id;
  36. pid_t pid;
  37. gint child_status;
  38. ev_child child_watcher;
  39. gint pipe_in, pipe_out, pipe_err;
  40. ev_io pipe_in_watcher, pipe_out_watcher, pipe_err_watcher;
  41. /* write queue */
  42. fastcgi_queue write_queue;
  43. };
  44. static fcgi_cgi_child* fcgi_cgi_child_create(fcgi_cgi_server *srv, fastcgi_connection *fcon);
  45. static void fcgi_cgi_child_check_done(fcgi_cgi_child *cld);
  46. static void fcgi_cgi_child_close_write(fcgi_cgi_child *cld);
  47. static void fcgi_cgi_child_close_read(fcgi_cgi_child *cld);
  48. static void fcgi_cgi_child_close_error(fcgi_cgi_child *cld);
  49. static void fcgi_cgi_child_free(fcgi_cgi_child *cld);
  50. static void fcgi_cgi_child_error(fcgi_cgi_child *cld);
  51. static void fcgi_cgi_child_start(fcgi_cgi_child *cld, const gchar *path);
  52. static void fcgi_cgi_wrote_data(fastcgi_connection *fcon);
  53. /* move a fd to another and close the old one */
  54. static void move2fd(int srcfd, int dstfd) {
  55. if (srcfd != dstfd) {
  56. close(dstfd);
  57. dup2(srcfd, dstfd);
  58. close(srcfd);
  59. }
  60. }
  61. static void fd_init(int fd) {
  62. #ifdef _WIN32
  63. int i = 1;
  64. #endif
  65. #ifdef FD_CLOEXEC
  66. /* close fd on exec (cgi) */
  67. fcntl(fd, F_SETFD, FD_CLOEXEC);
  68. #endif
  69. #ifdef O_NONBLOCK
  70. fcntl(fd, F_SETFL, O_NONBLOCK | O_RDWR);
  71. #elif defined _WIN32
  72. ioctlsocket(fd, FIONBIO, &i);
  73. #endif
  74. }
  75. static void fcgi_cgi_child_child_cb(struct ev_loop *loop, ev_child *w, int revents) {
  76. fcgi_cgi_child *cld = (fcgi_cgi_child*) w->data;
  77. UNUSED(revents);
  78. ev_child_stop(loop, w);
  79. fcgi_cgi_child_close_write(cld);
  80. cld->pid = -1;
  81. cld->child_status = w->rstatus;
  82. fcgi_cgi_child_check_done(cld);
  83. }
  84. static void write_queue(fcgi_cgi_child *cld) {
  85. if (-1 == cld->pipe_out) return;
  86. if (fastcgi_queue_write(cld->pipe_out, &cld->write_queue, 256*1024) < 0) {
  87. fcgi_cgi_child_close_write(cld);
  88. return;
  89. }
  90. if (-1 != cld->pipe_out) {
  91. if (cld->write_queue.length > 0) {
  92. ev_io_start(cld->srv->loop, &cld->pipe_out_watcher);
  93. if (cld->write_queue.length > MAX_BUFFER_SIZE) {
  94. fastcgi_suspend_read(cld->fcon);
  95. } else {
  96. fastcgi_resume_read(cld->fcon);
  97. }
  98. } else {
  99. if (cld->write_queue.closed) {
  100. fcgi_cgi_child_close_write(cld);
  101. } else {
  102. ev_io_stop(cld->srv->loop, &cld->pipe_out_watcher);
  103. fastcgi_resume_read(cld->fcon);
  104. }
  105. }
  106. }
  107. }
  108. static GByteArray* read_chunk(gint fd, guint maxlen) {
  109. gssize res;
  110. GByteArray *buf;
  111. int tmp_errno;
  112. buf = g_byte_array_sized_new(maxlen);
  113. g_byte_array_set_size(buf, maxlen);
  114. if (0 == maxlen) return buf;
  115. res = read(fd, buf->data, maxlen);
  116. if (res == -1) {
  117. tmp_errno = errno;
  118. g_byte_array_free(buf, TRUE);
  119. errno = tmp_errno;
  120. return NULL;
  121. } else if (res == 0) {
  122. g_byte_array_free(buf, TRUE);
  123. errno = ECONNRESET;
  124. return NULL;
  125. } else {
  126. g_byte_array_set_size(buf, res);
  127. return buf;
  128. }
  129. }
  130. static void fcgi_cgi_child_pipe_in_cb(struct ev_loop *loop, ev_io *w, int revents) {
  131. fcgi_cgi_child *cld = (fcgi_cgi_child*) w->data;
  132. GByteArray *buf;
  133. UNUSED(loop); UNUSED(revents);
  134. if (NULL == (buf = read_chunk(cld->pipe_in, 64*1024))) {
  135. switch (errno) {
  136. case EINTR:
  137. case EAGAIN:
  138. #if EWOULDBLOCK != EAGAIN
  139. case EWOULDBLOCK:
  140. #endif
  141. return; /* try again later */
  142. case ECONNRESET:
  143. fcgi_cgi_child_close_read(cld);
  144. break;
  145. default:
  146. ERROR("read from fd=%d failed, %s\n", cld->pipe_in, g_strerror(errno));
  147. fcgi_cgi_child_close_read(cld);
  148. break;
  149. }
  150. } else if (cld->fcon) {
  151. fastcgi_send_out_bytearray(cld->fcon, buf);
  152. fcgi_cgi_wrote_data(cld->fcon);
  153. } else {
  154. g_byte_array_free(buf, TRUE);
  155. }
  156. }
  157. static void fcgi_cgi_child_pipe_out_cb(struct ev_loop *loop, ev_io *w, int revents) {
  158. fcgi_cgi_child *cld = (fcgi_cgi_child*) w->data;
  159. UNUSED(loop); UNUSED(revents);
  160. write_queue(cld);
  161. }
  162. static void fcgi_cgi_child_pipe_err_cb(struct ev_loop *loop, ev_io *w, int revents) {
  163. fcgi_cgi_child *cld = (fcgi_cgi_child*) w->data;
  164. GByteArray *buf;
  165. UNUSED(loop); UNUSED(revents);
  166. if (NULL == (buf = read_chunk(cld->pipe_err, 64*1024))) {
  167. switch (errno) {
  168. case EINTR:
  169. case EAGAIN:
  170. #if EWOULDBLOCK != EAGAIN
  171. case EWOULDBLOCK:
  172. #endif
  173. return; /* try again later */
  174. case ECONNRESET:
  175. fcgi_cgi_child_close_error(cld);
  176. break;
  177. default:
  178. ERROR("read from fd=%d failed, %s\n", cld->pipe_err, g_strerror(errno));
  179. fcgi_cgi_child_close_error(cld);
  180. break;
  181. }
  182. } else if (cld->fcon) {
  183. fastcgi_send_err_bytearray(cld->fcon, buf);
  184. fcgi_cgi_wrote_data(cld->fcon);
  185. } else {
  186. g_byte_array_free(buf, TRUE);
  187. }
  188. }
  189. static fcgi_cgi_child* fcgi_cgi_child_create(fcgi_cgi_server *srv, fastcgi_connection *fcon) {
  190. fcgi_cgi_child *cld = g_slice_new0(fcgi_cgi_child);
  191. cld->srv = srv;
  192. cld->fcon = fcon;
  193. cld->aborted_id = -1;
  194. cld->pid = -1;
  195. ev_child_init(&cld->child_watcher, fcgi_cgi_child_child_cb, -1, 0);
  196. cld->child_watcher.data = cld;
  197. cld->pipe_in = cld->pipe_out = -1;
  198. ev_io_init(&cld->pipe_in_watcher, fcgi_cgi_child_pipe_in_cb, -1, 0);
  199. cld->pipe_in_watcher.data = cld;
  200. ev_io_init(&cld->pipe_out_watcher, fcgi_cgi_child_pipe_out_cb, -1, 0);
  201. cld->pipe_out_watcher.data = cld;
  202. ev_io_init(&cld->pipe_err_watcher, fcgi_cgi_child_pipe_err_cb, -1, 0);
  203. cld->pipe_err_watcher.data = cld;
  204. return cld;
  205. }
  206. static void fcgi_cgi_child_check_done(fcgi_cgi_child *cld) {
  207. if (!cld->fcon) {
  208. if (-1 != cld->aborted_id && (cld->pid == -1 || (cld->pipe_out == -1 && cld->pipe_in == -1))) {
  209. fcgi_cgi_child *t_cld;
  210. GPtrArray *a = cld->srv->aborted_pending_childs;
  211. guint i = a->len - 1;
  212. t_cld = g_ptr_array_index(a, cld->aborted_id) = g_ptr_array_index(a, i);
  213. g_ptr_array_set_size(a, i);
  214. t_cld->aborted_id = cld->aborted_id;
  215. cld->aborted_id = -1;
  216. fcgi_cgi_child_free(cld);
  217. }
  218. } else {
  219. if (cld->pid == -1 && cld->pipe_out == -1 && cld->pipe_in == -1 && cld->pipe_err == -1) {
  220. fastcgi_end_request(cld->fcon, cld->child_status, FCGI_REQUEST_COMPLETE);
  221. fcgi_cgi_child_free(cld);
  222. }
  223. }
  224. }
  225. static void fcgi_cgi_child_close_write(fcgi_cgi_child *cld) {
  226. if (cld->pipe_out != -1) {
  227. ev_io_stop(cld->srv->loop, &cld->pipe_out_watcher);
  228. close(cld->pipe_out);
  229. cld->pipe_out = -1;
  230. fastcgi_queue_clear(&cld->write_queue);
  231. cld->write_queue.closed = TRUE;
  232. fcgi_cgi_child_check_done(cld);
  233. }
  234. }
  235. static void fcgi_cgi_child_close_read(fcgi_cgi_child *cld) {
  236. if (cld->pipe_in != -1) {
  237. ev_io_stop(cld->srv->loop, &cld->pipe_in_watcher);
  238. close(cld->pipe_in);
  239. cld->pipe_in = -1;
  240. if (cld->fcon) fastcgi_send_out(cld->fcon, NULL);
  241. fcgi_cgi_child_check_done(cld);
  242. }
  243. }
  244. static void fcgi_cgi_child_close_error(fcgi_cgi_child *cld) {
  245. if (cld->pipe_err != -1) {
  246. ev_io_stop(cld->srv->loop, &cld->pipe_err_watcher);
  247. close(cld->pipe_err);
  248. cld->pipe_err = -1;
  249. if (cld->fcon) fastcgi_send_err(cld->fcon, NULL);
  250. fcgi_cgi_child_check_done(cld);
  251. }
  252. }
  253. static void fcgi_cgi_child_free(fcgi_cgi_child *cld) {
  254. if (cld->fcon) cld->fcon->data = NULL;
  255. cld->fcon = NULL;
  256. fcgi_cgi_child_close_write(cld);
  257. fcgi_cgi_child_close_read(cld);
  258. fcgi_cgi_child_close_error(cld);
  259. ev_child_stop(cld->srv->loop, &cld->child_watcher);
  260. g_slice_free(fcgi_cgi_child, cld);
  261. }
  262. static void fcgi_cgi_child_error(fcgi_cgi_child *cld) {
  263. if (cld->fcon) {
  264. fastcgi_connection_close(cld->fcon);
  265. }
  266. }
  267. static void copy_env_var(GPtrArray *env, const char *name) {
  268. guint i, namelen = strlen(name);
  269. const char *value;
  270. for (i = 0; i < env->len; ) {
  271. const char *entry = g_ptr_array_index(env, i);
  272. if (0 == strncmp(entry, name, namelen) && entry[namelen] == '=') {
  273. g_ptr_array_remove_index_fast(env, i);
  274. g_free((char*) entry);
  275. } else {
  276. ++i;
  277. }
  278. }
  279. value = getenv(name);
  280. if (NULL != value) {
  281. guint valuelen = strlen(value);
  282. char *entry = g_malloc(namelen + valuelen + 2);
  283. memcpy(entry, name, namelen);
  284. entry[namelen] = '=';
  285. memcpy(entry + namelen + 1, value, valuelen);
  286. entry[namelen+valuelen+1] = 0;
  287. g_ptr_array_add(env, entry);
  288. }
  289. }
  290. static void fcgi_cgi_child_start(fcgi_cgi_child *cld, const gchar *path) {
  291. int pipes_to[2] = {-1, -1}, pipes_from[2] = {-1, -1}, pipes_err[2] = {-1, -1};
  292. pid_t pid;
  293. if (-1 == pipe(pipes_to)) {
  294. ERROR("couldn't create pipe: %s\n", g_strerror(errno));
  295. goto error;
  296. }
  297. if (-1 == pipe(pipes_from)) {
  298. ERROR("couldn't create pipe: %s\n", g_strerror(errno));
  299. goto error;
  300. }
  301. if (-1 == pipe(pipes_err)) {
  302. ERROR("couldn't create pipe: %s\n", g_strerror(errno));
  303. goto error;
  304. }
  305. pid = fork();
  306. switch (pid) {
  307. case 0: {
  308. GPtrArray *enva = cld->fcon->environ;
  309. char **newenv;
  310. char *const args[] = { (char *) path, NULL };
  311. close(pipes_to[1]); close(pipes_from[0]); close(pipes_err[0]);
  312. move2fd(pipes_to[0], 0);
  313. move2fd(pipes_from[1], 1);
  314. move2fd(pipes_err[1], 2);
  315. /* try changing the directory. don't care about memleaks, execve() coming soon :) */
  316. {
  317. char *dir = strdup(path), *sep;
  318. if (NULL == (sep = strrchr(dir, '/'))) {
  319. chdir("/");
  320. } else {
  321. *sep = '\0';
  322. chdir(dir);
  323. }
  324. }
  325. copy_env_var(enva, "PATH");
  326. g_ptr_array_add(enva, NULL);
  327. newenv = (char**) g_ptr_array_free(enva, FALSE);
  328. execve(path, args, newenv);
  329. ERROR("couldn't execve '%s': %s\n", path, g_strerror(errno));
  330. exit(-1);
  331. }
  332. break;
  333. case -1:
  334. ERROR("couldn't fork: %s\n", g_strerror(errno));
  335. goto error;
  336. default:
  337. cld->pid = pid;
  338. ev_child_set(&cld->child_watcher, cld->pid, 0);
  339. ev_child_start(cld->srv->loop, &cld->child_watcher);
  340. close(pipes_to[0]); close(pipes_from[1]); close(pipes_err[1]);
  341. fd_init(pipes_to[1]); fd_init(pipes_from[0]); fd_init(pipes_err[0]);
  342. cld->pipe_out = pipes_to[1];
  343. cld->pipe_in = pipes_from[0];
  344. cld->pipe_err = pipes_err[0];
  345. ev_io_set(&cld->pipe_out_watcher, cld->pipe_out, EV_WRITE);
  346. ev_io_set(&cld->pipe_in_watcher, cld->pipe_in, EV_READ);
  347. ev_io_set(&cld->pipe_err_watcher, cld->pipe_err, EV_READ);
  348. if (cld->write_queue.length > 0) ev_io_start(cld->srv->loop, &cld->pipe_out_watcher);
  349. ev_io_start(cld->srv->loop, &cld->pipe_in_watcher);
  350. ev_io_start(cld->srv->loop, &cld->pipe_err_watcher);
  351. break;
  352. }
  353. return;
  354. error:
  355. close(pipes_to[0]); close(pipes_to[1]);
  356. close(pipes_from[0]); close(pipes_from[1]);
  357. close(pipes_err[0]); close(pipes_err[1]);
  358. fcgi_cgi_child_error(cld);
  359. }
  360. static void fcgi_cgi_direct_result(fastcgi_connection *fcon, int status) {
  361. GString *s = g_string_new(0);
  362. g_string_append_printf(s, "Status: %i\r\n\r\n", status);
  363. fastcgi_send_out(fcon, s);
  364. fastcgi_send_out(fcon, NULL);
  365. fastcgi_send_err(fcon, NULL);
  366. fastcgi_end_request(fcon, 0, FCGI_REQUEST_COMPLETE);
  367. }
  368. static void fcgi_cgi_new_request(fastcgi_connection *fcon) {
  369. fcgi_cgi_child *cld = (fcgi_cgi_child*) fcon->data;
  370. const gchar *binpath;
  371. struct stat st;
  372. if (cld) return;
  373. binpath = fastcgi_connection_environ_lookup(fcon, CONST_STR_LEN("INTERPRETER"));
  374. if (!binpath) binpath = fastcgi_connection_environ_lookup(fcon, CONST_STR_LEN("SCRIPT_FILENAME"));
  375. if (!binpath) {
  376. fcgi_cgi_direct_result(fcon, 500);
  377. return;
  378. }
  379. if (-1 == stat(binpath, &st)) {
  380. switch (errno) {
  381. case EACCES:
  382. case ENOTDIR:
  383. fcgi_cgi_direct_result(fcon, 403);
  384. break;
  385. case ENOENT:
  386. fcgi_cgi_direct_result(fcon, 404);
  387. break;
  388. default:
  389. fcgi_cgi_direct_result(fcon, 500);
  390. break;
  391. }
  392. return;
  393. }
  394. if (!S_ISREG(st.st_mode) || !((S_IXUSR | S_IXGRP | S_IXOTH) && st.st_mode)) {
  395. fcgi_cgi_direct_result(fcon, 403);
  396. return;
  397. }
  398. cld = fcgi_cgi_child_create(fcon->fsrv->data, fcon);
  399. fcon->data = cld;
  400. fcgi_cgi_child_start(cld, binpath);
  401. }
  402. static void fcgi_cgi_wrote_data(fastcgi_connection *fcon) {
  403. fcgi_cgi_child *cld = (fcgi_cgi_child*) fcon->data;
  404. if (!cld) return;
  405. if (cld->fcon->write_queue.length < MAX_BUFFER_SIZE) {
  406. if (-1 != cld->pipe_in) ev_io_start(cld->srv->loop, &cld->pipe_in_watcher);
  407. if (-1 != cld->pipe_err) ev_io_start(cld->srv->loop, &cld->pipe_err_watcher);
  408. } else {
  409. if (-1 != cld->pipe_in) ev_io_stop(cld->srv->loop, &cld->pipe_in_watcher);
  410. if (-1 != cld->pipe_err) ev_io_stop(cld->srv->loop, &cld->pipe_err_watcher);
  411. }
  412. }
  413. static void fcgi_cgi_received_stdin(fastcgi_connection *fcon, GByteArray *data) {
  414. fcgi_cgi_child *cld = (fcgi_cgi_child*) fcon->data;
  415. /* if proc is running but pipe closed -> drop data */
  416. if (!cld || cld->write_queue.closed) {
  417. if (data) g_byte_array_free(data, TRUE);
  418. return;
  419. }
  420. if (NULL != data) {
  421. fastcgi_queue_append_bytearray(&cld->write_queue, data);
  422. } else {
  423. cld->write_queue.closed = TRUE;
  424. }
  425. write_queue(cld); /* if we don't call this we have to check the write-queue length */
  426. }
  427. static void fcgi_cgi_request_aborted(fastcgi_connection *fcon) {
  428. fcgi_cgi_child *cld = (fcgi_cgi_child*) fcon->data;
  429. if (!cld) return;
  430. fcgi_cgi_child_close_write(cld);
  431. }
  432. static void fcgi_cgi_reset_connection(fastcgi_connection *fcon) {
  433. fcgi_cgi_child *cld = (fcgi_cgi_child*) fcon->data;
  434. if (!cld) return;
  435. fcon->data = NULL;
  436. cld->fcon = NULL;
  437. if (cld->pid == -1 || (cld->pipe_out == -1 && cld->pipe_in == -1)) {
  438. fcgi_cgi_child_free(cld);
  439. } else {
  440. fcgi_cgi_child_close_write(cld);
  441. if (-1 != cld->pipe_in) ev_io_start(cld->srv->loop, &cld->pipe_in_watcher);
  442. if (-1 != cld->pipe_err) ev_io_start(cld->srv->loop, &cld->pipe_err_watcher);
  443. cld->aborted_id = cld->srv->aborted_pending_childs->len;
  444. g_ptr_array_add(cld->srv->aborted_pending_childs, cld);
  445. }
  446. }
  447. static const fastcgi_callbacks cgi_callbacks = {
  448. /* cb_new_connection: */ NULL,
  449. /* cb_new_request: */ fcgi_cgi_new_request,
  450. /* cb_wrote_data: */ fcgi_cgi_wrote_data,
  451. /* cb_received_stdin: */ fcgi_cgi_received_stdin,
  452. /* cb_received_data: */ NULL,
  453. /* cb_request_aborted: */ fcgi_cgi_request_aborted,
  454. /* cb_reset_connection: */ fcgi_cgi_reset_connection
  455. };
  456. static fcgi_cgi_server* fcgi_cgi_server_create(struct ev_loop *loop, int fd, guint maxconns) {
  457. fcgi_cgi_server* srv = g_slice_new0(fcgi_cgi_server);
  458. srv->loop = loop;
  459. srv->aborted_pending_childs = g_ptr_array_new();
  460. srv->fsrv = fastcgi_server_create(loop, fd, &cgi_callbacks, maxconns);
  461. srv->fsrv->data = srv;
  462. return srv;
  463. }
  464. static void fcgi_cgi_server_free(fcgi_cgi_server* srv) {
  465. guint i;
  466. for (i = 0; i < srv->aborted_pending_childs->len; i++) {
  467. fcgi_cgi_child_free(g_ptr_array_index(srv->aborted_pending_childs, i));
  468. }
  469. g_ptr_array_free(srv->aborted_pending_childs, TRUE);
  470. fastcgi_server_free(srv->fsrv);
  471. g_slice_free(fcgi_cgi_server, srv);
  472. }
  473. #define CATCH_SIGNAL(loop, cb, n) do {\
  474. ev_init(&srv->sig_w_##n, cb); \
  475. ev_signal_set(&srv->sig_w_##n, SIG##n); \
  476. ev_signal_start(loop, &srv->sig_w_##n); \
  477. srv->sig_w_##n.data = srv; \
  478. ev_unref(loop); /* Signal watchers shouldn't keep loop alive */ \
  479. } while (0)
  480. #define UNCATCH_SIGNAL(loop, n) do {\
  481. ev_ref(loop); \
  482. ev_signal_stop(loop, &srv->sig_w_##n); \
  483. } while (0)
  484. static void sigint_cb(struct ev_loop *loop, struct ev_signal *w, int revents) {
  485. fcgi_cgi_server *srv = (fcgi_cgi_server*) w->data;
  486. UNUSED(revents);
  487. if (!srv->fsrv->do_shutdown) {
  488. ERROR("Got signal, shutdown\n");
  489. fastcgi_server_stop(srv->fsrv);
  490. } else {
  491. ERROR("Got second signal, force shutdown\n");
  492. ev_unloop(loop, EVUNLOOP_ALL);
  493. }
  494. }
  495. typedef struct {
  496. gint maxconns;
  497. gboolean show_version;
  498. } options;
  499. static options opts = {
  500. /* maxconns:*/ 16,
  501. /* version: */ FALSE,
  502. };
  503. static const GOptionEntry entries[] = {
  504. { "max-connections", 'c', 0, G_OPTION_ARG_INT, &opts.maxconns, "Maximum number of connections (default 16)", "number" },
  505. { "version", 'v', 0, G_OPTION_ARG_NONE, &opts.show_version, "Show version", NULL },
  506. { NULL, 0, 0, 0, NULL, NULL, NULL }
  507. };
  508. int main(int argc, char **argv) {
  509. GOptionContext *context;
  510. GError *error = NULL;
  511. struct ev_loop *loop;
  512. fcgi_cgi_server* srv;
  513. context = g_option_context_new("<application> [app arguments]");
  514. g_option_context_add_main_entries(context, entries, NULL);
  515. g_option_context_set_summary(context, PACKAGE_DESC);
  516. if (!g_option_context_parse (context, &argc, &argv, &error)) {
  517. g_printerr("Option parsing failed: %s\n", error->message);
  518. return -1;
  519. }
  520. if (opts.show_version) {
  521. g_printerr(PACKAGE_DESC);
  522. g_printerr("\nBuild-Date: " __DATE__ " " __TIME__ "\n");
  523. return 0;
  524. }
  525. loop = ev_default_loop(0);
  526. srv = fcgi_cgi_server_create(loop, 0, opts.maxconns);
  527. signal(SIGPIPE, SIG_IGN);
  528. CATCH_SIGNAL(loop, sigint_cb, INT);
  529. CATCH_SIGNAL(loop, sigint_cb, TERM);
  530. CATCH_SIGNAL(loop, sigint_cb, HUP);
  531. ev_loop(loop, 0);
  532. fcgi_cgi_server_free(srv);
  533. return 0;
  534. }