root/sapi/fpm/fpm/fpm_sockets.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fpm_sockets_cleanup
  2. fpm_get_in_addr
  3. fpm_get_in_port
  4. fpm_sockets_hash_op
  5. fpm_sockets_new_listening_socket
  6. fpm_sockets_get_listening_socket
  7. fpm_sockets_domain_from_address
  8. fpm_socket_af_inet_listening_socket
  9. fpm_socket_af_unix_listening_socket
  10. fpm_sockets_init_main
  11. fpm_socket_get_listening_queue
  12. fpm_socket_get_listening_queue
  13. fpm_socket_get_listening_queue
  14. fpm_socket_unix_test_connect

   1 
   2         /* $Id: fpm_sockets.c,v 1.20.2.1 2008/12/13 03:21:18 anight Exp $ */
   3         /* (c) 2007,2008 Andrei Nigmatulin */
   4 
   5 #include "fpm_config.h"
   6 
   7 #ifdef HAVE_ALLOCA_H
   8 #include <alloca.h>
   9 #endif
  10 #include <sys/types.h>
  11 #include <sys/stat.h> /* for chmod(2) */
  12 #include <sys/socket.h>
  13 #include <netinet/in.h>
  14 #include <arpa/inet.h>
  15 #include <sys/un.h>
  16 #include <netdb.h>
  17 #include <stdio.h>
  18 #include <stdlib.h>
  19 #include <string.h>
  20 #include <errno.h>
  21 #include <unistd.h>
  22 
  23 #include "zlog.h"
  24 #include "fpm_arrays.h"
  25 #include "fpm_sockets.h"
  26 #include "fpm_worker_pool.h"
  27 #include "fpm_unix.h"
  28 #include "fpm_str.h"
  29 #include "fpm_env.h"
  30 #include "fpm_cleanup.h"
  31 #include "fpm_scoreboard.h"
  32 
  33 struct listening_socket_s {
  34         int refcount;
  35         int sock;
  36         int type;
  37         char *key;
  38 };
  39 
  40 static struct fpm_array_s sockets_list;
  41 
  42 enum { FPM_GET_USE_SOCKET = 1, FPM_STORE_SOCKET = 2, FPM_STORE_USE_SOCKET = 3 };
  43 
  44 static void fpm_sockets_cleanup(int which, void *arg) /* {{{ */
  45 {
  46         unsigned i;
  47         char *env_value = 0;
  48         int p = 0;
  49         struct listening_socket_s *ls = sockets_list.data;
  50 
  51         for (i = 0; i < sockets_list.used; i++, ls++) {
  52                 if (which != FPM_CLEANUP_PARENT_EXEC) {
  53                         close(ls->sock);
  54                 } else { /* on PARENT EXEC we want socket fds to be inherited through environment variable */
  55                         char fd[32];
  56                         sprintf(fd, "%d", ls->sock);
  57                         env_value = realloc(env_value, p + (p ? 1 : 0) + strlen(ls->key) + 1 + strlen(fd) + 1);
  58                         p += sprintf(env_value + p, "%s%s=%s", p ? "," : "", ls->key, fd);
  59                 }
  60 
  61                 if (which == FPM_CLEANUP_PARENT_EXIT_MAIN) {
  62                         if (ls->type == FPM_AF_UNIX) {
  63                                 unlink(ls->key);
  64                         }
  65                 }
  66                 free(ls->key);
  67         }
  68 
  69         if (env_value) {
  70                 setenv("FPM_SOCKETS", env_value, 1);
  71                 free(env_value);
  72         }
  73 
  74         fpm_array_free(&sockets_list);
  75 }
  76 /* }}} */
  77 
  78 static void *fpm_get_in_addr(struct sockaddr *sa) /* {{{ */
  79 {
  80     if (sa->sa_family == AF_INET) {
  81         return &(((struct sockaddr_in*)sa)->sin_addr);
  82     }
  83 
  84     return &(((struct sockaddr_in6*)sa)->sin6_addr);
  85 }
  86 /* }}} */
  87 
  88 static int fpm_get_in_port(struct sockaddr *sa) /* {{{ */
  89 {
  90     if (sa->sa_family == AF_INET) {
  91         return ntohs(((struct sockaddr_in*)sa)->sin_port);
  92     }
  93 
  94     return ntohs(((struct sockaddr_in6*)sa)->sin6_port);
  95 }
  96 /* }}} */
  97 
  98 static int fpm_sockets_hash_op(int sock, struct sockaddr *sa, char *key, int type, int op) /* {{{ */
  99 {
 100         if (key == NULL) {
 101                 switch (type) {
 102                         case FPM_AF_INET : {
 103                                 key = alloca(INET6_ADDRSTRLEN+10);
 104                                 inet_ntop(sa->sa_family, fpm_get_in_addr(sa), key, INET6_ADDRSTRLEN);
 105                                 sprintf(key+strlen(key), ":%d", fpm_get_in_port(sa));
 106                                 break;
 107                         }
 108 
 109                         case FPM_AF_UNIX : {
 110                                 struct sockaddr_un *sa_un = (struct sockaddr_un *) sa;
 111                                 key = alloca(strlen(sa_un->sun_path) + 1);
 112                                 strcpy(key, sa_un->sun_path);
 113                                 break;
 114                         }
 115 
 116                         default :
 117                                 return -1;
 118                 }
 119         }
 120 
 121         switch (op) {
 122 
 123                 case FPM_GET_USE_SOCKET :
 124                 {
 125                         unsigned i;
 126                         struct listening_socket_s *ls = sockets_list.data;
 127 
 128                         for (i = 0; i < sockets_list.used; i++, ls++) {
 129                                 if (!strcmp(ls->key, key)) {
 130                                         ++ls->refcount;
 131                                         return ls->sock;
 132                                 }
 133                         }
 134                         break;
 135                 }
 136 
 137                 case FPM_STORE_SOCKET :                 /* inherited socket */
 138                 case FPM_STORE_USE_SOCKET :             /* just created */
 139                 {
 140                         struct listening_socket_s *ls;
 141 
 142                         ls = fpm_array_push(&sockets_list);
 143                         if (!ls) {
 144                                 break;
 145                         }
 146 
 147                         if (op == FPM_STORE_SOCKET) {
 148                                 ls->refcount = 0;
 149                         } else {
 150                                 ls->refcount = 1;
 151                         }
 152                         ls->type = type;
 153                         ls->sock = sock;
 154                         ls->key = strdup(key);
 155 
 156                         return 0;
 157                 }
 158         }
 159         return -1;
 160 }
 161 /* }}} */
 162 
 163 static int fpm_sockets_new_listening_socket(struct fpm_worker_pool_s *wp, struct sockaddr *sa, int socklen) /* {{{ */
 164 {
 165         int flags = 1;
 166         int sock;
 167         mode_t saved_umask = 0;
 168 
 169         sock = socket(sa->sa_family, SOCK_STREAM, 0);
 170 
 171         if (0 > sock) {
 172                 zlog(ZLOG_SYSERROR, "failed to create new listening socket: socket()");
 173                 return -1;
 174         }
 175 
 176         if (0 > setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags))) {
 177                 zlog(ZLOG_WARNING, "failed to change socket attribute");
 178         }
 179 
 180         if (wp->listen_address_domain == FPM_AF_UNIX) {
 181                 if (fpm_socket_unix_test_connect((struct sockaddr_un *)sa, socklen) == 0) {
 182                         zlog(ZLOG_ERROR, "An another FPM instance seems to already listen on %s", ((struct sockaddr_un *) sa)->sun_path);
 183                         close(sock);
 184                         return -1;
 185                 }
 186                 unlink( ((struct sockaddr_un *) sa)->sun_path);
 187                 saved_umask = umask(0777 ^ wp->socket_mode);
 188         }
 189 
 190         if (0 > bind(sock, sa, socklen)) {
 191                 zlog(ZLOG_SYSERROR, "unable to bind listening socket for address '%s'", wp->config->listen_address);
 192                 if (wp->listen_address_domain == FPM_AF_UNIX) {
 193                         umask(saved_umask);
 194                 }
 195                 close(sock);
 196                 return -1;
 197         }
 198 
 199         if (wp->listen_address_domain == FPM_AF_UNIX) {
 200                 char *path = ((struct sockaddr_un *) sa)->sun_path;
 201 
 202                 umask(saved_umask);
 203 
 204                 if (0 > fpm_unix_set_socket_premissions(wp, path)) {
 205                         close(sock);
 206                         return -1;
 207                 }
 208         }
 209 
 210         if (0 > listen(sock, wp->config->listen_backlog)) {
 211                 zlog(ZLOG_SYSERROR, "failed to listen to address '%s'", wp->config->listen_address);
 212                 close(sock);
 213                 return -1;
 214         }
 215 
 216         return sock;
 217 }
 218 /* }}} */
 219 
 220 static int fpm_sockets_get_listening_socket(struct fpm_worker_pool_s *wp, struct sockaddr *sa, int socklen) /* {{{ */
 221 {
 222         int sock;
 223 
 224         sock = fpm_sockets_hash_op(0, sa, 0, wp->listen_address_domain, FPM_GET_USE_SOCKET);
 225         if (sock >= 0) {
 226                 return sock;
 227         }
 228 
 229         sock = fpm_sockets_new_listening_socket(wp, sa, socklen);
 230         fpm_sockets_hash_op(sock, sa, 0, wp->listen_address_domain, FPM_STORE_USE_SOCKET);
 231 
 232         return sock;
 233 }
 234 /* }}} */
 235 
 236 enum fpm_address_domain fpm_sockets_domain_from_address(char *address) /* {{{ */
 237 {
 238         if (strchr(address, ':')) {
 239                 return FPM_AF_INET;
 240         }
 241 
 242         if (strlen(address) == strspn(address, "0123456789")) {
 243                 return FPM_AF_INET;
 244         }
 245         return FPM_AF_UNIX;
 246 }
 247 /* }}} */
 248 
 249 static int fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s *wp) /* {{{ */
 250 {
 251         struct addrinfo hints, *servinfo, *p;
 252         char *dup_address = strdup(wp->config->listen_address);
 253         char *port_str = strrchr(dup_address, ':');
 254         char *addr = NULL;
 255         char tmpbuf[INET6_ADDRSTRLEN];
 256         int addr_len;
 257         int port = 0;
 258         int sock = -1;
 259         int status;
 260 
 261         if (port_str) { /* this is host:port pair */
 262                 *port_str++ = '\0';
 263                 port = atoi(port_str);
 264                 addr = dup_address;
 265         } else if (strlen(dup_address) == strspn(dup_address, "0123456789")) { /* this is port */
 266                 port = atoi(dup_address);
 267                 port_str = dup_address;
 268         }
 269 
 270         if (port == 0) {
 271                 zlog(ZLOG_ERROR, "invalid port value '%s'", port_str);
 272                 return -1;
 273         }
 274 
 275         if (!addr) {
 276                 /* no address: default documented behavior, all IPv4 addresses */
 277                 struct sockaddr_in sa_in;
 278 
 279                 memset(&sa_in, 0, sizeof(sa_in));
 280                 sa_in.sin_family = AF_INET;
 281                 sa_in.sin_port = htons(port);
 282                 sa_in.sin_addr.s_addr = htonl(INADDR_ANY);
 283                 free(dup_address);
 284                 return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_in, sizeof(struct sockaddr_in));
 285         }
 286 
 287         /* strip brackets from address for getaddrinfo */
 288         addr_len = strlen(addr);
 289         if (addr[0] == '[' && addr[addr_len - 1] == ']') {
 290                 addr[addr_len - 1] = '\0';
 291                 addr++;
 292         }
 293 
 294         memset(&hints, 0, sizeof hints);
 295         hints.ai_family = AF_UNSPEC;
 296         hints.ai_socktype = SOCK_STREAM;
 297 
 298         if ((status = getaddrinfo(addr, port_str, &hints, &servinfo)) != 0) {
 299                 zlog(ZLOG_ERROR, "getaddrinfo: %s\n", gai_strerror(status));
 300                 free(dup_address);
 301                 return -1;
 302         }
 303 
 304         for (p = servinfo; p != NULL; p = p->ai_next) {
 305                 inet_ntop(p->ai_family, fpm_get_in_addr(p->ai_addr), tmpbuf, INET6_ADDRSTRLEN);
 306                 if (sock < 0) {
 307                         if ((sock = fpm_sockets_get_listening_socket(wp, p->ai_addr, p->ai_addrlen)) != -1) {
 308                                 zlog(ZLOG_DEBUG, "Found address for %s, socket opened on %s", dup_address, tmpbuf);
 309                         }
 310                 } else {
 311                         zlog(ZLOG_WARNING, "Found multiple addresses for %s, %s ignored", dup_address, tmpbuf);
 312                 }
 313         }
 314 
 315         free(dup_address);
 316         freeaddrinfo(servinfo);
 317 
 318         return sock;
 319 }
 320 /* }}} */
 321 
 322 static int fpm_socket_af_unix_listening_socket(struct fpm_worker_pool_s *wp) /* {{{ */
 323 {
 324         struct sockaddr_un sa_un;
 325 
 326         memset(&sa_un, 0, sizeof(sa_un));
 327         strlcpy(sa_un.sun_path, wp->config->listen_address, sizeof(sa_un.sun_path));
 328         sa_un.sun_family = AF_UNIX;
 329         return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_un, sizeof(struct sockaddr_un));
 330 }
 331 /* }}} */
 332 
 333 int fpm_sockets_init_main() /* {{{ */
 334 {
 335         unsigned i, lq_len;
 336         struct fpm_worker_pool_s *wp;
 337         char *inherited = getenv("FPM_SOCKETS");
 338         struct listening_socket_s *ls;
 339 
 340         if (0 == fpm_array_init(&sockets_list, sizeof(struct listening_socket_s), 10)) {
 341                 return -1;
 342         }
 343 
 344         /* import inherited sockets */
 345         while (inherited && *inherited) {
 346                 char *comma = strchr(inherited, ',');
 347                 int type, fd_no;
 348                 char *eq;
 349 
 350                 if (comma) {
 351                         *comma = '\0';
 352                 }
 353 
 354                 eq = strchr(inherited, '=');
 355                 if (eq) {
 356                         *eq = '\0';
 357                         fd_no = atoi(eq + 1);
 358                         type = fpm_sockets_domain_from_address(inherited);
 359                         zlog(ZLOG_NOTICE, "using inherited socket fd=%d, \"%s\"", fd_no, inherited);
 360                         fpm_sockets_hash_op(fd_no, 0, inherited, type, FPM_STORE_SOCKET);
 361                 }
 362 
 363                 if (comma) {
 364                         inherited = comma + 1;
 365                 } else {
 366                         inherited = 0;
 367                 }
 368         }
 369 
 370         /* create all required sockets */
 371         for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
 372                 switch (wp->listen_address_domain) {
 373                         case FPM_AF_INET :
 374                                 wp->listening_socket = fpm_socket_af_inet_listening_socket(wp);
 375                                 break;
 376 
 377                         case FPM_AF_UNIX :
 378                                 if (0 > fpm_unix_resolve_socket_premissions(wp)) {
 379                                         return -1;
 380                                 }
 381                                 wp->listening_socket = fpm_socket_af_unix_listening_socket(wp);
 382                                 break;
 383                 }
 384 
 385                 if (wp->listening_socket == -1) {
 386                         return -1;
 387                 }
 388 
 389         if (wp->listen_address_domain == FPM_AF_INET && fpm_socket_get_listening_queue(wp->listening_socket, NULL, &lq_len) >= 0) {
 390                         fpm_scoreboard_update(-1, -1, -1, (int)lq_len, -1, -1, 0, FPM_SCOREBOARD_ACTION_SET, wp->scoreboard);
 391                 }
 392         }
 393 
 394         /* close unused sockets that was inherited */
 395         ls = sockets_list.data;
 396 
 397         for (i = 0; i < sockets_list.used; ) {
 398                 if (ls->refcount == 0) {
 399                         close(ls->sock);
 400                         if (ls->type == FPM_AF_UNIX) {
 401                                 unlink(ls->key);
 402                         }
 403                         free(ls->key);
 404                         fpm_array_item_remove(&sockets_list, i);
 405                 } else {
 406                         ++i;
 407                         ++ls;
 408                 }
 409         }
 410 
 411         if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_sockets_cleanup, 0)) {
 412                 return -1;
 413         }
 414         return 0;
 415 }
 416 /* }}} */
 417 
 418 #if HAVE_FPM_LQ
 419 
 420 #ifdef HAVE_LQ_TCP_INFO
 421 
 422 #include <netinet/tcp.h>
 423 
 424 int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
 425 {
 426         struct tcp_info info;
 427         socklen_t len = sizeof(info);
 428 
 429         if (0 > getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, &len)) {
 430                 zlog(ZLOG_SYSERROR, "failed to retrieve TCP_INFO for socket");
 431                 return -1;
 432         }
 433 #if defined(__FreeBSD__)
 434         if (info.__tcpi_sacked == 0) {
 435                 return -1;
 436         }
 437 
 438         if (cur_lq) {
 439                 *cur_lq = info.__tcpi_unacked;
 440         }
 441 
 442         if (max_lq) {
 443                 *max_lq = info.__tcpi_sacked;
 444         }
 445 #else
 446         /* kernel >= 2.6.24 return non-zero here, that means operation is supported */
 447         if (info.tcpi_sacked == 0) {
 448                 return -1;
 449         }
 450 
 451         if (cur_lq) {
 452                 *cur_lq = info.tcpi_unacked;
 453         }
 454 
 455         if (max_lq) {
 456                 *max_lq = info.tcpi_sacked;
 457         }
 458 #endif
 459 
 460         return 0;
 461 }
 462 
 463 #endif
 464 
 465 #ifdef HAVE_LQ_SO_LISTENQ
 466 
 467 int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
 468 {
 469         int val;
 470         socklen_t len = sizeof(val);
 471 
 472         if (cur_lq) {
 473                 if (0 > getsockopt(sock, SOL_SOCKET, SO_LISTENQLEN, &val, &len)) {
 474                         return -1;
 475                 }
 476 
 477                 *cur_lq = val;
 478         }
 479 
 480         if (max_lq) {
 481                 if (0 > getsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &val, &len)) {
 482                         return -1;
 483                 }
 484 
 485                 *max_lq = val;
 486         }
 487 
 488         return 0;
 489 }
 490 
 491 #endif
 492 
 493 #else
 494 
 495 int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
 496 {
 497         return -1;
 498 }
 499 
 500 #endif
 501 
 502 int fpm_socket_unix_test_connect(struct sockaddr_un *sock, size_t socklen) /* {{{ */
 503 {
 504         int fd;
 505 
 506         if (!sock || sock->sun_family != AF_UNIX) {
 507                 return -1;
 508         }
 509 
 510         if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
 511                 return -1;
 512         }
 513 
 514         if (connect(fd, (struct sockaddr *)sock, socklen) == -1) {
 515                 close(fd);
 516                 return -1;
 517         }
 518 
 519         close(fd);
 520         return 0;
 521 }
 522 /* }}} */

/* [<][>][^][v][top][bottom][index][help] */