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 #define MAX_ACCEPT_RETRY  10  // Test accept this many times
00036 
00037 namespace drizzled
00038 {
00039 extern back_log_constraints back_log;
00040 extern uint32_t drizzled_bind_timeout;
00041 
00042 
00043 int plugin::ListenTcp::acceptTcp(int fd)
00044 {
00045   int new_fd;
00046   uint32_t retry;
00047 
00048   for (retry= 0; retry < MAX_ACCEPT_RETRY; retry++)
00049   {
00050     new_fd= accept(fd, NULL, 0);
00051     if (new_fd != -1 || (errno != EINTR && errno != EAGAIN))
00052       break;
00053   }
00054 
00055   if (new_fd == -1)
00056   {
00057     if ((accept_error_count++ & 255) == 0)
00058     {
00059       sql_perror(_("accept() failed with errno %d"));
00060     }
00061 
00062     if (errno == ENFILE || errno == EMFILE)
00063       sleep(1);
00064 
00065     return -1;
00066   }
00067 
00068   return new_fd;
00069 }
00070 
00071 bool plugin::ListenTcp::getFileDescriptors(std::vector<int> &fds)
00072 {
00073   int ret;
00074   char host_buf[NI_MAXHOST];
00075   char port_buf[NI_MAXSERV];
00076   struct addrinfo hints;
00077   struct addrinfo *ai;
00078   struct addrinfo *ai_list;
00079   int fd= -1;
00080   uint32_t waited;
00081   uint32_t this_wait;
00082   uint32_t retry;
00083   struct linger ling= {0, 0};
00084   int flags= 1;
00085 
00086   memset(&hints, 0, sizeof(struct addrinfo));
00087   hints.ai_flags= AI_PASSIVE;
00088   hints.ai_socktype= SOCK_STREAM;
00089 
00090   snprintf(port_buf, NI_MAXSERV, "%d", getPort());
00091   ret= getaddrinfo(getHost().empty() ? NULL : getHost().c_str(), port_buf, &hints, &ai_list);
00092   if (ret != 0)
00093   {
00094     errmsg_printf(error::ERROR, _("getaddrinfo() failed with error %s"),
00095                   gai_strerror(ret));
00096     return true;
00097   }
00098 
00099   for (ai= ai_list; ai != NULL; ai= ai->ai_next)
00100   {
00101     ret= getnameinfo(ai->ai_addr, ai->ai_addrlen, host_buf, NI_MAXHOST,
00102                      port_buf, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV);
00103     if (ret != 0)
00104     {
00105       strcpy(host_buf, "-");
00106       strcpy(port_buf, "-");
00107     }
00108 
00109     fd= socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
00110     if (fd == -1)
00111     {
00112       /*
00113         Call to socket() can fail for some getaddrinfo results, try another.
00114       */
00115       continue;
00116     }
00117 
00118 #ifdef IPV6_V6ONLY
00119     if (ai->ai_family == AF_INET6)
00120     {
00121       flags= 1;
00122       ret= setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flags, sizeof(flags));
00123       if (ret != 0)
00124       {
00125         sql_perror(_("setsockopt(IPV6_V6ONLY)"));
00126         return true;
00127       }
00128     }
00129 #endif
00130 
00131     ret= fcntl(fd, F_SETFD, FD_CLOEXEC);
00132     if (ret != 0 || !(fcntl(fd, F_GETFD, 0) & FD_CLOEXEC))
00133     {
00134       sql_perror(_("fcntl(FD_CLOEXEC)"));
00135       return true;
00136     }
00137 
00138     ret= setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
00139     if (ret != 0)
00140     {
00141       sql_perror(_("setsockopt(SO_REUSEADDR)"));
00142       return true;
00143     }
00144 
00145     ret= setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags));
00146     if (ret != 0)
00147     {
00148       sql_perror(_("setsockopt(SO_KEEPALIVE)"));
00149       return true;
00150     }
00151 
00152     ret= setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
00153     if (ret != 0)
00154     {
00155       sql_perror(_("setsockopt(SO_LINGER)"));
00156       return true;
00157     }
00158 
00159     ret= setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(flags));
00160     if (ret != 0)
00161     {
00162       sql_perror(_("setsockopt(TCP_NODELAY)"));
00163       return true;
00164     }
00165 
00166     /*
00167       Sometimes the port is not released fast enough when stopping and
00168       restarting the server. This happens quite often with the test suite
00169       on busy Linux systems. Retry to bind the address at these intervals:
00170       Sleep intervals: 1, 2, 4,  6,  9, 13, 17, 22, ...
00171       Retry at second: 1, 3, 7, 13, 22, 35, 52, 74, ...
00172       Limit the sequence by drizzled_bind_timeout.
00173     */
00174     for (waited= 0, retry= 1; ; retry++, waited+= this_wait)
00175     {
00176       if (((ret= ::bind(fd, ai->ai_addr, ai->ai_addrlen)) == 0) ||
00177           (errno != EADDRINUSE) || (waited >= drizzled_bind_timeout))
00178       {
00179         break;
00180       }
00181 
00182       errmsg_printf(error::INFO, _("Retrying bind() on %u"), getPort());
00183       this_wait= retry * retry / 3 + 1;
00184       sleep(this_wait);
00185     }
00186 
00187     if (ret < 0)
00188     {
00189       std::string error_message;
00190 
00191       error_message+= host_buf;
00192       error_message+= ":";
00193       error_message+= port_buf;
00194       error_message+= _(" failed to bind");
00195       sql_perror(error_message);
00196 
00197       return true;
00198     }
00199 
00200     if (listen(fd, (int) back_log) < 0)
00201     {
00202       sql_perror("listen()");
00203       return true;
00204     }
00205 
00206     fds.push_back(fd);
00207 
00208     errmsg_printf(error::INFO, _("Listening on %s:%s"), host_buf, port_buf);
00209   }
00210 
00211   freeaddrinfo(ai_list);
00212 
00213   return false;
00214 }
00215 
00216 const std::string plugin::ListenTcp::getHost(void) const
00217 {
00218   return "";
00219 }
00220 
00221 } /* namespace drizzled */