PageRenderTime 59ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/fpm/fpm_sockets.c

http://github.com/dreamcat4/php-fpm
C | 427 lines | 297 code | 124 blank | 6 comment | 72 complexity | abcd6bde27cfad30a29df186291ff0ff MD5 | raw file
  1. /* $Id: fpm_sockets.c,v 1.20.2.1 2008/12/13 03:21:18 anight Exp $ */
  2. /* (c) 2007,2008 Andrei Nigmatulin */
  3. #include "fpm_config.h"
  4. #ifdef HAVE_ALLOCA_H
  5. #include <alloca.h>
  6. #endif
  7. #include <sys/types.h>
  8. #include <sys/stat.h> /* for chmod(2) */
  9. #include <sys/socket.h>
  10. #include <netinet/in.h>
  11. #include <arpa/inet.h>
  12. #include <sys/un.h>
  13. #include <netdb.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include <errno.h>
  18. #include <unistd.h>
  19. #include "zlog.h"
  20. #include "fpm_arrays.h"
  21. #include "fpm_sockets.h"
  22. #include "fpm_worker_pool.h"
  23. #include "fpm_unix.h"
  24. #include "fpm_str.h"
  25. #include "fpm_env.h"
  26. #include "fpm_cleanup.h"
  27. struct listening_socket_s {
  28. int refcount;
  29. int sock;
  30. int type;
  31. char *key;
  32. };
  33. static struct fpm_array_s sockets_list;
  34. static int fpm_sockets_resolve_af_inet(char *node, char *service, struct sockaddr_in *addr)
  35. {
  36. struct addrinfo *res;
  37. struct addrinfo hints;
  38. int ret;
  39. memset(&hints, 0, sizeof(hints));
  40. hints.ai_family = AF_INET;
  41. ret = getaddrinfo(node, service, &hints, &res);
  42. if (ret != 0) {
  43. zlog(ZLOG_STUFF, ZLOG_ERROR, "can't resolve hostname '%s%s%s': getaddrinfo said: %s%s%s\n",
  44. node, service ? ":" : "", service ? service : "",
  45. gai_strerror(ret), ret == EAI_SYSTEM ? ", system error: " : "", ret == EAI_SYSTEM ? strerror(errno) : "");
  46. return -1;
  47. }
  48. *addr = *(struct sockaddr_in *) res->ai_addr;
  49. freeaddrinfo(res);
  50. return 0;
  51. }
  52. enum { FPM_GET_USE_SOCKET = 1, FPM_STORE_SOCKET = 2, FPM_STORE_USE_SOCKET = 3 };
  53. static void fpm_sockets_cleanup(int which, void *arg)
  54. {
  55. int i;
  56. char *env_value = 0;
  57. int p = 0;
  58. struct listening_socket_s *ls = sockets_list.data;
  59. for (i = 0; i < sockets_list.used; i++, ls++) {
  60. if (which != FPM_CLEANUP_PARENT_EXEC) {
  61. close(ls->sock);
  62. }
  63. else { /* on PARENT EXEC we want socket fds to be inherited through environment variable */
  64. char fd[32];
  65. sprintf(fd, "%d", ls->sock);
  66. env_value = realloc(env_value, p + (p ? 1 : 0) + strlen(ls->key) + 1 + strlen(fd) + 1);
  67. p += sprintf(env_value + p, "%s%s=%s", p ? "," : "", ls->key, fd);
  68. }
  69. if (which == FPM_CLEANUP_PARENT_EXIT_MAIN) {
  70. if (ls->type == FPM_AF_UNIX) {
  71. unlink(ls->key);
  72. }
  73. }
  74. free(ls->key);
  75. }
  76. if (env_value) {
  77. setenv("FPM_SOCKETS", env_value, 1);
  78. free(env_value);
  79. }
  80. fpm_array_free(&sockets_list);
  81. }
  82. static int fpm_sockets_hash_op(int sock, struct sockaddr *sa, char *key, int type, int op)
  83. {
  84. if (key == NULL) {
  85. switch (type) {
  86. case FPM_AF_INET : {
  87. struct sockaddr_in *sa_in = (struct sockaddr_in *) sa;
  88. key = alloca(sizeof("xxx.xxx.xxx.xxx:ppppp"));
  89. sprintf(key, "%u.%u.%u.%u:%u", IPQUAD(&sa_in->sin_addr), (unsigned int) ntohs(sa_in->sin_port));
  90. break;
  91. }
  92. case FPM_AF_UNIX : {
  93. struct sockaddr_un *sa_un = (struct sockaddr_un *) sa;
  94. key = alloca(strlen(sa_un->sun_path) + 1);
  95. strcpy(key, sa_un->sun_path);
  96. break;
  97. }
  98. default :
  99. return -1;
  100. }
  101. }
  102. switch (op) {
  103. case FPM_GET_USE_SOCKET :
  104. {
  105. int i;
  106. struct listening_socket_s *ls = sockets_list.data;
  107. for (i = 0; i < sockets_list.used; i++, ls++) {
  108. if (!strcmp(ls->key, key)) {
  109. ++ls->refcount;
  110. return ls->sock;
  111. }
  112. }
  113. break;
  114. }
  115. case FPM_STORE_SOCKET : /* inherited socket */
  116. case FPM_STORE_USE_SOCKET : /* just created */
  117. {
  118. struct listening_socket_s *ls;
  119. ls = fpm_array_push(&sockets_list);
  120. if (!ls) {
  121. break;
  122. }
  123. if (op == FPM_STORE_SOCKET) {
  124. ls->refcount = 0;
  125. }
  126. else {
  127. ls->refcount = 1;
  128. }
  129. ls->type = type;
  130. ls->sock = sock;
  131. ls->key = strdup(key);
  132. return 0;
  133. }
  134. }
  135. return -1;
  136. }
  137. static int fpm_sockets_new_listening_socket(struct fpm_worker_pool_s *wp, struct sockaddr *sa, int socklen)
  138. {
  139. int backlog = -1;
  140. int flags = 1;
  141. int sock;
  142. mode_t saved_umask;
  143. /* we have custom backlog value */
  144. if (wp->config->listen_options) {
  145. backlog = wp->config->listen_options->backlog;
  146. }
  147. sock = socket(sa->sa_family, SOCK_STREAM, 0);
  148. if (0 > sock) {
  149. zlog(ZLOG_STUFF, ZLOG_SYSERROR, "socket() failed");
  150. return -1;
  151. }
  152. setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
  153. if (wp->listen_address_domain == FPM_AF_UNIX) {
  154. unlink( ((struct sockaddr_un *) sa)->sun_path);
  155. }
  156. saved_umask = umask(0777 ^ wp->socket_mode);
  157. if (0 > bind(sock, sa, socklen)) {
  158. zlog(ZLOG_STUFF, ZLOG_SYSERROR, "bind() for address '%s' failed", wp->config->listen_address);
  159. return -1;
  160. }
  161. if (wp->listen_address_domain == FPM_AF_UNIX) {
  162. char *path = ((struct sockaddr_un *) sa)->sun_path;
  163. if (wp->socket_uid != -1 || wp->socket_gid != -1) {
  164. if (0 > chown(path, wp->socket_uid, wp->socket_gid)) {
  165. zlog(ZLOG_STUFF, ZLOG_SYSERROR, "chown() for address '%s' failed", wp->config->listen_address);
  166. return -1;
  167. }
  168. }
  169. }
  170. umask(saved_umask);
  171. if (0 > listen(sock, backlog)) {
  172. zlog(ZLOG_STUFF, ZLOG_SYSERROR, "listen() for address '%s' failed", wp->config->listen_address);
  173. return -1;
  174. }
  175. return sock;
  176. }
  177. static int fpm_sockets_get_listening_socket(struct fpm_worker_pool_s *wp, struct sockaddr *sa, int socklen)
  178. {
  179. int sock;
  180. sock = fpm_sockets_hash_op(0, sa, 0, wp->listen_address_domain, FPM_GET_USE_SOCKET);
  181. if (sock >= 0) { return sock; }
  182. sock = fpm_sockets_new_listening_socket(wp, sa, socklen);
  183. fpm_sockets_hash_op(sock, sa, 0, wp->listen_address_domain, FPM_STORE_USE_SOCKET);
  184. return sock;
  185. }
  186. enum fpm_address_domain fpm_sockets_domain_from_address(char *address)
  187. {
  188. if (strchr(address, ':')) { return FPM_AF_INET; }
  189. if (strlen(address) == strspn(address, "0123456789")) { return FPM_AF_INET; }
  190. return FPM_AF_UNIX;
  191. }
  192. static int fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s *wp)
  193. {
  194. struct sockaddr_in sa_in;
  195. char *dup_address = strdup(wp->config->listen_address);
  196. char *port_str = strchr(dup_address, ':');
  197. char *addr = NULL;
  198. int port = 0;
  199. if (port_str) { /* this is host:port pair */
  200. *port_str++ = '\0';
  201. port = atoi(port_str);
  202. addr = dup_address;
  203. }
  204. else if (strlen(dup_address) == strspn(dup_address, "0123456789")) { /* this is port */
  205. port = atoi(dup_address);
  206. port_str = dup_address;
  207. }
  208. if (port == 0) {
  209. zlog(ZLOG_STUFF, ZLOG_ERROR, "invalid port value '%s'", port_str);
  210. return -1;
  211. }
  212. memset(&sa_in, 0, sizeof(sa_in));
  213. if (addr) {
  214. sa_in.sin_addr.s_addr = inet_addr(addr);
  215. if (sa_in.sin_addr.s_addr == INADDR_NONE) { /* do resolve */
  216. if (0 > fpm_sockets_resolve_af_inet(addr, NULL, &sa_in)) {
  217. return -1;
  218. }
  219. zlog(ZLOG_STUFF, ZLOG_NOTICE, "address '%s' resolved as %u.%u.%u.%u", addr, IPQUAD(&sa_in.sin_addr));
  220. }
  221. }
  222. else {
  223. sa_in.sin_addr.s_addr = htonl(INADDR_ANY);
  224. }
  225. sa_in.sin_family = AF_INET;
  226. sa_in.sin_port = htons(port);
  227. free(dup_address);
  228. return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_in, sizeof(struct sockaddr_in));
  229. }
  230. static int fpm_socket_af_unix_listening_socket(struct fpm_worker_pool_s *wp)
  231. {
  232. struct sockaddr_un sa_un;
  233. memset(&sa_un, 0, sizeof(sa_un));
  234. cpystrn(sa_un.sun_path, wp->config->listen_address, sizeof(sa_un.sun_path));
  235. sa_un.sun_family = AF_UNIX;
  236. return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_un, sizeof(struct sockaddr_un));
  237. }
  238. int fpm_sockets_init_main()
  239. {
  240. int i;
  241. struct fpm_worker_pool_s *wp;
  242. char *inherited = getenv("FPM_SOCKETS");
  243. struct listening_socket_s *ls;
  244. if (0 == fpm_array_init(&sockets_list, sizeof(struct listening_socket_s), 10)) {
  245. return -1;
  246. }
  247. /* import inherited sockets */
  248. while (inherited && *inherited) {
  249. char *comma = strchr(inherited, ',');
  250. int type, fd_no;
  251. char *eq;
  252. if (comma) { *comma = '\0'; }
  253. eq = strchr(inherited, '=');
  254. if (eq) {
  255. *eq = '\0';
  256. fd_no = atoi(eq + 1);
  257. type = fpm_sockets_domain_from_address(inherited);
  258. zlog(ZLOG_STUFF, ZLOG_NOTICE, "using inherited socket fd=%d, \"%s\"", fd_no, inherited);
  259. fpm_sockets_hash_op(fd_no, 0, inherited, type, FPM_STORE_SOCKET);
  260. }
  261. if (comma) { inherited = comma + 1; }
  262. else { inherited = 0; }
  263. }
  264. /* create all required sockets */
  265. for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
  266. if (!wp->is_template) {
  267. switch (wp->listen_address_domain) {
  268. case FPM_AF_INET :
  269. wp->listening_socket = fpm_socket_af_inet_listening_socket(wp);
  270. break;
  271. case FPM_AF_UNIX :
  272. if (0 > fpm_unix_resolve_socket_premissions(wp)) {
  273. return -1;
  274. }
  275. wp->listening_socket = fpm_socket_af_unix_listening_socket(wp);
  276. break;
  277. }
  278. if (wp->listening_socket == -1) {
  279. return -1;
  280. }
  281. }
  282. }
  283. /* close unused sockets that was inherited */
  284. ls = sockets_list.data;
  285. for (i = 0; i < sockets_list.used; ) {
  286. if (ls->refcount == 0) {
  287. close(ls->sock);
  288. if (ls->type == FPM_AF_UNIX) {
  289. unlink(ls->key);
  290. }
  291. free(ls->key);
  292. fpm_array_item_remove(&sockets_list, i);
  293. }
  294. else {
  295. ++i;
  296. ++ls;
  297. }
  298. }
  299. if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_sockets_cleanup, 0)) {
  300. return -1;
  301. }
  302. return 0;
  303. }