Tests about a simple python3 fastcgi runner using libfcgi and the Python-C API.
python
c
wsgi
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.

conf.c 9.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. #include "conf.h"
  2. void usage()
  3. {
  4. static const struct option opts[] = PYFCGI_LONG_OPT;
  5. static const char *help[][2] = PYFCGI_OPT_HELP;
  6. size_t i;
  7. dprintf(2, "Usage : %s -e PYMODULE [-E PYFUN] [OPTIONS]\n", PYFCGI_NAME);
  8. dprintf(2, "\nOptions list :\n");
  9. i=0;
  10. while(opts[i].name)
  11. {
  12. dprintf(2, "\t-%c, --%s", opts[i].val, opts[i].name);
  13. switch(opts[i].has_arg)
  14. {
  15. case required_argument:
  16. dprintf(2, "=%s\n",
  17. help[i][1]?help[i][1]:"ARG");
  18. break;
  19. case optional_argument:
  20. dprintf(2, "[=%s]\n",
  21. help[i][1]?help[i][1]:"ARG");
  22. break;
  23. default: //no_argument
  24. dprintf(2, "\n");
  25. }
  26. dprintf(2, "\t\t%s\n", help[i][0]);
  27. i++;
  28. }
  29. dprintf(2, "%s", PYFCGI_HELP_TEXT);
  30. }
  31. void print_version(int fd)
  32. {
  33. char version[16];
  34. pyfcgi_python_version(version);
  35. dprintf(fd, "%s\nPython %s\n", PACKAGE_STRING, version);
  36. }
  37. void default_conf()
  38. {
  39. memset(&PyFCGI_conf, 0, sizeof(pyfcgi_conf_t));
  40. PyFCGI_conf.context.pid = getpid();
  41. PyFCGI_conf.min_wrk = 1;
  42. PyFCGI_conf.max_wrk = 5;
  43. PyFCGI_conf.max_reqs = 1000;
  44. PyFCGI_conf.pep333 = 1;
  45. PyFCGI_conf.verbosity = 0;
  46. PyFCGI_conf.pool_timeout = 5;
  47. PyFCGI_conf.worker_timeout = 3;
  48. PyFCGI_conf.worker_gc_timeout = 7;
  49. }
  50. int parse_args(int argc, char *argv[])
  51. {
  52. static const struct option long_options[] = PYFCGI_LONG_OPT;
  53. char ident[] = "pyfcgi[XXXXXXXX]";
  54. int c, opt_i;
  55. while(1)
  56. {
  57. c = getopt_long(argc, argv, PYFCGI_SHORT_OPT, long_options,
  58. &opt_i);
  59. if(c == -1) { break; }
  60. switch(c)
  61. {
  62. case 'V':
  63. print_version(1);
  64. exit(0);
  65. case 'C':
  66. dprintf(2, "Config parser not yet implemented :'(\n");
  67. exit(1);
  68. case 'e':
  69. PyFCGI_conf.py_entrymod = strdup(optarg);
  70. break;
  71. case 'E':
  72. PyFCGI_conf.py_entryfun = strdup(optarg);
  73. break;
  74. case 'A':
  75. PyFCGI_conf.pep333 = 0;
  76. break;
  77. case 'w':
  78. PyFCGI_conf.min_wrk = atoi(optarg);
  79. break;
  80. case 'W':
  81. PyFCGI_conf.max_wrk = atoi(optarg);
  82. break;
  83. case 'v':
  84. PyFCGI_conf.verbosity = 1;
  85. pyfcgi_logger_add(NULL, 0xFF, 0xFF,
  86. PYFCGI_LOGGER_FMT_DEFAULT);
  87. break;
  88. case 'm':
  89. PyFCGI_conf.max_reqs = atoi(optarg);
  90. if(PyFCGI_conf.max_reqs < 0)
  91. {
  92. PyFCGI_conf.max_reqs = 0;
  93. }
  94. break;
  95. case 'f':
  96. PyFCGI_conf.worker_fast_spawn = 1;
  97. break;
  98. case 't':
  99. PyFCGI_conf.worker_timeout = atoi(optarg);
  100. if(PyFCGI_conf.worker_timeout < 0)
  101. {
  102. PyFCGI_conf.worker_timeout = 0;
  103. }
  104. break;
  105. case 'L':
  106. if(parse_optlog(optarg))
  107. {
  108. exit(1);
  109. }
  110. break;
  111. case 'S':
  112. snprintf(ident+7, 8, "%4d]", PyFCGI_conf.context.pid);
  113. pyfcgi_logger_enable_syslog(ident);
  114. break;
  115. case 'P':
  116. PyFCGI_conf.pidfile = strdup(optarg);
  117. /**@todo create pidfile and put master pid in it */
  118. break;
  119. case 's':
  120. if(pyfcgi_monitor_check_sock(optarg) < 0)
  121. {
  122. exit(1);
  123. }
  124. PyFCGI_conf.mon_socket = strdup(optarg);
  125. /**@todo check strdup returned value */
  126. break;
  127. case 'h':
  128. usage();
  129. exit(0);
  130. default:
  131. usage();
  132. exit(1);
  133. }
  134. }
  135. if(optind < argc)
  136. {
  137. for(opt_i=optind; opt_i<argc; opt_i++)
  138. {
  139. dprintf(2, "Unkown argument '%s'\n", argv[opt_i]);
  140. }
  141. usage();
  142. exit(1);
  143. }
  144. if(!PyFCGI_conf.py_entrymod)
  145. {
  146. dprintf(2, "No python entry module given... exiting\n");
  147. usage();
  148. exit(2);
  149. }
  150. if(!PyFCGI_conf.py_entryfun)
  151. {
  152. PyFCGI_conf.py_entryfun = PYENTRY_DEFAULT_FUN;
  153. }
  154. if(check_entrypoint_import())
  155. {
  156. usage();
  157. exit(3);
  158. }
  159. return 0;
  160. }
  161. int check_entrypoint_import()
  162. {
  163. pid_t pid;
  164. int status;
  165. void *ret;
  166. pid = fork();
  167. if(!pid)
  168. {
  169. pyinit();
  170. ret = (void*)import_entrypoint();
  171. if(!ret)
  172. {
  173. dprintf(2, "Unable to import entrypoint...\n");
  174. exit(1);
  175. }
  176. pyfcgi_log(LOG_DEBUG, "Entrypoint import [OK]");
  177. Py_Exit(0);
  178. }
  179. waitpid(pid, &status, 0);
  180. return WEXITSTATUS(status);
  181. }
  182. int parse_optlog(const char* logspec)
  183. {
  184. char *filename, *filter, *fmt;
  185. int filt;
  186. filename = strdup(logspec); /**@todo check error */
  187. filter = filename;
  188. while(*filter && *filter != ';') { filter++; }
  189. if(*filter)
  190. {
  191. *filter = '\0';
  192. filter++;
  193. }
  194. fmt = filter;
  195. while(*fmt && *fmt != ';') { fmt++; }
  196. if(*fmt)
  197. {
  198. *fmt = '\0';
  199. fmt++;
  200. }
  201. if(!strlen(filter))
  202. {
  203. filt = 0xFF;
  204. }
  205. else
  206. {
  207. filt = strtol(filter, NULL, !strncmp(filter, "0x", 2)?16:10);
  208. }
  209. fmt = strlen(fmt)?fmt:NULL;
  210. if(pyfcgi_logger_add(filename, filt, filt, fmt))
  211. {
  212. return 1;
  213. }
  214. return 0;
  215. }
  216. int pyfcgi_wd_init(void (*wd_sig_cleaner)(int), const struct timespec *delay)
  217. {
  218. struct sigaction act;
  219. struct sigevent sev;
  220. pyfcgi_context_t *context;
  221. int err;
  222. context = &(PyFCGI_conf.context);
  223. PyFCGI_conf.context.wd_delay.tv_sec = delay->tv_sec;
  224. PyFCGI_conf.context.wd_delay.tv_nsec = delay->tv_nsec;
  225. pyfcgi_log(LOG_DEBUG, "Set watchdog with %ds timeout", PyFCGI_conf.context.wd_delay.tv_sec);
  226. // Creating new timer with default sigevent (SIGEV_SIGNAL with SIGALRM)
  227. if(timer_create(CLOCK_REALTIME, NULL, &(context->wd_timer)) < 0)
  228. {
  229. err = errno;
  230. pyfcgi_log(LOG_ALERT, "Unable to create watchdog timer : %s",
  231. strerror(err));
  232. errno = err;
  233. return -1;
  234. }
  235. // Creating a new timer sending SIGKILL after 1.5 delay
  236. sev.sigev_notify = SIGEV_SIGNAL;
  237. sev.sigev_signo = SIGKILL;
  238. if(timer_create(CLOCK_REALTIME, &sev, &(context->wd_timerkill)) < 0)
  239. {
  240. err = errno;
  241. pyfcgi_log(LOG_ALERT, "Unable to create kill watchdog timer : %s",
  242. strerror(err));
  243. errno = err;
  244. return -1;
  245. }
  246. // Registering SIGALRM signal handler
  247. act.sa_handler = wd_sig_cleaner?wd_sig_cleaner:pyfcgi_wd_default_sighandler;
  248. sigemptyset(&act.sa_mask);
  249. act.sa_flags = 0;
  250. act.sa_restorer = NULL;
  251. if(sigaction(SIGALRM, &act, &(context->wd_oldsig)) < 0)
  252. {
  253. err = errno;
  254. pyfcgi_log(LOG_ALERT, "Unable to set the signal handler for watchdog timer : %s",
  255. strerror(err));
  256. errno = err;
  257. return -1;
  258. }
  259. context->wd = 1;
  260. return 0;
  261. }
  262. int pyfcgi_wd_arm()
  263. {
  264. pyfcgi_context_t *context;
  265. struct itimerspec timeout;
  266. int err, res;
  267. context = &(PyFCGI_conf.context);
  268. if(!context->wd) { return 0; }
  269. timeout.it_value = context->wd_delay;
  270. timeout.it_interval.tv_sec = 0;
  271. timeout.it_interval.tv_nsec = 0;
  272. context = &(PyFCGI_conf.context);
  273. res = 0;
  274. if(timer_settime(context->wd_timer, 0, &timeout, NULL) < 0)
  275. {
  276. err = errno;
  277. pyfcgi_log(LOG_ALERT, "Unable to arm watchdog : %s",
  278. strerror(err));
  279. res = -1;
  280. }
  281. timeout.it_value.tv_sec = (time_t)(timeout.it_value.tv_sec * 1.5);
  282. timeout.it_value.tv_nsec = 500000000;
  283. timeout.it_interval = timeout.it_value;
  284. if(timer_settime(context->wd_timerkill, 0, &timeout, NULL) < 0)
  285. {
  286. err = errno;
  287. pyfcgi_log(LOG_ALERT, "Unable to pause killer watchdog : %s",
  288. strerror(err));
  289. res = -1;
  290. }
  291. return res;
  292. }
  293. int pyfcgi_wd_pause()
  294. {
  295. pyfcgi_context_t *context;
  296. struct itimerspec zero;
  297. int err, res;
  298. context = &(PyFCGI_conf.context);
  299. if(!context->wd) { return 0; }
  300. memset(&zero, 0, sizeof(struct itimerspec));
  301. res = 0;
  302. if(timer_settime(context->wd_timer, TIMER_ABSTIME, &zero, NULL) < 0)
  303. {
  304. err = errno;
  305. pyfcgi_log(LOG_ALERT, "Unable to pause watchdog : %s",
  306. strerror(err));
  307. res = -1;
  308. }
  309. if(timer_settime(context->wd_timerkill, TIMER_ABSTIME, &zero, NULL) < 0)
  310. {
  311. err = errno;
  312. pyfcgi_log(LOG_ALERT, "Unable to pause killer watchdog : %s",
  313. strerror(err));
  314. res = -1;
  315. }
  316. return res;
  317. }
  318. int pyfcgi_wd_stop()
  319. {
  320. int err, res;
  321. pyfcgi_context_t *context;
  322. context = &(PyFCGI_conf.context);
  323. if(!context->wd) { return 0; }
  324. res = 0;
  325. if(timer_delete(context->wd_timer) < 0)
  326. {
  327. err = errno;
  328. pyfcgi_log(LOG_WARNING, "Unable to delete watchdog timer : %s",
  329. strerror(err));
  330. res = -1;
  331. }
  332. if(timer_delete(context->wd_timerkill) < 0)
  333. {
  334. err = errno;
  335. pyfcgi_log(LOG_WARNING, "Unable to delete killer watchdog timer : %s",
  336. strerror(err));
  337. res = -1;
  338. }
  339. if(sigaction(SIGALRM, &(context->wd_oldsig), NULL) < 0)
  340. {
  341. err = errno;
  342. pyfcgi_log(LOG_WARNING, "Unable to restore signal handler : %s",
  343. strerror(err));
  344. res = -1;
  345. }
  346. context->wd = 0;
  347. pyfcgi_log(LOG_DEBUG, "Watchdog stopped");
  348. return res;
  349. }
  350. void pyfcgi_wd_default_sighandler(int signum)
  351. {
  352. pyfcgi_log(LOG_ALERT, "Timeout after %d.%09ds",
  353. PyFCGI_conf.context.wd_delay.tv_sec,
  354. PyFCGI_conf.context.wd_delay.tv_nsec);
  355. pyfcgi_wd_stop();
  356. exit(PYFCGI_TIMEOUT);
  357. }
  358. char *status2str(int status)
  359. {
  360. char buff[1024], *buffptr;
  361. size_t left = 1024, ret;
  362. short reason;
  363. reason = 0;
  364. buffptr = buff;
  365. if(status & (PYFCGI_WORKER_FAIL | PYFCGI_MASTER_FAIL))
  366. {
  367. buffptr += ret = snprintf(buffptr, left, "%s ",
  368. (status & PYFCGI_WORKER_FAIL)?"Worker":"Master");
  369. left -= ret;
  370. status &= ((0xFFFF) ^ (PYFCGI_WORKER_FAIL | PYFCGI_MASTER_FAIL));
  371. }
  372. if(status & PYFCGI_FATAL)
  373. {
  374. buffptr += ret = snprintf(buffptr, left, "fatal ");
  375. left -= ret;
  376. status ^= PYFCGI_FATAL;
  377. }
  378. if(status & EXIT_PYERR)
  379. {
  380. buffptr += ret = snprintf(buffptr, left, "Python");
  381. left -= ret;
  382. status ^= EXIT_PYERR;
  383. }
  384. if( status & PYFCGI_TIMEOUT)
  385. {
  386. buffptr += ret = snprintf(buffptr, left, "Timeout");
  387. left -= ret;
  388. reason = 1;
  389. status ^= PYFCGI_TIMEOUT;
  390. }
  391. if(status & PYFCGI_ERR)
  392. {
  393. buffptr += ret = snprintf(buffptr, left, "Error");
  394. left -= ret;
  395. reason = 1;
  396. status ^= PYFCGI_ERR;
  397. }
  398. if(!reason)
  399. {
  400. buffptr += ret = snprintf(buffptr, left, "Failure");
  401. left -= ret;
  402. }
  403. if(status)
  404. {
  405. buffptr += ret = snprintf(buffptr, left, "%d", status);
  406. left -= ret;
  407. }
  408. return strndup(buff, 1024);
  409. }