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

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