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 10KB

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