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

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