~ubuntu-branches/ubuntu/trusty/drizzle/trusty

« back to all changes in this revision

Viewing changes to drizzled/plugin/listen_tcp.cc

  • Committer: Bazaar Package Importer
  • Author(s): Monty Taylor
  • Date: 2010-03-18 12:12:31 UTC
  • Revision ID: james.westby@ubuntu.com-20100318121231-k6g1xe6cshbwa0f8
Tags: upstream-2010.03.1347
ImportĀ upstreamĀ versionĀ 2010.03.1347

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
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
 
 
26
#include <unistd.h>
 
27
#include <sys/socket.h>
 
28
#include <fcntl.h>
 
29
#include <netdb.h>
 
30
#include <netinet/tcp.h>
 
31
#include <cerrno>
 
32
 
 
33
using namespace std;
 
34
 
 
35
#define MAX_ACCEPT_RETRY        10      // Test accept this many times
 
36
 
 
37
namespace drizzled
 
38
{
 
39
extern uint32_t back_log;
 
40
extern uint32_t drizzled_bind_timeout;
 
41
 
 
42
 
 
43
int plugin::ListenTcp::acceptTcp(int fd)
 
44
{
 
45
  int new_fd;
 
46
  uint32_t retry;
 
47
 
 
48
  for (retry= 0; retry < MAX_ACCEPT_RETRY; retry++)
 
49
  {
 
50
    new_fd= accept(fd, NULL, 0);
 
51
    if (new_fd != -1 || (errno != EINTR && errno != EAGAIN))
 
52
      break;
 
53
  }
 
54
 
 
55
  if (new_fd == -1)
 
56
  {
 
57
    if ((accept_error_count++ & 255) == 0)
 
58
    {
 
59
      errmsg_printf(ERRMSG_LVL_ERROR, _("accept() failed with errno %d"),
 
60
                    errno);
 
61
    }
 
62
 
 
63
    if (errno == ENFILE || errno == EMFILE)
 
64
      sleep(1);
 
65
 
 
66
    return -1;
 
67
  }
 
68
 
 
69
  return new_fd;
 
70
}
 
71
 
 
72
bool plugin::ListenTcp::getFileDescriptors(std::vector<int> &fds)
 
73
{
 
74
  int ret;
 
75
  char host_buf[NI_MAXHOST];
 
76
  char port_buf[NI_MAXSERV];
 
77
  struct addrinfo hints;
 
78
  struct addrinfo *ai;
 
79
  struct addrinfo *ai_list;
 
80
  int fd= -1;
 
81
  uint32_t waited;
 
82
  uint32_t this_wait;
 
83
  uint32_t retry;
 
84
  struct linger ling= {0, 0};
 
85
  int flags= 1;
 
86
 
 
87
  memset(&hints, 0, sizeof(struct addrinfo));
 
88
  hints.ai_flags= AI_PASSIVE;
 
89
  hints.ai_socktype= SOCK_STREAM;
 
90
 
 
91
  snprintf(port_buf, NI_MAXSERV, "%d", getPort());
 
92
  ret= getaddrinfo(getHost(), port_buf, &hints, &ai_list);
 
93
  if (ret != 0)
 
94
  {
 
95
    errmsg_printf(ERRMSG_LVL_ERROR, _("getaddrinfo() failed with error %s"),
 
96
                  gai_strerror(ret));
 
97
    return true;
 
98
  }
 
99
 
 
100
  for (ai= ai_list; ai != NULL; ai= ai->ai_next)
 
101
  {
 
102
    ret= getnameinfo(ai->ai_addr, ai->ai_addrlen, host_buf, NI_MAXHOST,
 
103
                     port_buf, NI_MAXSERV, NI_NUMERICHOST | NI_NUMERICSERV);
 
104
    if (ret != 0)
 
105
    {
 
106
      strcpy(host_buf, "-");
 
107
      strcpy(port_buf, "-");
 
108
    }
 
109
 
 
110
    fd= socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
 
111
    if (fd == -1)
 
112
    {
 
113
      /*
 
114
        Call to socket() can fail for some getaddrinfo results, try another.
 
115
      */
 
116
      continue;
 
117
    }
 
118
 
 
119
#ifdef IPV6_V6ONLY
 
120
    if (ai->ai_family == AF_INET6)
 
121
    {
 
122
      flags= 1;
 
123
      ret= setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &flags, sizeof(flags));
 
124
      if (ret != 0)
 
125
      {
 
126
        errmsg_printf(ERRMSG_LVL_ERROR,
 
127
                      _("setsockopt(IPV6_V6ONLY) failed with errno %d"),
 
128
                      errno);
 
129
        return true;
 
130
      }
 
131
    }
 
132
#endif
 
133
 
 
134
    ret= fcntl(fd, F_SETFD, FD_CLOEXEC);
 
135
    if (ret != 0 || !(fcntl(fd, F_GETFD, 0) & FD_CLOEXEC))
 
136
    {
 
137
      errmsg_printf(ERRMSG_LVL_ERROR,
 
138
                    _("fcntl(FD_CLOEXEC) failed with errno %d"),
 
139
                    errno);
 
140
      return true;
 
141
    }
 
142
 
 
143
    ret= setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
 
144
    if (ret != 0)
 
145
    {
 
146
      errmsg_printf(ERRMSG_LVL_ERROR,
 
147
                    _("setsockopt(SO_REUSEADDR) failed with errno %d"),
 
148
                    errno);
 
149
      return true;
 
150
    }
 
151
 
 
152
    ret= setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags));
 
153
    if (ret != 0)
 
154
    {
 
155
      errmsg_printf(ERRMSG_LVL_ERROR,
 
156
                    _("setsockopt(SO_KEEPALIVE) failed with errno %d"),
 
157
                    errno);
 
158
      return true;
 
159
    }
 
160
 
 
161
    ret= setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
 
162
    if (ret != 0)
 
163
    {
 
164
      errmsg_printf(ERRMSG_LVL_ERROR,
 
165
                    _("setsockopt(SO_LINGER) failed with errno %d"),
 
166
                    errno);
 
167
      return true;
 
168
    }
 
169
 
 
170
    ret= setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(flags));
 
171
    if (ret != 0)
 
172
    {
 
173
      errmsg_printf(ERRMSG_LVL_ERROR,
 
174
                    _("setsockopt(TCP_NODELAY) failed with errno %d"),
 
175
                    errno);
 
176
      return true;
 
177
    }
 
178
 
 
179
    /*
 
180
      Sometimes the port is not released fast enough when stopping and
 
181
      restarting the server. This happens quite often with the test suite
 
182
      on busy Linux systems. Retry to bind the address at these intervals:
 
183
      Sleep intervals: 1, 2, 4,  6,  9, 13, 17, 22, ...
 
184
      Retry at second: 1, 3, 7, 13, 22, 35, 52, 74, ...
 
185
      Limit the sequence by drizzled_bind_timeout.
 
186
    */
 
187
    for (waited= 0, retry= 1; ; retry++, waited+= this_wait)
 
188
    {
 
189
      if (((ret= ::bind(fd, ai->ai_addr, ai->ai_addrlen)) == 0) ||
 
190
          (errno != EADDRINUSE) || (waited >= drizzled_bind_timeout))
 
191
      {
 
192
        break;
 
193
      }
 
194
 
 
195
      errmsg_printf(ERRMSG_LVL_INFO, _("Retrying bind() on %u"), getPort());
 
196
      this_wait= retry * retry / 3 + 1;
 
197
      sleep(this_wait);
 
198
    }
 
199
 
 
200
    if (ret < 0)
 
201
    {
 
202
      errmsg_printf(ERRMSG_LVL_ERROR, _("bind() failed with errno: %d"),
 
203
                    errno);
 
204
      errmsg_printf(ERRMSG_LVL_ERROR,
 
205
                    _("Do you already have another drizzled running?"));
 
206
      return true;
 
207
    }
 
208
 
 
209
    if (listen(fd, (int) back_log) < 0)
 
210
    {
 
211
      errmsg_printf(ERRMSG_LVL_ERROR,
 
212
                    _("listen() failed with errno %d"), errno);
 
213
      return true;
 
214
    }
 
215
 
 
216
    fds.push_back(fd);
 
217
 
 
218
    errmsg_printf(ERRMSG_LVL_INFO, _("Listening on %s:%s\n"), host_buf,
 
219
                  port_buf);
 
220
  }
 
221
 
 
222
  freeaddrinfo(ai_list);
 
223
 
 
224
  return false;
 
225
}
 
226
 
 
227
const char* plugin::ListenTcp::getHost(void) const
 
228
{
 
229
  return NULL;
 
230
}
 
231
 
 
232
} /* namespace drizzled */