Fixes for previous merges (and additional file added for murmur)
[awesomized/libmemcached] / lib / 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 if (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 close(ptr->fd);
214 ptr->fd= -1;
215 return MEMCACHED_ERRNO;
216 }
217
218 break;
219 }
220 /* We are spinning waiting on connect */
221 case EINTR:
222 goto test_connect;
223 case EISCONN: /* We were spinning waiting on connect */
224 break;
225 default:
226 ptr->cached_errno= errno;
227 WATCHPOINT_ERRNO(ptr->cached_errno);
228 close(ptr->fd);
229 ptr->fd= -1;
230 if (ptr->root->retry_timeout)
231 {
232 struct timeval next_time;
233
234 gettimeofday(&next_time, NULL);
235 ptr->next_retry= next_time.tv_sec + ptr->root->retry_timeout;
236 }
237 }
238 }
239 else
240 {
241 WATCHPOINT_ASSERT(ptr->cursor_active == 0);
242 return MEMCACHED_SUCCESS;
243 }
244 use = use->ai_next;
245 }
246 }
247
248 if (ptr->fd == -1)
249 return MEMCACHED_ERRNO; /* The last error should be from connect() */
250
251 return MEMCACHED_SUCCESS; /* The last error should be from connect() */
252 }
253
254
255 memcached_return memcached_connect(memcached_server_st *ptr)
256 {
257 memcached_return rc= MEMCACHED_NO_SERVERS;
258 LIBMEMCACHED_MEMCACHED_CONNECT_START();
259
260 if (ptr->root->retry_timeout)
261 {
262 struct timeval next_time;
263
264 gettimeofday(&next_time, NULL);
265 if (next_time.tv_sec < ptr->next_retry)
266 return MEMCACHED_TIMEOUT;
267 }
268 /* We need to clean up the multi startup piece */
269 switch (ptr->type)
270 {
271 case MEMCACHED_CONNECTION_UNKNOWN:
272 WATCHPOINT_ASSERT(0);
273 rc= MEMCACHED_NOT_SUPPORTED;
274 break;
275 case MEMCACHED_CONNECTION_UDP:
276 case MEMCACHED_CONNECTION_TCP:
277 rc= network_connect(ptr);
278 break;
279 case MEMCACHED_CONNECTION_UNIX_SOCKET:
280 rc= unix_socket_connect(ptr);
281 break;
282 default:
283 WATCHPOINT_ASSERT(0);
284 }
285
286 if (rc != MEMCACHED_SUCCESS)
287 WATCHPOINT_ERROR(rc);
288
289 LIBMEMCACHED_MEMCACHED_CONNECT_END();
290
291 return rc;
292 }