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.2KB

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