Back to index

php5  5.3.10
fpm_sockets.c
Go to the documentation of this file.
00001 
00002        /* $Id: fpm_sockets.c,v 1.20.2.1 2008/12/13 03:21:18 anight Exp $ */
00003        /* (c) 2007,2008 Andrei Nigmatulin */
00004 
00005 #include "fpm_config.h"
00006 
00007 #ifdef HAVE_ALLOCA_H
00008 #include <alloca.h>
00009 #endif
00010 #include <sys/types.h>
00011 #include <sys/stat.h> /* for chmod(2) */
00012 #include <sys/socket.h>
00013 #include <netinet/in.h>
00014 #include <arpa/inet.h>
00015 #include <sys/un.h>
00016 #include <netdb.h>
00017 #include <stdio.h>
00018 #include <stdlib.h>
00019 #include <string.h>
00020 #include <errno.h>
00021 #include <unistd.h>
00022 
00023 #include "zlog.h"
00024 #include "fpm_arrays.h"
00025 #include "fpm_sockets.h"
00026 #include "fpm_worker_pool.h"
00027 #include "fpm_unix.h"
00028 #include "fpm_str.h"
00029 #include "fpm_env.h"
00030 #include "fpm_cleanup.h"
00031 #include "fpm_scoreboard.h"
00032 
00033 struct listening_socket_s {
00034        int refcount;
00035        int sock;
00036        int type;
00037        char *key;
00038 };
00039 
00040 static struct fpm_array_s sockets_list;
00041 
00042 static int fpm_sockets_resolve_af_inet(char *node, char *service, struct sockaddr_in *addr) /* {{{ */
00043 {
00044        struct addrinfo *res;
00045        struct addrinfo hints;
00046        int ret;
00047 
00048        memset(&hints, 0, sizeof(hints));
00049        hints.ai_family = AF_INET;
00050        ret = getaddrinfo(node, service, &hints, &res);
00051 
00052        if (ret != 0) {
00053               zlog(ZLOG_ERROR, "can't resolve hostname '%s%s%s': getaddrinfo said: %s%s%s\n",
00054                                    node, service ? ":" : "", service ? service : "",
00055                                    gai_strerror(ret), ret == EAI_SYSTEM ? ", system error: " : "", ret == EAI_SYSTEM ? strerror(errno) : "");
00056               return -1;
00057        }
00058 
00059        *addr = *(struct sockaddr_in *) res->ai_addr;
00060        freeaddrinfo(res);
00061        return 0;
00062 }
00063 /* }}} */
00064 
00065 enum { FPM_GET_USE_SOCKET = 1, FPM_STORE_SOCKET = 2, FPM_STORE_USE_SOCKET = 3 };
00066 
00067 static void fpm_sockets_cleanup(int which, void *arg) /* {{{ */
00068 {
00069        unsigned i;
00070        char *env_value = 0;
00071        int p = 0;
00072        struct listening_socket_s *ls = sockets_list.data;
00073 
00074        for (i = 0; i < sockets_list.used; i++, ls++) {
00075               if (which != FPM_CLEANUP_PARENT_EXEC) {
00076                      close(ls->sock);
00077               } else { /* on PARENT EXEC we want socket fds to be inherited through environment variable */
00078                      char fd[32];
00079                      sprintf(fd, "%d", ls->sock);
00080                      env_value = realloc(env_value, p + (p ? 1 : 0) + strlen(ls->key) + 1 + strlen(fd) + 1);
00081                      p += sprintf(env_value + p, "%s%s=%s", p ? "," : "", ls->key, fd);
00082               }
00083 
00084               if (which == FPM_CLEANUP_PARENT_EXIT_MAIN) {
00085                      if (ls->type == FPM_AF_UNIX) {
00086                             unlink(ls->key);
00087                      }
00088               }
00089               free(ls->key);
00090        }
00091 
00092        if (env_value) {
00093               setenv("FPM_SOCKETS", env_value, 1);
00094               free(env_value);
00095        }
00096 
00097        fpm_array_free(&sockets_list);
00098 }
00099 /* }}} */
00100 
00101 static int fpm_sockets_hash_op(int sock, struct sockaddr *sa, char *key, int type, int op) /* {{{ */
00102 {
00103        if (key == NULL) {
00104               switch (type) {
00105                      case FPM_AF_INET : {
00106                             struct sockaddr_in *sa_in = (struct sockaddr_in *) sa;
00107                             key = alloca(sizeof("xxx.xxx.xxx.xxx:ppppp"));
00108                             sprintf(key, "%u.%u.%u.%u:%u", IPQUAD(&sa_in->sin_addr), (unsigned int) ntohs(sa_in->sin_port));
00109                             break;
00110                      }
00111 
00112                      case FPM_AF_UNIX : {
00113                             struct sockaddr_un *sa_un = (struct sockaddr_un *) sa;
00114                             key = alloca(strlen(sa_un->sun_path) + 1);
00115                             strcpy(key, sa_un->sun_path);
00116                             break;
00117                      }
00118 
00119                      default :
00120                             return -1;
00121               }
00122        }
00123 
00124        switch (op) {
00125 
00126               case FPM_GET_USE_SOCKET :
00127               {
00128                      unsigned i;
00129                      struct listening_socket_s *ls = sockets_list.data;
00130 
00131                      for (i = 0; i < sockets_list.used; i++, ls++) {
00132                             if (!strcmp(ls->key, key)) {
00133                                    ++ls->refcount;
00134                                    return ls->sock;
00135                             }
00136                      }
00137                      break;
00138               }
00139 
00140               case FPM_STORE_SOCKET :                   /* inherited socket */
00141               case FPM_STORE_USE_SOCKET :        /* just created */
00142               {
00143                      struct listening_socket_s *ls;
00144 
00145                      ls = fpm_array_push(&sockets_list);
00146                      if (!ls) {
00147                             break;
00148                      }
00149 
00150                      if (op == FPM_STORE_SOCKET) {
00151                             ls->refcount = 0;
00152                      } else {
00153                             ls->refcount = 1;
00154                      }
00155                      ls->type = type;
00156                      ls->sock = sock;
00157                      ls->key = strdup(key);
00158 
00159                      return 0;
00160               }
00161        }
00162        return -1;
00163 }
00164 /* }}} */
00165 
00166 static int fpm_sockets_new_listening_socket(struct fpm_worker_pool_s *wp, struct sockaddr *sa, int socklen) /* {{{ */
00167 {
00168        int flags = 1;
00169        int sock;
00170        mode_t saved_umask;
00171 
00172        sock = socket(sa->sa_family, SOCK_STREAM, 0);
00173 
00174        if (0 > sock) {
00175               zlog(ZLOG_SYSERROR, "failed to create new listening socket: socket()");
00176               return -1;
00177        }
00178 
00179        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
00180 
00181        if (wp->listen_address_domain == FPM_AF_UNIX) {
00182               unlink( ((struct sockaddr_un *) sa)->sun_path);
00183               saved_umask = umask(0777 ^ wp->socket_mode);
00184        }
00185 
00186        if (0 > bind(sock, sa, socklen)) {
00187               zlog(ZLOG_SYSERROR, "unable to bind listening socket for address '%s'", wp->config->listen_address);
00188               if (wp->listen_address_domain == FPM_AF_UNIX) {
00189                      umask(saved_umask);
00190               }
00191               return -1;
00192        }
00193 
00194        if (wp->listen_address_domain == FPM_AF_UNIX) {
00195               char *path = ((struct sockaddr_un *) sa)->sun_path;
00196 
00197               umask(saved_umask);
00198 
00199               if (wp->socket_uid != -1 || wp->socket_gid != -1) {
00200                      if (0 > chown(path, wp->socket_uid, wp->socket_gid)) {
00201                             zlog(ZLOG_SYSERROR, "failed to chown() the socket '%s'", wp->config->listen_address);
00202                             return -1;
00203                      }
00204               }
00205        }
00206 
00207        if (0 > listen(sock, wp->config->listen_backlog)) {
00208               zlog(ZLOG_SYSERROR, "failed to listen to address '%s'", wp->config->listen_address);
00209               return -1;
00210        }
00211 
00212        return sock;
00213 }
00214 /* }}} */
00215 
00216 static int fpm_sockets_get_listening_socket(struct fpm_worker_pool_s *wp, struct sockaddr *sa, int socklen) /* {{{ */
00217 {
00218        int sock;
00219 
00220        sock = fpm_sockets_hash_op(0, sa, 0, wp->listen_address_domain, FPM_GET_USE_SOCKET);
00221        if (sock >= 0) {
00222               return sock;
00223        }
00224 
00225        sock = fpm_sockets_new_listening_socket(wp, sa, socklen);
00226        fpm_sockets_hash_op(sock, sa, 0, wp->listen_address_domain, FPM_STORE_USE_SOCKET);
00227 
00228        return sock;
00229 }
00230 /* }}} */
00231 
00232 enum fpm_address_domain fpm_sockets_domain_from_address(char *address) /* {{{ */
00233 {
00234        if (strchr(address, ':')) {
00235               return FPM_AF_INET;
00236        }
00237 
00238        if (strlen(address) == strspn(address, "0123456789")) {
00239               return FPM_AF_INET;
00240        }
00241        return FPM_AF_UNIX;
00242 }
00243 /* }}} */
00244 
00245 static int fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s *wp) /* {{{ */
00246 {
00247        struct sockaddr_in sa_in;
00248        char *dup_address = strdup(wp->config->listen_address);
00249        char *port_str = strchr(dup_address, ':');
00250        char *addr = NULL;
00251        int port = 0;
00252 
00253        if (port_str) { /* this is host:port pair */
00254               *port_str++ = '\0';
00255               port = atoi(port_str);
00256               addr = dup_address;
00257        } else if (strlen(dup_address) == strspn(dup_address, "0123456789")) { /* this is port */
00258               port = atoi(dup_address);
00259               port_str = dup_address;
00260        }
00261 
00262        if (port == 0) {
00263               zlog(ZLOG_ERROR, "invalid port value '%s'", port_str);
00264               return -1;
00265        }
00266 
00267        memset(&sa_in, 0, sizeof(sa_in));
00268 
00269        if (addr) {
00270               sa_in.sin_addr.s_addr = inet_addr(addr);
00271               if (sa_in.sin_addr.s_addr == INADDR_NONE) { /* do resolve */
00272                      if (0 > fpm_sockets_resolve_af_inet(addr, NULL, &sa_in)) {
00273                             return -1;
00274                      }
00275                      zlog(ZLOG_NOTICE, "address '%s' resolved as %u.%u.%u.%u", addr, IPQUAD(&sa_in.sin_addr));
00276               }
00277        } else {
00278               sa_in.sin_addr.s_addr = htonl(INADDR_ANY);
00279        }
00280        sa_in.sin_family = AF_INET;
00281        sa_in.sin_port = htons(port);
00282        free(dup_address);
00283        return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_in, sizeof(struct sockaddr_in));
00284 }
00285 /* }}} */
00286 
00287 static int fpm_socket_af_unix_listening_socket(struct fpm_worker_pool_s *wp) /* {{{ */
00288 {
00289        struct sockaddr_un sa_un;
00290 
00291        memset(&sa_un, 0, sizeof(sa_un));
00292        strlcpy(sa_un.sun_path, wp->config->listen_address, sizeof(sa_un.sun_path));
00293        sa_un.sun_family = AF_UNIX;
00294        return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_un, sizeof(struct sockaddr_un));
00295 }
00296 /* }}} */
00297 
00298 int fpm_sockets_init_main() /* {{{ */
00299 {
00300        unsigned i, lq_len;
00301        struct fpm_worker_pool_s *wp;
00302        char *inherited = getenv("FPM_SOCKETS");
00303        struct listening_socket_s *ls;
00304 
00305        if (0 == fpm_array_init(&sockets_list, sizeof(struct listening_socket_s), 10)) {
00306               return -1;
00307        }
00308 
00309        /* import inherited sockets */
00310        while (inherited && *inherited) {
00311               char *comma = strchr(inherited, ',');
00312               int type, fd_no;
00313               char *eq;
00314 
00315               if (comma) {
00316                      *comma = '\0';
00317               }
00318 
00319               eq = strchr(inherited, '=');
00320               if (eq) {
00321                      *eq = '\0';
00322                      fd_no = atoi(eq + 1);
00323                      type = fpm_sockets_domain_from_address(inherited);
00324                      zlog(ZLOG_NOTICE, "using inherited socket fd=%d, \"%s\"", fd_no, inherited);
00325                      fpm_sockets_hash_op(fd_no, 0, inherited, type, FPM_STORE_SOCKET);
00326               }
00327 
00328               if (comma) {
00329                      inherited = comma + 1;
00330               } else {
00331                      inherited = 0;
00332               }
00333        }
00334 
00335        /* create all required sockets */
00336        for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
00337               switch (wp->listen_address_domain) {
00338                      case FPM_AF_INET :
00339                             wp->listening_socket = fpm_socket_af_inet_listening_socket(wp);
00340                             break;
00341 
00342                      case FPM_AF_UNIX :
00343                             if (0 > fpm_unix_resolve_socket_premissions(wp)) {
00344                                    return -1;
00345                             }
00346                             wp->listening_socket = fpm_socket_af_unix_listening_socket(wp);
00347                             break;
00348               }
00349 
00350               if (wp->listening_socket == -1) {
00351                      return -1;
00352               }
00353 
00354        if (wp->listen_address_domain == FPM_AF_INET && fpm_socket_get_listening_queue(wp->listening_socket, NULL, &lq_len) >= 0) {
00355                      fpm_scoreboard_update(-1, -1, -1, (int)lq_len, -1, -1, FPM_SCOREBOARD_ACTION_SET, wp->scoreboard);
00356               }
00357        }
00358 
00359        /* close unused sockets that was inherited */
00360        ls = sockets_list.data;
00361 
00362        for (i = 0; i < sockets_list.used; ) {
00363               if (ls->refcount == 0) {
00364                      close(ls->sock);
00365                      if (ls->type == FPM_AF_UNIX) {
00366                             unlink(ls->key);
00367                      }
00368                      free(ls->key);
00369                      fpm_array_item_remove(&sockets_list, i);
00370               } else {
00371                      ++i;
00372                      ++ls;
00373               }
00374        }
00375 
00376        if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_sockets_cleanup, 0)) {
00377               return -1;
00378        }
00379        return 0;
00380 }
00381 /* }}} */
00382 
00383 #if HAVE_FPM_LQ
00384 
00385 #ifdef HAVE_LQ_TCP_INFO
00386 
00387 #include <netinet/tcp.h>
00388 
00389 int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
00390 {
00391        struct tcp_info info;
00392        socklen_t len = sizeof(info);
00393 
00394        if (0 > getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, &len)) {
00395               zlog(ZLOG_SYSERROR, "failed to retrieve TCP_INFO for socket");
00396               return -1;
00397        }
00398 
00399        /* kernel >= 2.6.24 return non-zero here, that means operation is supported */
00400        if (info.tcpi_sacked == 0) {
00401               return -1;
00402        }
00403 
00404        if (cur_lq) {
00405               *cur_lq = info.tcpi_unacked;
00406        }
00407 
00408        if (max_lq) {
00409               *max_lq = info.tcpi_sacked;
00410        }
00411 
00412        return 0;
00413 }
00414 
00415 #endif
00416 
00417 #ifdef HAVE_LQ_SO_LISTENQ
00418 
00419 int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
00420 {
00421        int val;
00422        socklen_t len = sizeof(val);
00423 
00424        if (cur_lq) {
00425               if (0 > getsockopt(sock, SOL_SOCKET, SO_LISTENQLEN, &val, &len)) {
00426                      return -1;
00427               }
00428 
00429               *cur_lq = val;
00430        }
00431 
00432        if (max_lq) {
00433               if (0 > getsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &val, &len)) {
00434                      return -1;
00435               }
00436 
00437               *max_lq = val;
00438        }
00439 
00440        return 0;
00441 }
00442 
00443 #endif
00444 
00445 #else
00446 
00447 int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
00448 {
00449        return -1;
00450 }
00451 
00452 #endif