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