Update, fix build rules for -j
[awesomized/libmemcached] / libmemcached / connect.cc
1 /* vim:expandtab:shiftwidth=2:tabstop=2:smarttab:
2 *
3 * Libmemcached library
4 *
5 * Copyright (C) 2011 Data Differential, http://datadifferential.com/
6 * Copyright (C) 2006-2010 Brian Aker All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following disclaimer
17 * in the documentation and/or other materials provided with the
18 * distribution.
19 *
20 * * The names of its contributors may not be used to endorse or
21 * promote products derived from this software without specific prior
22 * written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
27 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
28 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
30 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 *
36 */
37
38
39 #include <libmemcached/common.h>
40
41 #include <cassert>
42
43 #ifndef SOCK_CLOEXEC
44 # define SOCK_CLOEXEC 0
45 #endif
46
47 #ifndef SOCK_NONBLOCK
48 # define SOCK_NONBLOCK 0
49 #endif
50
51 #ifndef FD_CLOEXEC
52 # define FD_CLOEXEC 0
53 #endif
54
55 static memcached_return_t connect_poll(org::libmemcached::Instance* server)
56 {
57 struct pollfd fds[1];
58 fds[0].fd= server->fd;
59 fds[0].events= POLLOUT;
60
61 size_t loop_max= 5;
62
63 if (server->root->poll_timeout == 0)
64 {
65 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT);
66 }
67
68 while (--loop_max) // Should only loop on cases of ERESTART or EINTR
69 {
70 int number_of;
71 if ((number_of= poll(fds, 1, server->root->connect_timeout)) <= 0)
72 {
73 if (number_of == -1)
74 {
75 int local_errno= get_socket_errno(); // We cache in case closesocket() modifies errno
76 switch (local_errno)
77 {
78 #ifdef TARGET_OS_LINUX
79 case ERESTART:
80 #endif
81 case EINTR:
82 continue;
83
84 case EFAULT:
85 case ENOMEM:
86 return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT);
87
88 case EINVAL:
89 return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT, memcached_literal_param("RLIMIT_NOFILE exceeded, or if OSX the timeout value was invalid"));
90
91 default: // This should not happen
92 if (fds[0].revents & POLLERR)
93 {
94 int err;
95 socklen_t len= sizeof(err);
96 if (getsockopt(server->fd, SOL_SOCKET, SO_ERROR, (char*)&err, &len) == 0)
97 {
98 if (err == 0)
99 {
100 // This should never happen, if it does? Punt.
101 continue;
102 }
103 local_errno= err;
104 }
105 }
106
107 assert_msg(server->fd != INVALID_SOCKET, "poll() was passed an invalid file descriptor");
108 (void)closesocket(server->fd);
109 server->fd= INVALID_SOCKET;
110 server->state= MEMCACHED_SERVER_STATE_NEW;
111
112 return memcached_set_errno(*server, local_errno, MEMCACHED_AT);
113 }
114 }
115 assert(number_of == 0);
116
117 server->io_wait_count.timeouts++;
118 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT);
119 }
120
121 if (fds[0].revents & POLLERR or
122 fds[0].revents & POLLHUP or
123 fds[0].revents & POLLNVAL)
124 {
125 int err;
126 socklen_t len= sizeof (err);
127 if (getsockopt(fds[0].fd, SOL_SOCKET, SO_ERROR, (char*)&err, &len) == 0)
128 {
129 // We check the value to see what happened wth the socket.
130 if (err == 0)
131 {
132 return MEMCACHED_SUCCESS;
133 }
134 errno= err;
135 }
136
137 return memcached_set_errno(*server, err, MEMCACHED_AT);
138 }
139 assert(fds[0].revents & POLLIN or fds[0].revents & POLLOUT);
140
141 return MEMCACHED_SUCCESS;
142 }
143
144 // This should only be possible from ERESTART or EINTR;
145 return memcached_set_errno(*server, get_socket_errno(), MEMCACHED_AT);
146 }
147
148 static memcached_return_t set_hostinfo(org::libmemcached::Instance* server)
149 {
150 assert(server->type != MEMCACHED_CONNECTION_UNIX_SOCKET);
151 if (server->address_info)
152 {
153 freeaddrinfo(server->address_info);
154 server->address_info= NULL;
155 server->address_info_next= NULL;
156 }
157
158 char str_port[MEMCACHED_NI_MAXSERV];
159 int length= snprintf(str_port, MEMCACHED_NI_MAXSERV, "%u", uint32_t(server->port()));
160 if (length >= MEMCACHED_NI_MAXSERV or length <= 0)
161 {
162 return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT,
163 memcached_literal_param("snprintf(NI_MAXSERV)"));
164 }
165
166 struct addrinfo hints;
167 memset(&hints, 0, sizeof(struct addrinfo));
168
169 #if 0
170 hints.ai_family= AF_INET;
171 #endif
172 if (memcached_is_udp(server->root))
173 {
174 hints.ai_protocol= IPPROTO_UDP;
175 hints.ai_socktype= SOCK_DGRAM;
176 }
177 else
178 {
179 hints.ai_socktype= SOCK_STREAM;
180 hints.ai_protocol= IPPROTO_TCP;
181 }
182
183 assert(server->address_info == NULL);
184 assert(server->address_info_next == NULL);
185 int errcode;
186 switch(errcode= getaddrinfo(server->hostname, str_port, &hints, &server->address_info))
187 {
188 case 0:
189 break;
190
191 case EAI_AGAIN:
192 if (server->address_info)
193 {
194 freeaddrinfo(server->address_info);
195 server->address_info= NULL;
196 server->address_info_next= NULL;
197 }
198 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT, memcached_string_make_from_cstr(gai_strerror(errcode)));
199
200 case EAI_SYSTEM:
201 if (server->address_info)
202 {
203 freeaddrinfo(server->address_info);
204 server->address_info= NULL;
205 server->address_info_next= NULL;
206 }
207 return memcached_set_errno(*server, errno, MEMCACHED_AT, memcached_literal_param("getaddrinfo(EAI_SYSTEM)"));
208
209 case EAI_BADFLAGS:
210 if (server->address_info)
211 {
212 freeaddrinfo(server->address_info);
213 server->address_info= NULL;
214 server->address_info_next= NULL;
215 }
216 return memcached_set_error(*server, MEMCACHED_INVALID_ARGUMENTS, MEMCACHED_AT, memcached_literal_param("getaddrinfo(EAI_BADFLAGS)"));
217
218 case EAI_MEMORY:
219 if (server->address_info)
220 {
221 freeaddrinfo(server->address_info);
222 server->address_info= NULL;
223 server->address_info_next= NULL;
224 }
225 return memcached_set_error(*server, MEMCACHED_MEMORY_ALLOCATION_FAILURE, MEMCACHED_AT, memcached_literal_param("getaddrinfo(EAI_MEMORY)"));
226
227 default:
228 {
229 if (server->address_info)
230 {
231 freeaddrinfo(server->address_info);
232 server->address_info= NULL;
233 server->address_info_next= NULL;
234 }
235 return memcached_set_error(*server, MEMCACHED_HOST_LOOKUP_FAILURE, MEMCACHED_AT, memcached_string_make_from_cstr(gai_strerror(errcode)));
236 }
237 }
238 server->address_info_next= server->address_info;
239 server->state= MEMCACHED_SERVER_STATE_ADDRINFO;
240
241 return MEMCACHED_SUCCESS;
242 }
243
244 static inline void set_socket_nonblocking(org::libmemcached::Instance* server)
245 {
246 #ifdef WIN32
247 u_long arg= 1;
248 if (ioctlsocket(server->fd, FIONBIO, &arg) == SOCKET_ERROR)
249 {
250 memcached_set_errno(*server, get_socket_errno(), NULL);
251 }
252 #else
253 int flags;
254
255 if (SOCK_NONBLOCK == 0)
256 {
257 do
258 {
259 flags= fcntl(server->fd, F_GETFL, 0);
260 } while (flags == -1 && (errno == EINTR || errno == EAGAIN));
261
262 if (flags == -1)
263 {
264 memcached_set_errno(*server, errno, NULL);
265 }
266 else if ((flags & O_NONBLOCK) == 0)
267 {
268 int rval;
269
270 do
271 {
272 rval= fcntl(server->fd, F_SETFL, flags | O_NONBLOCK);
273 } while (rval == -1 && (errno == EINTR or errno == EAGAIN));
274
275 if (rval == -1)
276 {
277 memcached_set_errno(*server, errno, NULL);
278 }
279 }
280 }
281 #endif
282 }
283
284 static void set_socket_options(org::libmemcached::Instance* server)
285 {
286 assert_msg(server->fd != INVALID_SOCKET, "invalid socket was passed to set_socket_options()");
287
288 if (memcached_is_udp(server->root))
289 {
290 return;
291 }
292
293 #ifdef HAVE_SNDTIMEO
294 if (server->root->snd_timeout > 0)
295 {
296 struct timeval waittime;
297
298 waittime.tv_sec= server->root->snd_timeout / 1000000;
299 waittime.tv_usec= server->root->snd_timeout % 1000000;
300
301 int error= setsockopt(server->fd, SOL_SOCKET, SO_SNDTIMEO,
302 (char*)&waittime, (socklen_t)sizeof(struct timeval));
303 (void)error;
304 assert(error == 0);
305 }
306 #endif
307
308 #ifdef HAVE_RCVTIMEO
309 if (server->root->rcv_timeout > 0)
310 {
311 struct timeval waittime;
312
313 waittime.tv_sec= server->root->rcv_timeout / 1000000;
314 waittime.tv_usec= server->root->rcv_timeout % 1000000;
315
316 int error= setsockopt(server->fd, SOL_SOCKET, SO_RCVTIMEO,
317 (char*)&waittime, (socklen_t)sizeof(struct timeval));
318 (void)(error);
319 assert(error == 0);
320 }
321 #endif
322
323
324 #if defined(__MACH__) && defined(__APPLE__) || defined(__FreeBSD__)
325 {
326 int set= 1;
327 int error= setsockopt(server->fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&set, sizeof(int));
328
329 assert(error == 0);
330
331 // This is not considered a fatal error
332 if (error == -1)
333 {
334 #if 0
335 perror("setsockopt(SO_NOSIGPIPE)");
336 #endif
337 }
338 }
339 #endif
340
341 if (server->root->flags.no_block)
342 {
343 struct linger linger;
344
345 linger.l_onoff= 1;
346 linger.l_linger= 0; /* By default on close() just drop the socket */
347 int error= setsockopt(server->fd, SOL_SOCKET, SO_LINGER,
348 (char*)&linger, (socklen_t)sizeof(struct linger));
349 (void)(error);
350 assert(error == 0);
351 }
352
353 if (server->root->flags.tcp_nodelay)
354 {
355 int flag= 1;
356
357 int error= setsockopt(server->fd, IPPROTO_TCP, TCP_NODELAY,
358 (char*)&flag, (socklen_t)sizeof(int));
359 (void)(error);
360 assert(error == 0);
361 }
362
363 if (server->root->flags.tcp_keepalive)
364 {
365 int flag= 1;
366
367 int error= setsockopt(server->fd, SOL_SOCKET, SO_KEEPALIVE,
368 (char*)&flag, (socklen_t)sizeof(int));
369 (void)(error);
370 assert(error == 0);
371 }
372
373 #ifdef TCP_KEEPIDLE
374 if (server->root->tcp_keepidle > 0)
375 {
376 int error= setsockopt(server->fd, IPPROTO_TCP, TCP_KEEPIDLE,
377 (char*)&server->root->tcp_keepidle, (socklen_t)sizeof(int));
378 (void)(error);
379 assert(error == 0);
380 }
381 #endif
382
383 if (server->root->send_size > 0)
384 {
385 int error= setsockopt(server->fd, SOL_SOCKET, SO_SNDBUF,
386 (char*)&server->root->send_size, (socklen_t)sizeof(int));
387 (void)(error);
388 assert(error == 0);
389 }
390
391 if (server->root->recv_size > 0)
392 {
393 int error= setsockopt(server->fd, SOL_SOCKET, SO_RCVBUF,
394 (char*)&server->root->recv_size, (socklen_t)sizeof(int));
395 (void)(error);
396 assert(error == 0);
397 }
398
399 /* libmemcached will always use nonblocking IO to avoid write deadlocks */
400 set_socket_nonblocking(server);
401 }
402
403 static memcached_return_t unix_socket_connect(org::libmemcached::Instance* server)
404 {
405 #ifndef WIN32
406 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
407
408 int type= SOCK_STREAM;
409 if (SOCK_CLOEXEC)
410 {
411 type|= SOCK_CLOEXEC;
412 }
413
414 if (SOCK_NONBLOCK)
415 {
416 type|= SOCK_NONBLOCK;
417 }
418
419 if ((server->fd= socket(AF_UNIX, type, 0)) < 0)
420 {
421 memcached_set_errno(*server, errno, NULL);
422 return MEMCACHED_CONNECTION_FAILURE;
423 }
424
425 struct sockaddr_un servAddr;
426
427 memset(&servAddr, 0, sizeof (struct sockaddr_un));
428 servAddr.sun_family= AF_UNIX;
429 strncpy(servAddr.sun_path, server->hostname, sizeof(servAddr.sun_path)); /* Copy filename */
430
431 do {
432 if (connect(server->fd, (struct sockaddr *)&servAddr, sizeof(servAddr)) < 0)
433 {
434 switch (errno)
435 {
436 case EINPROGRESS:
437 case EALREADY:
438 case EINTR:
439 continue;
440
441 case EISCONN: /* We were spinning waiting on connect */
442 {
443 assert(0); // Programmer error
444 break;
445 }
446
447 default:
448 WATCHPOINT_ERRNO(errno);
449 memcached_set_errno(*server, errno, MEMCACHED_AT);
450 return MEMCACHED_CONNECTION_FAILURE;
451 }
452 }
453 } while (0);
454 server->state= MEMCACHED_SERVER_STATE_CONNECTED;
455
456 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
457
458 return MEMCACHED_SUCCESS;
459 #else
460 (void)server;
461 return MEMCACHED_NOT_SUPPORTED;
462 #endif
463 }
464
465 static memcached_return_t network_connect(org::libmemcached::Instance* server)
466 {
467 bool timeout_error_occured= false;
468
469 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
470 WATCHPOINT_ASSERT(server->cursor_active_ == 0);
471
472 /*
473 We want to check both of these because if address_info_next has been fully tried, we want to do a new lookup to make sure we have picked up on any new DNS information.
474 */
475 if (server->address_info == NULL or server->address_info_next == NULL)
476 {
477 WATCHPOINT_ASSERT(server->state == MEMCACHED_SERVER_STATE_NEW);
478 server->address_info_next= NULL;
479 memcached_return_t rc= set_hostinfo(server);
480
481 if (memcached_failed(rc))
482 {
483 return rc;
484 }
485 }
486
487 if (server->address_info_next == NULL)
488 {
489 server->address_info_next= server->address_info;
490 server->state= MEMCACHED_SERVER_STATE_ADDRINFO;
491 }
492
493 /* Create the socket */
494 while (server->address_info_next and server->fd == INVALID_SOCKET)
495 {
496 /* Memcache server does not support IPV6 in udp mode, so skip if not ipv4 */
497 if (memcached_is_udp(server->root) and server->address_info_next->ai_family != AF_INET)
498 {
499 server->address_info_next= server->address_info_next->ai_next;
500 continue;
501 }
502
503 int type= server->address_info_next->ai_socktype;
504 if (SOCK_CLOEXEC)
505 {
506 type|= SOCK_CLOEXEC;
507 }
508
509 if (SOCK_NONBLOCK)
510 {
511 type|= SOCK_NONBLOCK;
512 }
513
514 server->fd= socket(server->address_info_next->ai_family,
515 type,
516 server->address_info_next->ai_protocol);
517
518 if (int(server->fd) == SOCKET_ERROR)
519 {
520 return memcached_set_errno(*server, get_socket_errno(), NULL);
521 }
522
523 #ifdef HAVE_FCNTL
524 // If SOCK_CLOEXEC exists then we don't need to call the following
525 if (SOCK_CLOEXEC == 0)
526 {
527 if (FD_CLOEXEC)
528 {
529 int rval;
530 do
531 {
532 rval= fcntl (server->fd, F_SETFD, FD_CLOEXEC);
533 } while (rval == -1 && (errno == EINTR or errno == EAGAIN));
534 }
535 }
536 #endif
537
538 set_socket_options(server);
539
540 /* connect to server */
541 if ((connect(server->fd, server->address_info_next->ai_addr, server->address_info_next->ai_addrlen) != SOCKET_ERROR))
542 {
543 server->state= MEMCACHED_SERVER_STATE_CONNECTED;
544 return MEMCACHED_SUCCESS;
545 }
546
547 /* An error occurred */
548 switch (get_socket_errno())
549 {
550 case ETIMEDOUT:
551 timeout_error_occured= true;
552 break;
553
554 case EAGAIN:
555 #if EWOULDBLOCK != EAGAIN
556 case EWOULDBLOCK:
557 #endif
558 case EINPROGRESS: // nonblocking mode - first return
559 case EALREADY: // nonblocking mode - subsequent returns
560 {
561 server->state= MEMCACHED_SERVER_STATE_IN_PROGRESS;
562 memcached_return_t rc= connect_poll(server);
563
564 if (memcached_success(rc))
565 {
566 server->state= MEMCACHED_SERVER_STATE_CONNECTED;
567 return MEMCACHED_SUCCESS;
568 }
569
570 // A timeout here is treated as an error, we will not retry
571 if (rc == MEMCACHED_TIMEOUT)
572 {
573 timeout_error_occured= true;
574 }
575 }
576 break;
577
578 case EISCONN: // we are connected :-)
579 WATCHPOINT_ASSERT(0); // This is a programmer's error
580 break;
581
582 case EINTR: // Special case, we retry ai_addr
583 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
584 (void)closesocket(server->fd);
585 server->fd= INVALID_SOCKET;
586 continue;
587
588 default:
589 break;
590 }
591
592 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
593 (void)closesocket(server->fd);
594 server->fd= INVALID_SOCKET;
595 server->address_info_next= server->address_info_next->ai_next;
596 }
597
598 WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
599
600 if (timeout_error_occured)
601 {
602 if (server->fd != INVALID_SOCKET)
603 {
604 (void)closesocket(server->fd);
605 server->fd= INVALID_SOCKET;
606 }
607 }
608
609 WATCHPOINT_STRING("Never got a good file descriptor");
610
611 if (memcached_has_current_error(*server))
612 {
613 return memcached_instance_error_return(server);
614 }
615
616 if (timeout_error_occured and server->state < MEMCACHED_SERVER_STATE_IN_PROGRESS)
617 {
618 return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT);
619 }
620
621 return memcached_set_error(*server, MEMCACHED_CONNECTION_FAILURE, MEMCACHED_AT); /* The last error should be from connect() */
622 }
623
624
625 /*
626 backoff_handling()
627
628 Based on time/failure count fail the connect without trying. This prevents waiting in a state where
629 we get caught spending cycles just waiting.
630 */
631 static memcached_return_t backoff_handling(org::libmemcached::Instance* server, bool& in_timeout)
632 {
633 struct timeval curr_time;
634 bool _gettime_success= (gettimeofday(&curr_time, NULL) == 0);
635
636 /*
637 If we hit server_failure_limit then something is completely wrong about the server.
638
639 1) If autoeject is enabled we do that.
640 2) If not? We go into timeout again, there is much else to do :(
641 */
642 if (server->server_failure_counter >= server->root->server_failure_limit)
643 {
644 /*
645 We just auto_eject if we hit this point
646 */
647 if (_is_auto_eject_host(server->root))
648 {
649 set_last_disconnected_host(server);
650
651 // Retry dead servers if requested
652 if (_gettime_success and server->root->dead_timeout > 0)
653 {
654 server->next_retry= curr_time.tv_sec +server->root->dead_timeout;
655
656 // We only retry dead servers once before assuming failure again
657 server->server_failure_counter= server->root->server_failure_limit -1;
658 }
659
660 memcached_return_t rc;
661 if (memcached_failed(rc= run_distribution((memcached_st *)server->root)))
662 {
663 return memcached_set_error(*server, rc, MEMCACHED_AT, memcached_literal_param("Backoff handling failed during run_distribution"));
664 }
665
666 return memcached_set_error(*server, MEMCACHED_SERVER_MARKED_DEAD, MEMCACHED_AT);
667 }
668
669 server->state= MEMCACHED_SERVER_STATE_IN_TIMEOUT;
670
671 // Sanity check/setting
672 if (server->next_retry == 0)
673 {
674 server->next_retry= 1;
675 }
676 }
677
678 if (server->state == MEMCACHED_SERVER_STATE_IN_TIMEOUT)
679 {
680 /*
681 If next_retry is less then our current time, then we reset and try everything again.
682 */
683 if (_gettime_success and server->next_retry < curr_time.tv_sec)
684 {
685 server->state= MEMCACHED_SERVER_STATE_NEW;
686 }
687 else
688 {
689 return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT);
690 }
691
692 in_timeout= true;
693 }
694
695 return MEMCACHED_SUCCESS;
696 }
697
698 static memcached_return_t _memcached_connect(org::libmemcached::Instance* server, const bool set_last_disconnected)
699 {
700 assert(server);
701 if (server->fd != INVALID_SOCKET)
702 {
703 return MEMCACHED_SUCCESS;
704 }
705
706 LIBMEMCACHED_MEMCACHED_CONNECT_START();
707
708 bool in_timeout= false;
709 memcached_return_t rc;
710 if (memcached_failed(rc= backoff_handling(server, in_timeout)))
711 {
712 set_last_disconnected_host(server);
713 return rc;
714 }
715
716 if (LIBMEMCACHED_WITH_SASL_SUPPORT and server->root->sasl.callbacks and memcached_is_udp(server->root))
717 {
718 return memcached_set_error(*server, MEMCACHED_INVALID_HOST_PROTOCOL, MEMCACHED_AT, memcached_literal_param("SASL is not supported for UDP connections"));
719 }
720
721 if (server->hostname[0] == '/')
722 {
723 server->type= MEMCACHED_CONNECTION_UNIX_SOCKET;
724 }
725
726 /* We need to clean up the multi startup piece */
727 switch (server->type)
728 {
729 case MEMCACHED_CONNECTION_UDP:
730 case MEMCACHED_CONNECTION_TCP:
731 rc= network_connect(server);
732
733 if (LIBMEMCACHED_WITH_SASL_SUPPORT)
734 {
735 if (server->fd != INVALID_SOCKET and server->root->sasl.callbacks)
736 {
737 rc= memcached_sasl_authenticate_connection(server);
738 fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, memcached_strerror(NULL, rc));
739 if (memcached_failed(rc) and server->fd != INVALID_SOCKET)
740 {
741 WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
742 (void)closesocket(server->fd);
743 server->fd= INVALID_SOCKET;
744 }
745 }
746 }
747 break;
748
749 case MEMCACHED_CONNECTION_UNIX_SOCKET:
750 rc= unix_socket_connect(server);
751 break;
752 }
753
754 if (memcached_success(rc))
755 {
756 server->mark_server_as_clean();
757 memcached_version_instance(server);
758 return rc;
759 }
760 else if (set_last_disconnected)
761 {
762 set_last_disconnected_host(server);
763 if (memcached_has_current_error(*server))
764 {
765 memcached_mark_server_for_timeout(server);
766 assert(memcached_failed(memcached_instance_error_return(server)));
767 }
768 else
769 {
770 memcached_set_error(*server, rc, MEMCACHED_AT);
771 memcached_mark_server_for_timeout(server);
772 }
773
774 LIBMEMCACHED_MEMCACHED_CONNECT_END();
775
776 if (in_timeout)
777 {
778 char buffer[1024];
779 int snprintf_length= snprintf(buffer, sizeof(buffer), "%s:%d", server->hostname, int(server->port()));
780 return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT, buffer, snprintf_length);
781 }
782 }
783
784 return rc;
785 }
786
787 memcached_return_t memcached_connect_try(org::libmemcached::Instance* server)
788 {
789 if (server and server->root and server->root->state.is_parsing)
790 {
791 return MEMCACHED_SUCCESS;
792 }
793
794 return _memcached_connect(server, false);
795 }
796
797 memcached_return_t memcached_connect(org::libmemcached::Instance* server)
798 {
799 return _memcached_connect(server, true);
800 }