xref: /titanic_51/usr/src/cmd/cmd-inet/usr.lib/ilbd/ilbd_hc.c (revision 808b84d02d9e58543ac8cde2701dec91a8c237f0)
1dbed73cbSSangeeta Misra /*
2dbed73cbSSangeeta Misra  * CDDL HEADER START
3dbed73cbSSangeeta Misra  *
4dbed73cbSSangeeta Misra  * The contents of this file are subject to the terms of the
5dbed73cbSSangeeta Misra  * Common Development and Distribution License (the "License").
6dbed73cbSSangeeta Misra  * You may not use this file except in compliance with the License.
7dbed73cbSSangeeta Misra  *
8dbed73cbSSangeeta Misra  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9dbed73cbSSangeeta Misra  * or http://www.opensolaris.org/os/licensing.
10dbed73cbSSangeeta Misra  * See the License for the specific language governing permissions
11dbed73cbSSangeeta Misra  * and limitations under the License.
12dbed73cbSSangeeta Misra  *
13dbed73cbSSangeeta Misra  * When distributing Covered Code, include this CDDL HEADER in each
14dbed73cbSSangeeta Misra  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15dbed73cbSSangeeta Misra  * If applicable, add the following below this CDDL HEADER, with the
16dbed73cbSSangeeta Misra  * fields enclosed by brackets "[]" replaced with your own identifying
17dbed73cbSSangeeta Misra  * information: Portions Copyright [yyyy] [name of copyright owner]
18dbed73cbSSangeeta Misra  *
19dbed73cbSSangeeta Misra  * CDDL HEADER END
20dbed73cbSSangeeta Misra  */
21dbed73cbSSangeeta Misra 
22dbed73cbSSangeeta Misra /*
23dbed73cbSSangeeta Misra  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24dbed73cbSSangeeta Misra  * Use is subject to license terms.
2533f5ff17SMilan Jurik  * Copyright 2012 Milan Jurik. All rights reserved.
26dbed73cbSSangeeta Misra  */
27dbed73cbSSangeeta Misra 
28dbed73cbSSangeeta Misra #include <sys/types.h>
29dbed73cbSSangeeta Misra #include <sys/socket.h>
30dbed73cbSSangeeta Misra #include <sys/list.h>
31dbed73cbSSangeeta Misra #include <sys/stropts.h>
32dbed73cbSSangeeta Misra #include <sys/siginfo.h>
33dbed73cbSSangeeta Misra #include <sys/wait.h>
34dbed73cbSSangeeta Misra #include <arpa/inet.h>
35dbed73cbSSangeeta Misra #include <netinet/in.h>
36dbed73cbSSangeeta Misra #include <stdlib.h>
37dbed73cbSSangeeta Misra #include <stdio.h>
38dbed73cbSSangeeta Misra #include <strings.h>
39dbed73cbSSangeeta Misra #include <stddef.h>
40dbed73cbSSangeeta Misra #include <unistd.h>
41dbed73cbSSangeeta Misra #include <libilb.h>
42dbed73cbSSangeeta Misra #include <port.h>
43dbed73cbSSangeeta Misra #include <time.h>
44dbed73cbSSangeeta Misra #include <signal.h>
45dbed73cbSSangeeta Misra #include <assert.h>
46dbed73cbSSangeeta Misra #include <errno.h>
47dbed73cbSSangeeta Misra #include <spawn.h>
48dbed73cbSSangeeta Misra #include <fcntl.h>
49dbed73cbSSangeeta Misra #include <limits.h>
50dbed73cbSSangeeta Misra #include "libilb_impl.h"
51dbed73cbSSangeeta Misra #include "ilbd.h"
52dbed73cbSSangeeta Misra 
53dbed73cbSSangeeta Misra /* Global list of HC objects */
54dbed73cbSSangeeta Misra list_t ilbd_hc_list;
55dbed73cbSSangeeta Misra 
56dbed73cbSSangeeta Misra /* Timer queue for all hc related timers. */
57dbed73cbSSangeeta Misra static iu_tq_t *ilbd_hc_timer_q;
58dbed73cbSSangeeta Misra 
59dbed73cbSSangeeta Misra /* Indicate whether the timer needs to be updated */
60dbed73cbSSangeeta Misra static boolean_t hc_timer_restarted;
61dbed73cbSSangeeta Misra 
62dbed73cbSSangeeta Misra static void ilbd_hc_probe_timer(iu_tq_t *, void *);
63dbed73cbSSangeeta Misra static ilb_status_t ilbd_hc_restart_timer(ilbd_hc_t *, ilbd_hc_srv_t *);
64dbed73cbSSangeeta Misra static boolean_t ilbd_run_probe(ilbd_hc_srv_t *);
65dbed73cbSSangeeta Misra 
66dbed73cbSSangeeta Misra #define	MAX(a, b)	((a) > (b) ? (a) : (b))
67dbed73cbSSangeeta Misra 
68dbed73cbSSangeeta Misra /*
69dbed73cbSSangeeta Misra  * Number of arguments passed to a probe.  argc[0] is the path name of
70dbed73cbSSangeeta Misra  * the probe.
71dbed73cbSSangeeta Misra  */
72dbed73cbSSangeeta Misra #define	HC_PROBE_ARGC	8
73dbed73cbSSangeeta Misra 
74dbed73cbSSangeeta Misra /*
75dbed73cbSSangeeta Misra  * Max number of characters to be read from the output of a probe.  It
76dbed73cbSSangeeta Misra  * is long enough to read in a 64 bit integer.
77dbed73cbSSangeeta Misra  */
78dbed73cbSSangeeta Misra #define	HC_MAX_PROBE_OUTPUT	24
79dbed73cbSSangeeta Misra 
80dbed73cbSSangeeta Misra void
81dbed73cbSSangeeta Misra i_ilbd_setup_hc_list(void)
82dbed73cbSSangeeta Misra {
83dbed73cbSSangeeta Misra 	list_create(&ilbd_hc_list, sizeof (ilbd_hc_t),
84dbed73cbSSangeeta Misra 	    offsetof(ilbd_hc_t, ihc_link));
85dbed73cbSSangeeta Misra }
86dbed73cbSSangeeta Misra 
87dbed73cbSSangeeta Misra /*
88dbed73cbSSangeeta Misra  * Given a hc object name, return a pointer to hc object if found.
89dbed73cbSSangeeta Misra  */
90dbed73cbSSangeeta Misra ilbd_hc_t *
91dbed73cbSSangeeta Misra ilbd_get_hc(const char *name)
92dbed73cbSSangeeta Misra {
93dbed73cbSSangeeta Misra 	ilbd_hc_t *hc;
94dbed73cbSSangeeta Misra 
95dbed73cbSSangeeta Misra 	for (hc = list_head(&ilbd_hc_list); hc != NULL;
96dbed73cbSSangeeta Misra 	    hc = list_next(&ilbd_hc_list, hc)) {
97dbed73cbSSangeeta Misra 		if (strcasecmp(hc->ihc_name, name) == 0)
98dbed73cbSSangeeta Misra 			return (hc);
99dbed73cbSSangeeta Misra 	}
100dbed73cbSSangeeta Misra 	return (NULL);
101dbed73cbSSangeeta Misra }
102dbed73cbSSangeeta Misra 
103dbed73cbSSangeeta Misra /*
104dbed73cbSSangeeta Misra  * Generates an audit record for create-healthcheck,
105dbed73cbSSangeeta Misra  * delete-healtcheck subcommands.
106dbed73cbSSangeeta Misra  */
107dbed73cbSSangeeta Misra static void
108dbed73cbSSangeeta Misra ilbd_audit_hc_event(const char *audit_hcname,
109dbed73cbSSangeeta Misra     const ilb_hc_info_t *audit_hcinfo, ilbd_cmd_t cmd,
110dbed73cbSSangeeta Misra     ilb_status_t rc, ucred_t *ucredp)
111dbed73cbSSangeeta Misra {
112dbed73cbSSangeeta Misra 	adt_session_data_t	*ah;
113dbed73cbSSangeeta Misra 	adt_event_data_t	*event;
114dbed73cbSSangeeta Misra 	au_event_t	flag;
115dbed73cbSSangeeta Misra 	int	audit_error;
116dbed73cbSSangeeta Misra 
117dbed73cbSSangeeta Misra 	if ((ucredp == NULL) && (cmd == ILBD_CREATE_HC))  {
118dbed73cbSSangeeta Misra 		/*
119dbed73cbSSangeeta Misra 		 * we came here from the path where ilbd incorporates
120dbed73cbSSangeeta Misra 		 * the configuration that is listed in SCF:
121dbed73cbSSangeeta Misra 		 * i_ilbd_read_config->ilbd_walk_hc_pgs->
122dbed73cbSSangeeta Misra 		 *   ->ilbd_scf_instance_walk_pg->ilbd_create_hc
123dbed73cbSSangeeta Misra 		 * We skip auditing in that case
124dbed73cbSSangeeta Misra 		 */
125dbed73cbSSangeeta Misra 		logdebug("ilbd_audit_hc_event: skipping auditing");
126dbed73cbSSangeeta Misra 		return;
127dbed73cbSSangeeta Misra 	}
128dbed73cbSSangeeta Misra 
129dbed73cbSSangeeta Misra 	if (adt_start_session(&ah, NULL, 0) != 0) {
130dbed73cbSSangeeta Misra 		logerr("ilbd_audit_hc_event: adt_start_session failed");
131dbed73cbSSangeeta Misra 		exit(EXIT_FAILURE);
132dbed73cbSSangeeta Misra 	}
133dbed73cbSSangeeta Misra 	if (adt_set_from_ucred(ah, ucredp, ADT_NEW) != 0) {
134dbed73cbSSangeeta Misra 		(void) adt_end_session(ah);
135dbed73cbSSangeeta Misra 		logerr("ilbd_audit_rule_event: adt_set_from_ucred failed");
136dbed73cbSSangeeta Misra 		exit(EXIT_FAILURE);
137dbed73cbSSangeeta Misra 	}
138dbed73cbSSangeeta Misra 	if (cmd == ILBD_CREATE_HC)
139dbed73cbSSangeeta Misra 		flag = ADT_ilb_create_healthcheck;
140dbed73cbSSangeeta Misra 	else if (cmd == ILBD_DESTROY_HC)
141dbed73cbSSangeeta Misra 		flag = ADT_ilb_delete_healthcheck;
142dbed73cbSSangeeta Misra 
143dbed73cbSSangeeta Misra 	if ((event = adt_alloc_event(ah, flag)) == NULL) {
144dbed73cbSSangeeta Misra 		logerr("ilbd_audit_hc_event: adt_alloc_event failed");
145dbed73cbSSangeeta Misra 		exit(EXIT_FAILURE);
146dbed73cbSSangeeta Misra 	}
147dbed73cbSSangeeta Misra 	(void) memset((char *)event, 0, sizeof (adt_event_data_t));
148dbed73cbSSangeeta Misra 
149dbed73cbSSangeeta Misra 	switch (cmd) {
150dbed73cbSSangeeta Misra 	case ILBD_CREATE_HC:
151dbed73cbSSangeeta Misra 		event->adt_ilb_create_healthcheck.auth_used =
152dbed73cbSSangeeta Misra 		    NET_ILB_CONFIG_AUTH;
153dbed73cbSSangeeta Misra 		event->adt_ilb_create_healthcheck.hc_test =
154dbed73cbSSangeeta Misra 		    (char *)audit_hcinfo->hci_test;
155dbed73cbSSangeeta Misra 		event->adt_ilb_create_healthcheck.hc_name =
156dbed73cbSSangeeta Misra 		    (char *)audit_hcinfo->hci_name;
157dbed73cbSSangeeta Misra 
158dbed73cbSSangeeta Misra 		/*
159dbed73cbSSangeeta Misra 		 * If the value 0 is stored, the default values are
160dbed73cbSSangeeta Misra 		 * set in the kernel. User land does not know about them
161dbed73cbSSangeeta Misra 		 * So if the user does not specify them, audit record
162dbed73cbSSangeeta Misra 		 * will show them as 0
163dbed73cbSSangeeta Misra 		 */
164dbed73cbSSangeeta Misra 		event->adt_ilb_create_healthcheck.hc_timeout =
165dbed73cbSSangeeta Misra 		    audit_hcinfo->hci_timeout;
166dbed73cbSSangeeta Misra 		event->adt_ilb_create_healthcheck.hc_count =
167dbed73cbSSangeeta Misra 		    audit_hcinfo->hci_count;
168dbed73cbSSangeeta Misra 		event->adt_ilb_create_healthcheck.hc_interval =
169dbed73cbSSangeeta Misra 		    audit_hcinfo->hci_interval;
170dbed73cbSSangeeta Misra 		break;
171dbed73cbSSangeeta Misra 	case ILBD_DESTROY_HC:
172dbed73cbSSangeeta Misra 		event->adt_ilb_delete_healthcheck.auth_used =
173dbed73cbSSangeeta Misra 		    NET_ILB_CONFIG_AUTH;
174dbed73cbSSangeeta Misra 		event->adt_ilb_delete_healthcheck.hc_name =
175dbed73cbSSangeeta Misra 		    (char *)audit_hcname;
176dbed73cbSSangeeta Misra 		break;
177dbed73cbSSangeeta Misra 	}
178dbed73cbSSangeeta Misra 
179dbed73cbSSangeeta Misra 	/* Fill in success/failure */
180dbed73cbSSangeeta Misra 	if (rc == ILB_STATUS_OK) {
181dbed73cbSSangeeta Misra 		if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
182dbed73cbSSangeeta Misra 			logerr("ilbd_audit_hc_event: adt_put_event failed");
183dbed73cbSSangeeta Misra 			exit(EXIT_FAILURE);
184dbed73cbSSangeeta Misra 		}
185dbed73cbSSangeeta Misra 	} else {
186dbed73cbSSangeeta Misra 		audit_error = ilberror2auditerror(rc);
187dbed73cbSSangeeta Misra 		if (adt_put_event(event, ADT_FAILURE, audit_error) != 0) {
188dbed73cbSSangeeta Misra 			logerr("ilbd_audit_hc_event: adt_put_event failed");
189dbed73cbSSangeeta Misra 			exit(EXIT_FAILURE);
190dbed73cbSSangeeta Misra 		}
191dbed73cbSSangeeta Misra 	}
192dbed73cbSSangeeta Misra 	adt_free_event(event);
193dbed73cbSSangeeta Misra 	(void) adt_end_session(ah);
194dbed73cbSSangeeta Misra }
195dbed73cbSSangeeta Misra 
196dbed73cbSSangeeta Misra /*
197dbed73cbSSangeeta Misra  * Given the ilb_hc_info_t passed in (from the libilb), create a hc object
198dbed73cbSSangeeta Misra  * in ilbd.  The parameter ev_port is not used, refer to comments of
199dbed73cbSSangeeta Misra  * ilbd_create_sg() in ilbd_sg.c
200dbed73cbSSangeeta Misra  */
201dbed73cbSSangeeta Misra /* ARGSUSED */
202dbed73cbSSangeeta Misra ilb_status_t
203dbed73cbSSangeeta Misra ilbd_create_hc(const ilb_hc_info_t *hc_info, int ev_port,
204dbed73cbSSangeeta Misra     const struct passwd *ps, ucred_t *ucredp)
205dbed73cbSSangeeta Misra {
206dbed73cbSSangeeta Misra 	ilbd_hc_t *hc;
207dbed73cbSSangeeta Misra 	ilb_status_t ret = ILB_STATUS_OK;
208dbed73cbSSangeeta Misra 
209dbed73cbSSangeeta Misra 	/*
210dbed73cbSSangeeta Misra 	 * ps == NULL is from the daemon when it starts and load configuration
211dbed73cbSSangeeta Misra 	 * ps != NULL is from client.
212dbed73cbSSangeeta Misra 	 */
213dbed73cbSSangeeta Misra 	if (ps != NULL) {
214dbed73cbSSangeeta Misra 		ret = ilbd_check_client_config_auth(ps);
215dbed73cbSSangeeta Misra 		if (ret != ILB_STATUS_OK) {
216dbed73cbSSangeeta Misra 			ilbd_audit_hc_event(NULL, hc_info, ILBD_CREATE_HC,
217dbed73cbSSangeeta Misra 			    ret, ucredp);
218dbed73cbSSangeeta Misra 			return (ret);
219dbed73cbSSangeeta Misra 		}
220dbed73cbSSangeeta Misra 	}
221dbed73cbSSangeeta Misra 
222dbed73cbSSangeeta Misra 	if (hc_info->hci_name[0] == '\0') {
223dbed73cbSSangeeta Misra 		logdebug("ilbd_create_hc: missing healthcheck info");
224dbed73cbSSangeeta Misra 		ilbd_audit_hc_event(NULL, hc_info, ILBD_CREATE_HC,
225dbed73cbSSangeeta Misra 		    ILB_STATUS_ENOHCINFO, ucredp);
226dbed73cbSSangeeta Misra 		return (ILB_STATUS_ENOHCINFO);
227dbed73cbSSangeeta Misra 	}
228dbed73cbSSangeeta Misra 
229dbed73cbSSangeeta Misra 	hc = ilbd_get_hc(hc_info->hci_name);
230dbed73cbSSangeeta Misra 	if (hc != NULL) {
231dbed73cbSSangeeta Misra 		logdebug("ilbd_create_hc: healthcheck name %s already"
232dbed73cbSSangeeta Misra 		    " exists", hc_info->hci_name);
233dbed73cbSSangeeta Misra 		ilbd_audit_hc_event(NULL, hc_info, ILBD_CREATE_HC,
234dbed73cbSSangeeta Misra 		    ILB_STATUS_EEXIST, ucredp);
235dbed73cbSSangeeta Misra 		return (ILB_STATUS_EEXIST);
236dbed73cbSSangeeta Misra 	}
237dbed73cbSSangeeta Misra 
238dbed73cbSSangeeta Misra 	/*
239dbed73cbSSangeeta Misra 	 * Sanity check on user supplied probe.  The given path name
240dbed73cbSSangeeta Misra 	 * must be a full path name (starts with '/') and is
241dbed73cbSSangeeta Misra 	 * executable.
242dbed73cbSSangeeta Misra 	 */
243dbed73cbSSangeeta Misra 	if (strcasecmp(hc_info->hci_test, ILB_HC_STR_TCP) != 0 &&
244dbed73cbSSangeeta Misra 	    strcasecmp(hc_info->hci_test, ILB_HC_STR_UDP) != 0 &&
245dbed73cbSSangeeta Misra 	    strcasecmp(hc_info->hci_test, ILB_HC_STR_PING) != 0 &&
246dbed73cbSSangeeta Misra 	    (hc_info->hci_test[0] != '/' ||
247dbed73cbSSangeeta Misra 	    access(hc_info->hci_test, X_OK) == -1)) {
248dbed73cbSSangeeta Misra 		if (errno == ENOENT) {
249dbed73cbSSangeeta Misra 			logdebug("ilbd_create_hc: user script %s doesn't "
250dbed73cbSSangeeta Misra 			    "exist", hc_info->hci_test);
251dbed73cbSSangeeta Misra 			ilbd_audit_hc_event(NULL, hc_info, ILBD_CREATE_HC,
252dbed73cbSSangeeta Misra 			    ILB_STATUS_ENOENT, ucredp);
253dbed73cbSSangeeta Misra 			return (ILB_STATUS_ENOENT);
254dbed73cbSSangeeta Misra 		} else {
255dbed73cbSSangeeta Misra 			logdebug("ilbd_create_hc: user script %s is "
256dbed73cbSSangeeta Misra 			    "invalid", hc_info->hci_test);
257dbed73cbSSangeeta Misra 			ilbd_audit_hc_event(NULL, hc_info, ILBD_CREATE_HC,
258dbed73cbSSangeeta Misra 			    ILB_STATUS_EINVAL, ucredp);
259dbed73cbSSangeeta Misra 			return (ILB_STATUS_EINVAL);
260dbed73cbSSangeeta Misra 		}
261dbed73cbSSangeeta Misra 	}
262dbed73cbSSangeeta Misra 
263dbed73cbSSangeeta Misra 	/* Create and add the hc object */
264dbed73cbSSangeeta Misra 	hc = calloc(1, sizeof (ilbd_hc_t));
265dbed73cbSSangeeta Misra 	if (hc == NULL) {
266dbed73cbSSangeeta Misra 		ilbd_audit_hc_event(NULL, hc_info, ILBD_CREATE_HC,
267dbed73cbSSangeeta Misra 		    ILB_STATUS_ENOMEM, ucredp);
268dbed73cbSSangeeta Misra 		return (ILB_STATUS_ENOMEM);
269dbed73cbSSangeeta Misra 	}
270dbed73cbSSangeeta Misra 	(void) memcpy(&hc->ihc_info, hc_info, sizeof (ilb_hc_info_t));
271dbed73cbSSangeeta Misra 	if (strcasecmp(hc->ihc_test, ILB_HC_STR_TCP) == 0)
272dbed73cbSSangeeta Misra 		hc->ihc_test_type = ILBD_HC_TCP;
273dbed73cbSSangeeta Misra 	else if (strcasecmp(hc->ihc_test, ILB_HC_STR_UDP) == 0)
274dbed73cbSSangeeta Misra 		hc->ihc_test_type = ILBD_HC_UDP;
275dbed73cbSSangeeta Misra 	else if (strcasecmp(hc->ihc_test, ILB_HC_STR_PING) == 0)
276dbed73cbSSangeeta Misra 		hc->ihc_test_type = ILBD_HC_PING;
277dbed73cbSSangeeta Misra 	else
278dbed73cbSSangeeta Misra 		hc->ihc_test_type = ILBD_HC_USER;
279dbed73cbSSangeeta Misra 	list_create(&hc->ihc_rules, sizeof (ilbd_hc_rule_t),
280dbed73cbSSangeeta Misra 	    offsetof(ilbd_hc_rule_t, hcr_link));
281dbed73cbSSangeeta Misra 
282dbed73cbSSangeeta Misra 	/* Update SCF */
283dbed73cbSSangeeta Misra 	if (ps != NULL) {
284dbed73cbSSangeeta Misra 		if ((ret = ilbd_create_pg(ILBD_SCF_HC, (void *)hc)) !=
285dbed73cbSSangeeta Misra 		    ILB_STATUS_OK) {
286dbed73cbSSangeeta Misra 			ilbd_audit_hc_event(NULL, hc_info, ILBD_CREATE_HC,
287dbed73cbSSangeeta Misra 			    ret, ucredp);
28856b8f71eSSerghei Samsi 			list_destroy(&hc->ihc_rules);
289dbed73cbSSangeeta Misra 			free(hc);
290dbed73cbSSangeeta Misra 			return (ret);
291dbed73cbSSangeeta Misra 		}
292dbed73cbSSangeeta Misra 	}
293dbed73cbSSangeeta Misra 
294dbed73cbSSangeeta Misra 	/* Everything is fine, now add it to the global list. */
295dbed73cbSSangeeta Misra 	list_insert_tail(&ilbd_hc_list, hc);
296dbed73cbSSangeeta Misra 	ilbd_audit_hc_event(NULL, hc_info, ILBD_CREATE_HC, ret, ucredp);
297dbed73cbSSangeeta Misra 	return (ret);
298dbed73cbSSangeeta Misra }
299dbed73cbSSangeeta Misra 
300dbed73cbSSangeeta Misra /*
301dbed73cbSSangeeta Misra  * Given a name of a hc object, destroy it.
302dbed73cbSSangeeta Misra  */
303dbed73cbSSangeeta Misra ilb_status_t
304dbed73cbSSangeeta Misra ilbd_destroy_hc(const char *hc_name, const struct passwd *ps,
305dbed73cbSSangeeta Misra     ucred_t *ucredp)
306dbed73cbSSangeeta Misra {
307dbed73cbSSangeeta Misra 	ilb_status_t ret;
308dbed73cbSSangeeta Misra 	ilbd_hc_t *hc;
309dbed73cbSSangeeta Misra 
310dbed73cbSSangeeta Misra 	/*
311dbed73cbSSangeeta Misra 	 * No need to check ps == NULL, daemon won't call any destroy func
312dbed73cbSSangeeta Misra 	 * at start up.
313dbed73cbSSangeeta Misra 	 */
314dbed73cbSSangeeta Misra 	ret = ilbd_check_client_config_auth(ps);
315dbed73cbSSangeeta Misra 	if (ret != ILB_STATUS_OK) {
316dbed73cbSSangeeta Misra 		ilbd_audit_hc_event(hc_name, NULL, ILBD_DESTROY_HC,
317dbed73cbSSangeeta Misra 		    ret, ucredp);
318dbed73cbSSangeeta Misra 		return (ret);
319dbed73cbSSangeeta Misra 	}
320dbed73cbSSangeeta Misra 
321dbed73cbSSangeeta Misra 	hc = ilbd_get_hc(hc_name);
322dbed73cbSSangeeta Misra 	if (hc == NULL) {
323dbed73cbSSangeeta Misra 		logdebug("ilbd_destroy_hc: healthcheck %s does not exist",
324dbed73cbSSangeeta Misra 		    hc_name);
325dbed73cbSSangeeta Misra 		ilbd_audit_hc_event(hc_name, NULL, ILBD_DESTROY_HC,
326dbed73cbSSangeeta Misra 		    ILB_STATUS_ENOENT, ucredp);
327dbed73cbSSangeeta Misra 		return (ILB_STATUS_ENOENT);
328dbed73cbSSangeeta Misra 	}
329dbed73cbSSangeeta Misra 
330dbed73cbSSangeeta Misra 	/* If hc is in use, cannot delete it */
331dbed73cbSSangeeta Misra 	if (hc->ihc_rule_cnt > 0) {
332dbed73cbSSangeeta Misra 		logdebug("ilbd_destroy_hc: healthcheck %s is associated"
333dbed73cbSSangeeta Misra 		    " with a rule - cannot remove", hc_name);
334dbed73cbSSangeeta Misra 		ilbd_audit_hc_event(hc_name, NULL, ILBD_DESTROY_HC,
335dbed73cbSSangeeta Misra 		    ILB_STATUS_INUSE, ucredp);
336dbed73cbSSangeeta Misra 		return (ILB_STATUS_INUSE);
337dbed73cbSSangeeta Misra 	}
338dbed73cbSSangeeta Misra 
339dbed73cbSSangeeta Misra 	if ((ret = ilbd_destroy_pg(ILBD_SCF_HC, hc_name)) !=
340dbed73cbSSangeeta Misra 	    ILB_STATUS_OK) {
341dbed73cbSSangeeta Misra 		logdebug("ilbd_destroy_hc: cannot destroy healthcheck %s "
342dbed73cbSSangeeta Misra 		    "property group", hc_name);
343dbed73cbSSangeeta Misra 		ilbd_audit_hc_event(hc_name, NULL, ILBD_DESTROY_HC,
344dbed73cbSSangeeta Misra 		    ret, ucredp);
345dbed73cbSSangeeta Misra 		return (ret);
346dbed73cbSSangeeta Misra 	}
347dbed73cbSSangeeta Misra 
348dbed73cbSSangeeta Misra 	list_remove(&ilbd_hc_list, hc);
34956b8f71eSSerghei Samsi 	list_destroy(&hc->ihc_rules);
350dbed73cbSSangeeta Misra 	free(hc);
351dbed73cbSSangeeta Misra 	ilbd_audit_hc_event(hc_name, NULL, ILBD_DESTROY_HC, ret, ucredp);
352dbed73cbSSangeeta Misra 	return (ret);
353dbed73cbSSangeeta Misra }
354dbed73cbSSangeeta Misra 
355dbed73cbSSangeeta Misra /*
356dbed73cbSSangeeta Misra  * Given a hc object name, return its information.  Used by libilb to
357dbed73cbSSangeeta Misra  * get hc info.
358dbed73cbSSangeeta Misra  */
359dbed73cbSSangeeta Misra ilb_status_t
360dbed73cbSSangeeta Misra ilbd_get_hc_info(const char *hc_name, uint32_t *rbuf, size_t *rbufsz)
361dbed73cbSSangeeta Misra {
362dbed73cbSSangeeta Misra 	ilbd_hc_t	*hc;
363dbed73cbSSangeeta Misra 	ilb_hc_info_t	*hc_info;
364dbed73cbSSangeeta Misra 	ilb_comm_t	*ic = (ilb_comm_t *)rbuf;
365dbed73cbSSangeeta Misra 
366dbed73cbSSangeeta Misra 	hc = ilbd_get_hc(hc_name);
367dbed73cbSSangeeta Misra 	if (hc == NULL) {
368dbed73cbSSangeeta Misra 		logdebug("%s: healthcheck %s does not exist", __func__,
369dbed73cbSSangeeta Misra 		    hc_name);
370dbed73cbSSangeeta Misra 		return (ILB_STATUS_ENOENT);
371dbed73cbSSangeeta Misra 	}
372dbed73cbSSangeeta Misra 	ilbd_reply_ok(rbuf, rbufsz);
373dbed73cbSSangeeta Misra 	hc_info = (ilb_hc_info_t *)&ic->ic_data;
374dbed73cbSSangeeta Misra 
375dbed73cbSSangeeta Misra 	(void) strlcpy(hc_info->hci_name, hc->ihc_name, sizeof (hc->ihc_name));
376dbed73cbSSangeeta Misra 	(void) strlcpy(hc_info->hci_test, hc->ihc_test, sizeof (hc->ihc_test));
377dbed73cbSSangeeta Misra 	hc_info->hci_timeout = hc->ihc_timeout;
378dbed73cbSSangeeta Misra 	hc_info->hci_count = hc->ihc_count;
379dbed73cbSSangeeta Misra 	hc_info->hci_interval = hc->ihc_interval;
380dbed73cbSSangeeta Misra 	hc_info->hci_def_ping = hc->ihc_def_ping;
381dbed73cbSSangeeta Misra 
382dbed73cbSSangeeta Misra 	*rbufsz += sizeof (ilb_hc_info_t);
383dbed73cbSSangeeta Misra 
384dbed73cbSSangeeta Misra 	return (ILB_STATUS_OK);
385dbed73cbSSangeeta Misra }
386dbed73cbSSangeeta Misra 
387dbed73cbSSangeeta Misra static void
388dbed73cbSSangeeta Misra ilbd_hc_copy_srvs(uint32_t *rbuf, size_t *rbufsz, ilbd_hc_rule_t *hc_rule,
389dbed73cbSSangeeta Misra     const char *rulename)
390dbed73cbSSangeeta Misra {
391dbed73cbSSangeeta Misra 	ilbd_hc_srv_t		*tmp_srv;
392dbed73cbSSangeeta Misra 	ilb_hc_srv_t		*dst_srv;
393dbed73cbSSangeeta Misra 	ilb_hc_rule_srv_t	*srvs;
394dbed73cbSSangeeta Misra 	size_t			tmp_rbufsz;
395dbed73cbSSangeeta Misra 	int			i;
396dbed73cbSSangeeta Misra 
397dbed73cbSSangeeta Misra 	tmp_rbufsz = *rbufsz;
398dbed73cbSSangeeta Misra 	/* Set up the reply buffer.  rbufsz will be set to the new size. */
399dbed73cbSSangeeta Misra 	ilbd_reply_ok(rbuf, rbufsz);
400dbed73cbSSangeeta Misra 
401dbed73cbSSangeeta Misra 	/* Calculate how much space is left for holding server info. */
402dbed73cbSSangeeta Misra 	*rbufsz += sizeof (ilb_hc_rule_srv_t);
403dbed73cbSSangeeta Misra 	tmp_rbufsz -= *rbufsz;
404dbed73cbSSangeeta Misra 
405dbed73cbSSangeeta Misra 	srvs = (ilb_hc_rule_srv_t *)&((ilb_comm_t *)rbuf)->ic_data;
406dbed73cbSSangeeta Misra 
407dbed73cbSSangeeta Misra 	tmp_srv = list_head(&hc_rule->hcr_servers);
408dbed73cbSSangeeta Misra 	for (i = 0; tmp_srv != NULL && tmp_rbufsz >= sizeof (*dst_srv); i++) {
409dbed73cbSSangeeta Misra 		dst_srv = &srvs->rs_srvs[i];
410dbed73cbSSangeeta Misra 
411dbed73cbSSangeeta Misra 		(void) strlcpy(dst_srv->hcs_rule_name, rulename, ILB_NAMESZ);
412dbed73cbSSangeeta Misra 		(void) strlcpy(dst_srv->hcs_ID, tmp_srv->shc_sg_srv->sgs_srvID,
413dbed73cbSSangeeta Misra 		    ILB_NAMESZ);
414dbed73cbSSangeeta Misra 		(void) strlcpy(dst_srv->hcs_hc_name,
415dbed73cbSSangeeta Misra 		    tmp_srv->shc_hc->ihc_name, ILB_NAMESZ);
416dbed73cbSSangeeta Misra 		dst_srv->hcs_IP = tmp_srv->shc_sg_srv->sgs_addr;
417dbed73cbSSangeeta Misra 		dst_srv->hcs_fail_cnt = tmp_srv->shc_fail_cnt;
418dbed73cbSSangeeta Misra 		dst_srv->hcs_status = tmp_srv->shc_status;
419dbed73cbSSangeeta Misra 		dst_srv->hcs_rtt = tmp_srv->shc_rtt;
420dbed73cbSSangeeta Misra 		dst_srv->hcs_lasttime = tmp_srv->shc_lasttime;
421dbed73cbSSangeeta Misra 		dst_srv->hcs_nexttime = tmp_srv->shc_nexttime;
422dbed73cbSSangeeta Misra 
423dbed73cbSSangeeta Misra 		tmp_srv = list_next(&hc_rule->hcr_servers, tmp_srv);
424dbed73cbSSangeeta Misra 		tmp_rbufsz -= sizeof (*dst_srv);
425dbed73cbSSangeeta Misra 	}
426dbed73cbSSangeeta Misra 	srvs->rs_num_srvs = i;
427dbed73cbSSangeeta Misra 	*rbufsz += i * sizeof (*dst_srv);
428dbed73cbSSangeeta Misra }
429dbed73cbSSangeeta Misra 
430dbed73cbSSangeeta Misra /*
431dbed73cbSSangeeta Misra  * Given a rule name, return the hc status of its servers.
432dbed73cbSSangeeta Misra  */
433dbed73cbSSangeeta Misra ilb_status_t
434dbed73cbSSangeeta Misra ilbd_get_hc_srvs(const char *rulename, uint32_t *rbuf, size_t *rbufsz)
435dbed73cbSSangeeta Misra {
436dbed73cbSSangeeta Misra 	ilbd_hc_t	*hc;
437dbed73cbSSangeeta Misra 	ilbd_hc_rule_t	*hc_rule;
438dbed73cbSSangeeta Misra 
439dbed73cbSSangeeta Misra 	for (hc = list_head(&ilbd_hc_list); hc != NULL;
440dbed73cbSSangeeta Misra 	    hc = list_next(&ilbd_hc_list, hc)) {
441dbed73cbSSangeeta Misra 		for (hc_rule = list_head(&hc->ihc_rules); hc_rule != NULL;
442dbed73cbSSangeeta Misra 		    hc_rule = list_next(&hc->ihc_rules, hc_rule)) {
443dbed73cbSSangeeta Misra 			if (strcasecmp(hc_rule->hcr_rule->irl_name,
444dbed73cbSSangeeta Misra 			    rulename) != 0) {
445dbed73cbSSangeeta Misra 				continue;
446dbed73cbSSangeeta Misra 			}
447dbed73cbSSangeeta Misra 			ilbd_hc_copy_srvs(rbuf, rbufsz, hc_rule, rulename);
448dbed73cbSSangeeta Misra 			return (ILB_STATUS_OK);
449dbed73cbSSangeeta Misra 		}
450dbed73cbSSangeeta Misra 	}
451dbed73cbSSangeeta Misra 	return (ILB_STATUS_RULE_NO_HC);
452dbed73cbSSangeeta Misra }
453dbed73cbSSangeeta Misra 
454dbed73cbSSangeeta Misra /*
455dbed73cbSSangeeta Misra  * Initialize the hc timer and associate the notification of timeout to
456dbed73cbSSangeeta Misra  * the given event port.
457dbed73cbSSangeeta Misra  */
458dbed73cbSSangeeta Misra void
459dbed73cbSSangeeta Misra ilbd_hc_timer_init(int ev_port, ilbd_timer_event_obj_t *ev_obj)
460dbed73cbSSangeeta Misra {
461dbed73cbSSangeeta Misra 	struct sigevent sigev;
462dbed73cbSSangeeta Misra 	port_notify_t notify;
463dbed73cbSSangeeta Misra 
464dbed73cbSSangeeta Misra 	if ((ilbd_hc_timer_q = iu_tq_create()) == NULL) {
465dbed73cbSSangeeta Misra 		logerr("%s: cannot create hc timer queue", __func__);
466dbed73cbSSangeeta Misra 		exit(EXIT_FAILURE);
467dbed73cbSSangeeta Misra 	}
468dbed73cbSSangeeta Misra 	hc_timer_restarted = B_FALSE;
469dbed73cbSSangeeta Misra 
470dbed73cbSSangeeta Misra 	ev_obj->ev = ILBD_EVENT_TIMER;
471dbed73cbSSangeeta Misra 	ev_obj->timerid = -1;
472dbed73cbSSangeeta Misra 
473dbed73cbSSangeeta Misra 	notify.portnfy_port = ev_port;
474dbed73cbSSangeeta Misra 	notify.portnfy_user = ev_obj;
475dbed73cbSSangeeta Misra 	sigev.sigev_notify = SIGEV_PORT;
476dbed73cbSSangeeta Misra 	sigev.sigev_value.sival_ptr = &notify;
477dbed73cbSSangeeta Misra 	if (timer_create(CLOCK_REALTIME, &sigev, &ev_obj->timerid) == -1) {
478dbed73cbSSangeeta Misra 		logerr("%s: cannot create timer", __func__);
479dbed73cbSSangeeta Misra 		exit(EXIT_FAILURE);
480dbed73cbSSangeeta Misra 	}
481dbed73cbSSangeeta Misra }
482dbed73cbSSangeeta Misra 
483dbed73cbSSangeeta Misra /*
484dbed73cbSSangeeta Misra  * HC timeout handler.
485dbed73cbSSangeeta Misra  */
486dbed73cbSSangeeta Misra void
487dbed73cbSSangeeta Misra ilbd_hc_timeout(void)
488dbed73cbSSangeeta Misra {
489dbed73cbSSangeeta Misra 	(void) iu_expire_timers(ilbd_hc_timer_q);
490dbed73cbSSangeeta Misra 	hc_timer_restarted = B_TRUE;
491dbed73cbSSangeeta Misra }
492dbed73cbSSangeeta Misra 
493dbed73cbSSangeeta Misra /*
494dbed73cbSSangeeta Misra  * Set up the timer to fire at the earliest timeout.
495dbed73cbSSangeeta Misra  */
496dbed73cbSSangeeta Misra void
497dbed73cbSSangeeta Misra ilbd_hc_timer_update(ilbd_timer_event_obj_t *ev_obj)
498dbed73cbSSangeeta Misra {
499dbed73cbSSangeeta Misra 	itimerspec_t itimeout;
500dbed73cbSSangeeta Misra 	int timeout;
501dbed73cbSSangeeta Misra 
502dbed73cbSSangeeta Misra 	/*
503dbed73cbSSangeeta Misra 	 * There is no change on the timer list, so no need to set up the
504dbed73cbSSangeeta Misra 	 * timer again.
505dbed73cbSSangeeta Misra 	 */
506dbed73cbSSangeeta Misra 	if (!hc_timer_restarted)
507dbed73cbSSangeeta Misra 		return;
508dbed73cbSSangeeta Misra 
509dbed73cbSSangeeta Misra restart:
510dbed73cbSSangeeta Misra 	if ((timeout = iu_earliest_timer(ilbd_hc_timer_q)) == INFTIM) {
511dbed73cbSSangeeta Misra 		hc_timer_restarted = B_FALSE;
512dbed73cbSSangeeta Misra 		return;
513dbed73cbSSangeeta Misra 	} else if (timeout == 0) {
514dbed73cbSSangeeta Misra 		/*
515dbed73cbSSangeeta Misra 		 * Handle the timeout immediately.  After that (clearing all
516dbed73cbSSangeeta Misra 		 * the expired timers), check to  see if there are still
517dbed73cbSSangeeta Misra 		 * timers running.  If yes, start them.
518dbed73cbSSangeeta Misra 		 */
519dbed73cbSSangeeta Misra 		(void) iu_expire_timers(ilbd_hc_timer_q);
520dbed73cbSSangeeta Misra 		goto restart;
521dbed73cbSSangeeta Misra 	}
522dbed73cbSSangeeta Misra 
523dbed73cbSSangeeta Misra 	itimeout.it_value.tv_sec = timeout / MILLISEC + 1;
524dbed73cbSSangeeta Misra 	itimeout.it_value.tv_nsec = 0;
525dbed73cbSSangeeta Misra 	itimeout.it_interval.tv_sec = 0;
526dbed73cbSSangeeta Misra 	itimeout.it_interval.tv_nsec = 0;
527dbed73cbSSangeeta Misra 
528dbed73cbSSangeeta Misra 	/*
529dbed73cbSSangeeta Misra 	 * Failure to set a timeout is "OK" since hopefully there will be
530dbed73cbSSangeeta Misra 	 * other events and timer_settime() will be called again.  So
531dbed73cbSSangeeta Misra 	 * we will only miss some timeouts.  But in the worst case, no event
532dbed73cbSSangeeta Misra 	 * will happen and ilbd will get stuck...
533dbed73cbSSangeeta Misra 	 */
534dbed73cbSSangeeta Misra 	if (timer_settime(ev_obj->timerid, 0, &itimeout, NULL) == -1)
535dbed73cbSSangeeta Misra 		logerr("%s: cannot set timer", __func__);
536dbed73cbSSangeeta Misra 	hc_timer_restarted = B_FALSE;
537dbed73cbSSangeeta Misra }
538dbed73cbSSangeeta Misra 
539dbed73cbSSangeeta Misra /*
540dbed73cbSSangeeta Misra  * Kill the probe process of a server.
541dbed73cbSSangeeta Misra  */
542dbed73cbSSangeeta Misra static void
543dbed73cbSSangeeta Misra ilbd_hc_kill_probe(ilbd_hc_srv_t *srv)
544dbed73cbSSangeeta Misra {
545dbed73cbSSangeeta Misra 	/*
546dbed73cbSSangeeta Misra 	 * First dissociate the fd from the event port.  It should not
547dbed73cbSSangeeta Misra 	 * fail.
548dbed73cbSSangeeta Misra 	 */
549dbed73cbSSangeeta Misra 	if (port_dissociate(srv->shc_ev_port, PORT_SOURCE_FD,
550dbed73cbSSangeeta Misra 	    srv->shc_child_fd) != 0) {
551dbed73cbSSangeeta Misra 		logdebug("%s: port_dissociate: %s", __func__, strerror(errno));
552dbed73cbSSangeeta Misra 	}
553dbed73cbSSangeeta Misra 	(void) close(srv->shc_child_fd);
554dbed73cbSSangeeta Misra 	free(srv->shc_ev);
555dbed73cbSSangeeta Misra 	srv->shc_ev = NULL;
556dbed73cbSSangeeta Misra 
557dbed73cbSSangeeta Misra 	/* Then kill the probe process. */
558dbed73cbSSangeeta Misra 	if (kill(srv->shc_child_pid, SIGKILL) != 0) {
559dbed73cbSSangeeta Misra 		logerr("%s: rule %s server %s: %s", __func__,
560dbed73cbSSangeeta Misra 		    srv->shc_hc_rule->hcr_rule->irl_name,
561dbed73cbSSangeeta Misra 		    srv->shc_sg_srv->sgs_srvID, strerror(errno));
562dbed73cbSSangeeta Misra 	}
563dbed73cbSSangeeta Misra 	/* Should not fail... */
564dbed73cbSSangeeta Misra 	if (waitpid(srv->shc_child_pid, NULL, 0) != srv->shc_child_pid) {
565dbed73cbSSangeeta Misra 		logdebug("%s: waitpid: rule %s server %s", __func__,
566dbed73cbSSangeeta Misra 		    srv->shc_hc_rule->hcr_rule->irl_name,
567dbed73cbSSangeeta Misra 		    srv->shc_sg_srv->sgs_srvID);
568dbed73cbSSangeeta Misra 	}
569dbed73cbSSangeeta Misra 	srv->shc_child_pid = 0;
570dbed73cbSSangeeta Misra }
571dbed73cbSSangeeta Misra 
572dbed73cbSSangeeta Misra /*
573dbed73cbSSangeeta Misra  * Disable the server, either because the server is dead or because a timer
574dbed73cbSSangeeta Misra  * cannot be started for this server.  Note that this only affects the
575dbed73cbSSangeeta Misra  * transient configuration, meaning only in memory.  The persistent
576dbed73cbSSangeeta Misra  * configuration is not affected.
577dbed73cbSSangeeta Misra  */
578dbed73cbSSangeeta Misra static void
579dbed73cbSSangeeta Misra ilbd_mark_server_disabled(ilbd_hc_srv_t *srv)
580dbed73cbSSangeeta Misra {
581dbed73cbSSangeeta Misra 	srv->shc_status = ILB_HCS_DISABLED;
582dbed73cbSSangeeta Misra 
583dbed73cbSSangeeta Misra 	/* Disable the server in kernel. */
584dbed73cbSSangeeta Misra 	if (ilbd_k_Xable_server(&srv->shc_sg_srv->sgs_addr,
585dbed73cbSSangeeta Misra 	    srv->shc_hc_rule->hcr_rule->irl_name,
586dbed73cbSSangeeta Misra 	    stat_declare_srv_dead) != ILB_STATUS_OK) {
587dbed73cbSSangeeta Misra 		logerr("%s: cannot disable server in kernel: rule %s "
588dbed73cbSSangeeta Misra 		    "server %s", __func__,
589dbed73cbSSangeeta Misra 		    srv->shc_hc_rule->hcr_rule->irl_name,
590dbed73cbSSangeeta Misra 		    srv->shc_sg_srv->sgs_srvID);
591dbed73cbSSangeeta Misra 	}
592dbed73cbSSangeeta Misra }
593dbed73cbSSangeeta Misra 
594dbed73cbSSangeeta Misra /*
595dbed73cbSSangeeta Misra  * A probe fails, set the state of the server.
596dbed73cbSSangeeta Misra  */
597dbed73cbSSangeeta Misra static void
598dbed73cbSSangeeta Misra ilbd_set_fail_state(ilbd_hc_srv_t *srv)
599dbed73cbSSangeeta Misra {
600dbed73cbSSangeeta Misra 	if (++srv->shc_fail_cnt < srv->shc_hc->ihc_count) {
601dbed73cbSSangeeta Misra 		/* Probe again */
602dbed73cbSSangeeta Misra 		ilbd_hc_probe_timer(ilbd_hc_timer_q, srv);
603dbed73cbSSangeeta Misra 		return;
604dbed73cbSSangeeta Misra 	}
605dbed73cbSSangeeta Misra 
606dbed73cbSSangeeta Misra 	logdebug("%s: rule %s server %s fails %u", __func__,
607dbed73cbSSangeeta Misra 	    srv->shc_hc_rule->hcr_rule->irl_name, srv->shc_sg_srv->sgs_srvID,
608dbed73cbSSangeeta Misra 	    srv->shc_fail_cnt);
609dbed73cbSSangeeta Misra 
610dbed73cbSSangeeta Misra 	/*
611dbed73cbSSangeeta Misra 	 * If this is a ping test, mark the server as
612dbed73cbSSangeeta Misra 	 * unreachable instead of dead.
613dbed73cbSSangeeta Misra 	 */
614dbed73cbSSangeeta Misra 	if (srv->shc_hc->ihc_test_type == ILBD_HC_PING ||
615dbed73cbSSangeeta Misra 	    srv->shc_state == ilbd_hc_def_pinging) {
616dbed73cbSSangeeta Misra 		srv->shc_status = ILB_HCS_UNREACH;
617dbed73cbSSangeeta Misra 	} else {
618dbed73cbSSangeeta Misra 		srv->shc_status = ILB_HCS_DEAD;
619dbed73cbSSangeeta Misra 	}
620dbed73cbSSangeeta Misra 
621dbed73cbSSangeeta Misra 	/* Disable the server in kernel. */
622dbed73cbSSangeeta Misra 	if (ilbd_k_Xable_server(&srv->shc_sg_srv->sgs_addr,
623dbed73cbSSangeeta Misra 	    srv->shc_hc_rule->hcr_rule->irl_name, stat_declare_srv_dead) !=
624dbed73cbSSangeeta Misra 	    ILB_STATUS_OK) {
625dbed73cbSSangeeta Misra 		logerr("%s: cannot disable server in kernel: rule %s "
626dbed73cbSSangeeta Misra 		    "server %s", __func__,
627dbed73cbSSangeeta Misra 		    srv->shc_hc_rule->hcr_rule->irl_name,
628dbed73cbSSangeeta Misra 		    srv->shc_sg_srv->sgs_srvID);
629dbed73cbSSangeeta Misra 	}
630dbed73cbSSangeeta Misra 
631dbed73cbSSangeeta Misra 	/* Still keep probing in case the server is alive again. */
632dbed73cbSSangeeta Misra 	if (ilbd_hc_restart_timer(srv->shc_hc, srv) != ILB_STATUS_OK) {
633dbed73cbSSangeeta Misra 		/* Only thing to do is to disable the server... */
634dbed73cbSSangeeta Misra 		logerr("%s: cannot restart timer: rule %s server %s", __func__,
635dbed73cbSSangeeta Misra 		    srv->shc_hc_rule->hcr_rule->irl_name,
636dbed73cbSSangeeta Misra 		    srv->shc_sg_srv->sgs_srvID);
637dbed73cbSSangeeta Misra 		srv->shc_status = ILB_HCS_DISABLED;
638dbed73cbSSangeeta Misra 	}
639dbed73cbSSangeeta Misra }
640dbed73cbSSangeeta Misra 
641dbed73cbSSangeeta Misra /*
642dbed73cbSSangeeta Misra  * A probe process has not returned for the ihc_timeout period, we should
643dbed73cbSSangeeta Misra  * kill it.  This function is the handler of this.
644dbed73cbSSangeeta Misra  */
645dbed73cbSSangeeta Misra /* ARGSUSED */
646dbed73cbSSangeeta Misra static void
647dbed73cbSSangeeta Misra ilbd_hc_kill_timer(iu_tq_t *tq, void *arg)
648dbed73cbSSangeeta Misra {
649dbed73cbSSangeeta Misra 	ilbd_hc_srv_t *srv = (ilbd_hc_srv_t *)arg;
650dbed73cbSSangeeta Misra 
651dbed73cbSSangeeta Misra 	ilbd_hc_kill_probe(srv);
652dbed73cbSSangeeta Misra 	ilbd_set_fail_state(srv);
653dbed73cbSSangeeta Misra }
654dbed73cbSSangeeta Misra 
655dbed73cbSSangeeta Misra /*
656dbed73cbSSangeeta Misra  * Probe timeout handler.  Send out the appropriate probe.
657dbed73cbSSangeeta Misra  */
658dbed73cbSSangeeta Misra /* ARGSUSED */
659dbed73cbSSangeeta Misra static void
660dbed73cbSSangeeta Misra ilbd_hc_probe_timer(iu_tq_t *tq, void *arg)
661dbed73cbSSangeeta Misra {
662dbed73cbSSangeeta Misra 	ilbd_hc_srv_t *srv = (ilbd_hc_srv_t *)arg;
663dbed73cbSSangeeta Misra 
664dbed73cbSSangeeta Misra 	/*
665dbed73cbSSangeeta Misra 	 * If starting the probe fails, just pretend that the timeout has
666dbed73cbSSangeeta Misra 	 * extended.
667dbed73cbSSangeeta Misra 	 */
668dbed73cbSSangeeta Misra 	if (!ilbd_run_probe(srv)) {
669dbed73cbSSangeeta Misra 		/*
670dbed73cbSSangeeta Misra 		 * If we cannot restart the timer, the only thing we can do
671dbed73cbSSangeeta Misra 		 * is to disable this server.  Hopefully the sys admin will
672dbed73cbSSangeeta Misra 		 * notice this and enable this server again later.
673dbed73cbSSangeeta Misra 		 */
674dbed73cbSSangeeta Misra 		if (ilbd_hc_restart_timer(srv->shc_hc, srv) != ILB_STATUS_OK) {
675dbed73cbSSangeeta Misra 			logerr("%s: cannot restart timer: rule %s server %s, "
676dbed73cbSSangeeta Misra 			    "disabling it", __func__,
677dbed73cbSSangeeta Misra 			    srv->shc_hc_rule->hcr_rule->irl_name,
678dbed73cbSSangeeta Misra 			    srv->shc_sg_srv->sgs_srvID);
679dbed73cbSSangeeta Misra 			ilbd_mark_server_disabled(srv);
680dbed73cbSSangeeta Misra 		}
681dbed73cbSSangeeta Misra 		return;
682dbed73cbSSangeeta Misra 	}
683dbed73cbSSangeeta Misra 
684dbed73cbSSangeeta Misra 	/*
685dbed73cbSSangeeta Misra 	 * Similar to above, if kill timer cannot be started, disable the
686dbed73cbSSangeeta Misra 	 * server.
687dbed73cbSSangeeta Misra 	 */
688dbed73cbSSangeeta Misra 	if ((srv->shc_tid = iu_schedule_timer(ilbd_hc_timer_q,
689dbed73cbSSangeeta Misra 	    srv->shc_hc->ihc_timeout, ilbd_hc_kill_timer, srv)) == -1) {
690dbed73cbSSangeeta Misra 		logerr("%s: cannot start kill timer: rule %s server %s, "
691dbed73cbSSangeeta Misra 		    "disabling it", __func__,
692dbed73cbSSangeeta Misra 		    srv->shc_hc_rule->hcr_rule->irl_name,
693dbed73cbSSangeeta Misra 		    srv->shc_sg_srv->sgs_srvID);
694dbed73cbSSangeeta Misra 		ilbd_mark_server_disabled(srv);
695dbed73cbSSangeeta Misra 	}
696dbed73cbSSangeeta Misra 	hc_timer_restarted = B_TRUE;
697dbed73cbSSangeeta Misra }
698dbed73cbSSangeeta Misra 
699dbed73cbSSangeeta Misra /* Restart the periodic timer for a given server. */
700dbed73cbSSangeeta Misra static ilb_status_t
701dbed73cbSSangeeta Misra ilbd_hc_restart_timer(ilbd_hc_t *hc, ilbd_hc_srv_t *srv)
702dbed73cbSSangeeta Misra {
703dbed73cbSSangeeta Misra 	int timeout;
704dbed73cbSSangeeta Misra 
705dbed73cbSSangeeta Misra 	/* Don't allow the timeout interval to be less than 1s */
706dbed73cbSSangeeta Misra 	timeout = MAX((hc->ihc_interval >> 1) + (gethrtime() %
707dbed73cbSSangeeta Misra 	    (hc->ihc_interval + 1)), 1);
708dbed73cbSSangeeta Misra 
709dbed73cbSSangeeta Misra 	/*
710dbed73cbSSangeeta Misra 	 * If the probe is actually a ping probe, there is no need to
711dbed73cbSSangeeta Misra 	 * do default pinging.  Just skip the step.
712dbed73cbSSangeeta Misra 	 */
713dbed73cbSSangeeta Misra 	if (hc->ihc_def_ping && hc->ihc_test_type != ILBD_HC_PING)
714dbed73cbSSangeeta Misra 		srv->shc_state = ilbd_hc_def_pinging;
715dbed73cbSSangeeta Misra 	else
716dbed73cbSSangeeta Misra 		srv->shc_state = ilbd_hc_probing;
717dbed73cbSSangeeta Misra 	srv->shc_tid = iu_schedule_timer(ilbd_hc_timer_q, timeout,
718dbed73cbSSangeeta Misra 	    ilbd_hc_probe_timer, srv);
719dbed73cbSSangeeta Misra 
720dbed73cbSSangeeta Misra 	if (srv->shc_tid == -1)
721dbed73cbSSangeeta Misra 		return (ILB_STATUS_TIMER);
722dbed73cbSSangeeta Misra 	srv->shc_lasttime = time(NULL);
723dbed73cbSSangeeta Misra 	srv->shc_nexttime = time(NULL) + timeout;
724dbed73cbSSangeeta Misra 
725dbed73cbSSangeeta Misra 	hc_timer_restarted = B_TRUE;
726dbed73cbSSangeeta Misra 	return (ILB_STATUS_OK);
727dbed73cbSSangeeta Misra }
728dbed73cbSSangeeta Misra 
729dbed73cbSSangeeta Misra /* Helper routine to associate a server with its hc object. */
730dbed73cbSSangeeta Misra static ilb_status_t
731dbed73cbSSangeeta Misra ilbd_hc_srv_add(ilbd_hc_t *hc, ilbd_hc_rule_t *hc_rule,
732dbed73cbSSangeeta Misra     const ilb_sg_srv_t *srv, int ev_port)
733dbed73cbSSangeeta Misra {
734dbed73cbSSangeeta Misra 	ilbd_hc_srv_t *new_srv;
735dbed73cbSSangeeta Misra 	ilb_status_t ret;
736dbed73cbSSangeeta Misra 
737dbed73cbSSangeeta Misra 	if ((new_srv = calloc(1, sizeof (ilbd_hc_srv_t))) == NULL)
738dbed73cbSSangeeta Misra 		return (ILB_STATUS_ENOMEM);
739dbed73cbSSangeeta Misra 	new_srv->shc_hc = hc;
740dbed73cbSSangeeta Misra 	new_srv->shc_hc_rule = hc_rule;
741dbed73cbSSangeeta Misra 	new_srv->shc_sg_srv = srv;
742dbed73cbSSangeeta Misra 	new_srv->shc_ev_port = ev_port;
743dbed73cbSSangeeta Misra 	new_srv->shc_tid = -1;
744dbed73cbSSangeeta Misra 	new_srv->shc_nexttime = time(NULL);
745dbed73cbSSangeeta Misra 	new_srv->shc_lasttime = new_srv->shc_nexttime;
746dbed73cbSSangeeta Misra 
747dbed73cbSSangeeta Misra 	if ((hc_rule->hcr_rule->irl_flags & ILB_FLAGS_RULE_ENABLED) &&
748dbed73cbSSangeeta Misra 	    ILB_IS_SRV_ENABLED(srv->sgs_flags)) {
749dbed73cbSSangeeta Misra 		new_srv->shc_status = ILB_HCS_UNINIT;
750dbed73cbSSangeeta Misra 		ret = ilbd_hc_restart_timer(hc, new_srv);
751dbed73cbSSangeeta Misra 		if (ret != ILB_STATUS_OK) {
752dbed73cbSSangeeta Misra 			free(new_srv);
753dbed73cbSSangeeta Misra 			return (ret);
754dbed73cbSSangeeta Misra 		}
755dbed73cbSSangeeta Misra 	} else {
756dbed73cbSSangeeta Misra 		new_srv->shc_status = ILB_HCS_DISABLED;
757dbed73cbSSangeeta Misra 	}
758dbed73cbSSangeeta Misra 
759dbed73cbSSangeeta Misra 	list_insert_tail(&hc_rule->hcr_servers, new_srv);
760dbed73cbSSangeeta Misra 	return (ILB_STATUS_OK);
761dbed73cbSSangeeta Misra }
762dbed73cbSSangeeta Misra 
763dbed73cbSSangeeta Misra /* Handy macro to cancel a server's timer. */
764dbed73cbSSangeeta Misra #define	HC_CANCEL_TIMER(srv)						\
765dbed73cbSSangeeta Misra {									\
766dbed73cbSSangeeta Misra 	void *arg;							\
767dbed73cbSSangeeta Misra 	int ret;							\
768dbed73cbSSangeeta Misra 	if ((srv)->shc_tid != -1) {					\
769dbed73cbSSangeeta Misra 		ret = iu_cancel_timer(ilbd_hc_timer_q, (srv)->shc_tid, &arg); \
770dbed73cbSSangeeta Misra 		(srv)->shc_tid = -1;					\
771dbed73cbSSangeeta Misra 		assert(ret == 1);					\
772dbed73cbSSangeeta Misra 		assert(arg == (srv));					\
773dbed73cbSSangeeta Misra 	}								\
774dbed73cbSSangeeta Misra 	hc_timer_restarted = B_TRUE;					\
775dbed73cbSSangeeta Misra }
776dbed73cbSSangeeta Misra 
777dbed73cbSSangeeta Misra /* Helper routine to dissociate a server from its hc object. */
778dbed73cbSSangeeta Misra static ilb_status_t
779dbed73cbSSangeeta Misra ilbd_hc_srv_rem(ilbd_hc_rule_t *hc_rule, const ilb_sg_srv_t *srv)
780dbed73cbSSangeeta Misra {
781dbed73cbSSangeeta Misra 	ilbd_hc_srv_t *tmp_srv;
782dbed73cbSSangeeta Misra 
783dbed73cbSSangeeta Misra 	for (tmp_srv = list_head(&hc_rule->hcr_servers); tmp_srv != NULL;
784dbed73cbSSangeeta Misra 	    tmp_srv = list_next(&hc_rule->hcr_servers, tmp_srv)) {
785dbed73cbSSangeeta Misra 		if (tmp_srv->shc_sg_srv == srv) {
786dbed73cbSSangeeta Misra 			list_remove(&hc_rule->hcr_servers, tmp_srv);
787dbed73cbSSangeeta Misra 			HC_CANCEL_TIMER(tmp_srv);
788dbed73cbSSangeeta Misra 			if (tmp_srv->shc_child_pid != 0)
789dbed73cbSSangeeta Misra 				ilbd_hc_kill_probe(tmp_srv);
790dbed73cbSSangeeta Misra 			free(tmp_srv);
791dbed73cbSSangeeta Misra 			return (ILB_STATUS_OK);
792dbed73cbSSangeeta Misra 		}
793dbed73cbSSangeeta Misra 	}
794dbed73cbSSangeeta Misra 	return (ILB_STATUS_ENOENT);
795dbed73cbSSangeeta Misra }
796dbed73cbSSangeeta Misra 
797dbed73cbSSangeeta Misra /* Helper routine to dissociate all servers of a rule from its hc object. */
798dbed73cbSSangeeta Misra static void
799dbed73cbSSangeeta Misra ilbd_hc_srv_rem_all(ilbd_hc_rule_t *hc_rule)
800dbed73cbSSangeeta Misra {
801dbed73cbSSangeeta Misra 	ilbd_hc_srv_t *srv;
802dbed73cbSSangeeta Misra 
803dbed73cbSSangeeta Misra 	while ((srv = list_remove_head(&hc_rule->hcr_servers)) != NULL) {
804dbed73cbSSangeeta Misra 		HC_CANCEL_TIMER(srv);
805dbed73cbSSangeeta Misra 		if (srv->shc_child_pid != 0)
806dbed73cbSSangeeta Misra 			ilbd_hc_kill_probe(srv);
807dbed73cbSSangeeta Misra 		free(srv);
808dbed73cbSSangeeta Misra 	}
809dbed73cbSSangeeta Misra }
810dbed73cbSSangeeta Misra 
811dbed73cbSSangeeta Misra /* Associate a rule with its hc object. */
812dbed73cbSSangeeta Misra ilb_status_t
813dbed73cbSSangeeta Misra ilbd_hc_associate_rule(const ilbd_rule_t *rule, int ev_port)
814dbed73cbSSangeeta Misra {
815dbed73cbSSangeeta Misra 	ilbd_hc_t	*hc;
816dbed73cbSSangeeta Misra 	ilbd_hc_rule_t	*hc_rule;
817dbed73cbSSangeeta Misra 	ilb_status_t	ret;
818dbed73cbSSangeeta Misra 	ilbd_sg_t	*sg;
819dbed73cbSSangeeta Misra 	ilbd_srv_t	*ilbd_srv;
820dbed73cbSSangeeta Misra 
821dbed73cbSSangeeta Misra 	/* The rule is assumed to be initialized appropriately. */
822dbed73cbSSangeeta Misra 	if ((hc = ilbd_get_hc(rule->irl_hcname)) == NULL) {
823dbed73cbSSangeeta Misra 		logdebug("ilbd_hc_associate_rule: healthcheck %s does not "
824dbed73cbSSangeeta Misra 		    "exist", rule->irl_hcname);
825dbed73cbSSangeeta Misra 		return (ILB_STATUS_ENOHCINFO);
826dbed73cbSSangeeta Misra 	}
827dbed73cbSSangeeta Misra 	if ((hc->ihc_test_type == ILBD_HC_TCP &&
828dbed73cbSSangeeta Misra 	    rule->irl_proto != IPPROTO_TCP) ||
829dbed73cbSSangeeta Misra 	    (hc->ihc_test_type == ILBD_HC_UDP &&
830dbed73cbSSangeeta Misra 	    rule->irl_proto != IPPROTO_UDP)) {
831dbed73cbSSangeeta Misra 		return (ILB_STATUS_RULE_HC_MISMATCH);
832dbed73cbSSangeeta Misra 	}
833dbed73cbSSangeeta Misra 	if ((hc_rule = calloc(1, sizeof (ilbd_hc_rule_t))) == NULL) {
834dbed73cbSSangeeta Misra 		logdebug("ilbd_hc_associate_rule: out of memory");
835dbed73cbSSangeeta Misra 		return (ILB_STATUS_ENOMEM);
836dbed73cbSSangeeta Misra 	}
837dbed73cbSSangeeta Misra 
838dbed73cbSSangeeta Misra 	hc_rule->hcr_rule = rule;
839dbed73cbSSangeeta Misra 	list_create(&hc_rule->hcr_servers, sizeof (ilbd_hc_srv_t),
840dbed73cbSSangeeta Misra 	    offsetof(ilbd_hc_srv_t, shc_srv_link));
841dbed73cbSSangeeta Misra 
842dbed73cbSSangeeta Misra 	/* Add all the servers. */
843dbed73cbSSangeeta Misra 	sg = rule->irl_sg;
844dbed73cbSSangeeta Misra 	for (ilbd_srv = list_head(&sg->isg_srvlist); ilbd_srv != NULL;
845dbed73cbSSangeeta Misra 	    ilbd_srv = list_next(&sg->isg_srvlist, ilbd_srv)) {
846dbed73cbSSangeeta Misra 		if ((ret = ilbd_hc_srv_add(hc, hc_rule, &ilbd_srv->isv_srv,
847dbed73cbSSangeeta Misra 		    ev_port)) != ILB_STATUS_OK) {
848dbed73cbSSangeeta Misra 			/* Remove all previously added servers */
849dbed73cbSSangeeta Misra 			ilbd_hc_srv_rem_all(hc_rule);
85056b8f71eSSerghei Samsi 			list_destroy(&hc_rule->hcr_servers);
851dbed73cbSSangeeta Misra 			free(hc_rule);
852dbed73cbSSangeeta Misra 			return (ret);
853dbed73cbSSangeeta Misra 		}
854dbed73cbSSangeeta Misra 	}
855dbed73cbSSangeeta Misra 	list_insert_tail(&hc->ihc_rules, hc_rule);
856dbed73cbSSangeeta Misra 	hc->ihc_rule_cnt++;
857dbed73cbSSangeeta Misra 
858dbed73cbSSangeeta Misra 	return (ILB_STATUS_OK);
859dbed73cbSSangeeta Misra }
860dbed73cbSSangeeta Misra 
861dbed73cbSSangeeta Misra /* Dissociate a rule from its hc object. */
862dbed73cbSSangeeta Misra ilb_status_t
863dbed73cbSSangeeta Misra ilbd_hc_dissociate_rule(const ilbd_rule_t *rule)
864dbed73cbSSangeeta Misra {
865dbed73cbSSangeeta Misra 	ilbd_hc_t	*hc;
866dbed73cbSSangeeta Misra 	ilbd_hc_rule_t	*hc_rule;
867dbed73cbSSangeeta Misra 
868dbed73cbSSangeeta Misra 	/* The rule is assumed to be initialized appropriately. */
869dbed73cbSSangeeta Misra 	if ((hc = ilbd_get_hc(rule->irl_hcname)) == NULL) {
870dbed73cbSSangeeta Misra 		logdebug("ilbd_hc_dissociate_rule: healthcheck %s does not "
871dbed73cbSSangeeta Misra 		    "exist", rule->irl_hcname);
872dbed73cbSSangeeta Misra 		return (ILB_STATUS_ENOENT);
873dbed73cbSSangeeta Misra 	}
874dbed73cbSSangeeta Misra 	for (hc_rule = list_head(&hc->ihc_rules); hc_rule != NULL;
875dbed73cbSSangeeta Misra 	    hc_rule = list_next(&hc->ihc_rules, hc_rule)) {
876dbed73cbSSangeeta Misra 		if (hc_rule->hcr_rule == rule)
877dbed73cbSSangeeta Misra 			break;
878dbed73cbSSangeeta Misra 	}
879dbed73cbSSangeeta Misra 	if (hc_rule == NULL) {
880dbed73cbSSangeeta Misra 		logdebug("ilbd_hc_dissociate_rule: rule %s is not associated "
881dbed73cbSSangeeta Misra 		    "with healtcheck %s", rule->irl_hcname, hc->ihc_name);
882dbed73cbSSangeeta Misra 		return (ILB_STATUS_ENOENT);
883dbed73cbSSangeeta Misra 	}
884dbed73cbSSangeeta Misra 	ilbd_hc_srv_rem_all(hc_rule);
885dbed73cbSSangeeta Misra 	list_remove(&hc->ihc_rules, hc_rule);
886dbed73cbSSangeeta Misra 	hc->ihc_rule_cnt--;
88756b8f71eSSerghei Samsi 	list_destroy(&hc_rule->hcr_servers);
88856b8f71eSSerghei Samsi 	free(hc_rule);
889dbed73cbSSangeeta Misra 	return (ILB_STATUS_OK);
890dbed73cbSSangeeta Misra }
891dbed73cbSSangeeta Misra 
892dbed73cbSSangeeta Misra /*
893dbed73cbSSangeeta Misra  * Given a hc object name and a rule, check to see if the rule is associated
894dbed73cbSSangeeta Misra  * with the hc object.  If it is, the hc object is returned in **hc and the
895dbed73cbSSangeeta Misra  * ilbd_hc_rule_t is returned in **hc_rule.
896dbed73cbSSangeeta Misra  */
897dbed73cbSSangeeta Misra static boolean_t
898dbed73cbSSangeeta Misra ilbd_hc_check_rule(const char *hc_name, const ilbd_rule_t *rule,
899dbed73cbSSangeeta Misra     ilbd_hc_t **hc, ilbd_hc_rule_t **hc_rule)
900dbed73cbSSangeeta Misra {
901dbed73cbSSangeeta Misra 	ilbd_hc_t	*tmp_hc;
902dbed73cbSSangeeta Misra 	ilbd_hc_rule_t	*tmp_hc_rule;
903dbed73cbSSangeeta Misra 
904dbed73cbSSangeeta Misra 	if ((tmp_hc = ilbd_get_hc(hc_name)) == NULL)
905dbed73cbSSangeeta Misra 		return (B_FALSE);
906dbed73cbSSangeeta Misra 	for (tmp_hc_rule = list_head(&tmp_hc->ihc_rules); tmp_hc_rule != NULL;
907dbed73cbSSangeeta Misra 	    tmp_hc_rule = list_next(&tmp_hc->ihc_rules, tmp_hc_rule)) {
908dbed73cbSSangeeta Misra 		if (tmp_hc_rule->hcr_rule == rule) {
909dbed73cbSSangeeta Misra 			*hc = tmp_hc;
910dbed73cbSSangeeta Misra 			*hc_rule = tmp_hc_rule;
911dbed73cbSSangeeta Misra 			return (B_TRUE);
912dbed73cbSSangeeta Misra 		}
913dbed73cbSSangeeta Misra 	}
914dbed73cbSSangeeta Misra 	return (B_FALSE);
915dbed73cbSSangeeta Misra }
916dbed73cbSSangeeta Misra 
917dbed73cbSSangeeta Misra /* Associate a server with its hc object. */
918dbed73cbSSangeeta Misra ilb_status_t
919dbed73cbSSangeeta Misra ilbd_hc_add_server(const ilbd_rule_t *rule, const ilb_sg_srv_t *srv,
920dbed73cbSSangeeta Misra     int ev_port)
921dbed73cbSSangeeta Misra {
922dbed73cbSSangeeta Misra 	ilbd_hc_t	*hc;
923dbed73cbSSangeeta Misra 	ilbd_hc_rule_t	*hc_rule;
924dbed73cbSSangeeta Misra 
925dbed73cbSSangeeta Misra 	if (!ilbd_hc_check_rule(rule->irl_hcname, rule, &hc, &hc_rule))
926dbed73cbSSangeeta Misra 		return (ILB_STATUS_ENOENT);
927dbed73cbSSangeeta Misra 	return (ilbd_hc_srv_add(hc, hc_rule, srv, ev_port));
928dbed73cbSSangeeta Misra }
929dbed73cbSSangeeta Misra 
930dbed73cbSSangeeta Misra /* Dissociate a server from its hc object. */
931dbed73cbSSangeeta Misra ilb_status_t
932dbed73cbSSangeeta Misra ilbd_hc_del_server(const ilbd_rule_t *rule, const ilb_sg_srv_t *srv)
933dbed73cbSSangeeta Misra {
934dbed73cbSSangeeta Misra 	ilbd_hc_t	*hc;
935dbed73cbSSangeeta Misra 	ilbd_hc_rule_t	*hc_rule;
936dbed73cbSSangeeta Misra 
937dbed73cbSSangeeta Misra 	if (!ilbd_hc_check_rule(rule->irl_hcname, rule, &hc, &hc_rule))
938dbed73cbSSangeeta Misra 		return (ILB_STATUS_ENOENT);
939dbed73cbSSangeeta Misra 	return (ilbd_hc_srv_rem(hc_rule, srv));
940dbed73cbSSangeeta Misra }
941dbed73cbSSangeeta Misra 
942dbed73cbSSangeeta Misra /* Helper routine to enable/disable a server's hc probe. */
943dbed73cbSSangeeta Misra static ilb_status_t
944dbed73cbSSangeeta Misra ilbd_hc_toggle_server(const ilbd_rule_t *rule, const ilb_sg_srv_t *srv,
945dbed73cbSSangeeta Misra     boolean_t enable)
946dbed73cbSSangeeta Misra {
947dbed73cbSSangeeta Misra 	ilbd_hc_t	*hc;
948dbed73cbSSangeeta Misra 	ilbd_hc_rule_t	*hc_rule;
949dbed73cbSSangeeta Misra 	ilbd_hc_srv_t	*tmp_srv;
950dbed73cbSSangeeta Misra 	ilb_status_t	ret;
951dbed73cbSSangeeta Misra 
952dbed73cbSSangeeta Misra 	if (!ilbd_hc_check_rule(rule->irl_hcname, rule, &hc, &hc_rule))
953dbed73cbSSangeeta Misra 		return (ILB_STATUS_ENOENT);
954dbed73cbSSangeeta Misra 	for (tmp_srv = list_head(&hc_rule->hcr_servers); tmp_srv != NULL;
955dbed73cbSSangeeta Misra 	    tmp_srv = list_next(&hc_rule->hcr_servers, tmp_srv)) {
956dbed73cbSSangeeta Misra 		if (tmp_srv->shc_sg_srv != srv) {
957dbed73cbSSangeeta Misra 			continue;
958dbed73cbSSangeeta Misra 		}
959dbed73cbSSangeeta Misra 		if (enable) {
960dbed73cbSSangeeta Misra 			if (tmp_srv->shc_status == ILB_HCS_DISABLED) {
961dbed73cbSSangeeta Misra 				ret = ilbd_hc_restart_timer(hc, tmp_srv);
962dbed73cbSSangeeta Misra 				if (ret != ILB_STATUS_OK) {
963dbed73cbSSangeeta Misra 					logerr("%s: cannot start timers for "
964dbed73cbSSangeeta Misra 					    "rule %s server %s", __func__,
965dbed73cbSSangeeta Misra 					    rule->irl_name,
966dbed73cbSSangeeta Misra 					    tmp_srv->shc_sg_srv->sgs_srvID);
967dbed73cbSSangeeta Misra 					return (ret);
968dbed73cbSSangeeta Misra 				}
969dbed73cbSSangeeta Misra 				/* Start from fresh... */
970dbed73cbSSangeeta Misra 				tmp_srv->shc_status = ILB_HCS_UNINIT;
971dbed73cbSSangeeta Misra 				tmp_srv->shc_rtt = 0;
972dbed73cbSSangeeta Misra 				tmp_srv->shc_fail_cnt = 0;
973dbed73cbSSangeeta Misra 			}
974dbed73cbSSangeeta Misra 		} else {
975dbed73cbSSangeeta Misra 			if (tmp_srv->shc_status != ILB_HCS_DISABLED) {
976dbed73cbSSangeeta Misra 				tmp_srv->shc_status = ILB_HCS_DISABLED;
977dbed73cbSSangeeta Misra 				HC_CANCEL_TIMER(tmp_srv);
978dbed73cbSSangeeta Misra 				if (tmp_srv->shc_child_pid != 0)
979dbed73cbSSangeeta Misra 					ilbd_hc_kill_probe(tmp_srv);
980dbed73cbSSangeeta Misra 			}
981dbed73cbSSangeeta Misra 		}
982dbed73cbSSangeeta Misra 		return (ILB_STATUS_OK);
983dbed73cbSSangeeta Misra 	}
984dbed73cbSSangeeta Misra 	return (ILB_STATUS_ENOENT);
985dbed73cbSSangeeta Misra }
986dbed73cbSSangeeta Misra 
987dbed73cbSSangeeta Misra ilb_status_t
988dbed73cbSSangeeta Misra ilbd_hc_enable_server(const ilbd_rule_t *rule, const ilb_sg_srv_t *srv)
989dbed73cbSSangeeta Misra {
990dbed73cbSSangeeta Misra 	return (ilbd_hc_toggle_server(rule, srv, B_TRUE));
991dbed73cbSSangeeta Misra }
992dbed73cbSSangeeta Misra 
993dbed73cbSSangeeta Misra ilb_status_t
994dbed73cbSSangeeta Misra ilbd_hc_disable_server(const ilbd_rule_t *rule, const ilb_sg_srv_t *srv)
995dbed73cbSSangeeta Misra {
996dbed73cbSSangeeta Misra 	return (ilbd_hc_toggle_server(rule, srv, B_FALSE));
997dbed73cbSSangeeta Misra }
998dbed73cbSSangeeta Misra 
999dbed73cbSSangeeta Misra /*
1000dbed73cbSSangeeta Misra  * Helper routine to enable/disable a rule's hc probe (including all its
1001dbed73cbSSangeeta Misra  * servers).
1002dbed73cbSSangeeta Misra  */
1003dbed73cbSSangeeta Misra static ilb_status_t
1004dbed73cbSSangeeta Misra ilbd_hc_toggle_rule(const ilbd_rule_t *rule, boolean_t enable)
1005dbed73cbSSangeeta Misra {
1006dbed73cbSSangeeta Misra 	ilbd_hc_t	*hc;
1007dbed73cbSSangeeta Misra 	ilbd_hc_rule_t	*hc_rule;
1008dbed73cbSSangeeta Misra 	ilbd_hc_srv_t	*tmp_srv;
1009dbed73cbSSangeeta Misra 	int		ret;
1010dbed73cbSSangeeta Misra 
1011dbed73cbSSangeeta Misra 	if (!ilbd_hc_check_rule(rule->irl_hcname, rule, &hc, &hc_rule))
1012dbed73cbSSangeeta Misra 		return (ILB_STATUS_ENOENT);
1013dbed73cbSSangeeta Misra 
1014dbed73cbSSangeeta Misra 	for (tmp_srv = list_head(&hc_rule->hcr_servers); tmp_srv != NULL;
1015dbed73cbSSangeeta Misra 	    tmp_srv = list_next(&hc_rule->hcr_servers, tmp_srv)) {
1016dbed73cbSSangeeta Misra 		if (enable) {
1017dbed73cbSSangeeta Misra 			/*
1018dbed73cbSSangeeta Misra 			 * If the server is disabled in the rule, do not
1019dbed73cbSSangeeta Misra 			 * restart its timer.
1020dbed73cbSSangeeta Misra 			 */
1021dbed73cbSSangeeta Misra 			if (tmp_srv->shc_status == ILB_HCS_DISABLED &&
1022dbed73cbSSangeeta Misra 			    ILB_IS_SRV_ENABLED(
1023dbed73cbSSangeeta Misra 			    tmp_srv->shc_sg_srv->sgs_flags)) {
1024dbed73cbSSangeeta Misra 				ret = ilbd_hc_restart_timer(hc, tmp_srv);
1025dbed73cbSSangeeta Misra 				if (ret != ILB_STATUS_OK) {
1026dbed73cbSSangeeta Misra 					logerr("%s: cannot start timers for "
1027dbed73cbSSangeeta Misra 					    "rule %s server %s", __func__,
1028dbed73cbSSangeeta Misra 					    rule->irl_name,
1029dbed73cbSSangeeta Misra 					    tmp_srv->shc_sg_srv->sgs_srvID);
1030dbed73cbSSangeeta Misra 					goto rollback;
1031dbed73cbSSangeeta Misra 				} else {
1032dbed73cbSSangeeta Misra 					/* Start from fresh... */
1033dbed73cbSSangeeta Misra 					tmp_srv->shc_status = ILB_HCS_UNINIT;
1034dbed73cbSSangeeta Misra 					tmp_srv->shc_rtt = 0;
1035dbed73cbSSangeeta Misra 					tmp_srv->shc_fail_cnt = 0;
1036dbed73cbSSangeeta Misra 				}
1037dbed73cbSSangeeta Misra 			}
1038dbed73cbSSangeeta Misra 		} else {
1039dbed73cbSSangeeta Misra 			if (tmp_srv->shc_status != ILB_HCS_DISABLED) {
1040dbed73cbSSangeeta Misra 				HC_CANCEL_TIMER(tmp_srv);
1041dbed73cbSSangeeta Misra 				tmp_srv->shc_status = ILB_HCS_DISABLED;
1042dbed73cbSSangeeta Misra 				if (tmp_srv->shc_child_pid != 0)
1043dbed73cbSSangeeta Misra 					ilbd_hc_kill_probe(tmp_srv);
1044dbed73cbSSangeeta Misra 			}
1045dbed73cbSSangeeta Misra 		}
1046dbed73cbSSangeeta Misra 	}
1047dbed73cbSSangeeta Misra 	return (ILB_STATUS_OK);
1048dbed73cbSSangeeta Misra rollback:
1049dbed73cbSSangeeta Misra 	enable = !enable;
1050dbed73cbSSangeeta Misra 	for (tmp_srv = list_prev(&hc_rule->hcr_servers, tmp_srv);
1051dbed73cbSSangeeta Misra 	    tmp_srv != NULL;
1052dbed73cbSSangeeta Misra 	    tmp_srv = list_prev(&hc_rule->hcr_servers, tmp_srv)) {
1053dbed73cbSSangeeta Misra 		if (enable) {
1054dbed73cbSSangeeta Misra 			if (tmp_srv->shc_status == ILB_HCS_DISABLED &&
1055dbed73cbSSangeeta Misra 			    ILB_IS_SRV_ENABLED(
1056dbed73cbSSangeeta Misra 			    tmp_srv->shc_sg_srv->sgs_flags)) {
1057dbed73cbSSangeeta Misra 				(void) ilbd_hc_restart_timer(hc, tmp_srv);
1058dbed73cbSSangeeta Misra 				tmp_srv->shc_status = ILB_HCS_UNINIT;
1059dbed73cbSSangeeta Misra 				tmp_srv->shc_rtt = 0;
1060dbed73cbSSangeeta Misra 				tmp_srv->shc_fail_cnt = 0;
1061dbed73cbSSangeeta Misra 			}
1062dbed73cbSSangeeta Misra 		} else {
1063dbed73cbSSangeeta Misra 			if (tmp_srv->shc_status != ILB_HCS_DISABLED) {
1064dbed73cbSSangeeta Misra 				HC_CANCEL_TIMER(tmp_srv);
1065dbed73cbSSangeeta Misra 				tmp_srv->shc_status = ILB_HCS_DISABLED;
1066dbed73cbSSangeeta Misra 				if (tmp_srv->shc_child_pid != 0)
1067dbed73cbSSangeeta Misra 					ilbd_hc_kill_probe(tmp_srv);
1068dbed73cbSSangeeta Misra 			}
1069dbed73cbSSangeeta Misra 		}
1070dbed73cbSSangeeta Misra 	}
1071dbed73cbSSangeeta Misra 	return (ret);
1072dbed73cbSSangeeta Misra }
1073dbed73cbSSangeeta Misra 
1074dbed73cbSSangeeta Misra ilb_status_t
1075dbed73cbSSangeeta Misra ilbd_hc_enable_rule(const ilbd_rule_t *rule)
1076dbed73cbSSangeeta Misra {
1077dbed73cbSSangeeta Misra 	return (ilbd_hc_toggle_rule(rule, B_TRUE));
1078dbed73cbSSangeeta Misra }
1079dbed73cbSSangeeta Misra 
1080dbed73cbSSangeeta Misra ilb_status_t
1081dbed73cbSSangeeta Misra ilbd_hc_disable_rule(const ilbd_rule_t *rule)
1082dbed73cbSSangeeta Misra {
1083dbed73cbSSangeeta Misra 	return (ilbd_hc_toggle_rule(rule, B_FALSE));
1084dbed73cbSSangeeta Misra }
1085dbed73cbSSangeeta Misra 
1086dbed73cbSSangeeta Misra static const char *
1087dbed73cbSSangeeta Misra topo_2_str(ilb_topo_t topo)
1088dbed73cbSSangeeta Misra {
1089dbed73cbSSangeeta Misra 	switch (topo) {
1090dbed73cbSSangeeta Misra 	case ILB_TOPO_DSR:
1091dbed73cbSSangeeta Misra 		return ("DSR");
1092dbed73cbSSangeeta Misra 	case ILB_TOPO_NAT:
1093dbed73cbSSangeeta Misra 		return ("NAT");
1094dbed73cbSSangeeta Misra 	case ILB_TOPO_HALF_NAT:
1095dbed73cbSSangeeta Misra 		return ("HALF_NAT");
1096dbed73cbSSangeeta Misra 	default:
1097dbed73cbSSangeeta Misra 		/* Should not happen. */
1098dbed73cbSSangeeta Misra 		logerr("%s: unknown topology", __func__);
1099dbed73cbSSangeeta Misra 		break;
1100dbed73cbSSangeeta Misra 	}
1101dbed73cbSSangeeta Misra 	return ("");
1102dbed73cbSSangeeta Misra }
1103dbed73cbSSangeeta Misra 
1104dbed73cbSSangeeta Misra /*
1105dbed73cbSSangeeta Misra  * Create the argument list to be passed to a hc probe command.
1106dbed73cbSSangeeta Misra  * The passed in argv is assumed to have HC_PROBE_ARGC elements.
1107dbed73cbSSangeeta Misra  */
1108dbed73cbSSangeeta Misra static boolean_t
1109dbed73cbSSangeeta Misra create_argv(ilbd_hc_srv_t *srv, char *argv[])
1110dbed73cbSSangeeta Misra {
1111dbed73cbSSangeeta Misra 	char buf[INET6_ADDRSTRLEN];
1112dbed73cbSSangeeta Misra 	ilbd_rule_t const *rule;
1113dbed73cbSSangeeta Misra 	ilb_sg_srv_t const *sg_srv;
1114dbed73cbSSangeeta Misra 	struct in_addr v4_addr;
1115dbed73cbSSangeeta Misra 	in_port_t port;
1116dbed73cbSSangeeta Misra 	int i;
1117dbed73cbSSangeeta Misra 
1118dbed73cbSSangeeta Misra 	rule = srv->shc_hc_rule->hcr_rule;
1119dbed73cbSSangeeta Misra 	sg_srv = srv->shc_sg_srv;
1120dbed73cbSSangeeta Misra 
1121dbed73cbSSangeeta Misra 	if (srv->shc_state == ilbd_hc_def_pinging) {
1122dbed73cbSSangeeta Misra 		if ((argv[0] = strdup(ILB_PROBE_PING)) == NULL)
1123dbed73cbSSangeeta Misra 			return (B_FALSE);
1124dbed73cbSSangeeta Misra 	} else {
1125dbed73cbSSangeeta Misra 		switch (srv->shc_hc->ihc_test_type) {
1126dbed73cbSSangeeta Misra 		case ILBD_HC_USER:
1127dbed73cbSSangeeta Misra 			if ((argv[0] = strdup(srv->shc_hc->ihc_test)) == NULL)
1128dbed73cbSSangeeta Misra 				return (B_FALSE);
1129dbed73cbSSangeeta Misra 			break;
1130dbed73cbSSangeeta Misra 		case ILBD_HC_TCP:
1131dbed73cbSSangeeta Misra 		case ILBD_HC_UDP:
1132dbed73cbSSangeeta Misra 			if ((argv[0] = strdup(ILB_PROBE_PROTO)) ==
1133dbed73cbSSangeeta Misra 			    NULL) {
1134dbed73cbSSangeeta Misra 				return (B_FALSE);
1135dbed73cbSSangeeta Misra 			}
1136dbed73cbSSangeeta Misra 			break;
1137dbed73cbSSangeeta Misra 		case ILBD_HC_PING:
1138dbed73cbSSangeeta Misra 			if ((argv[0] = strdup(ILB_PROBE_PING)) == NULL) {
1139dbed73cbSSangeeta Misra 				return (B_FALSE);
1140dbed73cbSSangeeta Misra 			}
1141dbed73cbSSangeeta Misra 			break;
1142dbed73cbSSangeeta Misra 		}
1143dbed73cbSSangeeta Misra 	}
1144dbed73cbSSangeeta Misra 
1145dbed73cbSSangeeta Misra 	/*
1146dbed73cbSSangeeta Misra 	 * argv[1] is the VIP.
1147dbed73cbSSangeeta Misra 	 *
1148dbed73cbSSangeeta Misra 	 * Right now, the VIP and the backend server addresses should be
1149dbed73cbSSangeeta Misra 	 * in the same IP address family.  Here we don't do that in case
1150dbed73cbSSangeeta Misra 	 * this assumption is changed in future.
1151dbed73cbSSangeeta Misra 	 */
1152dbed73cbSSangeeta Misra 	if (IN6_IS_ADDR_V4MAPPED(&rule->irl_vip)) {
1153dbed73cbSSangeeta Misra 		IN6_V4MAPPED_TO_INADDR(&rule->irl_vip, &v4_addr);
1154dbed73cbSSangeeta Misra 		if (inet_ntop(AF_INET, &v4_addr, buf, sizeof (buf)) == NULL)
1155dbed73cbSSangeeta Misra 			goto cleanup;
1156dbed73cbSSangeeta Misra 	} else {
1157dbed73cbSSangeeta Misra 		if (inet_ntop(AF_INET6, &rule->irl_vip, buf,
1158dbed73cbSSangeeta Misra 		    sizeof (buf)) == NULL) {
1159dbed73cbSSangeeta Misra 			goto cleanup;
1160dbed73cbSSangeeta Misra 		}
1161dbed73cbSSangeeta Misra 	}
1162dbed73cbSSangeeta Misra 	if ((argv[1] = strdup(buf)) == NULL)
1163dbed73cbSSangeeta Misra 		goto cleanup;
1164dbed73cbSSangeeta Misra 
1165dbed73cbSSangeeta Misra 	/*
1166dbed73cbSSangeeta Misra 	 * argv[2] is the backend server address.
1167dbed73cbSSangeeta Misra 	 */
1168dbed73cbSSangeeta Misra 	if (IN6_IS_ADDR_V4MAPPED(&sg_srv->sgs_addr)) {
1169dbed73cbSSangeeta Misra 		IN6_V4MAPPED_TO_INADDR(&sg_srv->sgs_addr, &v4_addr);
1170dbed73cbSSangeeta Misra 		if (inet_ntop(AF_INET, &v4_addr, buf, sizeof (buf)) == NULL)
1171dbed73cbSSangeeta Misra 			goto cleanup;
1172dbed73cbSSangeeta Misra 	} else {
1173dbed73cbSSangeeta Misra 		if (inet_ntop(AF_INET6, &sg_srv->sgs_addr, buf,
1174dbed73cbSSangeeta Misra 		    sizeof (buf)) == NULL) {
1175dbed73cbSSangeeta Misra 			goto cleanup;
1176dbed73cbSSangeeta Misra 		}
1177dbed73cbSSangeeta Misra 	}
1178dbed73cbSSangeeta Misra 	if ((argv[2] = strdup(buf)) == NULL)
1179dbed73cbSSangeeta Misra 		goto cleanup;
1180dbed73cbSSangeeta Misra 
1181dbed73cbSSangeeta Misra 	/*
1182dbed73cbSSangeeta Misra 	 * argv[3] is the transport protocol used in the rule.
1183dbed73cbSSangeeta Misra 	 */
1184dbed73cbSSangeeta Misra 	switch (rule->irl_proto) {
1185dbed73cbSSangeeta Misra 	case IPPROTO_TCP:
1186dbed73cbSSangeeta Misra 		argv[3] = strdup("TCP");
1187dbed73cbSSangeeta Misra 		break;
1188dbed73cbSSangeeta Misra 	case IPPROTO_UDP:
1189dbed73cbSSangeeta Misra 		argv[3] = strdup("UDP");
1190dbed73cbSSangeeta Misra 		break;
1191dbed73cbSSangeeta Misra 	default:
1192dbed73cbSSangeeta Misra 		logerr("%s: unknown protocol", __func__);
1193dbed73cbSSangeeta Misra 		goto cleanup;
1194dbed73cbSSangeeta Misra 	}
1195dbed73cbSSangeeta Misra 	if (argv[3] == NULL)
1196dbed73cbSSangeeta Misra 		goto cleanup;
1197dbed73cbSSangeeta Misra 
1198dbed73cbSSangeeta Misra 	/*
1199dbed73cbSSangeeta Misra 	 * argv[4] is the load balance mode, DSR, NAT, HALF-NAT.
1200dbed73cbSSangeeta Misra 	 */
1201dbed73cbSSangeeta Misra 	if ((argv[4] = strdup(topo_2_str(rule->irl_topo))) == NULL)
1202dbed73cbSSangeeta Misra 		goto cleanup;
1203dbed73cbSSangeeta Misra 
1204dbed73cbSSangeeta Misra 	/*
1205dbed73cbSSangeeta Misra 	 * argv[5] is the port range.  Right now, there should only be 1 port.
1206dbed73cbSSangeeta Misra 	 */
1207dbed73cbSSangeeta Misra 	switch (rule->irl_hcpflag) {
1208dbed73cbSSangeeta Misra 	case ILB_HCI_PROBE_FIX:
1209dbed73cbSSangeeta Misra 		port = ntohs(rule->irl_hcport);
1210dbed73cbSSangeeta Misra 		break;
1211dbed73cbSSangeeta Misra 	case ILB_HCI_PROBE_ANY: {
1212dbed73cbSSangeeta Misra 		in_port_t min, max;
1213dbed73cbSSangeeta Misra 
1214dbed73cbSSangeeta Misra 		if (ntohs(sg_srv->sgs_minport) == 0) {
1215dbed73cbSSangeeta Misra 			min = ntohs(rule->irl_minport);
1216dbed73cbSSangeeta Misra 			max = ntohs(rule->irl_maxport);
1217dbed73cbSSangeeta Misra 		} else {
1218dbed73cbSSangeeta Misra 			min = ntohs(sg_srv->sgs_minport);
1219dbed73cbSSangeeta Misra 			max = ntohs(sg_srv->sgs_maxport);
1220dbed73cbSSangeeta Misra 		}
1221dbed73cbSSangeeta Misra 		if (max > min)
1222dbed73cbSSangeeta Misra 			port = min + gethrtime() % (max - min + 1);
1223dbed73cbSSangeeta Misra 		else
1224dbed73cbSSangeeta Misra 			port = min;
1225dbed73cbSSangeeta Misra 		break;
1226dbed73cbSSangeeta Misra 	}
1227dbed73cbSSangeeta Misra 	default:
1228dbed73cbSSangeeta Misra 		logerr("%s: unknown HC flag", __func__);
1229dbed73cbSSangeeta Misra 		goto cleanup;
1230dbed73cbSSangeeta Misra 	}
1231dbed73cbSSangeeta Misra 	(void) sprintf(buf, "%d", port);
1232dbed73cbSSangeeta Misra 	if ((argv[5] = strdup(buf)) == NULL)
1233dbed73cbSSangeeta Misra 		goto cleanup;
1234dbed73cbSSangeeta Misra 
1235dbed73cbSSangeeta Misra 	/*
1236dbed73cbSSangeeta Misra 	 * argv[6] is the probe timeout.
1237dbed73cbSSangeeta Misra 	 */
1238dbed73cbSSangeeta Misra 	(void) sprintf(buf, "%d", srv->shc_hc->ihc_timeout);
1239dbed73cbSSangeeta Misra 	if ((argv[6] = strdup(buf)) == NULL)
1240dbed73cbSSangeeta Misra 		goto cleanup;
1241dbed73cbSSangeeta Misra 
1242dbed73cbSSangeeta Misra 	argv[7] = NULL;
1243dbed73cbSSangeeta Misra 	return (B_TRUE);
1244dbed73cbSSangeeta Misra 
1245dbed73cbSSangeeta Misra cleanup:
1246dbed73cbSSangeeta Misra 	for (i = 0; i < HC_PROBE_ARGC; i++) {
1247dbed73cbSSangeeta Misra 		if (argv[i] != NULL)
1248dbed73cbSSangeeta Misra 			free(argv[i]);
1249dbed73cbSSangeeta Misra 	}
1250dbed73cbSSangeeta Misra 	return (B_FALSE);
1251dbed73cbSSangeeta Misra }
1252dbed73cbSSangeeta Misra 
1253dbed73cbSSangeeta Misra static void
1254dbed73cbSSangeeta Misra destroy_argv(char *argv[])
1255dbed73cbSSangeeta Misra {
1256dbed73cbSSangeeta Misra 	int i;
1257dbed73cbSSangeeta Misra 
1258dbed73cbSSangeeta Misra 	for (i = 0; argv[i] != NULL; i++)
1259dbed73cbSSangeeta Misra 		free(argv[i]);
1260dbed73cbSSangeeta Misra }
1261dbed73cbSSangeeta Misra 
1262dbed73cbSSangeeta Misra /* Spawn a process to run the hc probe on the given server. */
1263dbed73cbSSangeeta Misra static boolean_t
1264dbed73cbSSangeeta Misra ilbd_run_probe(ilbd_hc_srv_t *srv)
1265dbed73cbSSangeeta Misra {
1266dbed73cbSSangeeta Misra 	posix_spawn_file_actions_t	fd_actions;
1267*808b84d0SSerghei Samsi 	boolean_t			init_fd_actions = B_FALSE;
1268dbed73cbSSangeeta Misra 	posix_spawnattr_t		attr;
1269*808b84d0SSerghei Samsi 	boolean_t			init_attr = B_FALSE;
1270dbed73cbSSangeeta Misra 	sigset_t			child_sigset;
1271dbed73cbSSangeeta Misra 	int				fds[2];
1272dbed73cbSSangeeta Misra 	int				fdflags;
1273dbed73cbSSangeeta Misra 	pid_t				pid;
1274dbed73cbSSangeeta Misra 	char				*child_argv[HC_PROBE_ARGC];
1275dbed73cbSSangeeta Misra 	ilbd_hc_probe_event_t		*probe_ev;
1276dbed73cbSSangeeta Misra 	char				*probe_name;
1277dbed73cbSSangeeta Misra 
1278dbed73cbSSangeeta Misra 	bzero(child_argv, HC_PROBE_ARGC * sizeof (char *));
1279dbed73cbSSangeeta Misra 	if ((probe_ev = calloc(1, sizeof (*probe_ev))) == NULL) {
1280dbed73cbSSangeeta Misra 		logdebug("ilbd_run_probe: calloc");
1281dbed73cbSSangeeta Misra 		return (B_FALSE);
1282dbed73cbSSangeeta Misra 	}
1283dbed73cbSSangeeta Misra 
1284dbed73cbSSangeeta Misra 	/* Set up a pipe to get output from probe command. */
1285dbed73cbSSangeeta Misra 	if (pipe(fds) < 0) {
1286dbed73cbSSangeeta Misra 		logdebug("ilbd_run_probe: cannot create pipe");
1287dbed73cbSSangeeta Misra 		free(probe_ev);
1288dbed73cbSSangeeta Misra 		return (B_FALSE);
1289dbed73cbSSangeeta Misra 	}
1290dbed73cbSSangeeta Misra 	/* Set our side of the pipe to be non-blocking */
1291dbed73cbSSangeeta Misra 	if ((fdflags = fcntl(fds[0], F_GETFL, 0)) == -1) {
1292dbed73cbSSangeeta Misra 		logdebug("ilbd_run_probe: fcntl(F_GETFL)");
1293dbed73cbSSangeeta Misra 		goto cleanup;
1294dbed73cbSSangeeta Misra 	}
1295dbed73cbSSangeeta Misra 	if (fcntl(fds[0], F_SETFL, fdflags | O_NONBLOCK) == -1) {
1296dbed73cbSSangeeta Misra 		logdebug("ilbd_run_probe: fcntl(F_SETFL)");
1297dbed73cbSSangeeta Misra 		goto cleanup;
1298dbed73cbSSangeeta Misra 	}
1299dbed73cbSSangeeta Misra 
1300dbed73cbSSangeeta Misra 	if (posix_spawn_file_actions_init(&fd_actions) != 0) {
1301dbed73cbSSangeeta Misra 		logdebug("ilbd_run_probe: posix_spawn_file_actions_init");
1302dbed73cbSSangeeta Misra 		goto cleanup;
1303dbed73cbSSangeeta Misra 	}
1304*808b84d0SSerghei Samsi 	init_fd_actions = B_TRUE;
1305dbed73cbSSangeeta Misra 	if (posix_spawnattr_init(&attr) != 0) {
1306dbed73cbSSangeeta Misra 		logdebug("ilbd_run_probe: posix_spawnattr_init");
1307dbed73cbSSangeeta Misra 		goto cleanup;
1308dbed73cbSSangeeta Misra 	}
1309*808b84d0SSerghei Samsi 	init_attr = B_TRUE;
1310dbed73cbSSangeeta Misra 	if (posix_spawn_file_actions_addclose(&fd_actions, fds[0]) != 0) {
1311dbed73cbSSangeeta Misra 		logdebug("ilbd_run_probe: posix_spawn_file_actions_addclose");
1312dbed73cbSSangeeta Misra 		goto cleanup;
1313dbed73cbSSangeeta Misra 	}
1314dbed73cbSSangeeta Misra 	if (posix_spawn_file_actions_adddup2(&fd_actions, fds[1],
1315dbed73cbSSangeeta Misra 	    STDOUT_FILENO) != 0) {
1316dbed73cbSSangeeta Misra 		logdebug("ilbd_run_probe: posix_spawn_file_actions_dup2");
1317dbed73cbSSangeeta Misra 		goto cleanup;
1318dbed73cbSSangeeta Misra 	}
1319dbed73cbSSangeeta Misra 	if (posix_spawn_file_actions_addclose(&fd_actions, fds[1]) != 0) {
1320dbed73cbSSangeeta Misra 		logdebug("ilbd_run_probe: posix_spawn_file_actions_addclose");
1321dbed73cbSSangeeta Misra 		goto cleanup;
1322dbed73cbSSangeeta Misra 	}
1323dbed73cbSSangeeta Misra 
1324dbed73cbSSangeeta Misra 	/* Reset all signal handling of the child to default. */
1325dbed73cbSSangeeta Misra 	(void) sigfillset(&child_sigset);
1326dbed73cbSSangeeta Misra 	if (posix_spawnattr_setsigdefault(&attr, &child_sigset) != 0) {
1327dbed73cbSSangeeta Misra 		logdebug("ilbd_run_probe: posix_spawnattr_setsigdefault");
1328dbed73cbSSangeeta Misra 		goto cleanup;
1329dbed73cbSSangeeta Misra 	}
1330dbed73cbSSangeeta Misra 	/* Don't want SIGCHLD. */
1331dbed73cbSSangeeta Misra 	if (posix_spawnattr_setflags(&attr, POSIX_SPAWN_NOSIGCHLD_NP|
1332dbed73cbSSangeeta Misra 	    POSIX_SPAWN_SETSIGDEF) != 0) {
1333dbed73cbSSangeeta Misra 		logdebug("ilbd_run_probe: posix_spawnattr_setflags");
1334dbed73cbSSangeeta Misra 		goto cleanup;
1335dbed73cbSSangeeta Misra 	}
1336dbed73cbSSangeeta Misra 
1337dbed73cbSSangeeta Misra 	if (!create_argv(srv, child_argv)) {
1338dbed73cbSSangeeta Misra 		logdebug("ilbd_run_probe: create_argv");
1339dbed73cbSSangeeta Misra 		goto cleanup;
1340dbed73cbSSangeeta Misra 	}
1341dbed73cbSSangeeta Misra 
1342dbed73cbSSangeeta Misra 	/*
1343dbed73cbSSangeeta Misra 	 * If we are doing default pinging or not using a user supplied
1344dbed73cbSSangeeta Misra 	 * probe, we should execute our standard supplied probe.  The
1345dbed73cbSSangeeta Misra 	 * supplied probe command handles all types of probes.  And the
1346dbed73cbSSangeeta Misra 	 * type used depends on argv[0], as filled in by create_argv().
1347dbed73cbSSangeeta Misra 	 */
1348dbed73cbSSangeeta Misra 	if (srv->shc_state == ilbd_hc_def_pinging ||
1349dbed73cbSSangeeta Misra 	    srv->shc_hc->ihc_test_type != ILBD_HC_USER) {
1350dbed73cbSSangeeta Misra 		probe_name = ILB_PROBE_PROTO;
1351dbed73cbSSangeeta Misra 	} else {
1352dbed73cbSSangeeta Misra 		probe_name = srv->shc_hc->ihc_test;
1353dbed73cbSSangeeta Misra 	}
1354dbed73cbSSangeeta Misra 	if (posix_spawn(&pid, probe_name, &fd_actions, &attr, child_argv,
1355dbed73cbSSangeeta Misra 	    NULL) != 0) {
1356dbed73cbSSangeeta Misra 		logerr("%s: posix_spawn: %s for server %s: %s", __func__,
1357dbed73cbSSangeeta Misra 		    srv->shc_hc->ihc_test, srv->shc_sg_srv->sgs_srvID,
1358dbed73cbSSangeeta Misra 		    strerror(errno));
1359dbed73cbSSangeeta Misra 		goto cleanup;
1360dbed73cbSSangeeta Misra 	}
1361dbed73cbSSangeeta Misra 
1362dbed73cbSSangeeta Misra 	(void) close(fds[1]);
1363dbed73cbSSangeeta Misra 	srv->shc_child_pid = pid;
1364dbed73cbSSangeeta Misra 	srv->shc_child_fd = fds[0];
1365dbed73cbSSangeeta Misra 	srv->shc_ev = probe_ev;
1366dbed73cbSSangeeta Misra 
1367dbed73cbSSangeeta Misra 	probe_ev->ihp_ev = ILBD_EVENT_PROBE;
1368dbed73cbSSangeeta Misra 	probe_ev->ihp_srv = srv;
1369dbed73cbSSangeeta Misra 	probe_ev->ihp_pid = pid;
1370dbed73cbSSangeeta Misra 	if (port_associate(srv->shc_ev_port, PORT_SOURCE_FD, fds[0],
1371dbed73cbSSangeeta Misra 	    POLLRDNORM, probe_ev) != 0) {
1372dbed73cbSSangeeta Misra 		/*
1373dbed73cbSSangeeta Misra 		 * Need to kill the child.  It will free the srv->shc_ev,
1374dbed73cbSSangeeta Misra 		 * which is probe_ev.  So set probe_ev to NULL.
1375dbed73cbSSangeeta Misra 		 */
1376dbed73cbSSangeeta Misra 		ilbd_hc_kill_probe(srv);
1377dbed73cbSSangeeta Misra 		probe_ev = NULL;
1378dbed73cbSSangeeta Misra 		goto cleanup;
1379dbed73cbSSangeeta Misra 	}
1380dbed73cbSSangeeta Misra 
1381*808b84d0SSerghei Samsi 	destroy_argv(child_argv);
1382*808b84d0SSerghei Samsi 	(void) posix_spawn_file_actions_destroy(&fd_actions);
1383*808b84d0SSerghei Samsi 	(void) posix_spawnattr_destroy(&attr);
1384dbed73cbSSangeeta Misra 	return (B_TRUE);
1385dbed73cbSSangeeta Misra 
1386dbed73cbSSangeeta Misra cleanup:
1387*808b84d0SSerghei Samsi 	destroy_argv(child_argv);
1388*808b84d0SSerghei Samsi 	if (init_fd_actions == B_TRUE)
1389*808b84d0SSerghei Samsi 		(void) posix_spawn_file_actions_destroy(&fd_actions);
1390*808b84d0SSerghei Samsi 	if (init_attr == B_TRUE)
1391*808b84d0SSerghei Samsi 		(void) posix_spawnattr_destroy(&attr);
1392dbed73cbSSangeeta Misra 	(void) close(fds[0]);
1393dbed73cbSSangeeta Misra 	(void) close(fds[1]);
1394dbed73cbSSangeeta Misra 	if (probe_ev != NULL)
1395dbed73cbSSangeeta Misra 		free(probe_ev);
1396dbed73cbSSangeeta Misra 	return (B_FALSE);
1397dbed73cbSSangeeta Misra }
1398dbed73cbSSangeeta Misra 
1399dbed73cbSSangeeta Misra /*
1400dbed73cbSSangeeta Misra  * Called by ild_hc_probe_return() to re-associate the fd to a child to
1401dbed73cbSSangeeta Misra  * the event port.
1402dbed73cbSSangeeta Misra  */
1403dbed73cbSSangeeta Misra static void
1404dbed73cbSSangeeta Misra reassociate_port(int ev_port, int fd, ilbd_hc_probe_event_t *ev)
1405dbed73cbSSangeeta Misra {
1406dbed73cbSSangeeta Misra 	if (port_associate(ev_port, PORT_SOURCE_FD, fd,
1407dbed73cbSSangeeta Misra 	    POLLRDNORM, ev) != 0) {
1408dbed73cbSSangeeta Misra 		/*
1409dbed73cbSSangeeta Misra 		 * If we cannot reassociate with the port, the only
1410dbed73cbSSangeeta Misra 		 * thing we can do now is to kill the child and
1411dbed73cbSSangeeta Misra 		 * do a blocking wait here...
1412dbed73cbSSangeeta Misra 		 */
1413dbed73cbSSangeeta Misra 		logdebug("%s: port_associate: %s", __func__, strerror(errno));
1414dbed73cbSSangeeta Misra 		if (kill(ev->ihp_pid, SIGKILL) != 0)
1415dbed73cbSSangeeta Misra 			logerr("%s: kill: %s", __func__, strerror(errno));
1416dbed73cbSSangeeta Misra 		if (waitpid(ev->ihp_pid, NULL, 0) != ev->ihp_pid)
1417dbed73cbSSangeeta Misra 			logdebug("%s: waitpid: %s", __func__, strerror(errno));
1418dbed73cbSSangeeta Misra 		free(ev);
1419dbed73cbSSangeeta Misra 	}
1420dbed73cbSSangeeta Misra }
1421dbed73cbSSangeeta Misra 
1422dbed73cbSSangeeta Misra /*
1423dbed73cbSSangeeta Misra  * To handle a child probe process hanging up.
1424dbed73cbSSangeeta Misra  */
1425dbed73cbSSangeeta Misra static void
1426dbed73cbSSangeeta Misra ilbd_hc_child_hup(int ev_port, int fd, ilbd_hc_probe_event_t *ev)
1427dbed73cbSSangeeta Misra {
1428dbed73cbSSangeeta Misra 	ilbd_hc_srv_t *srv;
1429dbed73cbSSangeeta Misra 	pid_t ret_pid;
1430dbed73cbSSangeeta Misra 	int ret;
1431dbed73cbSSangeeta Misra 
1432dbed73cbSSangeeta Misra 	srv = ev->ihp_srv;
1433dbed73cbSSangeeta Misra 
1434dbed73cbSSangeeta Misra 	if (!ev->ihp_done) {
1435dbed73cbSSangeeta Misra 		/* ilbd does not care about this process anymore ... */
1436dbed73cbSSangeeta Misra 		ev->ihp_done = B_TRUE;
1437dbed73cbSSangeeta Misra 		srv->shc_ev = NULL;
1438dbed73cbSSangeeta Misra 		srv->shc_child_pid = 0;
1439dbed73cbSSangeeta Misra 		HC_CANCEL_TIMER(srv);
1440dbed73cbSSangeeta Misra 		ilbd_set_fail_state(srv);
1441dbed73cbSSangeeta Misra 	}
1442dbed73cbSSangeeta Misra 	ret_pid = waitpid(ev->ihp_pid, &ret, WNOHANG);
1443dbed73cbSSangeeta Misra 	switch (ret_pid) {
1444dbed73cbSSangeeta Misra 	case -1:
1445dbed73cbSSangeeta Misra 		logperror("ilbd_hc_child_hup: waitpid");
1446dbed73cbSSangeeta Misra 		/* FALLTHROUGH */
1447dbed73cbSSangeeta Misra 	case 0:
1448dbed73cbSSangeeta Misra 		/* The child has not completed the exit. Wait again. */
1449dbed73cbSSangeeta Misra 		reassociate_port(ev_port, fd, ev);
1450dbed73cbSSangeeta Misra 		break;
1451dbed73cbSSangeeta Misra 	default:
1452dbed73cbSSangeeta Misra 		/* Right now, we just ignore the exit status. */
1453dbed73cbSSangeeta Misra 		if (WIFEXITED(ret))
1454dbed73cbSSangeeta Misra 			ret = WEXITSTATUS(ret);
1455dbed73cbSSangeeta Misra 		(void) close(fd);
1456dbed73cbSSangeeta Misra 		free(ev);
1457dbed73cbSSangeeta Misra 	}
1458dbed73cbSSangeeta Misra }
1459dbed73cbSSangeeta Misra 
1460dbed73cbSSangeeta Misra /*
1461dbed73cbSSangeeta Misra  * To read the output of a child probe process.
1462dbed73cbSSangeeta Misra  */
1463dbed73cbSSangeeta Misra static void
1464dbed73cbSSangeeta Misra ilbd_hc_child_data(int fd, ilbd_hc_probe_event_t *ev)
1465dbed73cbSSangeeta Misra {
1466dbed73cbSSangeeta Misra 	ilbd_hc_srv_t *srv;
1467dbed73cbSSangeeta Misra 	char buf[HC_MAX_PROBE_OUTPUT];
1468dbed73cbSSangeeta Misra 	int ret;
1469dbed73cbSSangeeta Misra 	int64_t rtt;
1470dbed73cbSSangeeta Misra 
1471dbed73cbSSangeeta Misra 	srv = ev->ihp_srv;
1472dbed73cbSSangeeta Misra 
1473dbed73cbSSangeeta Misra 	bzero(buf, HC_MAX_PROBE_OUTPUT);
1474dbed73cbSSangeeta Misra 	ret = read(fd, buf, HC_MAX_PROBE_OUTPUT - 1);
1475dbed73cbSSangeeta Misra 	/* Should not happen since event port should have caught this. */
1476dbed73cbSSangeeta Misra 	assert(ret > 0);
1477dbed73cbSSangeeta Misra 
1478dbed73cbSSangeeta Misra 	/*
1479dbed73cbSSangeeta Misra 	 * We expect the probe command to print out the RTT only.  But
1480dbed73cbSSangeeta Misra 	 * the command may misbehave and print out more than what we intend to
1481dbed73cbSSangeeta Misra 	 * read in.  So need to do this check below to "flush" out all the
1482dbed73cbSSangeeta Misra 	 * output from the command.
1483dbed73cbSSangeeta Misra 	 */
1484dbed73cbSSangeeta Misra 	if (!ev->ihp_done) {
1485dbed73cbSSangeeta Misra 		ev->ihp_done = B_TRUE;
1486dbed73cbSSangeeta Misra 		/* We don't need to know about this event anymore. */
1487dbed73cbSSangeeta Misra 		srv->shc_ev = NULL;
1488dbed73cbSSangeeta Misra 		srv->shc_child_pid = 0;
1489dbed73cbSSangeeta Misra 		HC_CANCEL_TIMER(srv);
1490dbed73cbSSangeeta Misra 	} else {
1491dbed73cbSSangeeta Misra 		return;
1492dbed73cbSSangeeta Misra 	}
1493dbed73cbSSangeeta Misra 
1494dbed73cbSSangeeta Misra 	rtt = strtoll(buf, NULL, 10);
1495dbed73cbSSangeeta Misra 
1496dbed73cbSSangeeta Misra 	/*
1497dbed73cbSSangeeta Misra 	 * -1 means the server is dead or the probe somehow fails.  Treat
1498dbed73cbSSangeeta Misra 	 * them both as server is dead.
1499dbed73cbSSangeeta Misra 	 */
1500dbed73cbSSangeeta Misra 	if (rtt == -1) {
1501dbed73cbSSangeeta Misra 		ilbd_set_fail_state(srv);
1502dbed73cbSSangeeta Misra 		return;
1503dbed73cbSSangeeta Misra 	} else if (rtt > 0) {
1504dbed73cbSSangeeta Misra 		/* If the returned RTT value is not valid, just ignore it. */
1505dbed73cbSSangeeta Misra 		if (rtt > 0 && rtt <= UINT_MAX) {
1506dbed73cbSSangeeta Misra 			/* Set rtt to be the simple smoothed average. */
1507dbed73cbSSangeeta Misra 			if (srv->shc_rtt == 0) {
1508dbed73cbSSangeeta Misra 				srv->shc_rtt = rtt;
1509dbed73cbSSangeeta Misra 			} else {
1510dbed73cbSSangeeta Misra 				srv->shc_rtt = 3 * ((srv)->shc_rtt >> 2) +
1511dbed73cbSSangeeta Misra 				    (rtt >> 2);
1512dbed73cbSSangeeta Misra 			}
1513dbed73cbSSangeeta Misra 		}
1514dbed73cbSSangeeta Misra 
1515dbed73cbSSangeeta Misra 	}
1516dbed73cbSSangeeta Misra 
1517dbed73cbSSangeeta Misra 	switch (srv->shc_state) {
1518dbed73cbSSangeeta Misra 	case ilbd_hc_def_pinging:
1519dbed73cbSSangeeta Misra 		srv->shc_state = ilbd_hc_probing;
1520dbed73cbSSangeeta Misra 
1521dbed73cbSSangeeta Misra 		/* Ping is OK, now start the probe. */
1522dbed73cbSSangeeta Misra 		ilbd_hc_probe_timer(ilbd_hc_timer_q, srv);
1523dbed73cbSSangeeta Misra 		break;
1524dbed73cbSSangeeta Misra 	case ilbd_hc_probing:
1525dbed73cbSSangeeta Misra 		srv->shc_fail_cnt = 0;
1526dbed73cbSSangeeta Misra 
1527dbed73cbSSangeeta Misra 		/* Server is dead before, re-enable it. */
1528dbed73cbSSangeeta Misra 		if (srv->shc_status == ILB_HCS_UNREACH ||
1529dbed73cbSSangeeta Misra 		    srv->shc_status == ILB_HCS_DEAD) {
1530dbed73cbSSangeeta Misra 			/*
1531dbed73cbSSangeeta Misra 			 * If enabling the server in kernel fails now,
1532dbed73cbSSangeeta Misra 			 * hopefully when the timer fires again later, the
1533dbed73cbSSangeeta Misra 			 * enabling can be done.
1534dbed73cbSSangeeta Misra 			 */
1535dbed73cbSSangeeta Misra 			if (ilbd_k_Xable_server(&srv->shc_sg_srv->sgs_addr,
1536dbed73cbSSangeeta Misra 			    srv->shc_hc_rule->hcr_rule->irl_name,
1537dbed73cbSSangeeta Misra 			    stat_declare_srv_alive) != ILB_STATUS_OK) {
1538dbed73cbSSangeeta Misra 				logerr("%s: cannot enable server in kernel: "
1539dbed73cbSSangeeta Misra 				    " rule %s server %s", __func__,
1540dbed73cbSSangeeta Misra 				    srv->shc_hc_rule->hcr_rule->irl_name,
1541dbed73cbSSangeeta Misra 				    srv->shc_sg_srv->sgs_srvID);
1542dbed73cbSSangeeta Misra 			} else {
1543dbed73cbSSangeeta Misra 				srv->shc_status = ILB_HCS_ALIVE;
1544dbed73cbSSangeeta Misra 			}
1545dbed73cbSSangeeta Misra 		} else {
1546dbed73cbSSangeeta Misra 			srv->shc_status = ILB_HCS_ALIVE;
1547dbed73cbSSangeeta Misra 		}
1548dbed73cbSSangeeta Misra 		if (ilbd_hc_restart_timer(srv->shc_hc, srv) != ILB_STATUS_OK) {
1549dbed73cbSSangeeta Misra 			logerr("%s: cannot restart timer: rule %s server %s",
1550dbed73cbSSangeeta Misra 			    __func__, srv->shc_hc_rule->hcr_rule->irl_name,
1551dbed73cbSSangeeta Misra 			    srv->shc_sg_srv->sgs_srvID);
1552dbed73cbSSangeeta Misra 			ilbd_mark_server_disabled(srv);
1553dbed73cbSSangeeta Misra 		}
1554dbed73cbSSangeeta Misra 		break;
1555dbed73cbSSangeeta Misra 	default:
1556dbed73cbSSangeeta Misra 		logdebug("%s: unknown state", __func__);
1557dbed73cbSSangeeta Misra 		break;
1558dbed73cbSSangeeta Misra 	}
1559dbed73cbSSangeeta Misra }
1560dbed73cbSSangeeta Misra 
1561dbed73cbSSangeeta Misra /*
1562dbed73cbSSangeeta Misra  * Handle the return event of a child probe fd.
1563dbed73cbSSangeeta Misra  */
1564dbed73cbSSangeeta Misra void
1565dbed73cbSSangeeta Misra ilbd_hc_probe_return(int ev_port, int fd, int port_events,
1566dbed73cbSSangeeta Misra     ilbd_hc_probe_event_t *ev)
1567dbed73cbSSangeeta Misra {
1568dbed73cbSSangeeta Misra 	/*
1569dbed73cbSSangeeta Misra 	 * Note that there can be more than one events delivered to us at
1570dbed73cbSSangeeta Misra 	 * the same time.  So we need to check them individually.
1571dbed73cbSSangeeta Misra 	 */
1572dbed73cbSSangeeta Misra 	if (port_events & POLLRDNORM)
1573dbed73cbSSangeeta Misra 		ilbd_hc_child_data(fd, ev);
1574dbed73cbSSangeeta Misra 
1575dbed73cbSSangeeta Misra 	if (port_events & (POLLHUP|POLLERR)) {
1576dbed73cbSSangeeta Misra 		ilbd_hc_child_hup(ev_port, fd, ev);
1577dbed73cbSSangeeta Misra 		return;
1578dbed73cbSSangeeta Misra 	}
1579dbed73cbSSangeeta Misra 
1580dbed73cbSSangeeta Misra 	/*
1581dbed73cbSSangeeta Misra 	 * Re-associate the fd with the port so that when the child
1582dbed73cbSSangeeta Misra 	 * exits, we can reap the status.
1583dbed73cbSSangeeta Misra 	 */
1584dbed73cbSSangeeta Misra 	reassociate_port(ev_port, fd, ev);
1585dbed73cbSSangeeta Misra }
1586