Drizzled Public API Documentation

listen_tcp.cc
00001 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
00002  *  vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
00003  *
00004  *  Copyright (C) 2008 Sun Microsystems, Inc.
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; version 2 of the License.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00018  */
00019 
00020 #include <config.h>
00021 #include <drizzled/gettext.h>
00022 #include <drizzled/error.h>
00023 #include <drizzled/plugin/listen_tcp.h>
00024 #include <drizzled/errmsg_print.h>
00025 #include <drizzled/constrained_value.h>
00026 
00027 #include <cstdio>
00028 #include <unistd.h>
00029 #include <sys/socket.h>
00030 #include <fcntl.h>
00031 #include <netdb.h>
00032 #include <netinet/tcp.h>
00033 #include <cerrno>
00034 
00035 namespace drizzled {
00036 
00037 extern back_log_constraints back_log;
00038 extern uint32_t drizzled_bind_timeout;
00039 
00040 int plugin::ListenTcp::acceptTcp(int fd)
00041 {
00042   for (int retry= 0; retry < 10; retry++)
00043   {
00044     int new_fd= accept(fd, NULL, 0);
00045     if (new_fd != -1)
00046       return new_fd;
00047     if (errno != EINTR && errno != EAGAIN)
00048       break;
00049   }
00050   if ((accept_error_count++ & 255) == 0)
00051   {
00052     sql_perror(_("accept() failed with errno %d"));
00053   }
00054   if (errno == ENFILE || errno == EMFILE)
00055     sleep(1);
00056   return -1;
00057 }
00058 
00059 bool plugin::ListenTcp::getFileDescriptors(std::vector<int> &fds)
00060 {
00061   int ret;
00062   char host_buf[NI_MAXHOST];
00063   char port_buf[NI_MAXSERV];
00064   addrinfo hints;
00065   addrinfo *ai_list;
00066   uint32_t this_wait;
00067   int flags= 1;
00068 
00069   memset(&hints, 0, sizeof(struct addrinfo));
00070   hints.ai_flags= AI_PASSIVE;
00071   hints.ai_socktype= SOCK_STREAM;
00072 
00073   snprintf(port_buf, NI_MAXSERV, "%d", getPort());
00074   ret= getaddrinfo(getHost().empty() ? NULL : getHost().c_str(), port_buf, &hints, &ai_list);
00075   if (ret != 0)
00076   {
00077     errmsg_printf(error::ERROR, _("getaddrinfo() failed with error %s"),
00078                   gai_strerror(ret));
00079     return true;
00080   }
00081 
00082   for (addrinfo* ai= ai_list; ai != NULL; ai= ai->ai_next)
00083   {
00084     ret= getnameinfo(ai->ai_addr, ai->ai_addrlen, host_buf, NI_MAXHOST,
00085                      port_buf, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV);
00086     if (ret != 0)
00087     {
00088       strcpy(host_buf, "-");
00089       strcpy(port_buf, "-");
00090     }
00091 
00092     int fd= socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
00093     if (fd == -1)
00094     {
00095       /*
00096         Call to socket() can fail for some getaddrinfo results, try another.
00097       */
00098       continue;
00099     }
00100 
00101 #ifdef IPV6_V6ONLY
00102     if (ai->ai_family == AF_INET6)
00103     {
00104       flags= 1;
00105       ret= setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flags, sizeof(flags));
00106       if (ret != 0)
00107       {
00108         sql_perror(_("setsockopt(IPV6_V6ONLY)"));
00109         return true;
00110       }
00111     }
00112 #endif
00113 
00114     ret= fcntl(fd, F_SETFD, FD_CLOEXEC);
00115     if (ret != 0 || !(fcntl(fd, F_GETFD, 0) & FD_CLOEXEC))
00116     {
00117       sql_perror(_("fcntl(FD_CLOEXEC)"));
00118       return true;
00119     }
00120 
00121     ret= setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
00122     if (ret != 0)
00123     {
00124       sql_perror(_("setsockopt(SO_REUSEADDR)"));
00125       return true;
00126     }
00127 
00128     ret= setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags));
00129     if (ret != 0)
00130     {
00131       sql_perror(_("setsockopt(SO_KEEPALIVE)"));
00132       return true;
00133     }
00134 
00135     linger ling= {0, 0};
00136     ret= setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
00137     if (ret != 0)
00138     {
00139       sql_perror(_("setsockopt(SO_LINGER)"));
00140       return true;
00141     }
00142 
00143     ret= setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(flags));
00144     if (ret != 0)
00145     {
00146       sql_perror(_("setsockopt(TCP_NODELAY)"));
00147       return true;
00148     }
00149 
00150     /*
00151       Sometimes the port is not released fast enough when stopping and
00152       restarting the server. This happens quite often with the test suite
00153       on busy Linux systems. Retry to bind the address at these intervals:
00154       Sleep intervals: 1, 2, 4,  6,  9, 13, 17, 22, ...
00155       Retry at second: 1, 3, 7, 13, 22, 35, 52, 74, ...
00156       Limit the sequence by drizzled_bind_timeout.
00157     */
00158     for (uint32_t waited= 0, retry= 1; ; retry++, waited+= this_wait)
00159     {
00160       if (((ret= ::bind(fd, ai->ai_addr, ai->ai_addrlen)) == 0) ||
00161           (errno != EADDRINUSE) || (waited >= drizzled_bind_timeout))
00162       {
00163         break;
00164       }
00165 
00166       errmsg_printf(error::INFO, _("Retrying bind() on %u"), getPort());
00167       this_wait= retry * retry / 3 + 1;
00168       sleep(this_wait);
00169     }
00170 
00171     if (ret < 0)
00172     {
00173       std::string error_message;
00174 
00175       error_message+= host_buf;
00176       error_message+= ":";
00177       error_message+= port_buf;
00178       error_message+= _(" failed to bind");
00179       sql_perror(error_message);
00180 
00181       return true;
00182     }
00183 
00184     if (listen(fd, (int) back_log) < 0)
00185     {
00186       sql_perror("listen()");
00187       return true;
00188     }
00189 
00190     fds.push_back(fd);
00191 
00192     errmsg_printf(error::INFO, _("Listening on %s:%s"), host_buf, port_buf);
00193   }
00194 
00195   freeaddrinfo(ai_list);
00196 
00197   return false;
00198 }
00199 
00200 const std::string plugin::ListenTcp::getHost() const
00201 {
00202   return "";
00203 }
00204 
00205 } /* namespace drizzled */