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.

monitor.c 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. #include "monitor.h"
  2. static pyfcgi_monitor_t pyfcgi_mon;
  3. static void clean_exit(int status)
  4. {
  5. pyfcgi_wd_stop();
  6. if(pyfcgi_mon.sockserv)
  7. {
  8. if(pyfcgi_mon.sockcli)
  9. {
  10. if(shutdown(pyfcgi_mon.sockcli, SHUT_RDWR) < 0)
  11. {
  12. pyfcgi_log(LOG_WARNING, "Unable to shutdown clisocket : %s",
  13. strerror(errno));
  14. }
  15. if(close(pyfcgi_mon.sockcli) < 0)
  16. {
  17. pyfcgi_log(LOG_WARNING, "Unable to close clisocket : %s",
  18. strerror(errno));
  19. }
  20. }
  21. pyfcgi_log(LOG_INFO, "Closing listening socket...");
  22. if(shutdown(pyfcgi_mon.sockserv, SHUT_RDWR) < 0)
  23. {
  24. pyfcgi_log(LOG_WARNING, "Unable to shutdown socket : %s",
  25. strerror(errno));
  26. }
  27. if(close(pyfcgi_mon.sockserv) < 0)
  28. {
  29. pyfcgi_log(LOG_WARNING, "Unable to close socket : %s",
  30. strerror(errno));
  31. }
  32. }
  33. exit(status);
  34. }
  35. pid_t pyfcgi_spawn_monitor()
  36. {
  37. pid_t res;
  38. struct sigaction act, actdrop;
  39. struct timespec wd_delay;
  40. int err;
  41. wd_delay.tv_sec = 1;
  42. wd_delay.tv_nsec = 0;
  43. act.sa_handler = pyfcgi_monitor_sighandler;
  44. sigemptyset(&act.sa_mask);
  45. act.sa_flags = 0;
  46. act.sa_restorer = NULL;
  47. actdrop.sa_handler = pyfcgi_sighandler_drop;
  48. sigemptyset(&act.sa_mask);
  49. act.sa_flags = 0;
  50. act.sa_restorer = NULL;
  51. if(!PyFCGI_conf.mon_socket)
  52. {
  53. pyfcgi_log(LOG_ERR, "No socket url set but pyfcgi_spawn_monitor called !");
  54. return -1;
  55. }
  56. memset(&pyfcgi_mon, 0, sizeof(pyfcgi_mon));
  57. res = fork();
  58. if(res == -1)
  59. {
  60. pyfcgi_log(LOG_ALERT, "Unable to fork into monitoring server process : %s",
  61. strerror(errno));
  62. sleep(1);
  63. }
  64. if(!res)
  65. { //child process
  66. pyfcgi_logger_set_ident("StatServ");
  67. if(sigaction(SIGTERM, &act, NULL))
  68. {
  69. err = errno;
  70. pyfcgi_log(LOG_ERR, "Unable to run sigaction for SIGTERM : %s",
  71. strerror(err));
  72. exit(PYFCGI_FATAL);
  73. }
  74. if(sigaction(SIGINT, &actdrop, NULL))
  75. {
  76. pyfcgi_log(LOG_WARNING, "Unable run sigaction for SIGINT sigaction : %s",
  77. strerror(errno));
  78. }
  79. if(sigaction(SIGALRM, &actdrop, NULL))
  80. {
  81. pyfcgi_log(LOG_WARNING, "Unable to restore ALARM sigaction : %s",
  82. strerror(errno));
  83. }
  84. if(pyfcgi_wd_init(pyfcgi_monitor_timeout, &wd_delay) < 0)
  85. {
  86. pyfcgi_log(LOG_ALERT,
  87. "Unable to start watchdog, sleep 1s then exiting...");
  88. sleep(1);
  89. exit(PYFCGI_FATAL);
  90. }
  91. pyfcgi_monitor_loop();
  92. pyfcgi_log(LOG_ALERT, "Monitor loop should never return but just did it...");
  93. clean_exit(PYFCGI_FATAL);
  94. }
  95. return res;
  96. }
  97. int pyfcgi_monitor_IPC_init()
  98. {
  99. int ret;
  100. ret = 0;
  101. if(!PyFCGI_SEM_OPEN(SEM_WREQS))
  102. {
  103. if(pyfcgi_IPC_init(IPC_WREQS)) { ret = -1; }
  104. }
  105. if(!PyFCGI_SEM_OPEN(SEM_STATS))
  106. {
  107. if(pyfcgi_IPC_init(IPC_SEMST)) { ret = -1; }
  108. }
  109. if(!PyFCGI_conf.shm.ptr)
  110. {
  111. if(pyfcgi_IPC_init(IPC_SHMST)) { ret = -1; }
  112. }
  113. return ret;
  114. }
  115. void pyfcgi_monitor_loop()
  116. {
  117. pyfcgi_monitor_addr_t addr;
  118. int *sockargs, reuse;
  119. socklen_t addrlen;
  120. sockargs = pyfcgi_mon.sockargs;
  121. if(pyfcgi_monitor_parse_sock(PyFCGI_conf.mon_socket, sockargs,
  122. &addr) < 0)
  123. {
  124. pyfcgi_log(LOG_WARNING,
  125. "Unable to parse socket URL, monitoring server not starting...");
  126. free(PyFCGI_conf.mon_socket);
  127. PyFCGI_conf.mon_socket = NULL;
  128. clean_exit(PYFCGI_ERR);
  129. }
  130. if( (pyfcgi_mon.sockserv = socket(sockargs[0], sockargs[1],
  131. sockargs[2])) < 0)
  132. {
  133. pyfcgi_log(LOG_ERR, "Unable to create socket : %s",
  134. strerror(errno));
  135. sleep(1);
  136. clean_exit(PYFCGI_ERR);
  137. }
  138. if(sockargs[0] == AF_INET)
  139. {
  140. addrlen = sizeof(struct sockaddr_in);
  141. }
  142. else if(sockargs[0] == AF_INET6)
  143. {
  144. addrlen = sizeof(struct sockaddr_in6);
  145. }
  146. else
  147. {
  148. addrlen = sizeof(struct sockaddr_un);
  149. }
  150. reuse = 1;
  151. if(setsockopt(pyfcgi_mon.sockserv, SOL_SOCKET, SO_REUSEADDR, &reuse,
  152. sizeof(int)) < 0)
  153. {
  154. pyfcgi_log(LOG_WARNING, "Unable to set socket option : %s",
  155. strerror(errno));
  156. }
  157. if(bind(pyfcgi_mon.sockserv, (struct sockaddr*)&addr, addrlen) < 0)
  158. {
  159. pyfcgi_log(LOG_ERR, "Unable to bind socket... : %s",
  160. strerror(errno));
  161. pyfcgi_log(LOG_INFO, "Retrying in 10s");
  162. close(pyfcgi_mon.sockserv);
  163. sleep(10);
  164. clean_exit(PYFCGI_ERR);
  165. }
  166. pyfcgi_log(LOG_INFO, "Listening on %s", PyFCGI_conf.mon_socket);
  167. //bind successfull, sending to appropriate response loop
  168. if(pyfcgi_monitor_IPC_init() < 0)
  169. {
  170. pyfcgi_log(LOG_ERR, "Unable to initialize IPC");
  171. sleep(1);
  172. exit(PYFCGI_ERR);
  173. }
  174. pyfcgi_stats_init();
  175. if(pyfcgi_mon.sockargs[1] == SOCK_DGRAM)
  176. {
  177. pyfcgi_monitor_dgram_loop();
  178. }
  179. else
  180. {
  181. pyfcgi_monitor_stream_loop();
  182. }
  183. }
  184. void pyfcgi_monitor_stream_loop(pyfcgi_monitor_addr_t addr_serv)
  185. {
  186. pyfcgi_monitor_addr_t cliaddr;
  187. socklen_t addrlen;
  188. int sockcli, err, ret;
  189. char ipstr[64];
  190. const char *buff;
  191. size_t bufflen;
  192. addrlen = sizeof(cliaddr);
  193. if(listen(pyfcgi_mon.sockserv, PYFCGI_MONITOR_STREAM_BACKLOG) < 0)
  194. {
  195. pyfcgi_log(LOG_ERR, "Unable to listen on socket : %s",
  196. strerror(errno));
  197. clean_exit(PYFCGI_ERR);
  198. }
  199. while(1)
  200. {
  201. sockcli = pyfcgi_mon.sockcli = accept(pyfcgi_mon.sockserv,
  202. (struct sockaddr*)&cliaddr, &addrlen);
  203. pyfcgi_wd_arm(); //watchdog
  204. if(sockcli < 0)
  205. {
  206. err = errno;
  207. if(err == EINTR) { continue; }
  208. break;
  209. }
  210. pyfcgi_log(LOG_DEBUG, "New client");
  211. if(!inet_ntop(pyfcgi_mon.sockargs[0], (void*)&cliaddr, ipstr, 64))
  212. {
  213. pyfcgi_log(LOG_WARNING,
  214. "inet_ntop fails to represent client address : %s",
  215. strerror(errno));
  216. strcpy(ipstr, "XXX");
  217. }
  218. if(!pyfcgi_stats_format())
  219. {
  220. pyfcgi_log(LOG_ERR, "Unable to format stats...");
  221. close(sockcli);
  222. continue;
  223. }
  224. pyfcgi_stats_buff(&buff, &bufflen);
  225. pyfcgi_log(LOG_INFO, "Sending stats to %s", ipstr);
  226. do
  227. {
  228. ret = send(sockcli, buff, bufflen, 0);
  229. err = errno;
  230. }while(ret == -1 && err == EINTR);
  231. if(ret == -1)
  232. {
  233. pyfcgi_log(LOG_WARNING, "Unable to send stats to client : %s",
  234. strerror(err));
  235. }
  236. shutdown(sockcli, SHUT_RDWR);
  237. close(sockcli);
  238. pyfcgi_wd_pause(); //watchdog
  239. pyfcgi_mon.sockcli = 0;
  240. addrlen = sizeof(cliaddr);
  241. }
  242. pyfcgi_log(LOG_ERR, "Unable to accept new connection on socket : %s",
  243. strerror(errno));
  244. }
  245. void pyfcgi_monitor_dgram_loop()
  246. {
  247. pyfcgi_log(LOG_ERR, "Dgram server not implemented.... exiting server");
  248. free(PyFCGI_conf.mon_socket);
  249. PyFCGI_conf.mon_socket = NULL;
  250. clean_exit(PYFCGI_ERR);
  251. }
  252. int pyfcgi_monitor_check_sock(const char* sockurl)
  253. {
  254. const char *port;
  255. short tcp;
  256. const char *urlorig;
  257. urlorig = sockurl;
  258. if(!(tcp = strncasecmp("tcp://", sockurl, 6)) ||
  259. !strncasecmp("udp://", sockurl, 6))
  260. {
  261. sockurl += 6; //first addr chr
  262. }
  263. else if(!strncasecmp("unix://", sockurl, 7))
  264. {
  265. sockurl += 7;
  266. if(!strlen(sockurl))
  267. {
  268. dprintf(2, "UNIX socket missing file path : '%s'\n",
  269. urlorig);
  270. return -1;
  271. }
  272. if(strlen(sockurl) > UNIX_SOCKPATH_MAX)
  273. {
  274. dprintf(2, "UNIX socket support only path with length <= %d but given path is %ld bytes long : '%s'",
  275. UNIX_SOCKPATH_MAX, strlen(sockurl), urlorig);
  276. return -1;
  277. }
  278. return 0;
  279. }
  280. else
  281. {
  282. dprintf(2, "Invalid protocol in '%s'\n", sockurl);
  283. return -1;
  284. }
  285. do { sockurl++; } while(*sockurl && *sockurl != ':');
  286. if(!sockurl)
  287. {
  288. dprintf(2, "%s protocol choosen but not port given : '%s'\n",
  289. tcp?"TCP":"UDP", sockurl);
  290. return -1;
  291. }
  292. if(!*sockurl)
  293. {
  294. dprintf(2, "Port missing in socket URL '%s'\n", urlorig);
  295. return -1;
  296. }
  297. sockurl++;
  298. port = sockurl;
  299. while(*sockurl && *sockurl >= '0' && *sockurl <= '9')
  300. {
  301. sockurl++;
  302. }
  303. if(*sockurl)
  304. {
  305. dprintf(2, "Invalid port '%s' in socket URL '%s'\n",
  306. port, urlorig);
  307. return -1;
  308. }
  309. return 0;
  310. }
  311. int pyfcgi_monitor_parse_sock(const char *sockurl, int sockargs[3],
  312. pyfcgi_monitor_addr_t *listen_addr)
  313. {
  314. const char *addr_ptr;
  315. short tcp;
  316. int *domain, *type, *protocol;
  317. struct sockaddr_un *addr_un;
  318. domain = &sockargs[0];
  319. type = &sockargs[1];
  320. protocol = &sockargs[2];
  321. addr_un = &(listen_addr->un);
  322. if(!strncasecmp("unix://", sockurl, 7))
  323. {
  324. addr_ptr = sockurl + 7;
  325. *domain = AF_UNIX;
  326. *type = SOCK_STREAM;
  327. *protocol = 0;
  328. addr_un->sun_family = AF_UNIX;
  329. strncpy(addr_un->sun_path, addr_ptr, UNIX_SOCKPATH_MAX);
  330. return 0;
  331. }
  332. if((tcp = strncasecmp("tcp://", sockurl, 6)) &&
  333. strncasecmp("udp://", sockurl, 6))
  334. { //Unchecked URL??!!
  335. pyfcgi_log(LOG_ERR, "Invalid protocol in URL : '%s'",
  336. sockurl);
  337. return -1;
  338. }
  339. addr_ptr = sockurl + 6;
  340. *type = tcp ? SOCK_DGRAM : SOCK_STREAM;
  341. *protocol = 0;
  342. if(pyfcgi_monitor_parse_inet_addr(addr_ptr, *type, listen_addr, domain))
  343. {
  344. return -1;
  345. }
  346. return 0;
  347. }
  348. int pyfcgi_monitor_parse_inet_addr(const char* addr_str, int socktype,
  349. pyfcgi_monitor_addr_t *listen_addr, int* domain)
  350. {
  351. char *addr, *port, *ptr, ipstr[64];
  352. struct addrinfo *infos, hints, *info;
  353. short v4, v6;
  354. int ret;
  355. // initialize temporary address & port pointers
  356. addr = strdup(addr_str);
  357. if(!addr)
  358. {
  359. pyfcgi_log(LOG_ALERT, "strdup() failed to copy socket addr : %s",
  360. strerror(errno));
  361. return -1;
  362. }
  363. ptr = addr;
  364. do { ptr++; }while(*ptr && *ptr != ':');
  365. if(!ptr)
  366. {
  367. pyfcgi_log(LOG_ERR, "No port found in INET url : %s",
  368. addr_str);
  369. goto free_err;
  370. }
  371. *ptr = '\0';
  372. port = ptr+1;
  373. memset(&hints, 0, sizeof(struct addrinfo));
  374. v4 = PyFCGI_conf.ipv4;
  375. v6 = PyFCGI_conf.ipv6;
  376. hints.ai_family = (v4?AF_INET:(v6?AF_INET6:PF_UNSPEC));
  377. hints.ai_socktype = socktype;
  378. hints.ai_flags = AI_CANONNAME;
  379. if((ret = getaddrinfo(addr, port, &hints, &infos)))
  380. {
  381. pyfcgi_log(LOG_ALERT, "getaddrinfo fails on '%s' : %s",
  382. gai_strerror(ret));
  383. goto free_err;
  384. }
  385. for(info = infos; info != NULL; info = info->ai_next)
  386. {
  387. if(info->ai_family == AF_INET)
  388. {
  389. memcpy(&listen_addr->in, info->ai_addr,
  390. info->ai_addrlen);
  391. }
  392. else if(info->ai_family == AF_INET6)
  393. {
  394. memcpy(&listen_addr->in6, info->ai_addr,
  395. info->ai_addrlen);
  396. }
  397. else
  398. {
  399. continue;
  400. }
  401. *domain = info->ai_family;
  402. if(!inet_ntop(*domain, info->ai_addr, ipstr, 64))
  403. {
  404. pyfcgi_log(LOG_ERR, "Unable to format IP in string : %s",
  405. strerror(errno));
  406. strcpy(ipstr, "IP");
  407. }
  408. pyfcgi_log(LOG_DEBUG, "Listen addr resolved to %s(%s)",
  409. info->ai_canonname, ipstr);
  410. freeaddrinfo(infos);
  411. free(addr);
  412. return 0;
  413. }
  414. pyfcgi_log(LOG_ERR, "Unable to resolve to a valid AF_INET[6] address");
  415. freeaddrinfo(infos);
  416. free_err:
  417. free(addr);
  418. return -1;
  419. }
  420. void pyfcgi_monitor_sighandler(int signum)
  421. {
  422. pyfcgi_log(LOG_NOTICE, "Received signal %s, exiting",
  423. strsignal(signum));
  424. clean_exit(0);
  425. }
  426. void pyfcgi_monitor_timeout(int signum)
  427. {
  428. pyfcgi_log(LOG_ALERT, "Monitor server timeout, killed by watchdog");
  429. clean_exit(PYFCGI_TIMEOUT);
  430. }