17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5eed64e98Sgm  * Common Development and Distribution License (the "License").
6eed64e98Sgm  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*5e2844d4SRenaud Manus  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate /*
277c478bd9Sstevel@tonic-gate  * This file contains a set of routines used to perform wait based method
287c478bd9Sstevel@tonic-gate  * reaping.
297c478bd9Sstevel@tonic-gate  */
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #include <wait.h>
327c478bd9Sstevel@tonic-gate #include <sys/param.h>
337c478bd9Sstevel@tonic-gate #include <fcntl.h>
347c478bd9Sstevel@tonic-gate #include <libcontract.h>
357c478bd9Sstevel@tonic-gate #include <errno.h>
367c478bd9Sstevel@tonic-gate #include <libintl.h>
377c478bd9Sstevel@tonic-gate #include <unistd.h>
387c478bd9Sstevel@tonic-gate #include <stdlib.h>
397c478bd9Sstevel@tonic-gate #include <string.h>
407c478bd9Sstevel@tonic-gate #include <sys/resource.h>
417c478bd9Sstevel@tonic-gate #include "inetd_impl.h"
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate /* inetd's open file limit, set in method_init() */
447c478bd9Sstevel@tonic-gate #define	INETD_NOFILE_LIMIT RLIM_INFINITY
457c478bd9Sstevel@tonic-gate 
467c478bd9Sstevel@tonic-gate /* structure used to represent an active method process */
477c478bd9Sstevel@tonic-gate typedef struct {
487c478bd9Sstevel@tonic-gate 	int			fd;	/* fd of process's /proc psinfo file */
497c478bd9Sstevel@tonic-gate 	/* associated contract id if known, else -1 */
507c478bd9Sstevel@tonic-gate 	ctid_t			cid;
517c478bd9Sstevel@tonic-gate 	pid_t			pid;
527c478bd9Sstevel@tonic-gate 	instance_t		*inst;	/* pointer to associated instance */
537c478bd9Sstevel@tonic-gate 	instance_method_t	method;	/* the method type running */
54*5e2844d4SRenaud Manus 	/* associated endpoint protocol name if known, else NULL */
55*5e2844d4SRenaud Manus 	char			*proto_name;
567c478bd9Sstevel@tonic-gate 	uu_list_node_t		link;
577c478bd9Sstevel@tonic-gate } method_el_t;
587c478bd9Sstevel@tonic-gate 
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate static void unregister_method(method_el_t *);
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate 
637c478bd9Sstevel@tonic-gate /* list of currently executing method processes */
647c478bd9Sstevel@tonic-gate static uu_list_pool_t		*method_pool = NULL;
657c478bd9Sstevel@tonic-gate static uu_list_t		*method_list = NULL;
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate /*
687c478bd9Sstevel@tonic-gate  * File limit saved during initialization before modification, so that it can
697c478bd9Sstevel@tonic-gate  * be reverted back to for inetd's exec'd methods.
707c478bd9Sstevel@tonic-gate  */
717c478bd9Sstevel@tonic-gate static struct rlimit		saved_file_limit;
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate /*
747c478bd9Sstevel@tonic-gate  * Setup structures used for method termination monitoring.
757c478bd9Sstevel@tonic-gate  * Returns -1 if an allocation failure occurred, else 0.
767c478bd9Sstevel@tonic-gate  */
777c478bd9Sstevel@tonic-gate int
method_init(void)787c478bd9Sstevel@tonic-gate method_init(void)
797c478bd9Sstevel@tonic-gate {
807c478bd9Sstevel@tonic-gate 	struct rlimit rl;
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate 	/*
837c478bd9Sstevel@tonic-gate 	 * Save aside the old file limit and impose one large enough to support
847c478bd9Sstevel@tonic-gate 	 * all the /proc file handles we could have open.
857c478bd9Sstevel@tonic-gate 	 */
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate 	(void) getrlimit(RLIMIT_NOFILE, &saved_file_limit);
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate 	rl.rlim_cur = rl.rlim_max = INETD_NOFILE_LIMIT;
907c478bd9Sstevel@tonic-gate 	if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
917c478bd9Sstevel@tonic-gate 		error_msg("Failed to set file limit: %s", strerror(errno));
927c478bd9Sstevel@tonic-gate 		return (-1);
937c478bd9Sstevel@tonic-gate 	}
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate 	if ((method_pool = uu_list_pool_create("method_pool",
967c478bd9Sstevel@tonic-gate 	    sizeof (method_el_t), offsetof(method_el_t, link), NULL,
977c478bd9Sstevel@tonic-gate 	    UU_LIST_POOL_DEBUG)) == NULL) {
987c478bd9Sstevel@tonic-gate 		error_msg("%s: %s", gettext("Failed to create method pool"),
997c478bd9Sstevel@tonic-gate 		    uu_strerror(uu_error()));
1007c478bd9Sstevel@tonic-gate 		return (-1);
1017c478bd9Sstevel@tonic-gate 	}
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate 	if ((method_list = uu_list_create(method_pool, NULL, 0)) == NULL) {
1047c478bd9Sstevel@tonic-gate 		error_msg("%s: %s",
1057c478bd9Sstevel@tonic-gate 		    gettext("Failed to create method list"),
1067c478bd9Sstevel@tonic-gate 		    uu_strerror(uu_error()));
1077c478bd9Sstevel@tonic-gate 		/* let method_fini() clean-up */
1087c478bd9Sstevel@tonic-gate 		return (-1);
1097c478bd9Sstevel@tonic-gate 	}
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate 	return (0);
1127c478bd9Sstevel@tonic-gate }
1137c478bd9Sstevel@tonic-gate 
1147c478bd9Sstevel@tonic-gate /*
1157c478bd9Sstevel@tonic-gate  * Tear-down structures created in method_init().
1167c478bd9Sstevel@tonic-gate  */
1177c478bd9Sstevel@tonic-gate void
method_fini(void)1187c478bd9Sstevel@tonic-gate method_fini(void)
1197c478bd9Sstevel@tonic-gate {
1207c478bd9Sstevel@tonic-gate 	if (method_list != NULL) {
1217c478bd9Sstevel@tonic-gate 		method_el_t *me;
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate 		while ((me = uu_list_first(method_list)) != NULL)
1247c478bd9Sstevel@tonic-gate 			unregister_method(me);
1257c478bd9Sstevel@tonic-gate 
1267c478bd9Sstevel@tonic-gate 		(void) uu_list_destroy(method_list);
1277c478bd9Sstevel@tonic-gate 		method_list = NULL;
1287c478bd9Sstevel@tonic-gate 	}
1297c478bd9Sstevel@tonic-gate 	if (method_pool != NULL) {
1307c478bd9Sstevel@tonic-gate 		(void) uu_list_pool_destroy(method_pool);
1317c478bd9Sstevel@tonic-gate 		method_pool = NULL;
1327c478bd9Sstevel@tonic-gate 	}
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 	/* revert file limit */
1357c478bd9Sstevel@tonic-gate 	method_preexec();
1367c478bd9Sstevel@tonic-gate }
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate /*
1397c478bd9Sstevel@tonic-gate  * Revert file limit back to pre-initialization one. This shouldn't fail as
1407c478bd9Sstevel@tonic-gate  * long as its called *after* descriptor cleanup.
1417c478bd9Sstevel@tonic-gate  */
1427c478bd9Sstevel@tonic-gate void
method_preexec(void)1437c478bd9Sstevel@tonic-gate method_preexec(void)
1447c478bd9Sstevel@tonic-gate {
1457c478bd9Sstevel@tonic-gate 	(void) setrlimit(RLIMIT_NOFILE, &saved_file_limit);
1467c478bd9Sstevel@tonic-gate }
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate 
1497c478bd9Sstevel@tonic-gate /*
1507c478bd9Sstevel@tonic-gate  * Callback function that handles the timeout of an instance's method.
1517c478bd9Sstevel@tonic-gate  * 'arg' points at the method_el_t representing the method.
1527c478bd9Sstevel@tonic-gate  */
1537c478bd9Sstevel@tonic-gate /* ARGSUSED0 */
1547c478bd9Sstevel@tonic-gate static void
method_timeout(iu_tq_t * tq,void * arg)1557c478bd9Sstevel@tonic-gate method_timeout(iu_tq_t *tq, void *arg)
1567c478bd9Sstevel@tonic-gate {
1577c478bd9Sstevel@tonic-gate 	method_el_t *mp = arg;
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate 	error_msg(gettext("The %s method of instance %s timed-out"),
1607c478bd9Sstevel@tonic-gate 	    methods[mp->method].name, mp->inst->fmri);
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate 	mp->inst->timer_id = -1;
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate 	if (mp->method == IM_START) {
165*5e2844d4SRenaud Manus 		process_start_term(mp->inst, mp->proto_name);
1667c478bd9Sstevel@tonic-gate 	} else {
1677c478bd9Sstevel@tonic-gate 		process_non_start_term(mp->inst, IMRET_FAILURE);
1687c478bd9Sstevel@tonic-gate 	}
1697c478bd9Sstevel@tonic-gate 
1707c478bd9Sstevel@tonic-gate 	unregister_method(mp);
1717c478bd9Sstevel@tonic-gate }
1727c478bd9Sstevel@tonic-gate 
1737c478bd9Sstevel@tonic-gate /*
1747c478bd9Sstevel@tonic-gate  * Registers the attributes of a running method passed as arguments so that
1757c478bd9Sstevel@tonic-gate  * the method's termination is noticed and any further processing of the
1767c478bd9Sstevel@tonic-gate  * associated instance is carried out. The function also sets up any
1777c478bd9Sstevel@tonic-gate  * necessary timers so we can detect hung methods.
1787c478bd9Sstevel@tonic-gate  * Returns -1 if either it failed to open the /proc psinfo file which is used
1797c478bd9Sstevel@tonic-gate  * to monitor the method process, it failed to setup a required timer or
1807c478bd9Sstevel@tonic-gate  * memory allocation failed; else 0.
1817c478bd9Sstevel@tonic-gate  */
1827c478bd9Sstevel@tonic-gate int
register_method(instance_t * ins,pid_t pid,ctid_t cid,instance_method_t mthd,char * proto_name)183*5e2844d4SRenaud Manus register_method(instance_t *ins, pid_t pid, ctid_t cid, instance_method_t mthd,
184*5e2844d4SRenaud Manus     char *proto_name)
1857c478bd9Sstevel@tonic-gate {
1867c478bd9Sstevel@tonic-gate 	char		path[MAXPATHLEN];
1877c478bd9Sstevel@tonic-gate 	int		fd;
1887c478bd9Sstevel@tonic-gate 	method_el_t	*me;
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate 	/* open /proc psinfo file of process to listen for POLLHUP events on */
1917c478bd9Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "/proc/%u/psinfo", pid);
1927c478bd9Sstevel@tonic-gate 	for (;;) {
1937c478bd9Sstevel@tonic-gate 		if ((fd = open(path, O_RDONLY)) >= 0) {
1947c478bd9Sstevel@tonic-gate 			break;
1957c478bd9Sstevel@tonic-gate 		} else if (errno != EINTR) {
1967c478bd9Sstevel@tonic-gate 			/*
1977c478bd9Sstevel@tonic-gate 			 * Don't output an error for ENOENT; we get this
1987c478bd9Sstevel@tonic-gate 			 * if a method has gone away whilst we were stopped,
1997c478bd9Sstevel@tonic-gate 			 * and we're now trying to re-listen for it.
2007c478bd9Sstevel@tonic-gate 			 */
2017c478bd9Sstevel@tonic-gate 			if (errno != ENOENT) {
2027c478bd9Sstevel@tonic-gate 				error_msg(gettext("Failed to open %s: %s"),
2037c478bd9Sstevel@tonic-gate 				    path, strerror(errno));
2047c478bd9Sstevel@tonic-gate 			}
2057c478bd9Sstevel@tonic-gate 			return (-1);
2067c478bd9Sstevel@tonic-gate 		}
2077c478bd9Sstevel@tonic-gate 	}
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate 	/* add method record to in-memory list */
2107c478bd9Sstevel@tonic-gate 	if ((me = calloc(1, sizeof (method_el_t))) == NULL) {
2117c478bd9Sstevel@tonic-gate 		error_msg(strerror(errno));
2127c478bd9Sstevel@tonic-gate 		(void) close(fd);
2137c478bd9Sstevel@tonic-gate 		return (-1);
2147c478bd9Sstevel@tonic-gate 	}
2157c478bd9Sstevel@tonic-gate 	me->fd = fd;
2167c478bd9Sstevel@tonic-gate 	me->inst = (instance_t *)ins;
2177c478bd9Sstevel@tonic-gate 	me->method = mthd;
2187c478bd9Sstevel@tonic-gate 	me->pid = pid;
2197c478bd9Sstevel@tonic-gate 	me->cid = cid;
220*5e2844d4SRenaud Manus 	if (proto_name != NULL) {
221*5e2844d4SRenaud Manus 		if ((me->proto_name = strdup(proto_name)) == NULL) {
222*5e2844d4SRenaud Manus 			error_msg(strerror(errno));
223*5e2844d4SRenaud Manus 			free(me);
224*5e2844d4SRenaud Manus 			(void) close(fd);
225*5e2844d4SRenaud Manus 			return (-1);
226*5e2844d4SRenaud Manus 		}
227*5e2844d4SRenaud Manus 	} else
228*5e2844d4SRenaud Manus 		me->proto_name = NULL;
2297c478bd9Sstevel@tonic-gate 
2307c478bd9Sstevel@tonic-gate 	/* register a timeout for the method, if required */
2317c478bd9Sstevel@tonic-gate 	if (mthd != IM_START) {
2327c478bd9Sstevel@tonic-gate 		method_info_t *mi = ins->config->methods[mthd];
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate 		if (mi->timeout > 0) {
2357c478bd9Sstevel@tonic-gate 			assert(ins->timer_id == -1);
2367c478bd9Sstevel@tonic-gate 			ins->timer_id = iu_schedule_timer(timer_queue,
2377c478bd9Sstevel@tonic-gate 			    mi->timeout, method_timeout, me);
2387c478bd9Sstevel@tonic-gate 			if (ins->timer_id == -1) {
2397c478bd9Sstevel@tonic-gate 				error_msg(gettext(
2407c478bd9Sstevel@tonic-gate 				    "Failed to schedule method timeout"));
241*5e2844d4SRenaud Manus 				if (me->proto_name != NULL)
242*5e2844d4SRenaud Manus 					free(me->proto_name);
2437c478bd9Sstevel@tonic-gate 				free(me);
2447c478bd9Sstevel@tonic-gate 				(void) close(fd);
2457c478bd9Sstevel@tonic-gate 				return (-1);
2467c478bd9Sstevel@tonic-gate 			}
2477c478bd9Sstevel@tonic-gate 		}
2487c478bd9Sstevel@tonic-gate 	}
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 	/*
2517c478bd9Sstevel@tonic-gate 	 * Add fd of psinfo file to poll set, but pass 0 for events to
2527c478bd9Sstevel@tonic-gate 	 * poll for, so we should only get a POLLHUP event on the fd.
2537c478bd9Sstevel@tonic-gate 	 */
2547c478bd9Sstevel@tonic-gate 	if (set_pollfd(fd, 0) == -1) {
2557c478bd9Sstevel@tonic-gate 		cancel_inst_timer(ins);
256*5e2844d4SRenaud Manus 		if (me->proto_name != NULL)
257*5e2844d4SRenaud Manus 			free(me->proto_name);
2587c478bd9Sstevel@tonic-gate 		free(me);
2597c478bd9Sstevel@tonic-gate 		(void) close(fd);
2607c478bd9Sstevel@tonic-gate 		return (-1);
2617c478bd9Sstevel@tonic-gate 	}
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate 	uu_list_node_init(me, &me->link, method_pool);
2647c478bd9Sstevel@tonic-gate 	(void) uu_list_insert_after(method_list, NULL, me);
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 	return (0);
2677c478bd9Sstevel@tonic-gate }
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate /*
2707c478bd9Sstevel@tonic-gate  * A counterpart to register_method(), this function stops the monitoring of a
2717c478bd9Sstevel@tonic-gate  * method process for its termination.
2727c478bd9Sstevel@tonic-gate  */
2737c478bd9Sstevel@tonic-gate static void
unregister_method(method_el_t * me)2747c478bd9Sstevel@tonic-gate unregister_method(method_el_t *me)
2757c478bd9Sstevel@tonic-gate {
2767c478bd9Sstevel@tonic-gate 	/* cancel any timer associated with the method */
2777c478bd9Sstevel@tonic-gate 	if (me->inst->timer_id != -1)
2787c478bd9Sstevel@tonic-gate 		cancel_inst_timer(me->inst);
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate 	/* stop polling on the psinfo file fd */
2817c478bd9Sstevel@tonic-gate 	clear_pollfd(me->fd);
2827c478bd9Sstevel@tonic-gate 	(void) close(me->fd);
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 	/* remove method record from list */
2857c478bd9Sstevel@tonic-gate 	uu_list_remove(method_list, me);
2867c478bd9Sstevel@tonic-gate 
287*5e2844d4SRenaud Manus 	if (me->proto_name != NULL)
288*5e2844d4SRenaud Manus 		free(me->proto_name);
2897c478bd9Sstevel@tonic-gate 	free(me);
2907c478bd9Sstevel@tonic-gate }
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate /*
2937c478bd9Sstevel@tonic-gate  * Unregister all methods associated with instance 'inst'.
2947c478bd9Sstevel@tonic-gate  */
2957c478bd9Sstevel@tonic-gate void
unregister_instance_methods(const instance_t * inst)2967c478bd9Sstevel@tonic-gate unregister_instance_methods(const instance_t *inst)
2977c478bd9Sstevel@tonic-gate {
2987c478bd9Sstevel@tonic-gate 	method_el_t *me = uu_list_first(method_list);
2997c478bd9Sstevel@tonic-gate 
3007c478bd9Sstevel@tonic-gate 	while (me != NULL) {
3017c478bd9Sstevel@tonic-gate 		if (me->inst == inst) {
3027c478bd9Sstevel@tonic-gate 			method_el_t *tmp = me;
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 			me = uu_list_next(method_list, me);
3057c478bd9Sstevel@tonic-gate 			unregister_method(tmp);
3067c478bd9Sstevel@tonic-gate 		} else  {
3077c478bd9Sstevel@tonic-gate 			me = uu_list_next(method_list, me);
3087c478bd9Sstevel@tonic-gate 		}
3097c478bd9Sstevel@tonic-gate 	}
3107c478bd9Sstevel@tonic-gate }
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate /*
3137c478bd9Sstevel@tonic-gate  * Process any terminated methods. For each method determined to have
3147c478bd9Sstevel@tonic-gate  * terminated, the function determines its return value and calls the
3157c478bd9Sstevel@tonic-gate  * appropriate handling function, depending on the type of the method.
3167c478bd9Sstevel@tonic-gate  */
3177c478bd9Sstevel@tonic-gate void
process_terminated_methods(void)3187c478bd9Sstevel@tonic-gate process_terminated_methods(void)
3197c478bd9Sstevel@tonic-gate {
3207c478bd9Sstevel@tonic-gate 	method_el_t	*me = uu_list_first(method_list);
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate 	while (me != NULL) {
3237c478bd9Sstevel@tonic-gate 		struct pollfd	*pfd;
3247c478bd9Sstevel@tonic-gate 		pid_t		pid;
3257c478bd9Sstevel@tonic-gate 		int		status;
3267c478bd9Sstevel@tonic-gate 		int		ret;
3277c478bd9Sstevel@tonic-gate 		method_el_t	*tmp;
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate 		pfd = find_pollfd(me->fd);
3307c478bd9Sstevel@tonic-gate 
3317c478bd9Sstevel@tonic-gate 		/*
3327c478bd9Sstevel@tonic-gate 		 * We expect to get a POLLHUP back on the fd of the process's
3337c478bd9Sstevel@tonic-gate 		 * open psinfo file from /proc when the method terminates.
3347c478bd9Sstevel@tonic-gate 		 * A POLLERR could(?) mask a POLLHUP, so handle this
3357c478bd9Sstevel@tonic-gate 		 * also.
3367c478bd9Sstevel@tonic-gate 		 */
3377c478bd9Sstevel@tonic-gate 		if ((pfd->revents & (POLLHUP|POLLERR)) == 0) {
3387c478bd9Sstevel@tonic-gate 			me = uu_list_next(method_list, me);
3397c478bd9Sstevel@tonic-gate 			continue;
3407c478bd9Sstevel@tonic-gate 		}
3417c478bd9Sstevel@tonic-gate 
3427c478bd9Sstevel@tonic-gate 		/* get the method's exit code (no need to loop for EINTR) */
3437c478bd9Sstevel@tonic-gate 		pid = waitpid(me->pid, &status, WNOHANG);
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 		switch (pid) {
3467c478bd9Sstevel@tonic-gate 		case 0:					/* child still around */
3477c478bd9Sstevel@tonic-gate 			/*
3487c478bd9Sstevel@tonic-gate 			 * Either poll() is sending us invalid POLLHUP events
3497c478bd9Sstevel@tonic-gate 			 * or is flagging a POLLERR on the fd. Neither should
3507c478bd9Sstevel@tonic-gate 			 * happen, but in the event they do, ignore this fd
3517c478bd9Sstevel@tonic-gate 			 * this time around and wait out the termination
3527c478bd9Sstevel@tonic-gate 			 * of its associated method. This may result in
3537c478bd9Sstevel@tonic-gate 			 * inetd swiftly looping in event_loop(), but means
3547c478bd9Sstevel@tonic-gate 			 * we don't miss the termination of a method.
3557c478bd9Sstevel@tonic-gate 			 */
3567c478bd9Sstevel@tonic-gate 			me = uu_list_next(method_list, me);
3577c478bd9Sstevel@tonic-gate 			continue;
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate 		case -1:				/* non-existent child */
3607c478bd9Sstevel@tonic-gate 			assert(errno == ECHILD);
3617c478bd9Sstevel@tonic-gate 			/*
3627c478bd9Sstevel@tonic-gate 			 * the method must not be owned by inetd due to it
3637c478bd9Sstevel@tonic-gate 			 * persisting over an inetd restart. Let's assume the
3647c478bd9Sstevel@tonic-gate 			 * best, that it was successful.
3657c478bd9Sstevel@tonic-gate 			 */
3667c478bd9Sstevel@tonic-gate 			ret = IMRET_SUCCESS;
3677c478bd9Sstevel@tonic-gate 			break;
3687c478bd9Sstevel@tonic-gate 
3697c478bd9Sstevel@tonic-gate 		default:				/* child terminated */
3707c478bd9Sstevel@tonic-gate 			if (WIFEXITED(status)) {
3717c478bd9Sstevel@tonic-gate 				ret = WEXITSTATUS(status);
3728793b36bSNick Todd 				debug_msg("process %ld of instance %s returned "
3737c478bd9Sstevel@tonic-gate 				    "%d", pid, me->inst->fmri, ret);
3747c478bd9Sstevel@tonic-gate 			} else if (WIFSIGNALED(status)) {
3757c478bd9Sstevel@tonic-gate 				/*
3767c478bd9Sstevel@tonic-gate 				 * Terminated by signal.  This may be due
3777c478bd9Sstevel@tonic-gate 				 * to a kill that we sent from a disable or
3787c478bd9Sstevel@tonic-gate 				 * offline event. We flag it as a failure, but
3797c478bd9Sstevel@tonic-gate 				 * this flagged failure will only be processed
3807c478bd9Sstevel@tonic-gate 				 * in the case of non-start methods, or when
3817c478bd9Sstevel@tonic-gate 				 * the instance is still enabled.
3827c478bd9Sstevel@tonic-gate 				 */
3838793b36bSNick Todd 				debug_msg("process %ld of instance %s exited "
3847c478bd9Sstevel@tonic-gate 				    "due to signal %d", pid, me->inst->fmri,
3857c478bd9Sstevel@tonic-gate 				    WTERMSIG(status));
3867c478bd9Sstevel@tonic-gate 				ret = IMRET_FAILURE;
3877c478bd9Sstevel@tonic-gate 			} else {
3887c478bd9Sstevel@tonic-gate 				/*
3897c478bd9Sstevel@tonic-gate 				 * Can we actually get here?  Don't think so.
3907c478bd9Sstevel@tonic-gate 				 * Treat it as a failure, anyway.
3917c478bd9Sstevel@tonic-gate 				 */
3927c478bd9Sstevel@tonic-gate 				debug_msg("waitpid() for %s method of "
3937c478bd9Sstevel@tonic-gate 				    "instance %s returned %d",
3947c478bd9Sstevel@tonic-gate 				    methods[me->method].name, me->inst->fmri,
3957c478bd9Sstevel@tonic-gate 				    status);
3967c478bd9Sstevel@tonic-gate 				ret = IMRET_FAILURE;
3977c478bd9Sstevel@tonic-gate 			}
3987c478bd9Sstevel@tonic-gate 		}
3997c478bd9Sstevel@tonic-gate 
4007c478bd9Sstevel@tonic-gate 		remove_method_ids(me->inst, me->pid, me->cid, me->method);
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 		/* continue state transition processing of the instance */
4037c478bd9Sstevel@tonic-gate 		if (me->method != IM_START) {
4047c478bd9Sstevel@tonic-gate 			process_non_start_term(me->inst, ret);
4057c478bd9Sstevel@tonic-gate 		} else {
406*5e2844d4SRenaud Manus 			process_start_term(me->inst, me->proto_name);
4077c478bd9Sstevel@tonic-gate 		}
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 		if (me->cid != -1)
4107c478bd9Sstevel@tonic-gate 			(void) abandon_contract(me->cid);
4117c478bd9Sstevel@tonic-gate 
4127c478bd9Sstevel@tonic-gate 		tmp = me;
4137c478bd9Sstevel@tonic-gate 		me = uu_list_next(method_list, me);
4147c478bd9Sstevel@tonic-gate 		unregister_method(tmp);
4157c478bd9Sstevel@tonic-gate 	}
4167c478bd9Sstevel@tonic-gate }
417