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