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