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

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