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

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