Drizzled Public API Documentation

listen_tcp.cc
1 /* -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
3  *
4  * Copyright (C) 2008 Sun Microsystems, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18  */
19 
20 #include <config.h>
21 #include <drizzled/gettext.h>
22 #include <drizzled/error.h>
23 #include <drizzled/plugin/listen_tcp.h>
24 #include <drizzled/errmsg_print.h>
25 #include <drizzled/constrained_value.h>
26 
27 #include <cstdio>
28 #include <unistd.h>
29 #include <sys/socket.h>
30 #include <fcntl.h>
31 #include <netdb.h>
32 #include <netinet/tcp.h>
33 #include <cerrno>
34 
35 namespace drizzled {
36 
37 extern back_log_constraints back_log;
38 extern uint32_t drizzled_bind_timeout;
39 
41 {
42  for (int retry= 0; retry < 10; retry++)
43  {
44  int new_fd= accept(fd, NULL, 0);
45  if (new_fd != -1)
46  return new_fd;
47  if (errno != EINTR && errno != EAGAIN)
48  break;
49  }
50  if ((accept_error_count++ & 255) == 0)
51  {
52  sql_perror(_("accept() failed with errno %d"));
53  }
54  if (errno == ENFILE || errno == EMFILE)
55  sleep(1);
56  return -1;
57 }
58 
59 bool plugin::ListenTcp::getFileDescriptors(std::vector<int> &fds)
60 {
61  int ret;
62  char host_buf[NI_MAXHOST];
63  char port_buf[NI_MAXSERV];
64  addrinfo hints;
65  addrinfo *ai_list;
66  uint32_t this_wait;
67  int flags= 1;
68 
69  memset(&hints, 0, sizeof(struct addrinfo));
70  hints.ai_flags= AI_PASSIVE;
71  hints.ai_socktype= SOCK_STREAM;
72 
73  snprintf(port_buf, NI_MAXSERV, "%d", getPort());
74  ret= getaddrinfo(getHost().empty() ? NULL : getHost().c_str(), port_buf, &hints, &ai_list);
75  if (ret != 0)
76  {
77  errmsg_printf(error::ERROR, _("getaddrinfo() failed with error %s"),
78  gai_strerror(ret));
79  return true;
80  }
81 
82  for (addrinfo* ai= ai_list; ai != NULL; ai= ai->ai_next)
83  {
84  ret= getnameinfo(ai->ai_addr, ai->ai_addrlen, host_buf, NI_MAXHOST,
85  port_buf, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV);
86  if (ret != 0)
87  {
88  strcpy(host_buf, "-");
89  strcpy(port_buf, "-");
90  }
91 
92  int fd= socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
93  if (fd == -1)
94  {
95  /*
96  Call to socket() can fail for some getaddrinfo results, try another.
97  */
98  continue;
99  }
100 
101 #ifdef IPV6_V6ONLY
102  if (ai->ai_family == AF_INET6)
103  {
104  flags= 1;
105  ret= setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flags, sizeof(flags));
106  if (ret != 0)
107  {
108  sql_perror(_("setsockopt(IPV6_V6ONLY)"));
109  return true;
110  }
111  }
112 #endif
113 
114  ret= fcntl(fd, F_SETFD, FD_CLOEXEC);
115  if (ret != 0 || !(fcntl(fd, F_GETFD, 0) & FD_CLOEXEC))
116  {
117  sql_perror(_("fcntl(FD_CLOEXEC)"));
118  return true;
119  }
120 
121  ret= setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
122  if (ret != 0)
123  {
124  sql_perror(_("setsockopt(SO_REUSEADDR)"));
125  return true;
126  }
127 
128  ret= setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags));
129  if (ret != 0)
130  {
131  sql_perror(_("setsockopt(SO_KEEPALIVE)"));
132  return true;
133  }
134 
135  linger ling= {0, 0};
136  ret= setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
137  if (ret != 0)
138  {
139  sql_perror(_("setsockopt(SO_LINGER)"));
140  return true;
141  }
142 
143  ret= setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(flags));
144  if (ret != 0)
145  {
146  sql_perror(_("setsockopt(TCP_NODELAY)"));
147  return true;
148  }
149 
150  /*
151  Sometimes the port is not released fast enough when stopping and
152  restarting the server. This happens quite often with the test suite
153  on busy Linux systems. Retry to bind the address at these intervals:
154  Sleep intervals: 1, 2, 4, 6, 9, 13, 17, 22, ...
155  Retry at second: 1, 3, 7, 13, 22, 35, 52, 74, ...
156  Limit the sequence by drizzled_bind_timeout.
157  */
158  for (uint32_t waited= 0, retry= 1; ; retry++, waited+= this_wait)
159  {
160  if (((ret= ::bind(fd, ai->ai_addr, ai->ai_addrlen)) == 0) ||
161  (errno != EADDRINUSE) || (waited >= drizzled_bind_timeout))
162  {
163  break;
164  }
165 
166  errmsg_printf(error::INFO, _("Retrying bind() on %u"), getPort());
167  this_wait= retry * retry / 3 + 1;
168  sleep(this_wait);
169  }
170 
171  if (ret < 0)
172  {
173  std::string error_message;
174 
175  error_message+= host_buf;
176  error_message+= ":";
177  error_message+= port_buf;
178  error_message+= _(" failed to bind");
179  sql_perror(error_message);
180 
181  return true;
182  }
183 
184  if (listen(fd, (int) back_log) < 0)
185  {
186  sql_perror("listen()");
187  return true;
188  }
189 
190  fds.push_back(fd);
191 
192  errmsg_printf(error::INFO, _("Listening on %s:%s"), host_buf, port_buf);
193  }
194 
195  freeaddrinfo(ai_list);
196 
197  return false;
198 }
199 
200 const std::string plugin::ListenTcp::getHost() const
201 {
202  return "";
203 }
204 
205 } /* namespace drizzled */
virtual bool getFileDescriptors(std::vector< int > &)
Definition: listen_tcp.cc:59
virtual const std::string getHost() const
Definition: listen_tcp.cc:200