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                 /* IPv6 catch-all + IPv4-mapped */
 269                 addr = "::";
 270         }
 271 
 272         if (port == 0) {
 273                 zlog(ZLOG_ERROR, "invalid port value '%s'", port_str);
 274                 return -1;
 275         }
 276 
 277         /* strip brackets from address for getaddrinfo */
 278         addr_len = strlen(addr);
 279         if (addr[0] == '[' && addr[addr_len - 1] == ']') {
 280                 addr[addr_len - 1] = '\0';
 281                 addr++;
 282         }
 283 
 284         memset(&hints, 0, sizeof hints);
 285         hints.ai_family = AF_UNSPEC;
 286         hints.ai_socktype = SOCK_STREAM;
 287 
 288         if ((status = getaddrinfo(addr, port_str, &hints, &servinfo)) != 0) {
 289                 zlog(ZLOG_ERROR, "getaddrinfo: %s\n", gai_strerror(status));
 290                 free(dup_address);
 291                 return -1;
 292         }
 293 
 294         for (p = servinfo; p != NULL; p = p->ai_next) {
 295                 inet_ntop(p->ai_family, fpm_get_in_addr(p->ai_addr), tmpbuf, INET6_ADDRSTRLEN);
 296                 if (sock < 0) {
 297                         if ((sock = fpm_sockets_get_listening_socket(wp, p->ai_addr, p->ai_addrlen)) != -1) {
 298                                 zlog(ZLOG_DEBUG, "Found address for %s, socket opened on %s", addr, tmpbuf);
 299                         }
 300                 } else {
 301                         zlog(ZLOG_WARNING, "Found multiple addresses for %s, %s ignored", addr, tmpbuf);
 302                 }
 303         }
 304 
 305         free(dup_address);
 306         freeaddrinfo(servinfo);
 307 
 308         return sock;
 309 }
 310 /* }}} */
 311 
 312 static int fpm_socket_af_unix_listening_socket(struct fpm_worker_pool_s *wp) /* {{{ */
 313 {
 314         struct sockaddr_un sa_un;
 315 
 316         memset(&sa_un, 0, sizeof(sa_un));
 317         strlcpy(sa_un.sun_path, wp->config->listen_address, sizeof(sa_un.sun_path));
 318         sa_un.sun_family = AF_UNIX;
 319         return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_un, sizeof(struct sockaddr_un));
 320 }
 321 /* }}} */
 322 
 323 int fpm_sockets_init_main() /* {{{ */
 324 {
 325         unsigned i, lq_len;
 326         struct fpm_worker_pool_s *wp;
 327         char *inherited = getenv("FPM_SOCKETS");
 328         struct listening_socket_s *ls;
 329 
 330         if (0 == fpm_array_init(&sockets_list, sizeof(struct listening_socket_s), 10)) {
 331                 return -1;
 332         }
 333 
 334         /* import inherited sockets */
 335         while (inherited && *inherited) {
 336                 char *comma = strchr(inherited, ',');
 337                 int type, fd_no;
 338                 char *eq;
 339 
 340                 if (comma) {
 341                         *comma = '\0';
 342                 }
 343 
 344                 eq = strchr(inherited, '=');
 345                 if (eq) {
 346                         *eq = '\0';
 347                         fd_no = atoi(eq + 1);
 348                         type = fpm_sockets_domain_from_address(inherited);
 349                         zlog(ZLOG_NOTICE, "using inherited socket fd=%d, \"%s\"", fd_no, inherited);
 350                         fpm_sockets_hash_op(fd_no, 0, inherited, type, FPM_STORE_SOCKET);
 351                 }
 352 
 353                 if (comma) {
 354                         inherited = comma + 1;
 355                 } else {
 356                         inherited = 0;
 357                 }
 358         }
 359 
 360         /* create all required sockets */
 361         for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
 362                 switch (wp->listen_address_domain) {
 363                         case FPM_AF_INET :
 364                                 wp->listening_socket = fpm_socket_af_inet_listening_socket(wp);
 365                                 break;
 366 
 367                         case FPM_AF_UNIX :
 368                                 if (0 > fpm_unix_resolve_socket_premissions(wp)) {
 369                                         return -1;
 370                                 }
 371                                 wp->listening_socket = fpm_socket_af_unix_listening_socket(wp);
 372                                 break;
 373                 }
 374 
 375                 if (wp->listening_socket == -1) {
 376                         return -1;
 377                 }
 378 
 379         if (wp->listen_address_domain == FPM_AF_INET && fpm_socket_get_listening_queue(wp->listening_socket, NULL, &lq_len) >= 0) {
 380                         fpm_scoreboard_update(-1, -1, -1, (int)lq_len, -1, -1, 0, FPM_SCOREBOARD_ACTION_SET, wp->scoreboard);
 381                 }
 382         }
 383 
 384         /* close unused sockets that was inherited */
 385         ls = sockets_list.data;
 386 
 387         for (i = 0; i < sockets_list.used; ) {
 388                 if (ls->refcount == 0) {
 389                         close(ls->sock);
 390                         if (ls->type == FPM_AF_UNIX) {
 391                                 unlink(ls->key);
 392                         }
 393                         free(ls->key);
 394                         fpm_array_item_remove(&sockets_list, i);
 395                 } else {
 396                         ++i;
 397                         ++ls;
 398                 }
 399         }
 400 
 401         if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_sockets_cleanup, 0)) {
 402                 return -1;
 403         }
 404         return 0;
 405 }
 406 /* }}} */
 407 
 408 #if HAVE_FPM_LQ
 409 
 410 #ifdef HAVE_LQ_TCP_INFO
 411 
 412 #include <netinet/tcp.h>
 413 
 414 int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
 415 {
 416         struct tcp_info info;
 417         socklen_t len = sizeof(info);
 418 
 419         if (0 > getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, &len)) {
 420                 zlog(ZLOG_SYSERROR, "failed to retrieve TCP_INFO for socket");
 421                 return -1;
 422         }
 423 #if defined(__FreeBSD__) || defined(__NetBSD__)
 424         if (info.__tcpi_sacked == 0) {
 425                 return -1;
 426         }
 427 
 428         if (cur_lq) {
 429                 *cur_lq = info.__tcpi_unacked;
 430         }
 431 
 432         if (max_lq) {
 433                 *max_lq = info.__tcpi_sacked;
 434         }
 435 #else
 436         /* kernel >= 2.6.24 return non-zero here, that means operation is supported */
 437         if (info.tcpi_sacked == 0) {
 438                 return -1;
 439         }
 440 
 441         if (cur_lq) {
 442                 *cur_lq = info.tcpi_unacked;
 443         }
 444 
 445         if (max_lq) {
 446                 *max_lq = info.tcpi_sacked;
 447         }
 448 #endif
 449 
 450         return 0;
 451 }
 452 
 453 #endif
 454 
 455 #ifdef HAVE_LQ_SO_LISTENQ
 456 
 457 int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
 458 {
 459         int val;
 460         socklen_t len = sizeof(val);
 461 
 462         if (cur_lq) {
 463                 if (0 > getsockopt(sock, SOL_SOCKET, SO_LISTENQLEN, &val, &len)) {
 464                         return -1;
 465                 }
 466 
 467                 *cur_lq = val;
 468         }
 469 
 470         if (max_lq) {
 471                 if (0 > getsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &val, &len)) {
 472                         return -1;
 473                 }
 474 
 475                 *max_lq = val;
 476         }
 477 
 478         return 0;
 479 }
 480 
 481 #endif
 482 
 483 #else
 484 
 485 int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
 486 {
 487         return -1;
 488 }
 489 
 490 #endif
 491 
 492 int fpm_socket_unix_test_connect(struct sockaddr_un *sock, size_t socklen) /* {{{ */
 493 {
 494         int fd;
 495 
 496         if (!sock || sock->sun_family != AF_UNIX) {
 497                 return -1;
 498         }
 499 
 500         if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
 501                 return -1;
 502         }
 503 
 504         if (connect(fd, (struct sockaddr *)sock, socklen) == -1) {
 505                 close(fd);
 506                 return -1;
 507         }
 508 
 509         close(fd);
 510         return 0;
 511 }
 512 /* }}} */

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