xref: /php-src/sapi/fpm/fpm/fpm_sockets.c (revision 880ff82f)
1 	/* (c) 2007,2008 Andrei Nigmatulin */
2 
3 #include "fpm_config.h"
4 
5 #ifdef HAVE_ALLOCA_H
6 #include <alloca.h>
7 #endif
8 #include <sys/types.h>
9 #include <sys/stat.h> /* for chmod(2) */
10 #include <sys/socket.h>
11 #include <netinet/in.h>
12 #include <arpa/inet.h>
13 #include <sys/un.h>
14 #include <netdb.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <errno.h>
19 #include <unistd.h>
20 
21 #include "zlog.h"
22 #include "fpm_arrays.h"
23 #include "fpm_sockets.h"
24 #include "fpm_worker_pool.h"
25 #include "fpm_unix.h"
26 #include "fpm_str.h"
27 #include "fpm_env.h"
28 #include "fpm_cleanup.h"
29 #include "fpm_scoreboard.h"
30 
31 struct listening_socket_s {
32 	int refcount;
33 	int sock;
34 	int type;
35 	char *key;
36 };
37 
38 static struct fpm_array_s sockets_list;
39 
40 enum { FPM_GET_USE_SOCKET = 1, FPM_STORE_SOCKET = 2, FPM_STORE_USE_SOCKET = 3 };
41 
42 #ifdef SO_SETFIB
43 static int routemax = -1;
44 #endif
45 
fpm_sockets_get_env_name(char * envname,unsigned idx)46 static inline void fpm_sockets_get_env_name(char *envname, unsigned idx) /* {{{ */
47 {
48 	if (!idx) {
49 		strcpy(envname, "FPM_SOCKETS");
50 	} else {
51 		sprintf(envname, "FPM_SOCKETS_%d", idx);
52 	}
53 }
54 /* }}} */
55 
fpm_sockets_cleanup(int which,void * arg)56 static void fpm_sockets_cleanup(int which, void *arg) /* {{{ */
57 {
58 	unsigned i;
59 	unsigned socket_set_count = 0;
60 	unsigned socket_set[FPM_ENV_SOCKET_SET_MAX];
61 	unsigned socket_set_buf = 0;
62 	char envname[32];
63 	char *env_value = 0;
64 	int p = 0;
65 	struct listening_socket_s *ls = sockets_list.data;
66 
67 	for (i = 0; i < sockets_list.used; i++, ls++) {
68 		if (which != FPM_CLEANUP_PARENT_EXEC) {
69 			close(ls->sock);
70 		} else { /* on PARENT EXEC we want socket fds to be inherited through environment variable */
71 			char fd[32];
72 			char *tmpenv_value;
73 			sprintf(fd, "%d", ls->sock);
74 
75 			socket_set_buf = (i % FPM_ENV_SOCKET_SET_SIZE == 0 && i) ? 1 : 0;
76 			tmpenv_value = realloc(env_value, p + (p ? 1 : 0) + strlen(ls->key) + 1 + strlen(fd) + socket_set_buf + 1);
77 			if (!tmpenv_value) {
78 				zlog(ZLOG_SYSERROR, "failure to inherit data on parent exec for socket `%s` due to memory allocation failure", ls->key);
79 				free(ls->key);
80 				break;
81 			}
82 
83 			env_value = tmpenv_value;
84 
85 			if (i % FPM_ENV_SOCKET_SET_SIZE == 0) {
86 				socket_set[socket_set_count] = p + socket_set_buf;
87 				socket_set_count++;
88 				if (i) {
89 					*(env_value + p + 1) = 0;
90 				}
91 			}
92 
93 			p += sprintf(env_value + p + socket_set_buf, "%s%s=%s", (p && !socket_set_buf) ? "," : "", ls->key, fd);
94 			p += socket_set_buf;
95 		}
96 
97 		if (which == FPM_CLEANUP_PARENT_EXIT_MAIN) {
98 			if (ls->type == FPM_AF_UNIX) {
99 				unlink(ls->key);
100 			}
101 		}
102 		free(ls->key);
103 	}
104 
105 	if (env_value) {
106 		for (i = 0; i < socket_set_count; i++) {
107 			fpm_sockets_get_env_name(envname, i);
108 			setenv(envname, env_value + socket_set[i], 1);
109 		}
110 		fpm_sockets_get_env_name(envname, socket_set_count);
111 		unsetenv(envname);
112 		free(env_value);
113 	}
114 
115 	fpm_array_free(&sockets_list);
116 }
117 /* }}} */
118 
fpm_get_in_addr(struct sockaddr * sa)119 static void *fpm_get_in_addr(struct sockaddr *sa) /* {{{ */
120 {
121 	if (sa->sa_family == AF_INET) {
122 		return &(((struct sockaddr_in*)sa)->sin_addr);
123 	}
124 
125 	return &(((struct sockaddr_in6*)sa)->sin6_addr);
126 }
127 /* }}} */
128 
fpm_get_in_port(struct sockaddr * sa)129 static int fpm_get_in_port(struct sockaddr *sa) /* {{{ */
130 {
131 	if (sa->sa_family == AF_INET) {
132 		return ntohs(((struct sockaddr_in*)sa)->sin_port);
133 	}
134 
135 	return ntohs(((struct sockaddr_in6*)sa)->sin6_port);
136 }
137 /* }}} */
138 
fpm_sockets_hash_op(int sock,struct sockaddr * sa,char * key,int type,int op)139 static int fpm_sockets_hash_op(int sock, struct sockaddr *sa, char *key, int type, int op) /* {{{ */
140 {
141 	if (key == NULL) {
142 		switch (type) {
143 			case FPM_AF_INET : {
144 				key = alloca(INET6_ADDRSTRLEN+10);
145 				inet_ntop(sa->sa_family, fpm_get_in_addr(sa), key, INET6_ADDRSTRLEN);
146 				sprintf(key+strlen(key), ":%d", fpm_get_in_port(sa));
147 				break;
148 			}
149 
150 			case FPM_AF_UNIX : {
151 				struct sockaddr_un *sa_un = (struct sockaddr_un *) sa;
152 				key = alloca(strlen(sa_un->sun_path) + 1);
153 				strcpy(key, sa_un->sun_path);
154 				break;
155 			}
156 
157 			default :
158 				return -1;
159 		}
160 	}
161 
162 	switch (op) {
163 
164 		case FPM_GET_USE_SOCKET :
165 		{
166 			unsigned i;
167 			struct listening_socket_s *ls = sockets_list.data;
168 
169 			for (i = 0; i < sockets_list.used; i++, ls++) {
170 				if (!strcmp(ls->key, key)) {
171 					++ls->refcount;
172 					return ls->sock;
173 				}
174 			}
175 			break;
176 		}
177 
178 		case FPM_STORE_SOCKET :			/* inherited socket */
179 		case FPM_STORE_USE_SOCKET :		/* just created */
180 		{
181 			struct listening_socket_s *ls;
182 
183 			ls = fpm_array_push(&sockets_list);
184 			if (!ls) {
185 				break;
186 			}
187 
188 			if (op == FPM_STORE_SOCKET) {
189 				ls->refcount = 0;
190 			} else {
191 				ls->refcount = 1;
192 			}
193 			ls->type = type;
194 			ls->sock = sock;
195 			ls->key = strdup(key);
196 
197 			return 0;
198 		}
199 	}
200 	return -1;
201 }
202 /* }}} */
203 
fpm_sockets_new_listening_socket(struct fpm_worker_pool_s * wp,struct sockaddr * sa,int socklen)204 static int fpm_sockets_new_listening_socket(struct fpm_worker_pool_s *wp, struct sockaddr *sa, int socklen) /* {{{ */
205 {
206 	int flags = 1;
207 	int sock;
208 	mode_t saved_umask = 0;
209 
210 	sock = socket(sa->sa_family, SOCK_STREAM, 0);
211 
212 	if (0 > sock) {
213 		zlog(ZLOG_SYSERROR, "failed to create new listening socket: socket()");
214 		return -1;
215 	}
216 
217 	if (0 > setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags))) {
218 		zlog(ZLOG_WARNING, "failed to change socket attribute");
219 	}
220 
221 	if (wp->listen_address_domain == FPM_AF_UNIX) {
222 		if (fpm_socket_unix_test_connect((struct sockaddr_un *)sa, socklen) == 0) {
223 			zlog(ZLOG_ERROR, "Another FPM instance seems to already listen on %s", ((struct sockaddr_un *) sa)->sun_path);
224 			close(sock);
225 			return -1;
226 		}
227 		unlink( ((struct sockaddr_un *) sa)->sun_path);
228 		saved_umask = umask(0777 ^ wp->socket_mode);
229 	}
230 
231 	if (0 > bind(sock, sa, socklen)) {
232 		zlog(ZLOG_SYSERROR, "unable to bind listening socket for address '%s'", wp->config->listen_address);
233 		if (wp->listen_address_domain == FPM_AF_UNIX) {
234 			umask(saved_umask);
235 		}
236 		close(sock);
237 		return -1;
238 	}
239 
240 	if (wp->listen_address_domain == FPM_AF_UNIX) {
241 		char *path = ((struct sockaddr_un *) sa)->sun_path;
242 
243 		umask(saved_umask);
244 
245 		if (0 > fpm_unix_set_socket_permissions(wp, path)) {
246 			close(sock);
247 			return -1;
248 		}
249 	}
250 
251 	if (0 > listen(sock, wp->config->listen_backlog)) {
252 		zlog(ZLOG_SYSERROR, "failed to listen to address '%s'", wp->config->listen_address);
253 		close(sock);
254 		return -1;
255 	}
256 
257 #ifdef SO_SETFIB
258 	if (-1 < wp->config->listen_setfib) {
259 		if (routemax < wp->config->listen_setfib) {
260 			zlog(ZLOG_ERROR, "Invalid routing table id %d, max is %d", wp->config->listen_setfib, routemax);
261 			close(sock);
262 			return -1;
263 		}
264 
265 		if (0 > setsockopt(sock, SOL_SOCKET, SO_SETFIB, &wp->config->listen_setfib, sizeof(wp->config->listen_setfib))) {
266 			zlog(ZLOG_WARNING, "failed to change socket SO_SETFIB attribute");
267 		}
268 	}
269 #endif
270 
271 	return sock;
272 }
273 /* }}} */
274 
fpm_sockets_get_listening_socket(struct fpm_worker_pool_s * wp,struct sockaddr * sa,int socklen)275 static int fpm_sockets_get_listening_socket(struct fpm_worker_pool_s *wp, struct sockaddr *sa, int socklen) /* {{{ */
276 {
277 	int sock;
278 
279 	sock = fpm_sockets_hash_op(0, sa, 0, wp->listen_address_domain, FPM_GET_USE_SOCKET);
280 	if (sock >= 0) {
281 		return sock;
282 	}
283 
284 	sock = fpm_sockets_new_listening_socket(wp, sa, socklen);
285 	fpm_sockets_hash_op(sock, sa, 0, wp->listen_address_domain, FPM_STORE_USE_SOCKET);
286 
287 	return sock;
288 }
289 /* }}} */
290 
fpm_sockets_domain_from_address(char * address)291 enum fpm_address_domain fpm_sockets_domain_from_address(char *address) /* {{{ */
292 {
293 	if (strchr(address, ':')) {
294 		return FPM_AF_INET;
295 	}
296 
297 	if (strlen(address) == strspn(address, "0123456789")) {
298 		return FPM_AF_INET;
299 	}
300 	return FPM_AF_UNIX;
301 }
302 /* }}} */
303 
fpm_socket_af_inet_socket_by_addr(struct fpm_worker_pool_s * wp,const char * addr,const char * port)304 static int fpm_socket_af_inet_socket_by_addr(struct fpm_worker_pool_s *wp, const char *addr, const char *port) /* {{{ */
305 {
306 	struct addrinfo hints, *servinfo, *p;
307 	char tmpbuf[INET6_ADDRSTRLEN];
308 	int status;
309 	int sock = -1;
310 
311 	memset(&hints, 0, sizeof hints);
312 	hints.ai_family = AF_UNSPEC;
313 	hints.ai_socktype = SOCK_STREAM;
314 
315 	if ((status = getaddrinfo(addr, port, &hints, &servinfo)) != 0) {
316 		zlog(ZLOG_ERROR, "getaddrinfo: %s", gai_strerror(status));
317 		return -1;
318 	}
319 
320 	for (p = servinfo; p != NULL; p = p->ai_next) {
321 		inet_ntop(p->ai_family, fpm_get_in_addr(p->ai_addr), tmpbuf, INET6_ADDRSTRLEN);
322 		if (sock < 0) {
323 			if ((sock = fpm_sockets_get_listening_socket(wp, p->ai_addr, p->ai_addrlen)) != -1) {
324 				zlog(ZLOG_DEBUG, "Found address for %s, socket opened on %s", addr, tmpbuf);
325 			}
326 		} else {
327 			zlog(ZLOG_WARNING, "Found multiple addresses for %s, %s ignored", addr, tmpbuf);
328 		}
329 	}
330 
331 	freeaddrinfo(servinfo);
332 
333 	return sock;
334 }
335 /* }}} */
336 
fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s * wp)337 static int fpm_socket_af_inet_listening_socket(struct fpm_worker_pool_s *wp) /* {{{ */
338 {
339 	char *dup_address = strdup(wp->config->listen_address);
340 	char *port_str = strrchr(dup_address, ':');
341 	char *addr = NULL;
342 	int addr_len;
343 	int port = 0;
344 	int sock = -1;
345 
346 	if (port_str) { /* this is host:port pair */
347 		*port_str++ = '\0';
348 		port = atoi(port_str);
349 		addr = dup_address;
350 
351 		/* strip brackets from address for getaddrinfo */
352 		addr_len = strlen(addr);
353 		if (addr[0] == '[' && addr[addr_len - 1] == ']') {
354 			addr[addr_len - 1] = '\0';
355 			addr++;
356 		}
357 
358 	} else if (strlen(dup_address) == strspn(dup_address, "0123456789")) { /* this is port */
359 		port = atoi(dup_address);
360 		port_str = dup_address;
361 	}
362 
363 	if (port < 1 || port > 65535) {
364 		zlog(ZLOG_ERROR, "invalid port value '%s'", port_str);
365 		free(dup_address);
366 		return -1;
367 	}
368 
369 	if (addr) {
370 		/* Bind a specific address */
371 		sock = fpm_socket_af_inet_socket_by_addr(wp, addr, port_str);
372 	} else {
373 		/* Bind ANYADDR
374 		 *
375 		 * Try "::" first as that covers IPv6 ANYADDR and mapped IPv4 ANYADDR
376 		 * silencing warnings since failure is an option
377 		 *
378 		 * If that fails (because AF_INET6 is unsupported) retry with 0.0.0.0
379 		 */
380 		int old_level = zlog_set_level(ZLOG_ALERT);
381 		sock = fpm_socket_af_inet_socket_by_addr(wp, "::", port_str);
382 		zlog_set_level(old_level);
383 
384 		if (sock < 0) {
385 			zlog(ZLOG_NOTICE, "Failed implicitly binding to ::, retrying with 0.0.0.0");
386 			sock = fpm_socket_af_inet_socket_by_addr(wp, "0.0.0.0", port_str);
387 		}
388 	}
389 
390 	free(dup_address);
391 
392 	return sock;
393 }
394 /* }}} */
395 
fpm_socket_af_unix_listening_socket(struct fpm_worker_pool_s * wp)396 static int fpm_socket_af_unix_listening_socket(struct fpm_worker_pool_s *wp) /* {{{ */
397 {
398 	struct sockaddr_un sa_un;
399     size_t socket_length = sizeof(sa_un.sun_path);
400     size_t address_length = strlen(wp->config->listen_address);
401 
402 	memset(&sa_un, 0, sizeof(sa_un));
403 	strlcpy(sa_un.sun_path, wp->config->listen_address, socket_length);
404 
405     if (address_length >= socket_length) {
406         zlog(
407             ZLOG_WARNING,
408             "[pool %s] cannot bind to UNIX socket '%s' as path is too long (found length: %zu, "
409 				"maximal length: %zu), trying cut socket path instead '%s'",
410             wp->config->name,
411 			wp->config->listen_address,
412 			address_length,
413 			socket_length,
414 			sa_un.sun_path
415         );
416     }
417 
418 	sa_un.sun_family = AF_UNIX;
419 	return fpm_sockets_get_listening_socket(wp, (struct sockaddr *) &sa_un, sizeof(struct sockaddr_un));
420 }
421 /* }}} */
422 
423 #ifdef SO_SETFIB
fpm_socket_setfib_init(void)424 static zend_result fpm_socket_setfib_init(void)
425 {
426 	/* potentially up to 65536 but needs to check the actual cap beforehand */
427 	size_t len = sizeof(routemax);
428 	if (sysctlbyname("net.fibs", &routemax, &len, NULL, 0) < 0) {
429 		zlog(ZLOG_ERROR, "failed to get max routing table");
430 		return FAILURE;
431 	}
432 
433 	return SUCCESS;
434 }
435 #endif
436 
fpm_sockets_init_main(void)437 int fpm_sockets_init_main(void)
438 {
439 	unsigned i, lq_len;
440 	struct fpm_worker_pool_s *wp;
441 	char envname[32];
442 	char sockpath[256];
443 	char *inherited;
444 	struct listening_socket_s *ls;
445 
446 	if (0 == fpm_array_init(&sockets_list, sizeof(struct listening_socket_s), 10)) {
447 		return -1;
448 	}
449 
450 #ifdef SO_SETFIB
451 	if (fpm_socket_setfib_init() == FAILURE) {
452 		return -1;
453 	}
454 #endif
455 
456 	/* import inherited sockets */
457 	for (i = 0; i < FPM_ENV_SOCKET_SET_MAX; i++) {
458 		fpm_sockets_get_env_name(envname, i);
459 		inherited = getenv(envname);
460 		if (!inherited) {
461 			break;
462 		}
463 
464 		while (inherited && *inherited) {
465 			char *comma = strchr(inherited, ',');
466 			int type, fd_no;
467 			char *eq;
468 
469 			if (comma) {
470 				*comma = '\0';
471 			}
472 
473 			eq = strchr(inherited, '=');
474 			if (eq) {
475 				int sockpath_len = eq - inherited;
476 				if (sockpath_len > 255) {
477 					/* this should never happen as UDS limit is lower */
478 					sockpath_len = 255;
479 				}
480 				memcpy(sockpath, inherited, sockpath_len);
481 				sockpath[sockpath_len] = '\0';
482 				fd_no = atoi(eq + 1);
483 				type = fpm_sockets_domain_from_address(sockpath);
484 				zlog(ZLOG_NOTICE, "using inherited socket fd=%d, \"%s\"", fd_no, sockpath);
485 				fpm_sockets_hash_op(fd_no, 0, sockpath, type, FPM_STORE_SOCKET);
486 			}
487 
488 			if (comma) {
489 				inherited = comma + 1;
490 			} else {
491 				inherited = 0;
492 			}
493 		}
494 	}
495 
496 	/* create all required sockets */
497 	for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
498 		switch (wp->listen_address_domain) {
499 			case FPM_AF_INET :
500 				wp->listening_socket = fpm_socket_af_inet_listening_socket(wp);
501 				break;
502 
503 			case FPM_AF_UNIX :
504 				if (0 > fpm_unix_resolve_socket_permissions(wp)) {
505 					return -1;
506 				}
507 				wp->listening_socket = fpm_socket_af_unix_listening_socket(wp);
508 				break;
509 		}
510 
511 		if (wp->listening_socket == -1) {
512 			return -1;
513 		}
514 
515 	if (wp->listen_address_domain == FPM_AF_INET && fpm_socket_get_listening_queue(wp->listening_socket, NULL, &lq_len) >= 0) {
516 			fpm_scoreboard_update(-1, -1, -1, (int)lq_len, -1, -1, 0, FPM_SCOREBOARD_ACTION_SET, wp->scoreboard);
517 		}
518 	}
519 
520 	/* close unused sockets that was inherited */
521 	ls = sockets_list.data;
522 
523 	for (i = 0; i < sockets_list.used; ) {
524 		if (ls->refcount == 0) {
525 			close(ls->sock);
526 			if (ls->type == FPM_AF_UNIX) {
527 				unlink(ls->key);
528 			}
529 			free(ls->key);
530 			fpm_array_item_remove(&sockets_list, i);
531 		} else {
532 			++i;
533 			++ls;
534 		}
535 	}
536 
537 	if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_sockets_cleanup, 0)) {
538 		return -1;
539 	}
540 	return 0;
541 }
542 
543 #if HAVE_FPM_LQ
544 
545 #ifdef HAVE_LQ_TCP_INFO
546 
547 #include <netinet/tcp.h>
548 
fpm_socket_get_listening_queue(int sock,unsigned * cur_lq,unsigned * max_lq)549 int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
550 {
551 	struct tcp_info info;
552 	socklen_t len = sizeof(info);
553 
554 	if (0 > getsockopt(sock, IPPROTO_TCP, TCP_INFO, &info, &len)) {
555 		zlog(ZLOG_SYSERROR, "failed to retrieve TCP_INFO for socket");
556 		return -1;
557 	}
558 #if defined(__FreeBSD__) || defined(__NetBSD__)
559 	if (info.__tcpi_sacked == 0) {
560 		return -1;
561 	}
562 
563 	if (cur_lq) {
564 		*cur_lq = info.__tcpi_unacked;
565 	}
566 
567 	if (max_lq) {
568 		*max_lq = info.__tcpi_sacked;
569 	}
570 #else
571 	/* kernel >= 2.6.24 return non-zero here, that means operation is supported */
572 	if (info.tcpi_sacked == 0) {
573 		return -1;
574 	}
575 
576 	if (cur_lq) {
577 		*cur_lq = info.tcpi_unacked;
578 	}
579 
580 	if (max_lq) {
581 		*max_lq = info.tcpi_sacked;
582 	}
583 #endif
584 
585 	return 0;
586 }
587 
588 #elif defined(HAVE_LQ_TCP_CONNECTION_INFO)
589 
590 #include <netinet/tcp.h>
591 
fpm_socket_get_listening_queue(int sock,unsigned * cur_lq,unsigned * max_lq)592 int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
593 {
594 	struct tcp_connection_info info;
595 	socklen_t len = sizeof(info);
596 
597 	if (0 > getsockopt(sock, IPPROTO_TCP, TCP_CONNECTION_INFO, &info, &len)) {
598 		zlog(ZLOG_SYSERROR, "failed to retrieve TCP_CONNECTION_INFO for socket");
599 		return -1;
600 	}
601 
602 	if (cur_lq) {
603 		*cur_lq = info.tcpi_tfo_syn_data_acked;
604 	}
605 
606 	if (max_lq) {
607 		*max_lq = 0;
608 	}
609 
610 	return 0;
611 }
612 
613 #elif defined(HAVE_LQ_SO_LISTENQ)
614 
fpm_socket_get_listening_queue(int sock,unsigned * cur_lq,unsigned * max_lq)615 int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
616 {
617 	int val;
618 	socklen_t len = sizeof(val);
619 
620 	if (cur_lq) {
621 		if (0 > getsockopt(sock, SOL_SOCKET, SO_LISTENQLEN, &val, &len)) {
622 			return -1;
623 		}
624 
625 		*cur_lq = val;
626 	}
627 
628 	if (max_lq) {
629 		if (0 > getsockopt(sock, SOL_SOCKET, SO_LISTENQLIMIT, &val, &len)) {
630 			return -1;
631 		}
632 
633 		*max_lq = val;
634 	}
635 
636 	return 0;
637 }
638 
639 #endif
640 
641 #else
642 
fpm_socket_get_listening_queue(int sock,unsigned * cur_lq,unsigned * max_lq)643 int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
644 {
645 	return -1;
646 }
647 
648 #endif
649 
fpm_socket_unix_test_connect(struct sockaddr_un * sock,size_t socklen)650 int fpm_socket_unix_test_connect(struct sockaddr_un *sock, size_t socklen) /* {{{ */
651 {
652 	int fd;
653 
654 	if (!sock || sock->sun_family != AF_UNIX) {
655 		return -1;
656 	}
657 
658 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
659 		return -1;
660 	}
661 
662 	if (connect(fd, (struct sockaddr *)sock, socklen) == -1) {
663 		close(fd);
664 		return -1;
665 	}
666 
667 	close(fd);
668 	return 0;
669 }
670 /* }}} */
671