Fix for bad disconnect during test run.
[awesomized/libmemcached] / libmemcached / memcached_connect.c
1 #include "common.h"
2 #include <netdb.h>
3 #include <poll.h>
4 #include <sys/time.h>
5
6 static memcached_return set_hostinfo(memcached_server_st *server)
7 {
8 struct addrinfo *ai;
9 struct addrinfo hints;
10 int e;
11 char str_port[NI_MAXSERV];
12
13 sprintf(str_port, "%u", server->port);
14
15 memset(&hints, 0, sizeof(hints));
16
17 // hints.ai_family= AF_INET;
18 if (server->type == MEMCACHED_CONNECTION_UDP)
19 {
20 hints.ai_protocol= IPPROTO_UDP;
21 hints.ai_socktype= SOCK_DGRAM;
22 }
23 else
24 {
25 hints.ai_socktype= SOCK_STREAM;
26 hints.ai_protocol= IPPROTO_TCP;
27 }
28
29 e= getaddrinfo(server->hostname, str_port, &hints, &ai);
30 if (e != 0)
31 {
32 WATCHPOINT_STRING(server->hostname);
33 WATCHPOINT_STRING(gai_strerror(e));
34 return MEMCACHED_HOST_LOOKUP_FAILURE;
35 }
36
37 if (server->address_info)
38 {
39 freeaddrinfo(server->address_info);
40 server->address_info= NULL;
41 }
42 server->address_info= ai;
43
44 return MEMCACHED_SUCCESS;
45 }
46
47 static memcached_return set_socket_options(memcached_server_st *ptr)
48 {
49 WATCHPOINT_ASSERT(ptr->fd != -1);
50
51 if (ptr->type == MEMCACHED_CONNECTION_UDP)
52 return MEMCACHED_SUCCESS;
53
54 if (ptr->root->snd_timeout)
55 {
56 int error;
57 struct timeval waittime;
58
59 waittime.tv_sec= 0;
60 waittime.tv_usec= ptr->root->snd_timeout;
61
62 error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDTIMEO,
63 &waittime, (socklen_t)sizeof(struct timeval));
64 WATCHPOINT_ASSERT(error == 0);
65 }
66
67 if (ptr->root->rcv_timeout)
68 {
69 int error;
70 struct timeval waittime;
71
72 waittime.tv_sec= 0;
73 waittime.tv_usec= ptr->root->rcv_timeout;
74
75 error= setsockopt(ptr->fd, SOL_SOCKET, SO_RCVTIMEO,
76 &waittime, (socklen_t)sizeof(struct timeval));
77 WATCHPOINT_ASSERT(error == 0);
78 }
79
80 {
81 int error;
82 struct linger linger;
83
84 linger.l_onoff= 1;
85 linger.l_linger= MEMCACHED_DEFAULT_TIMEOUT;
86 error= setsockopt(ptr->fd, SOL_SOCKET, SO_LINGER,
87 &linger, (socklen_t)sizeof(struct linger));
88 WATCHPOINT_ASSERT(error == 0);
89 }
90
91 if (ptr->root->flags & MEM_TCP_NODELAY)
92 {
93 int flag= 1;
94 int error;
95
96 error= setsockopt(ptr->fd, IPPROTO_TCP, TCP_NODELAY,
97 &flag, (socklen_t)sizeof(int));
98 WATCHPOINT_ASSERT(error == 0);
99 }
100
101 if (ptr->root->send_size)
102 {
103 int error;
104
105 error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF,
106 &ptr->root->send_size, (socklen_t)sizeof(int));
107 WATCHPOINT_ASSERT(error == 0);
108 }
109
110 if (ptr->root->recv_size)
111 {
112 int error;
113
114 error= setsockopt(ptr->fd, SOL_SOCKET, SO_SNDBUF,
115 &ptr->root->recv_size, (socklen_t)sizeof(int));
116 WATCHPOINT_ASSERT(error == 0);
117 }
118
119 /* For the moment, not getting a nonblocking mode will not be fatal */
120 if (ptr->root->flags & MEM_NO_BLOCK)
121 {
122 int flags;
123
124 flags= fcntl(ptr->fd, F_GETFL, 0);
125 unlikely (flags != -1)
126 {
127 (void)fcntl(ptr->fd, F_SETFL, flags | O_NONBLOCK);
128 }
129 }
130
131 return MEMCACHED_SUCCESS;
132 }
133
134 static memcached_return unix_socket_connect(memcached_server_st *ptr)
135 {
136 struct sockaddr_un servAddr;
137 socklen_t addrlen;
138
139 if (ptr->fd == -1)
140 {
141 if ((ptr->fd= socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
142 {
143 ptr->cached_errno= errno;
144 return MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE;
145 }
146
147 memset(&servAddr, 0, sizeof (struct sockaddr_un));
148 servAddr.sun_family= AF_UNIX;
149 strcpy(servAddr.sun_path, ptr->hostname); /* Copy filename */
150
151 addrlen= strlen(servAddr.sun_path) + sizeof(servAddr.sun_family);
152
153 test_connect:
154 if (connect(ptr->fd,
155 (struct sockaddr *)&servAddr,
156 sizeof(servAddr)) < 0)
157 {
158 switch (errno) {
159 case EINPROGRESS:
160 case EALREADY:
161 case EINTR:
162 goto test_connect;
163 case EISCONN: /* We were spinning waiting on connect */
164 break;
165 default:
166 WATCHPOINT_ERRNO(errno);
167 ptr->cached_errno= errno;
168 return MEMCACHED_ERRNO;
169 }
170 }
171 }
172
173 WATCHPOINT_ASSERT(ptr->fd != -1);
174 return MEMCACHED_SUCCESS;
175 }
176
177 static memcached_return network_connect(memcached_server_st *ptr)
178 {
179 if (ptr->fd == -1)
180 {
181 struct addrinfo *use;
182
183 if (ptr->root->server_failure_limit != 0)
184 {
185 if (ptr->server_failure_counter >= ptr->root->server_failure_limit)
186 {
187 memcached_server_remove(ptr);
188 return MEMCACHED_FAILURE;
189 }
190 }
191
192 if (ptr->sockaddr_inited == MEMCACHED_NOT_ALLOCATED ||
193 (!(ptr->root->flags & MEM_USE_CACHE_LOOKUPS)))
194 {
195 memcached_return rc;
196
197 rc= set_hostinfo(ptr);
198 if (rc != MEMCACHED_SUCCESS)
199 return rc;
200 ptr->sockaddr_inited= MEMCACHED_ALLOCATED;
201 }
202
203 use= ptr->address_info;
204 /* Create the socket */
205 while (use != NULL)
206 {
207 if ((ptr->fd= socket(use->ai_family,
208 use->ai_socktype,
209 use->ai_protocol)) < 0)
210 {
211 ptr->cached_errno= errno;
212 WATCHPOINT_ERRNO(errno);
213 return MEMCACHED_CONNECTION_SOCKET_CREATE_FAILURE;
214 }
215
216 (void)set_socket_options(ptr);
217
218 /* connect to server */
219 test_connect:
220 if (connect(ptr->fd,
221 use->ai_addr,
222 use->ai_addrlen) < 0)
223 {
224 switch (errno) {
225 /* We are spinning waiting on connect */
226 case EALREADY:
227 case EINPROGRESS:
228 {
229 struct pollfd fds[1];
230 int error;
231
232 memset(&fds, 0, sizeof(struct pollfd));
233 fds[0].fd= ptr->fd;
234 fds[0].events= POLLOUT | POLLERR;
235 error= poll(fds, 1, ptr->root->connect_timeout);
236
237 if (error == 0)
238 {
239 goto handle_retry;
240 }
241 else if (error != 1 && fds[0].revents & POLLERR)
242 {
243 ptr->cached_errno= errno;
244 WATCHPOINT_ERRNO(ptr->cached_errno);
245 WATCHPOINT_NUMBER(ptr->root->connect_timeout);
246 memcached_quit_server(ptr, 1);
247
248 if (ptr->root->retry_timeout)
249 {
250 struct timeval next_time;
251
252 gettimeofday(&next_time, NULL);
253 ptr->next_retry= next_time.tv_sec + ptr->root->retry_timeout;
254 }
255 ptr->server_failure_counter+= 1;
256
257 return MEMCACHED_ERRNO;
258 }
259
260 break;
261 }
262 /* We are spinning waiting on connect */
263 case EINTR:
264 goto test_connect;
265 case EISCONN: /* We were spinning waiting on connect */
266 break;
267 default:
268 handle_retry:
269 ptr->cached_errno= errno;
270 close(ptr->fd);
271 ptr->fd= -1;
272 if (ptr->root->retry_timeout)
273 {
274 struct timeval next_time;
275
276 gettimeofday(&next_time, NULL);
277 ptr->next_retry= next_time.tv_sec + ptr->root->retry_timeout;
278 }
279 }
280 }
281 else
282 {
283 WATCHPOINT_ASSERT(ptr->cursor_active == 0);
284 ptr->server_failure_counter= 0;
285 return MEMCACHED_SUCCESS;
286 }
287 use = use->ai_next;
288 }
289 }
290
291 if (ptr->fd == -1) {
292 ptr->server_failure_counter+= 1;
293 return MEMCACHED_ERRNO; /* The last error should be from connect() */
294 }
295
296 ptr->server_failure_counter= 0;
297 return MEMCACHED_SUCCESS; /* The last error should be from connect() */
298 }
299
300
301 memcached_return memcached_connect(memcached_server_st *ptr)
302 {
303 memcached_return rc= MEMCACHED_NO_SERVERS;
304 LIBMEMCACHED_MEMCACHED_CONNECT_START();
305
306 if (ptr->root->retry_timeout)
307 {
308 struct timeval next_time;
309
310 gettimeofday(&next_time, NULL);
311 if (next_time.tv_sec < ptr->next_retry)
312 return MEMCACHED_TIMEOUT;
313 }
314 /* We need to clean up the multi startup piece */
315 switch (ptr->type)
316 {
317 case MEMCACHED_CONNECTION_UNKNOWN:
318 WATCHPOINT_ASSERT(0);
319 rc= MEMCACHED_NOT_SUPPORTED;
320 break;
321 case MEMCACHED_CONNECTION_UDP:
322 case MEMCACHED_CONNECTION_TCP:
323 rc= network_connect(ptr);
324 break;
325 case MEMCACHED_CONNECTION_UNIX_SOCKET:
326 rc= unix_socket_connect(ptr);
327 break;
328 default:
329 WATCHPOINT_ASSERT(0);
330 }
331
332 LIBMEMCACHED_MEMCACHED_CONNECT_END();
333
334 return rc;
335 }