1d9638e54Smws /*
2d9638e54Smws  * CDDL HEADER START
3d9638e54Smws  *
4d9638e54Smws  * The contents of this file are subject to the terms of the
56925cc42Scindi  * Common Development and Distribution License (the "License").
66925cc42Scindi  * You may not use this file except in compliance with the License.
7d9638e54Smws  *
8d9638e54Smws  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d9638e54Smws  * or http://www.opensolaris.org/os/licensing.
10d9638e54Smws  * See the License for the specific language governing permissions
11d9638e54Smws  * and limitations under the License.
12d9638e54Smws  *
13d9638e54Smws  * When distributing Covered Code, include this CDDL HEADER in each
14d9638e54Smws  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d9638e54Smws  * If applicable, add the following below this CDDL HEADER, with the
16d9638e54Smws  * fields enclosed by brackets "[]" replaced with your own identifying
17d9638e54Smws  * information: Portions Copyright [yyyy] [name of copyright owner]
18d9638e54Smws  *
19d9638e54Smws  * CDDL HEADER END
20d9638e54Smws  */
21d9638e54Smws 
22d9638e54Smws /*
23b5875dddSJames Kremer  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24d9638e54Smws  */
25d9638e54Smws 
26d9638e54Smws #include <sys/types.h>
27d9638e54Smws #include <sys/socket.h>
28d9638e54Smws #include <sys/sysmacros.h>
29d9638e54Smws #include <sys/fm/protocol.h>
30d9638e54Smws 
31d9638e54Smws #include <netinet/in.h>
32d9638e54Smws #include <arpa/inet.h>
33d9638e54Smws 
34d9638e54Smws #include <strings.h>
35d9638e54Smws #include <unistd.h>
36d9638e54Smws #include <pthread.h>
37d9638e54Smws #include <fcntl.h>
38d9638e54Smws #include <errno.h>
39d9638e54Smws #include <netdb.h>
40d9638e54Smws #include <poll.h>
41b5875dddSJames Kremer #include <stdarg.h>
42d9638e54Smws 
43d9638e54Smws #include <fm/fmd_api.h>
44d9638e54Smws 
45d9638e54Smws #define	IP_MAGIC	"\177FMA" /* magic string identifying a packet header */
46d9638e54Smws #define	IP_MAGLEN	4	/* length of magic string */
47b5875dddSJames Kremer #define	IP_DEBUG_OFF	0	/* No informational debugging printed */
48b5875dddSJames Kremer #define	IP_DEBUG_FINE	1	/* Basic debug information printed (default) */
49b5875dddSJames Kremer #define	IP_DEBUG_FINER	2	/* More debug information printed. */
50b5875dddSJames Kremer #define	IP_DEBUG_FINEST	3	/* All debug information printed */
51d9638e54Smws 
52d9638e54Smws typedef struct ip_hdr {
53d9638e54Smws 	char iph_magic[IP_MAGLEN]; /* magic string */
54d9638e54Smws 	uint32_t iph_size;	/* packed size */
55d9638e54Smws } ip_hdr_t;
56d9638e54Smws 
57d9638e54Smws typedef struct ip_buf {
58d9638e54Smws 	void *ipb_buf;		/* data buffer */
59d9638e54Smws 	size_t ipb_size;	/* size of buffer */
60d9638e54Smws } ip_buf_t;
61d9638e54Smws 
62dc8f2b85SJames Kremer typedef struct ip_cinfo {	    /* Connection specific information */
63dc8f2b85SJames Kremer 	struct addrinfo *ipc_addr;  /* Connection address(es) */
64dc8f2b85SJames Kremer 	char *ipc_name;		    /* The name of the server or interface */
65dc8f2b85SJames Kremer 	int ipc_retry;		    /* The number of connection retries */
66dc8f2b85SJames Kremer 	boolean_t ipc_accept;	    /* Will connection accept clients */
67dc8f2b85SJames Kremer 	id_t ipc_timer;		    /* FMD timer id for connection */
68dc8f2b85SJames Kremer 	struct ip_cinfo *ipc_next;  /* Next conneciton in list */
69b5875dddSJames Kremer } ip_cinfo_t;
70b5875dddSJames Kremer 
71d9638e54Smws typedef struct ip_xprt {
72d9638e54Smws 	fmd_xprt_t *ipx_xprt;	/* transport handle */
73d9638e54Smws 	int ipx_flags;		/* transport flags */
74d9638e54Smws 	int ipx_fd;		/* socket file descriptor */
75d9638e54Smws 	int ipx_done;		/* flag indicating connection closed */
76d9638e54Smws 	pthread_t ipx_tid;	/* recv-side auxiliary thread */
77d9638e54Smws 	ip_buf_t ipx_sndbuf;	/* buffer for sending events */
78d9638e54Smws 	ip_buf_t ipx_rcvbuf;	/* buffer for receiving events */
79b5875dddSJames Kremer 	ip_cinfo_t *ipx_cinfo;	/* info for reconnect */
80dc8f2b85SJames Kremer 	id_t ipx_spnd_timer;	/* connection suspend timer */
81b5875dddSJames Kremer 	char *ipx_addr;		/* address:port of remote connection */
82b5875dddSJames Kremer 	struct ip_xprt *ipx_next;	/* next ip_xprt in global list */
83d9638e54Smws } ip_xprt_t;
84d9638e54Smws 
85dc8f2b85SJames Kremer #define	IPX_ID(a) ((a)->ipx_addr == NULL ? "(Not connected)" : (a)->ipx_addr)
86b5875dddSJames Kremer 
87d9638e54Smws typedef struct ip_stat {
88d9638e54Smws 	fmd_stat_t ips_accfail;	/* failed accepts */
89d9638e54Smws 	fmd_stat_t ips_badmagic; /* invalid packet headers */
90d9638e54Smws 	fmd_stat_t ips_packfail; /* failed packs */
91d9638e54Smws 	fmd_stat_t ips_unpackfail; /* failed unpacks */
92d9638e54Smws } ip_stat_t;
93d9638e54Smws 
94b5875dddSJames Kremer static void ip_xprt_create(fmd_xprt_t *, int, int, ip_cinfo_t *, char *);
95d9638e54Smws static void ip_xprt_destroy(ip_xprt_t *);
96d9638e54Smws 
97d9638e54Smws static ip_stat_t ip_stat = {
98d9638e54Smws 	{ "accfail", FMD_TYPE_UINT64, "failed accepts" },
99d9638e54Smws 	{ "badmagic", FMD_TYPE_UINT64, "invalid packet headers" },
100d9638e54Smws 	{ "packfail", FMD_TYPE_UINT64, "failed packs" },
101d9638e54Smws 	{ "unpackfail", FMD_TYPE_UINT64, "failed unpacks" },
102d9638e54Smws };
103d9638e54Smws 
104d9638e54Smws static fmd_hdl_t *ip_hdl;	/* module handle */
105d9638e54Smws static pthread_mutex_t ip_lock;	/* lock for ip_xps list */
106d9638e54Smws static ip_xprt_t *ip_xps;	/* list of active transports */
107dc8f2b85SJames Kremer static pthread_mutex_t ip_conns_lock;	/* lock for ip_conns list */
108dc8f2b85SJames Kremer static ip_cinfo_t *ip_conns;	/* list of all configured connection info */
109d9638e54Smws static nvlist_t *ip_auth;	/* authority to use for transport(s) */
110d9638e54Smws static size_t ip_size;		/* default buffer size */
111d9638e54Smws static volatile int ip_quit;	/* signal to quit */
112d9638e54Smws static int ip_qlen;		/* queue length for listen(3SOCKET) */
113d9638e54Smws static int ip_mtbf;		/* mtbf for simulating packet drop */
114cbf75e67SStephen Hanson static int ip_external;		/* set transport to be "external" */
115cbf75e67SStephen Hanson static int ip_no_remote_repair;	/* disallow remote repair */
116cbf75e67SStephen Hanson static int ip_hconly;		/* only cache faults that are hc-scheme */
117cbf75e67SStephen Hanson static int ip_rdonly;		/* force transport to be rdonly */
118cbf75e67SStephen Hanson static int ip_hc_present_only;	/* only cache faults if hc-scheme and present */
119cbf75e67SStephen Hanson static char *ip_domain_name;	/* set domain name for received list.suspects */
120d9638e54Smws static hrtime_t ip_burp;	/* make mtbf slower by adding this much delay */
121d9638e54Smws static int ip_translate;	/* call fmd_xprt_translate() before sending */
122d9638e54Smws static char *ip_port;		/* port to connect to (or bind to if server) */
123b5875dddSJames Kremer static int ip_retry;		/* retry count for ip_xprt_setup() -1=forever */
124d9638e54Smws static hrtime_t ip_sleep;	/* sleep delay for ip_xprt_setup() */
125b5875dddSJames Kremer static int ip_debug_level;	/* level for printing debug messages */
126b5875dddSJames Kremer 
127b5875dddSJames Kremer /*
128b5875dddSJames Kremer  * Prints a debug message to the fmd debug framework if the debug level is set
129b5875dddSJames Kremer  * to at least the given level.
130b5875dddSJames Kremer  */
131b5875dddSJames Kremer static void
ip_debug(int level,char * fmt,...)132b5875dddSJames Kremer ip_debug(int level, char *fmt, ...)
133b5875dddSJames Kremer {
134b5875dddSJames Kremer 	if (ip_debug_level >= level) {
135b5875dddSJames Kremer 		va_list args;
136b5875dddSJames Kremer 		va_start(args, fmt);
137b5875dddSJames Kremer 		fmd_hdl_vdebug(ip_hdl, fmt, args);
138b5875dddSJames Kremer 		va_end(args);
139b5875dddSJames Kremer 	}
140b5875dddSJames Kremer }
141d9638e54Smws 
142d9638e54Smws /*
143d9638e54Smws  * Allocate space in ipx_sndbuf for a header and a packed XDR encoding of
144d9638e54Smws  * the specified nvlist, and then send the buffer to our remote peer.
145d9638e54Smws  */
146d9638e54Smws static int
ip_fmdo_send(fmd_hdl_t * hdl,fmd_xprt_t * xp,fmd_event_t * ep,nvlist_t * nvl)147af9cbda5SJames Kremer ip_fmdo_send(fmd_hdl_t *hdl, fmd_xprt_t *xp, fmd_event_t *ep, nvlist_t *nvl)
148d9638e54Smws {
149af9cbda5SJames Kremer 	ip_xprt_t *ipx;
150d9638e54Smws 	size_t size, nvsize;
151d9638e54Smws 	char *buf, *nvbuf;
152d9638e54Smws 	ip_hdr_t *iph;
153d9638e54Smws 	ssize_t r, n;
154d9638e54Smws 	int err;
155d9638e54Smws 
156af9cbda5SJames Kremer 	if (xp == NULL) {
157af9cbda5SJames Kremer 		ip_debug(IP_DEBUG_FINE, "ip_fmdo_send failed: xp=NULL\n");
158af9cbda5SJames Kremer 		return (FMD_SEND_FAILED);
159af9cbda5SJames Kremer 	}
160af9cbda5SJames Kremer 	ipx = fmd_xprt_getspecific(hdl, xp);
161af9cbda5SJames Kremer 
162d9638e54Smws 	/*
163d9638e54Smws 	 * For testing purposes, if ip_mtbf is non-zero, use this to pseudo-
164d9638e54Smws 	 * randomly simulate the need for retries.  If ip_burp is also set,
165d9638e54Smws 	 * then we also suspend the transport for a bit and wake it up again.
166d9638e54Smws 	 */
167d9638e54Smws 	if (ip_mtbf != 0 && gethrtime() % ip_mtbf == 0) {
168d9638e54Smws 		if (ip_burp != 0) {
169b5875dddSJames Kremer 			ip_debug(IP_DEBUG_FINE, "burping ipx %s", IPX_ID(ipx));
170d9638e54Smws 			ipx->ipx_flags |= FMD_XPRT_SUSPENDED;
171dc8f2b85SJames Kremer 			ipx->ipx_spnd_timer = fmd_timer_install(
172dc8f2b85SJames Kremer 			    ip_hdl, ipx, NULL, ip_burp);
173d9638e54Smws 			fmd_xprt_suspend(ip_hdl, xp);
174d9638e54Smws 		}
175d9638e54Smws 		return (FMD_SEND_RETRY);
176d9638e54Smws 	}
177d9638e54Smws 
178d9638e54Smws 	if (ip_translate && (nvl = fmd_xprt_translate(hdl, xp, ep)) == NULL) {
179d9638e54Smws 		fmd_hdl_error(hdl, "failed to translate event %p", (void *)ep);
180d9638e54Smws 		return (FMD_SEND_FAILED);
181d9638e54Smws 	}
182d9638e54Smws 
183d9638e54Smws 	(void) nvlist_size(nvl, &nvsize, NV_ENCODE_XDR);
184d9638e54Smws 	size = r = sizeof (ip_hdr_t) + nvsize;
185d9638e54Smws 
186d9638e54Smws 	if (ipx->ipx_sndbuf.ipb_size < size) {
187d9638e54Smws 		fmd_hdl_free(hdl, ipx->ipx_sndbuf.ipb_buf,
188d9638e54Smws 		    ipx->ipx_sndbuf.ipb_size);
189d9638e54Smws 		ipx->ipx_sndbuf.ipb_size = P2ROUNDUP(size, 16);
190d9638e54Smws 		ipx->ipx_sndbuf.ipb_buf = fmd_hdl_alloc(hdl,
191d9638e54Smws 		    ipx->ipx_sndbuf.ipb_size, FMD_SLEEP);
192d9638e54Smws 	}
193d9638e54Smws 
194d9638e54Smws 	buf = ipx->ipx_sndbuf.ipb_buf;
195d9638e54Smws 	iph = (ip_hdr_t *)(uintptr_t)buf;
196d9638e54Smws 	nvbuf = buf + sizeof (ip_hdr_t);
197d9638e54Smws 
198d9638e54Smws 	bcopy(IP_MAGIC, iph->iph_magic, IP_MAGLEN);
199d9638e54Smws 	iph->iph_size = htonl(nvsize);
200d9638e54Smws 	err = nvlist_pack(nvl, &nvbuf, &nvsize, NV_ENCODE_XDR, 0);
201d9638e54Smws 
202d9638e54Smws 	if (ip_translate)
203d9638e54Smws 		nvlist_free(nvl);
204d9638e54Smws 
205d9638e54Smws 	if (err != 0) {
206d9638e54Smws 		fmd_hdl_error(ip_hdl, "failed to pack event for "
207d9638e54Smws 		    "transport %p: %s\n", (void *)ipx->ipx_xprt, strerror(err));
208d9638e54Smws 		ip_stat.ips_packfail.fmds_value.ui64++;
209d9638e54Smws 		return (FMD_SEND_FAILED);
210d9638e54Smws 	}
211d9638e54Smws 
212d9638e54Smws 	while (!ip_quit && r != 0) {
213d9638e54Smws 		if ((n = send(ipx->ipx_fd, buf, r, 0)) < 0) {
214d9638e54Smws 			if (errno != EINTR && errno != EWOULDBLOCK) {
215b5875dddSJames Kremer 				ip_debug(IP_DEBUG_FINE,
216b5875dddSJames Kremer 				    "failed to send to %s", IPX_ID(ipx));
217d9638e54Smws 				return (FMD_SEND_FAILED);
218d9638e54Smws 			}
219d9638e54Smws 			continue;
220d9638e54Smws 		}
221d9638e54Smws 		buf += n;
222d9638e54Smws 		r -= n;
223d9638e54Smws 	}
224d9638e54Smws 
225b5875dddSJames Kremer 	ip_debug(IP_DEBUG_FINEST, "Sent event %d bytes to %s",
226b5875dddSJames Kremer 	    size, IPX_ID(ipx));
227d9638e54Smws 	return (FMD_SEND_SUCCESS);
228d9638e54Smws }
229d9638e54Smws 
230af9cbda5SJames Kremer /*
231af9cbda5SJames Kremer  * Sends events over transports that are configured read only.  When the module
232af9cbda5SJames Kremer  * is in read only mode it will receive all events and only send events that
233af9cbda5SJames Kremer  * have a subscription set.
234af9cbda5SJames Kremer  *
235af9cbda5SJames Kremer  * The configuration file will have to set prop ip_rdonly true and also
236af9cbda5SJames Kremer  * subscribe for events that are desired to be sent over the transport in order
237af9cbda5SJames Kremer  * for this function to be used.
238af9cbda5SJames Kremer  */
239af9cbda5SJames Kremer /* ARGSUSED */
240af9cbda5SJames Kremer static void
ip_fmdo_recv(fmd_hdl_t * hdl,fmd_event_t * ep,nvlist_t * nvl,const char * class)241af9cbda5SJames Kremer ip_fmdo_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
242af9cbda5SJames Kremer {
243af9cbda5SJames Kremer 	int err;
244af9cbda5SJames Kremer 	ip_xprt_t *ipx;
245af9cbda5SJames Kremer 
246dc8f2b85SJames Kremer 	if (ip_rdonly && !ip_quit) {
247af9cbda5SJames Kremer 		(void) pthread_mutex_lock(&ip_lock);
248af9cbda5SJames Kremer 
249af9cbda5SJames Kremer 		for (ipx = ip_xps; ipx != NULL; ipx = ipx->ipx_next) {
250af9cbda5SJames Kremer 			err = ip_fmdo_send(hdl, ipx->ipx_xprt, ep, nvl);
251af9cbda5SJames Kremer 			while (FMD_SEND_RETRY == err) {
252af9cbda5SJames Kremer 				err = ip_fmdo_send(hdl, ipx->ipx_xprt, ep, nvl);
253af9cbda5SJames Kremer 			}
254af9cbda5SJames Kremer 		}
255af9cbda5SJames Kremer 		(void) pthread_mutex_unlock(&ip_lock);
256af9cbda5SJames Kremer 	}
257af9cbda5SJames Kremer }
258af9cbda5SJames Kremer 
259d9638e54Smws /*
260d9638e54Smws  * Receive a chunk of data of the specified size from our remote peer.  The
261d9638e54Smws  * data is received into ipx_rcvbuf, and then a pointer to the buffer is
262d9638e54Smws  * returned.  NOTE: The data is only valid until the next call to ip_xprt_recv.
263d9638e54Smws  * If the connection breaks or ip_quit is set during receive, NULL is returned.
264d9638e54Smws  */
265d9638e54Smws static void *
ip_xprt_recv(ip_xprt_t * ipx,size_t size)266d9638e54Smws ip_xprt_recv(ip_xprt_t *ipx, size_t size)
267d9638e54Smws {
268d9638e54Smws 	char *buf = ipx->ipx_rcvbuf.ipb_buf;
269d9638e54Smws 	ssize_t n, r = size;
270d9638e54Smws 
271d9638e54Smws 	if (ipx->ipx_rcvbuf.ipb_size < size) {
272d9638e54Smws 		fmd_hdl_free(ip_hdl, ipx->ipx_rcvbuf.ipb_buf,
273d9638e54Smws 		    ipx->ipx_rcvbuf.ipb_size);
274d9638e54Smws 		ipx->ipx_rcvbuf.ipb_size = P2ROUNDUP(size, 16);
275d9638e54Smws 		ipx->ipx_rcvbuf.ipb_buf = buf = fmd_hdl_alloc(ip_hdl,
276d9638e54Smws 		    ipx->ipx_rcvbuf.ipb_size, FMD_SLEEP);
277d9638e54Smws 	}
278d9638e54Smws 
279d9638e54Smws 	while (!ip_quit && r != 0) {
280d9638e54Smws 		if ((n = recv(ipx->ipx_fd, buf, r, MSG_WAITALL)) == 0) {
281d9638e54Smws 			ipx->ipx_done++;
282d9638e54Smws 			return (NULL);
283d9638e54Smws 		}
284d9638e54Smws 
285d9638e54Smws 		if (n < 0) {
286d9638e54Smws 			if (errno != EINTR && errno != EWOULDBLOCK) {
287b5875dddSJames Kremer 				ip_debug(IP_DEBUG_FINE,
288b5875dddSJames Kremer 				    "failed to recv on ipx %s", IPX_ID(ipx));
289d9638e54Smws 			}
290d9638e54Smws 			continue;
291d9638e54Smws 		}
292b5875dddSJames Kremer 		/* Reset retry counter after a successful connection */
293b5875dddSJames Kremer 		if (ipx->ipx_cinfo) {
294dc8f2b85SJames Kremer 			ipx->ipx_cinfo->ipc_retry = ip_retry;
295b5875dddSJames Kremer 		}
296d9638e54Smws 
297d9638e54Smws 		buf += n;
298d9638e54Smws 		r -= n;
299d9638e54Smws 	}
300d9638e54Smws 
301d9638e54Smws 	return (r ? NULL: ipx->ipx_rcvbuf.ipb_buf);
302d9638e54Smws }
303d9638e54Smws 
304b5875dddSJames Kremer /*
305b5875dddSJames Kremer  * Sets the address/port of the remote connection in the connection info struct
306b5875dddSJames Kremer  * This is called after a TCP session has been set up with a known remote
307b5875dddSJames Kremer  * address (sap)
308b5875dddSJames Kremer  */
309b5875dddSJames Kremer static void
ip_xprt_set_addr(ip_xprt_t * ipx,const struct sockaddr * sap)310b5875dddSJames Kremer ip_xprt_set_addr(ip_xprt_t *ipx, const struct sockaddr *sap)
311d9638e54Smws {
312d9638e54Smws 	const struct sockaddr_in6 *sin6 = (const void *)sap;
313d9638e54Smws 	const struct sockaddr_in *sin = (const void *)sap;
314d9638e54Smws 
315d9638e54Smws 	char buf[INET6_ADDRSTRLEN + 16];
316d9638e54Smws 	struct in_addr v4addr;
317d9638e54Smws 	in_port_t port;
318b5875dddSJames Kremer 	int n;
319d9638e54Smws 
320af9cbda5SJames Kremer 	ip_debug(IP_DEBUG_FINER, "Enter ip_xprt_set_addr");
321d9638e54Smws 
322d9638e54Smws 	if (sap->sa_family == AF_INET6 &&
323d9638e54Smws 	    IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
324d9638e54Smws 		IN6_V4MAPPED_TO_INADDR(&sin6->sin6_addr, &v4addr);
325d9638e54Smws 		(void) inet_ntop(AF_INET, &v4addr, buf, sizeof (buf));
326d9638e54Smws 		port = ntohs(sin6->sin6_port);
327d9638e54Smws 	} else if (sap->sa_family == AF_INET6) {
328d9638e54Smws 		(void) inet_ntop(AF_INET6, &sin6->sin6_addr, buf, sizeof (buf));
329d9638e54Smws 		port = ntohs(sin6->sin6_port);
330d9638e54Smws 	} else {
331d9638e54Smws 		(void) inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof (buf));
332d9638e54Smws 		port = ntohs(sin->sin_port);
333d9638e54Smws 	}
334d9638e54Smws 
335d9638e54Smws 	n = strlen(buf);
336d9638e54Smws 	(void) snprintf(buf + n, sizeof (buf) - n, ":%u", port);
337b5875dddSJames Kremer 
338b5875dddSJames Kremer 	if (ipx->ipx_addr)
339b5875dddSJames Kremer 		fmd_hdl_strfree(ip_hdl, ipx->ipx_addr);
340b5875dddSJames Kremer 	ipx->ipx_addr = fmd_hdl_strdup(ip_hdl, buf, FMD_SLEEP);
341b5875dddSJames Kremer 	ip_debug(IP_DEBUG_FINE, "connection addr is %s on %p",
342b5875dddSJames Kremer 	    ipx->ipx_addr, (void *)ipx);
343b5875dddSJames Kremer }
344b5875dddSJames Kremer 
345b5875dddSJames Kremer static nvlist_t *
ip_xprt_auth(ip_xprt_t * ipx)346b5875dddSJames Kremer ip_xprt_auth(ip_xprt_t *ipx)
347b5875dddSJames Kremer {
348b5875dddSJames Kremer 	nvlist_t *nvl;
349b5875dddSJames Kremer 	int err;
350b5875dddSJames Kremer 
351b5875dddSJames Kremer 	ip_debug(IP_DEBUG_FINER, "Enter ip_xprt_auth");
352b5875dddSJames Kremer 
353b5875dddSJames Kremer 	if (ip_auth != NULL)
354b5875dddSJames Kremer 		err = nvlist_dup(ip_auth, &nvl, 0);
355b5875dddSJames Kremer 	else
356b5875dddSJames Kremer 		err = nvlist_alloc(&nvl, 0, 0);
357b5875dddSJames Kremer 
358b5875dddSJames Kremer 	if (err != 0) {
359b5875dddSJames Kremer 		fmd_hdl_abort(ip_hdl, "failed to create nvlist for "
360b5875dddSJames Kremer 		    "authority: %s\n", strerror(err));
361b5875dddSJames Kremer 	}
362b5875dddSJames Kremer 
363b5875dddSJames Kremer 	if (ip_auth != NULL)
364b5875dddSJames Kremer 		return (nvl);
365b5875dddSJames Kremer 
366b5875dddSJames Kremer 	ip_debug(IP_DEBUG_FINE, "ip_authority %s=%s\n",
367b5875dddSJames Kremer 	    FM_FMRI_AUTH_SERVER, ipx->ipx_addr);
368d9638e54Smws 
369d9638e54Smws 	(void) nvlist_add_uint8(nvl, FM_VERSION, FM_FMRI_AUTH_VERSION);
370b5875dddSJames Kremer 	(void) nvlist_add_string(nvl, FM_FMRI_AUTH_SERVER, ipx->ipx_addr);
371d9638e54Smws 
372d9638e54Smws 	return (nvl);
373d9638e54Smws }
374d9638e54Smws 
375d9638e54Smws static void
ip_xprt_accept(ip_xprt_t * ipx)376d9638e54Smws ip_xprt_accept(ip_xprt_t *ipx)
377d9638e54Smws {
3786925cc42Scindi 	struct sockaddr_storage sa;
379d9638e54Smws 	socklen_t salen = sizeof (sa);
380d9638e54Smws 	fmd_xprt_t *xp;
381d9638e54Smws 	int fd;
382d9638e54Smws 
383b5875dddSJames Kremer 	ip_debug(IP_DEBUG_FINER, "Enter ip_xprt_accept");
384b5875dddSJames Kremer 
3856925cc42Scindi 	if ((fd = accept(ipx->ipx_fd, (struct sockaddr *)&sa, &salen)) == -1) {
386d9638e54Smws 		fmd_hdl_error(ip_hdl, "failed to accept connection");
387d9638e54Smws 		ip_stat.ips_accfail.fmds_value.ui64++;
388d9638e54Smws 		return;
389d9638e54Smws 	}
390b5875dddSJames Kremer 	ip_debug(IP_DEBUG_FINE, "Accepted socket on fd %d", fd);
391d9638e54Smws 
392b5875dddSJames Kremer 	ip_xprt_set_addr(ipx, (struct sockaddr *)&sa);
3936925cc42Scindi 	xp = fmd_xprt_open(ip_hdl, ipx->ipx_flags,
394b5875dddSJames Kremer 	    ip_xprt_auth(ipx), NULL);
395dc8f2b85SJames Kremer 	ip_xprt_create(xp, fd, ipx->ipx_flags, ipx->ipx_cinfo, ipx->ipx_addr);
396d9638e54Smws }
397d9638e54Smws 
398d9638e54Smws static void
ip_xprt_recv_event(ip_xprt_t * ipx)399d9638e54Smws ip_xprt_recv_event(ip_xprt_t *ipx)
400d9638e54Smws {
401d9638e54Smws 	ip_hdr_t *iph;
402d9638e54Smws 	nvlist_t *nvl;
403d9638e54Smws 	size_t size;
404d9638e54Smws 	void *buf;
405d9638e54Smws 	int err;
406d9638e54Smws 
407d9638e54Smws 	if ((iph = ip_xprt_recv(ipx, sizeof (ip_hdr_t))) == NULL)
408d9638e54Smws 		return; /* connection broken */
409d9638e54Smws 
410d9638e54Smws 	if (bcmp(iph->iph_magic, IP_MAGIC, IP_MAGLEN) != 0) {
411d9638e54Smws 		fmd_hdl_error(ip_hdl,
412b5875dddSJames Kremer 		    "invalid hdr magic %x.%x.%x.%x from transport %s\n",
413d9638e54Smws 		    iph->iph_magic[0], iph->iph_magic[1], iph->iph_magic[2],
414b5875dddSJames Kremer 		    iph->iph_magic[3], IPX_ID(ipx));
415d9638e54Smws 		ip_stat.ips_badmagic.fmds_value.ui64++;
416d9638e54Smws 		return;
417d9638e54Smws 	}
418d9638e54Smws 
419d9638e54Smws 	size = ntohl(iph->iph_size);
420d9638e54Smws 
421d9638e54Smws 	if ((buf = ip_xprt_recv(ipx, size)) == NULL)
422d9638e54Smws 		return; /* connection broken */
423d9638e54Smws 
424d9638e54Smws 	if ((err = nvlist_unpack(buf, size, &nvl, 0)) != 0) {
425d9638e54Smws 		fmd_hdl_error(ip_hdl, "failed to unpack event from "
426b5875dddSJames Kremer 		    "transport %s: %s\n",
427b5875dddSJames Kremer 		    IPX_ID(ipx), strerror(err));
428d9638e54Smws 		ip_stat.ips_unpackfail.fmds_value.ui64++;
429cbf75e67SStephen Hanson 	} else {
430cbf75e67SStephen Hanson 		if (ip_domain_name)
431cbf75e67SStephen Hanson 			fmd_xprt_add_domain(ip_hdl, nvl, ip_domain_name);
432d9638e54Smws 		fmd_xprt_post(ip_hdl, ipx->ipx_xprt, nvl, 0);
433cbf75e67SStephen Hanson 	}
434d9638e54Smws 
435d9638e54Smws 	if (fmd_xprt_error(ip_hdl, ipx->ipx_xprt)) {
436d9638e54Smws 		fmd_hdl_error(ip_hdl, "protocol error on transport %p",
437d9638e54Smws 		    (void *)ipx->ipx_xprt);
438d9638e54Smws 		ipx->ipx_done++;
439d9638e54Smws 	}
440b5875dddSJames Kremer 	ip_debug(IP_DEBUG_FINEST, "Recv event %d bytes from %s",
441b5875dddSJames Kremer 	    size, IPX_ID(ipx));
442d9638e54Smws }
443d9638e54Smws 
444d9638e54Smws static void
ip_xprt_thread(void * arg)445d9638e54Smws ip_xprt_thread(void *arg)
446d9638e54Smws {
447d9638e54Smws 	ip_xprt_t *ipx = arg;
4486925cc42Scindi 	struct sockaddr_storage sa;
449d9638e54Smws 	socklen_t salen = sizeof (sa);
450d9638e54Smws 	struct pollfd pfd;
451d9638e54Smws 
452b5875dddSJames Kremer 	ip_debug(IP_DEBUG_FINER, "Enter ip_xprt_thread");
453b5875dddSJames Kremer 
454d9638e54Smws 	while (!ip_quit && !ipx->ipx_done) {
455d9638e54Smws 		if (ipx->ipx_xprt != NULL || (ipx->ipx_flags & FMD_XPRT_ACCEPT))
456d9638e54Smws 			pfd.events = POLLIN;
457d9638e54Smws 		else
458d9638e54Smws 			pfd.events = POLLOUT;
459d9638e54Smws 
460d9638e54Smws 		pfd.fd = ipx->ipx_fd;
461d9638e54Smws 		pfd.revents = 0;
462d9638e54Smws 
463d9638e54Smws 		if (poll(&pfd, 1, -1) <= 0)
464d9638e54Smws 			continue; /* loop around and check ip_quit */
465d9638e54Smws 
466d9638e54Smws 		if (pfd.revents & (POLLHUP | POLLERR)) {
467b5875dddSJames Kremer 			ip_debug(IP_DEBUG_FINE, "hangup fd %d\n", ipx->ipx_fd);
468d9638e54Smws 			break;
469d9638e54Smws 		}
470d9638e54Smws 
471d9638e54Smws 		if (pfd.revents & POLLOUT) {
472d9638e54Smws 			/*
473d9638e54Smws 			 * Once we're connected, there's no reason to have our
474d9638e54Smws 			 * calls to recv() and send() be non-blocking since we
475d9638e54Smws 			 * we have separate threads for each: clear O_NONBLOCK.
476d9638e54Smws 			 */
477d9638e54Smws 			(void) fcntl(ipx->ipx_fd, F_SETFL,
478d9638e54Smws 			    fcntl(ipx->ipx_fd, F_GETFL, 0) & ~O_NONBLOCK);
479d9638e54Smws 
4806925cc42Scindi 			if (getpeername(ipx->ipx_fd, (struct sockaddr *)&sa,
4816925cc42Scindi 			    &salen) != 0) {
482dc8f2b85SJames Kremer 				ip_debug(IP_DEBUG_FINE,
483dc8f2b85SJames Kremer 				    "Not connected, no remote name for fd %d. "
484dc8f2b85SJames Kremer 				    " Will retry.",
485dc8f2b85SJames Kremer 				    ipx->ipx_fd);
486d9638e54Smws 				bzero(&sa, sizeof (sa));
487af9cbda5SJames Kremer 				break;
488d9638e54Smws 			}
489b5875dddSJames Kremer 			ip_xprt_set_addr(ipx, (struct sockaddr *)&sa);
4906925cc42Scindi 			ipx->ipx_xprt = fmd_xprt_open(ip_hdl, ipx->ipx_flags,
491b5875dddSJames Kremer 			    ip_xprt_auth(ipx), ipx);
492d9638e54Smws 
493b5875dddSJames Kremer 			ip_debug(IP_DEBUG_FINE, "connect fd %d ipx %p",
494b5875dddSJames Kremer 			    ipx->ipx_fd, (void *)ipx);
495d9638e54Smws 			continue;
496d9638e54Smws 		}
497d9638e54Smws 
498d9638e54Smws 		if (pfd.revents & POLLIN) {
499d9638e54Smws 			if (ipx->ipx_xprt == NULL)
500d9638e54Smws 				ip_xprt_accept(ipx);
501d9638e54Smws 			else
502d9638e54Smws 				ip_xprt_recv_event(ipx);
503d9638e54Smws 		}
504d9638e54Smws 	}
505d9638e54Smws 
506dc8f2b85SJames Kremer 	ipx->ipx_cinfo->ipc_timer = fmd_timer_install(ip_hdl, ipx, NULL, 0);
507dc8f2b85SJames Kremer 	ip_debug(IP_DEBUG_FINE, "close fd %d (timer %d)", ipx->ipx_fd,
508dc8f2b85SJames Kremer 	    (int)ipx->ipx_cinfo->ipc_timer);
509d9638e54Smws }
510d9638e54Smws 
511d9638e54Smws static void
ip_xprt_create(fmd_xprt_t * xp,int fd,int flags,ip_cinfo_t * cinfo,char * addr)512b5875dddSJames Kremer ip_xprt_create(fmd_xprt_t *xp, int fd, int flags, ip_cinfo_t *cinfo, char *addr)
513d9638e54Smws {
514d9638e54Smws 	ip_xprt_t *ipx = fmd_hdl_zalloc(ip_hdl, sizeof (ip_xprt_t), FMD_SLEEP);
515d9638e54Smws 
516b5875dddSJames Kremer 	ip_debug(IP_DEBUG_FINER, "Enter ip_xprt_create %p", (void *)ipx);
517b5875dddSJames Kremer 
518d9638e54Smws 	ipx->ipx_xprt = xp;
519d9638e54Smws 	ipx->ipx_flags = flags;
520d9638e54Smws 	ipx->ipx_fd = fd;
521d9638e54Smws 	ipx->ipx_tid = fmd_thr_create(ip_hdl, ip_xprt_thread, ipx);
522b5875dddSJames Kremer 	ipx->ipx_cinfo = cinfo;
523b5875dddSJames Kremer 	ipx->ipx_addr = fmd_hdl_strdup(ip_hdl, addr, FMD_SLEEP);
524d9638e54Smws 
525d9638e54Smws 	if (ipx->ipx_xprt != NULL)
526d9638e54Smws 		fmd_xprt_setspecific(ip_hdl, ipx->ipx_xprt, ipx);
527d9638e54Smws 
528d9638e54Smws 	(void) pthread_mutex_lock(&ip_lock);
529d9638e54Smws 
530d9638e54Smws 	ipx->ipx_next = ip_xps;
531d9638e54Smws 	ip_xps = ipx;
532d9638e54Smws 
533d9638e54Smws 	(void) pthread_mutex_unlock(&ip_lock);
534d9638e54Smws }
535d9638e54Smws 
536d9638e54Smws static void
ip_xprt_destroy(ip_xprt_t * ipx)537d9638e54Smws ip_xprt_destroy(ip_xprt_t *ipx)
538d9638e54Smws {
539d9638e54Smws 	ip_xprt_t *ipp, **ppx = &ip_xps;
540d9638e54Smws 
541b5875dddSJames Kremer 	ip_debug(IP_DEBUG_FINER, "Enter ip_xprt_destory %s %p",
542b5875dddSJames Kremer 	    IPX_ID(ipx), (void *)ipx);
543b5875dddSJames Kremer 
544d9638e54Smws 	(void) pthread_mutex_lock(&ip_lock);
545d9638e54Smws 
546d9638e54Smws 	for (ipp = *ppx; ipp != NULL; ipp = ipp->ipx_next) {
547d9638e54Smws 		if (ipp != ipx)
548d9638e54Smws 			ppx = &ipp->ipx_next;
549d9638e54Smws 		else
550d9638e54Smws 			break;
551d9638e54Smws 	}
552d9638e54Smws 
553d9638e54Smws 	if (ipp != ipx) {
554d9638e54Smws 		(void) pthread_mutex_unlock(&ip_lock);
555d9638e54Smws 		fmd_hdl_abort(ip_hdl, "ipx %p not on xps list\n", (void *)ipx);
556d9638e54Smws 	}
557d9638e54Smws 
558d9638e54Smws 	*ppx = ipx->ipx_next;
559d9638e54Smws 	ipx->ipx_next = NULL;
560d9638e54Smws 
561d9638e54Smws 	(void) pthread_mutex_unlock(&ip_lock);
562d9638e54Smws 
563dc8f2b85SJames Kremer 	if (ipx->ipx_spnd_timer)
564dc8f2b85SJames Kremer 		fmd_timer_remove(ip_hdl, ipx->ipx_spnd_timer);
565dc8f2b85SJames Kremer 
566d9638e54Smws 	fmd_thr_signal(ip_hdl, ipx->ipx_tid);
567d9638e54Smws 	fmd_thr_destroy(ip_hdl, ipx->ipx_tid);
568d9638e54Smws 
569d9638e54Smws 	if (ipx->ipx_xprt != NULL)
570d9638e54Smws 		fmd_xprt_close(ip_hdl, ipx->ipx_xprt);
571d9638e54Smws 
572d9638e54Smws 	fmd_hdl_free(ip_hdl, ipx->ipx_sndbuf.ipb_buf, ipx->ipx_sndbuf.ipb_size);
573d9638e54Smws 	fmd_hdl_free(ip_hdl, ipx->ipx_rcvbuf.ipb_buf, ipx->ipx_rcvbuf.ipb_size);
574d9638e54Smws 
575d9638e54Smws 	(void) close(ipx->ipx_fd);
576b5875dddSJames Kremer 	if (ipx->ipx_addr) {
577b5875dddSJames Kremer 		fmd_hdl_strfree(ip_hdl, ipx->ipx_addr);
578b5875dddSJames Kremer 		ipx->ipx_addr = NULL;
579b5875dddSJames Kremer 	}
580d9638e54Smws 	fmd_hdl_free(ip_hdl, ipx, sizeof (ip_xprt_t));
581d9638e54Smws }
582d9638e54Smws 
583d9638e54Smws /*
584b5875dddSJames Kremer  * Loop through the addresses in the connection info structure that were
585b5875dddSJames Kremer  * created by getaddrinfo() in ip_setup_addr during initialization (_fmd_init)
586d9638e54Smws  * and for each one attempt to create a socket and initialize it.  If we are
587d9638e54Smws  * successful, return zero.  If we fail, we check ip_retry: if it is non-zero
588d9638e54Smws  * we return the last errno and let our caller retry ip_xprt_setup() later.  If
589d9638e54Smws  * ip_retry reaches zero, we call fmd_hdl_abort() with an appropriate message.
590d9638e54Smws  */
591d9638e54Smws static int
ip_xprt_setup(fmd_hdl_t * hdl,ip_cinfo_t * cinfo)592b5875dddSJames Kremer ip_xprt_setup(fmd_hdl_t *hdl, ip_cinfo_t *cinfo)
593d9638e54Smws {
594d9638e54Smws 	int err, fd, oflags, xflags, optval = 1;
595d9638e54Smws 	struct addrinfo *aip;
596d9638e54Smws 	const char *s1, *s2;
597dc8f2b85SJames Kremer 	struct addrinfo *ail = cinfo->ipc_addr;
598b5875dddSJames Kremer 
599dc8f2b85SJames Kremer 	ip_debug(IP_DEBUG_FINER, "Enter ip_xprt_setup %s\n",
600dc8f2b85SJames Kremer 	    cinfo->ipc_name == NULL ? "localhost" : cinfo->ipc_name);
601d9638e54Smws 
602cbf75e67SStephen Hanson 	/*
603cbf75e67SStephen Hanson 	 * Set up flags as specified in the .conf file. Note that these are
604cbf75e67SStephen Hanson 	 * mostly only used for testing purposes, allowing the transport to
605cbf75e67SStephen Hanson 	 * be set up in various modes.
606cbf75e67SStephen Hanson 	 */
607dc8f2b85SJames Kremer 	xflags = (ip_rdonly == FMD_B_TRUE) ? FMD_XPRT_RDONLY : FMD_XPRT_RDWR;
608dc8f2b85SJames Kremer 	if (cinfo->ipc_accept)
609dc8f2b85SJames Kremer 		xflags |= FMD_XPRT_ACCEPT;
610cbf75e67SStephen Hanson 	if (ip_external == FMD_B_TRUE)
611cbf75e67SStephen Hanson 		xflags |= FMD_XPRT_EXTERNAL;
612cbf75e67SStephen Hanson 	if (ip_no_remote_repair == FMD_B_TRUE)
613cbf75e67SStephen Hanson 		xflags |= FMD_XPRT_NO_REMOTE_REPAIR;
614cbf75e67SStephen Hanson 	if (ip_hconly == FMD_B_TRUE)
615cbf75e67SStephen Hanson 		xflags |= FMD_XPRT_HCONLY;
616cbf75e67SStephen Hanson 	if (ip_hc_present_only == FMD_B_TRUE)
617cbf75e67SStephen Hanson 		xflags |= FMD_XPRT_HC_PRESENT_ONLY;
618d9638e54Smws 
619b5875dddSJames Kremer 	for (aip = ail; aip != NULL; aip = aip->ai_next) {
620d9638e54Smws 		if (aip->ai_family != AF_INET && aip->ai_family != AF_INET6)
621d9638e54Smws 			continue; /* ignore anything that isn't IPv4 or IPv6 */
622d9638e54Smws 
623d9638e54Smws 		if ((fd = socket(aip->ai_family,
624d9638e54Smws 		    aip->ai_socktype, aip->ai_protocol)) == -1) {
625d9638e54Smws 			err = errno;
626d9638e54Smws 			continue;
627d9638e54Smws 		}
628d9638e54Smws 
629d9638e54Smws 		oflags = fcntl(fd, F_GETFL, 0);
630d9638e54Smws 		(void) fcntl(fd, F_SETFL, oflags | O_NONBLOCK);
631d9638e54Smws 
632d9638e54Smws 		if (xflags & FMD_XPRT_ACCEPT) {
633d9638e54Smws 			err = setsockopt(fd, SOL_SOCKET,
634d9638e54Smws 			    SO_REUSEADDR, &optval, sizeof (optval)) != 0 ||
635d9638e54Smws 			    bind(fd, aip->ai_addr, aip->ai_addrlen) != 0 ||
636d9638e54Smws 			    listen(fd, ip_qlen) != 0;
637d9638e54Smws 		} else {
638b5875dddSJames Kremer 			err = connect(fd, aip->ai_addr, aip->ai_addrlen);
639b5875dddSJames Kremer 			if (err)
640b5875dddSJames Kremer 				err = errno;
641b5875dddSJames Kremer 			if (err == EINPROGRESS)
642b5875dddSJames Kremer 				err = 0;
643d9638e54Smws 		}
644d9638e54Smws 
645d9638e54Smws 		if (err == 0) {
646b5875dddSJames Kremer 			ip_xprt_create(NULL, fd, xflags, cinfo, NULL);
647b5875dddSJames Kremer 			ip_debug(IP_DEBUG_FINER, "Exit ip_xprt_setup");
648d9638e54Smws 			return (0);
649d9638e54Smws 		}
650d9638e54Smws 
651b5875dddSJames Kremer 		ip_debug(IP_DEBUG_FINE, "Error=%d errno=%d", err, errno);
652b5875dddSJames Kremer 
653d9638e54Smws 		err = errno;
654d9638e54Smws 		(void) close(fd);
655d9638e54Smws 	}
656d9638e54Smws 
657dc8f2b85SJames Kremer 	if (cinfo->ipc_name != NULL) {
658d9638e54Smws 		s1 = "failed to connect to";
659dc8f2b85SJames Kremer 		s2 = cinfo->ipc_name;
660d9638e54Smws 	} else {
661d9638e54Smws 		s1 = "failed to listen on";
662d9638e54Smws 		s2 = ip_port;
663d9638e54Smws 	}
664d9638e54Smws 
665dc8f2b85SJames Kremer 	if (err == EACCES || cinfo->ipc_retry-- == 0)
666d9638e54Smws 		fmd_hdl_abort(hdl, "%s %s: %s\n", s1, s2, strerror(err));
667d9638e54Smws 
668b5875dddSJames Kremer 	ip_debug(IP_DEBUG_FINE, "%s %s: %s (will retry)\n",
669b5875dddSJames Kremer 	    s1, s2, strerror(err));
670b5875dddSJames Kremer 	ip_debug(IP_DEBUG_FINER, "Exit ip_xprt_setup");
671b5875dddSJames Kremer 	return (err);
672b5875dddSJames Kremer }
673b5875dddSJames Kremer 
674b5875dddSJames Kremer /*
675b5875dddSJames Kremer  * Free address based resources
676b5875dddSJames Kremer  */
677b5875dddSJames Kremer static void
ip_addr_cleanup()678b5875dddSJames Kremer ip_addr_cleanup()
679b5875dddSJames Kremer {
680dc8f2b85SJames Kremer 	ip_cinfo_t *conn;
681dc8f2b85SJames Kremer 
682dc8f2b85SJames Kremer 	(void) pthread_mutex_lock(&ip_conns_lock);
683dc8f2b85SJames Kremer 	conn = ip_conns;
684dc8f2b85SJames Kremer 	while (conn != NULL) {
685dc8f2b85SJames Kremer 		ip_conns = conn->ipc_next;
686dc8f2b85SJames Kremer 		if (conn->ipc_addr != NULL)
687dc8f2b85SJames Kremer 			freeaddrinfo(conn->ipc_addr);
688dc8f2b85SJames Kremer 		conn->ipc_addr = NULL;
689dc8f2b85SJames Kremer 		if (conn->ipc_timer)
690dc8f2b85SJames Kremer 			fmd_timer_remove(ip_hdl, conn->ipc_timer);
691dc8f2b85SJames Kremer 		fmd_hdl_strfree(ip_hdl, conn->ipc_name);
692dc8f2b85SJames Kremer 		fmd_hdl_free(ip_hdl, conn, sizeof (ip_cinfo_t));
693dc8f2b85SJames Kremer 		conn = ip_conns;
694b5875dddSJames Kremer 	}
695dc8f2b85SJames Kremer 	(void) pthread_mutex_unlock(&ip_conns_lock);
696dc8f2b85SJames Kremer 
697b5875dddSJames Kremer 	fmd_prop_free_string(ip_hdl, ip_port);
698b5875dddSJames Kremer }
699b5875dddSJames Kremer 
700dc8f2b85SJames Kremer static boolean_t
ip_argis_cinfo(void * arg)701dc8f2b85SJames Kremer ip_argis_cinfo(void *arg)
702dc8f2b85SJames Kremer {
703dc8f2b85SJames Kremer 	boolean_t exists = B_FALSE;
704dc8f2b85SJames Kremer 	ip_cinfo_t *conn;
705dc8f2b85SJames Kremer 
706dc8f2b85SJames Kremer 	(void) pthread_mutex_lock(&ip_conns_lock);
707dc8f2b85SJames Kremer 	for (conn = ip_conns; conn != NULL; conn = conn->ipc_next) {
708dc8f2b85SJames Kremer 		if (conn == arg) {
709dc8f2b85SJames Kremer 			exists = B_TRUE;
710dc8f2b85SJames Kremer 			break;
711dc8f2b85SJames Kremer 		}
712dc8f2b85SJames Kremer 	}
713dc8f2b85SJames Kremer 	(void) pthread_mutex_unlock(&ip_conns_lock);
714dc8f2b85SJames Kremer 
715dc8f2b85SJames Kremer 	return (exists);
716dc8f2b85SJames Kremer }
717dc8f2b85SJames Kremer 
718dc8f2b85SJames Kremer 
719dc8f2b85SJames Kremer static ip_cinfo_t *
ip_create_cinfo(char * server,boolean_t accept)720dc8f2b85SJames Kremer ip_create_cinfo(char *server, boolean_t accept)
721b5875dddSJames Kremer {
722b5875dddSJames Kremer 	int err;
723dc8f2b85SJames Kremer 	struct addrinfo aih;
724dc8f2b85SJames Kremer 	ip_cinfo_t *cinfo = fmd_hdl_zalloc(
725dc8f2b85SJames Kremer 	    ip_hdl, sizeof (ip_cinfo_t), FMD_NOSLEEP);
726dc8f2b85SJames Kremer 
727dc8f2b85SJames Kremer 	if (cinfo == NULL)
728dc8f2b85SJames Kremer 		return (NULL);
729dc8f2b85SJames Kremer 
730dc8f2b85SJames Kremer 	cinfo->ipc_accept = accept;
731dc8f2b85SJames Kremer 	cinfo->ipc_retry = ip_retry;
732dc8f2b85SJames Kremer 	if (server != NULL) {
733dc8f2b85SJames Kremer 		cinfo->ipc_name = fmd_hdl_strdup(ip_hdl, server, FMD_NOSLEEP);
734dc8f2b85SJames Kremer 		if (cinfo->ipc_name == NULL) {
735dc8f2b85SJames Kremer 			fmd_hdl_free(ip_hdl, cinfo, sizeof (ip_cinfo_t));
736dc8f2b85SJames Kremer 			return (NULL);
737dc8f2b85SJames Kremer 		}
738dc8f2b85SJames Kremer 	}
739b5875dddSJames Kremer 
740b5875dddSJames Kremer 	bzero(&aih, sizeof (aih));
741b5875dddSJames Kremer 	aih.ai_flags = AI_ADDRCONFIG;
742b5875dddSJames Kremer 	aih.ai_family = AF_UNSPEC;
743b5875dddSJames Kremer 	aih.ai_socktype = SOCK_STREAM;
744b5875dddSJames Kremer 	if (server != NULL) {
745b5875dddSJames Kremer 		ip_debug(IP_DEBUG_FINE, "resolving %s:%s\n", server, ip_port);
746b5875dddSJames Kremer 	} else {
747b5875dddSJames Kremer 		aih.ai_flags |= AI_PASSIVE;
748dc8f2b85SJames Kremer 		cinfo->ipc_name = fmd_hdl_strdup(
749dc8f2b85SJames Kremer 		    ip_hdl, "localhost", FMD_NOSLEEP);
750dc8f2b85SJames Kremer 		if (cinfo->ipc_name == NULL) {
751dc8f2b85SJames Kremer 			fmd_hdl_free(ip_hdl, cinfo, sizeof (ip_cinfo_t));
752dc8f2b85SJames Kremer 			return (NULL);
753dc8f2b85SJames Kremer 		}
754b5875dddSJames Kremer 	}
755b5875dddSJames Kremer 
756dc8f2b85SJames Kremer 	err = getaddrinfo(server, ip_port, &aih, &cinfo->ipc_addr);
757b5875dddSJames Kremer 	if (err != 0) {
758b5875dddSJames Kremer 		fmd_hdl_error(ip_hdl, "failed to resolve host %s port %s: %s\n",
759dc8f2b85SJames Kremer 		    cinfo->ipc_name, ip_port, gai_strerror(err));
760dc8f2b85SJames Kremer 		cinfo->ipc_addr = NULL;
761dc8f2b85SJames Kremer 		fmd_hdl_strfree(ip_hdl, cinfo->ipc_name);
762dc8f2b85SJames Kremer 		fmd_hdl_free(ip_hdl, cinfo, sizeof (ip_cinfo_t));
763dc8f2b85SJames Kremer 		cinfo = NULL;
764b5875dddSJames Kremer 	}
765dc8f2b85SJames Kremer 	return (cinfo);
766b5875dddSJames Kremer }
767b5875dddSJames Kremer 
768b5875dddSJames Kremer /*
769dc8f2b85SJames Kremer  * Setup a single ip address for ip connection.
770dc8f2b85SJames Kremer  * If unable to setup any of the addresses then all addresses will be cleaned up
771dc8f2b85SJames Kremer  * and non-zero will be returned.
772b5875dddSJames Kremer  */
773b5875dddSJames Kremer static int
ip_setup_addr(char * server,boolean_t accept)774dc8f2b85SJames Kremer ip_setup_addr(char *server, boolean_t accept)
775b5875dddSJames Kremer {
776b5875dddSJames Kremer 	int err = 0;
777dc8f2b85SJames Kremer 	ip_cinfo_t *cinfo = ip_create_cinfo(server, accept);
778b5875dddSJames Kremer 
779dc8f2b85SJames Kremer 	if (cinfo == NULL) {
780dc8f2b85SJames Kremer 		ip_addr_cleanup();
781dc8f2b85SJames Kremer 		err++;
782dc8f2b85SJames Kremer 	} else {
783dc8f2b85SJames Kremer 		(void) pthread_mutex_lock(&ip_conns_lock);
784dc8f2b85SJames Kremer 		cinfo->ipc_next = ip_conns;
785dc8f2b85SJames Kremer 		ip_conns = cinfo;
786dc8f2b85SJames Kremer 		(void) pthread_mutex_unlock(&ip_conns_lock);
787b5875dddSJames Kremer 	}
788dc8f2b85SJames Kremer 	return (err);
789dc8f2b85SJames Kremer }
790dc8f2b85SJames Kremer 
791dc8f2b85SJames Kremer /*
792dc8f2b85SJames Kremer  * Setup a ip addresses for an ip connection.  The address can be a comma
793dc8f2b85SJames Kremer  * separated list of addresses as well.
794dc8f2b85SJames Kremer  * If unable to setup any of the addresses then all addresses will be cleaned up
795dc8f2b85SJames Kremer  * and non-zero will be returned.
796dc8f2b85SJames Kremer  */
797dc8f2b85SJames Kremer static int
ip_setup_addrs(char * server,boolean_t accept)798dc8f2b85SJames Kremer ip_setup_addrs(char *server, boolean_t accept)
799dc8f2b85SJames Kremer {
800dc8f2b85SJames Kremer 	int err = 0;
801dc8f2b85SJames Kremer 	char *addr = server;
802dc8f2b85SJames Kremer 	char *p;
803dc8f2b85SJames Kremer 
804dc8f2b85SJames Kremer 	for (p = server; *p != '\0'; p++) {
805dc8f2b85SJames Kremer 		if (*p == ',') {
806dc8f2b85SJames Kremer 			*p = '\0';
807dc8f2b85SJames Kremer 			err = ip_setup_addr(addr, accept);
808dc8f2b85SJames Kremer 			*p = ',';
809dc8f2b85SJames Kremer 			if (err)
810dc8f2b85SJames Kremer 				return (err);
811dc8f2b85SJames Kremer 			addr = ++p;
812dc8f2b85SJames Kremer 			if (*addr == '\0')
813dc8f2b85SJames Kremer 				break;
814dc8f2b85SJames Kremer 		}
815b5875dddSJames Kremer 	}
816dc8f2b85SJames Kremer 	if (*addr != '\0') {
817dc8f2b85SJames Kremer 		err = ip_setup_addr(addr, accept);
818b5875dddSJames Kremer 	}
819d9638e54Smws 	return (err);
820d9638e54Smws }
821d9638e54Smws 
822dc8f2b85SJames Kremer /*
823dc8f2b85SJames Kremer  * Starts all connections for each configured network address.  If there is an
824dc8f2b85SJames Kremer  * error starting a connection a timer will be started for a retry.
825dc8f2b85SJames Kremer  */
826dc8f2b85SJames Kremer static void
ip_start_connections()827dc8f2b85SJames Kremer ip_start_connections()
828dc8f2b85SJames Kremer {
829dc8f2b85SJames Kremer 	ip_cinfo_t *conn;
830dc8f2b85SJames Kremer 
831dc8f2b85SJames Kremer 	(void) pthread_mutex_lock(&ip_conns_lock);
832dc8f2b85SJames Kremer 	for (conn = ip_conns; conn != NULL; conn = conn->ipc_next) {
833dc8f2b85SJames Kremer 		if (ip_xprt_setup(ip_hdl, conn) != 0) {
834dc8f2b85SJames Kremer 			conn->ipc_timer = fmd_timer_install(ip_hdl, conn, NULL,
835dc8f2b85SJames Kremer 			    ip_sleep);
836dc8f2b85SJames Kremer 		}
837dc8f2b85SJames Kremer 	}
838dc8f2b85SJames Kremer 	(void) pthread_mutex_unlock(&ip_conns_lock);
839dc8f2b85SJames Kremer }
840dc8f2b85SJames Kremer 
841d9638e54Smws /*
842af9cbda5SJames Kremer  * Timeout handler for the transport module.  We use these types of timeouts:
843d9638e54Smws  *
844af9cbda5SJames Kremer  * (a) arg is ip_cinfo_t: attempt ip_xprt_setup(), re-install timeout to retry
845af9cbda5SJames Kremer  * (b) arg is ip_xprt_t, FMD_XPRT_SUSPENDED: call fmd_xprt_resume() on arg
846af9cbda5SJames Kremer  * (c) arg is ip_xprt_t, !FMD_XPRT_SUSPENDED: call ip_xprt_destroy() on arg
847af9cbda5SJames Kremer  * (d) arg is NULL, ignore as this shouldn't happen
848d9638e54Smws  *
849d9638e54Smws  * Case (c) is required as we need to cause the module's main thread, which
850d9638e54Smws  * runs this timeout handler, to join with the transport's auxiliary thread.
851b5875dddSJames Kremer  * If the connection is a client then a timer will be installed to retry
852b5875dddSJames Kremer  * connecting to the server.
853d9638e54Smws  */
854d9638e54Smws static void
ip_timeout(fmd_hdl_t * hdl,id_t id,void * arg)855*940f2f58SToomas Soome ip_timeout(fmd_hdl_t *hdl, id_t id, void *arg)
856*940f2f58SToomas Soome {
857af9cbda5SJames Kremer 	int install_timer;
858af9cbda5SJames Kremer 	ip_cinfo_t *cinfo;
859b5875dddSJames Kremer 	ip_xprt_t *ipx;
860b5875dddSJames Kremer 
861af9cbda5SJames Kremer 	if (arg == NULL) {
862af9cbda5SJames Kremer 		fmd_hdl_error(hdl, "ip_timeout failed because hg arg is NULL");
863dc8f2b85SJames Kremer 	} else if (ip_argis_cinfo(arg)) {
864b5875dddSJames Kremer 		ip_debug(IP_DEBUG_FINER,
865*940f2f58SToomas Soome 		    "Enter ip_timeout (a) install new timer");
866dc8f2b85SJames Kremer 		cinfo = arg;
867dc8f2b85SJames Kremer 		if ((ip_xprt_setup(hdl, arg) != 0) && !ip_quit)
868dc8f2b85SJames Kremer 			cinfo->ipc_timer = fmd_timer_install(
869*940f2f58SToomas Soome 			    hdl, cinfo, NULL, ip_sleep);
870dc8f2b85SJames Kremer 		else
871*940f2f58SToomas Soome 			cinfo->ipc_timer = 0;
872d9638e54Smws 	} else {
873b5875dddSJames Kremer 		ipx = arg;
874b5875dddSJames Kremer 		if (ipx->ipx_flags & FMD_XPRT_SUSPENDED) {
875*940f2f58SToomas Soome 			ipx->ipx_spnd_timer = 0;
876b5875dddSJames Kremer 			ip_debug(IP_DEBUG_FINE, "timer %d waking ipx %p",
877*940f2f58SToomas Soome 			    (int)id, arg);
878b5875dddSJames Kremer 			ipx->ipx_flags &= ~FMD_XPRT_SUSPENDED;
879b5875dddSJames Kremer 			fmd_xprt_resume(hdl, ipx->ipx_xprt);
880b5875dddSJames Kremer 		} else {
881b5875dddSJames Kremer 			ip_debug(IP_DEBUG_FINE, "timer %d closing ipx %p",
882*940f2f58SToomas Soome 			    (int)id, arg);
883af9cbda5SJames Kremer 			cinfo = ipx->ipx_cinfo;
884af9cbda5SJames Kremer 			install_timer = (ipx->ipx_flags & FMD_XPRT_ACCEPT) !=
885*940f2f58SToomas Soome 			    FMD_XPRT_ACCEPT;
886b5875dddSJames Kremer 			ip_xprt_destroy(ipx);
887dc8f2b85SJames Kremer 			if (install_timer && !ip_quit)
888dc8f2b85SJames Kremer 				cinfo->ipc_timer = fmd_timer_install(
889*940f2f58SToomas Soome 				    hdl, cinfo, NULL, ip_sleep);
890dc8f2b85SJames Kremer 			else
891*940f2f58SToomas Soome 				cinfo->ipc_timer = 0;
892b5875dddSJames Kremer 		}
893d9638e54Smws 	}
894d9638e54Smws }
895d9638e54Smws 
896d9638e54Smws static const fmd_prop_t fmd_props[] = {
897d9638e54Smws 	{ "ip_authority", FMD_TYPE_STRING, NULL },
898d9638e54Smws 	{ "ip_bufsize", FMD_TYPE_SIZE, "4k" },
899d9638e54Smws 	{ "ip_burp", FMD_TYPE_TIME, "0" },
900d9638e54Smws 	{ "ip_enable", FMD_TYPE_BOOL, "false" },
901d9638e54Smws 	{ "ip_mtbf", FMD_TYPE_INT32, "0" },
902cbf75e67SStephen Hanson 	{ "ip_external", FMD_TYPE_BOOL, "true" },
903cbf75e67SStephen Hanson 	{ "ip_no_remote_repair", FMD_TYPE_BOOL, "true" },
904cbf75e67SStephen Hanson 	{ "ip_hconly", FMD_TYPE_BOOL, "false" },
905cbf75e67SStephen Hanson 	{ "ip_rdonly", FMD_TYPE_BOOL, "false" },
906cbf75e67SStephen Hanson 	{ "ip_hc_present_only", FMD_TYPE_BOOL, "false" },
907cbf75e67SStephen Hanson 	{ "ip_domain_name", FMD_TYPE_STRING, NULL },
908d9638e54Smws 	{ "ip_port", FMD_TYPE_STRING, "664" },
909d9638e54Smws 	{ "ip_qlen", FMD_TYPE_INT32, "32" },
910b5875dddSJames Kremer 	{ "ip_retry", FMD_TYPE_INT32, "-1" },	    /* -1=forever */
911b5875dddSJames Kremer 	{ "ip_server", FMD_TYPE_STRING, NULL },	    /* server name */
912d9638e54Smws 	{ "ip_sleep", FMD_TYPE_TIME, "10s" },
913d9638e54Smws 	{ "ip_translate", FMD_TYPE_BOOL, "false" },
914b5875dddSJames Kremer 	{ "ip_bind_addr", FMD_TYPE_STRING, NULL },  /* network interface addr */
915b5875dddSJames Kremer 	{ "ip_debug_level", FMD_TYPE_INT32, "1" },  /* debug levels 0-3 */
916d9638e54Smws 	{ NULL, 0, NULL }
917d9638e54Smws };
918d9638e54Smws 
919d9638e54Smws static const fmd_hdl_ops_t fmd_ops = {
920af9cbda5SJames Kremer 	ip_fmdo_recv,		/* fmdo_recv */
921d9638e54Smws 	ip_timeout,		/* fmdo_timeout */
922d9638e54Smws 	NULL,			/* fmdo_close */
923d9638e54Smws 	NULL,			/* fmdo_stats */
924d9638e54Smws 	NULL,			/* fmdo_gc */
925af9cbda5SJames Kremer 	ip_fmdo_send,		/* fmdo_send */
926d9638e54Smws };
927d9638e54Smws 
928d9638e54Smws static const fmd_hdl_info_t fmd_info = {
929d9638e54Smws 	"IP Transport Agent", "1.0", &fmd_ops, fmd_props
930d9638e54Smws };
931d9638e54Smws 
932d9638e54Smws /*
933d9638e54Smws  * Initialize the ip-transport module as either a server or a client.  Note
934d9638e54Smws  * that the ip-transport module is not enabled by default under Solaris:
935b5875dddSJames Kremer  * at present we require a developer or tool to "setprop ip_enable true".
936d9638e54Smws  * If ip-transport is needed in the future out-of-the-box on one or more Sun
937d9638e54Smws  * platforms, the code to check 'ip_enable' should be replaced with:
938d9638e54Smws  *
939d9638e54Smws  * (a) configuring ip-transport to operate in client mode by default,
940d9638e54Smws  * (b) a platform-specific configuration mechanism, or
941d9638e54Smws  * (c) a means to assure security and prevent denial-of-service attacks.
942d9638e54Smws  *
943d9638e54Smws  * Note that (c) is only an issue when the transport module operates
944d9638e54Smws  * in server mode (i.e. with the ip_server property set to NULL) on a
945d9638e54Smws  * generic Solaris system which may be exposed directly to the Internet.
946b5875dddSJames Kremer  * The property ip_bind_addr can be used to define a private network interface
947b5875dddSJames Kremer  * to use so that the service is not exposed to the Internet.
948d9638e54Smws  */
949d9638e54Smws void
_fmd_init(fmd_hdl_t * hdl)950d9638e54Smws _fmd_init(fmd_hdl_t *hdl)
951d9638e54Smws {
952dc8f2b85SJames Kremer 	char *addr, *auth, *p, *q, *r, *s;
953dc8f2b85SJames Kremer 	int err;
954d9638e54Smws 
955d9638e54Smws 	if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0)
956d9638e54Smws 		return; /* failed to register handle */
957d9638e54Smws 
958d9638e54Smws 	if (fmd_prop_get_int32(hdl, "ip_enable") == FMD_B_FALSE) {
959d9638e54Smws 		fmd_hdl_unregister(hdl);
960d9638e54Smws 		return;
961d9638e54Smws 	}
962d9638e54Smws 
963d9638e54Smws 	(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC,
964d9638e54Smws 	    sizeof (ip_stat) / sizeof (fmd_stat_t), (fmd_stat_t *)&ip_stat);
965d9638e54Smws 
966d9638e54Smws 	ip_hdl = hdl;
967d9638e54Smws 	(void) pthread_mutex_init(&ip_lock, NULL);
968d9638e54Smws 
969d9638e54Smws 	ip_burp = fmd_prop_get_int64(hdl, "ip_burp");
970d9638e54Smws 	ip_mtbf = fmd_prop_get_int32(hdl, "ip_mtbf");
971cbf75e67SStephen Hanson 	ip_external = fmd_prop_get_int32(hdl, "ip_external");
972cbf75e67SStephen Hanson 	ip_no_remote_repair = fmd_prop_get_int32(hdl, "ip_no_remote_repair");
973cbf75e67SStephen Hanson 	ip_hconly = fmd_prop_get_int32(hdl, "ip_hconly");
974cbf75e67SStephen Hanson 	ip_rdonly = fmd_prop_get_int32(hdl, "ip_rdonly");
975cbf75e67SStephen Hanson 	ip_hc_present_only = fmd_prop_get_int32(hdl, "ip_hc_present_only");
976cbf75e67SStephen Hanson 	ip_domain_name = fmd_prop_get_string(hdl, "ip_domain_name");
977d9638e54Smws 	ip_qlen = fmd_prop_get_int32(hdl, "ip_qlen");
978d9638e54Smws 	ip_retry = fmd_prop_get_int32(hdl, "ip_retry");
979d9638e54Smws 	ip_sleep = fmd_prop_get_int64(hdl, "ip_sleep");
980d9638e54Smws 	ip_translate = fmd_prop_get_int32(hdl, "ip_translate");
981d9638e54Smws 
982d9638e54Smws 	ip_size = (size_t)fmd_prop_get_int64(hdl, "ip_bufsize");
983d9638e54Smws 	ip_size = MAX(ip_size, sizeof (ip_hdr_t));
984d9638e54Smws 	ip_port = fmd_prop_get_string(hdl, "ip_port");
985b5875dddSJames Kremer 	ip_debug_level = fmd_prop_get_int32(hdl, "ip_debug_level");
986d9638e54Smws 
987dc8f2b85SJames Kremer 	ip_conns = NULL;
988dc8f2b85SJames Kremer 	addr = fmd_prop_get_string(hdl, "ip_bind_addr");
989dc8f2b85SJames Kremer 	if (addr != NULL) {
990dc8f2b85SJames Kremer 		err = ip_setup_addrs(addr, B_TRUE);
991dc8f2b85SJames Kremer 		if (err) {
992dc8f2b85SJames Kremer 			fmd_hdl_abort(hdl, "Unable to setup ip_bind_addr %s",
993dc8f2b85SJames Kremer 			    addr);
994dc8f2b85SJames Kremer 			return;
995dc8f2b85SJames Kremer 		}
996dc8f2b85SJames Kremer 		fmd_prop_free_string(hdl, addr);
997dc8f2b85SJames Kremer 	}
998dc8f2b85SJames Kremer 	addr = fmd_prop_get_string(hdl, "ip_server");
999dc8f2b85SJames Kremer 	if (addr != NULL) {
1000dc8f2b85SJames Kremer 		err = ip_setup_addrs(addr, B_FALSE);
1001dc8f2b85SJames Kremer 		if (err) {
1002dc8f2b85SJames Kremer 			fmd_hdl_abort(hdl, "Unable to setup ip_server %s",
1003dc8f2b85SJames Kremer 			    addr);
1004dc8f2b85SJames Kremer 			return;
1005dc8f2b85SJames Kremer 		}
1006dc8f2b85SJames Kremer 		fmd_prop_free_string(hdl, addr);
1007d9638e54Smws 	}
1008d9638e54Smws 
1009dc8f2b85SJames Kremer 	/*
1010dc8f2b85SJames Kremer 	 * If no specific connecitons configured then set up general server
1011dc8f2b85SJames Kremer 	 * listening on all network ports.
1012dc8f2b85SJames Kremer 	 */
1013dc8f2b85SJames Kremer 	if (ip_conns == NULL) {
1014dc8f2b85SJames Kremer 		if (ip_setup_addr(NULL, B_TRUE) != 0) {
1015dc8f2b85SJames Kremer 			fmd_hdl_abort(hdl, "Unable to setup server.");
1016dc8f2b85SJames Kremer 			return;
1017dc8f2b85SJames Kremer 		}
1018dc8f2b85SJames Kremer 	}
1019b5875dddSJames Kremer 
1020d9638e54Smws 	/*
1021d9638e54Smws 	 * If ip_authority is set, tokenize this string and turn it into an
1022d9638e54Smws 	 * FMA authority represented as a name-value pair list.  We will use
1023d9638e54Smws 	 * this authority for all transports created by this module.  If
1024d9638e54Smws 	 * ip_authority isn't set, we'll compute authorities on the fly.
1025d9638e54Smws 	 */
1026d9638e54Smws 	if ((auth = fmd_prop_get_string(hdl, "ip_authority")) != NULL) {
1027d9638e54Smws 		(void) nvlist_alloc(&ip_auth, 0, 0);
1028d9638e54Smws 		(void) nvlist_add_uint8(ip_auth,
1029d9638e54Smws 		    FM_VERSION, FM_FMRI_AUTH_VERSION);
1030d9638e54Smws 
103123a1cceaSRoger A. Faulkner 		s = strdupa(auth);
1032d9638e54Smws 		fmd_prop_free_string(hdl, auth);
1033d9638e54Smws 
1034d9638e54Smws 		for (p = strtok_r(s, ",", &q); p != NULL;
1035d9638e54Smws 		    p = strtok_r(NULL, ",", &q)) {
1036d9638e54Smws 
1037d9638e54Smws 			if ((r = strchr(p, '=')) == NULL) {
1038b5875dddSJames Kremer 				ip_addr_cleanup();
1039d9638e54Smws 				fmd_hdl_abort(hdl, "ip_authority element <%s> "
1040d9638e54Smws 				    "must be in <name>=<value> form\n", p);
1041d9638e54Smws 			}
1042d9638e54Smws 
1043d9638e54Smws 			*r = '\0';
1044d9638e54Smws 			(void) nvlist_add_string(ip_auth, p, r + 1);
1045d9638e54Smws 			*r = '=';
1046d9638e54Smws 		}
1047d9638e54Smws 	}
1048d9638e54Smws 
1049dc8f2b85SJames Kremer 	ip_start_connections();
1050d9638e54Smws }
1051d9638e54Smws 
1052d9638e54Smws void
_fmd_fini(fmd_hdl_t * hdl)1053d9638e54Smws _fmd_fini(fmd_hdl_t *hdl)
1054d9638e54Smws {
1055d9638e54Smws 	ip_quit++; /* set quit flag before signalling auxiliary threads */
1056d9638e54Smws 
1057d9638e54Smws 	while (ip_xps != NULL)
1058d9638e54Smws 		ip_xprt_destroy(ip_xps);
1059d9638e54Smws 
1060aab83bb8SJosef 'Jeff' Sipek 	nvlist_free(ip_auth);
1061dc8f2b85SJames Kremer 
1062b5875dddSJames Kremer 	ip_addr_cleanup();
1063d9638e54Smws 
1064dc8f2b85SJames Kremer 	if (ip_domain_name != NULL)
1065dc8f2b85SJames Kremer 		fmd_prop_free_string(ip_hdl, ip_domain_name);
1066dc8f2b85SJames Kremer 
1067d9638e54Smws 	fmd_hdl_unregister(hdl);
1068d9638e54Smws }
1069