xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.lib/inetd/wait.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate /*
30*7c478bd9Sstevel@tonic-gate  * This file contains a set of routines used to perform wait based method
31*7c478bd9Sstevel@tonic-gate  * reaping.
32*7c478bd9Sstevel@tonic-gate  */
33*7c478bd9Sstevel@tonic-gate 
34*7c478bd9Sstevel@tonic-gate #include <wait.h>
35*7c478bd9Sstevel@tonic-gate #include <sys/param.h>
36*7c478bd9Sstevel@tonic-gate #include <fcntl.h>
37*7c478bd9Sstevel@tonic-gate #include <libcontract.h>
38*7c478bd9Sstevel@tonic-gate #include <errno.h>
39*7c478bd9Sstevel@tonic-gate #include <libintl.h>
40*7c478bd9Sstevel@tonic-gate #include <unistd.h>
41*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
42*7c478bd9Sstevel@tonic-gate #include <string.h>
43*7c478bd9Sstevel@tonic-gate #include <sys/resource.h>
44*7c478bd9Sstevel@tonic-gate #include "inetd_impl.h"
45*7c478bd9Sstevel@tonic-gate 
46*7c478bd9Sstevel@tonic-gate /* inetd's open file limit, set in method_init() */
47*7c478bd9Sstevel@tonic-gate #define	INETD_NOFILE_LIMIT RLIM_INFINITY
48*7c478bd9Sstevel@tonic-gate 
49*7c478bd9Sstevel@tonic-gate /* structure used to represent an active method process */
50*7c478bd9Sstevel@tonic-gate typedef struct {
51*7c478bd9Sstevel@tonic-gate 	int			fd;	/* fd of process's /proc psinfo file */
52*7c478bd9Sstevel@tonic-gate 	/* associated contract id if known, else -1 */
53*7c478bd9Sstevel@tonic-gate 	ctid_t			cid;
54*7c478bd9Sstevel@tonic-gate 	pid_t			pid;
55*7c478bd9Sstevel@tonic-gate 	instance_t		*inst;	/* pointer to associated instance */
56*7c478bd9Sstevel@tonic-gate 	instance_method_t	method;	/* the method type running */
57*7c478bd9Sstevel@tonic-gate 	uu_list_node_t		link;
58*7c478bd9Sstevel@tonic-gate } method_el_t;
59*7c478bd9Sstevel@tonic-gate 
60*7c478bd9Sstevel@tonic-gate 
61*7c478bd9Sstevel@tonic-gate static void unregister_method(method_el_t *);
62*7c478bd9Sstevel@tonic-gate 
63*7c478bd9Sstevel@tonic-gate 
64*7c478bd9Sstevel@tonic-gate /* list of currently executing method processes */
65*7c478bd9Sstevel@tonic-gate static uu_list_pool_t		*method_pool = NULL;
66*7c478bd9Sstevel@tonic-gate static uu_list_t		*method_list = NULL;
67*7c478bd9Sstevel@tonic-gate 
68*7c478bd9Sstevel@tonic-gate /*
69*7c478bd9Sstevel@tonic-gate  * File limit saved during initialization before modification, so that it can
70*7c478bd9Sstevel@tonic-gate  * be reverted back to for inetd's exec'd methods.
71*7c478bd9Sstevel@tonic-gate  */
72*7c478bd9Sstevel@tonic-gate static struct rlimit		saved_file_limit;
73*7c478bd9Sstevel@tonic-gate 
74*7c478bd9Sstevel@tonic-gate /*
75*7c478bd9Sstevel@tonic-gate  * Setup structures used for method termination monitoring.
76*7c478bd9Sstevel@tonic-gate  * Returns -1 if an allocation failure occurred, else 0.
77*7c478bd9Sstevel@tonic-gate  */
78*7c478bd9Sstevel@tonic-gate int
79*7c478bd9Sstevel@tonic-gate method_init(void)
80*7c478bd9Sstevel@tonic-gate {
81*7c478bd9Sstevel@tonic-gate 	struct rlimit rl;
82*7c478bd9Sstevel@tonic-gate 
83*7c478bd9Sstevel@tonic-gate 	debug_msg("Entering method_init");
84*7c478bd9Sstevel@tonic-gate 
85*7c478bd9Sstevel@tonic-gate 	/*
86*7c478bd9Sstevel@tonic-gate 	 * Save aside the old file limit and impose one large enough to support
87*7c478bd9Sstevel@tonic-gate 	 * all the /proc file handles we could have open.
88*7c478bd9Sstevel@tonic-gate 	 */
89*7c478bd9Sstevel@tonic-gate 
90*7c478bd9Sstevel@tonic-gate 	(void) getrlimit(RLIMIT_NOFILE, &saved_file_limit);
91*7c478bd9Sstevel@tonic-gate 
92*7c478bd9Sstevel@tonic-gate 	rl.rlim_cur = rl.rlim_max = INETD_NOFILE_LIMIT;
93*7c478bd9Sstevel@tonic-gate 	if (setrlimit(RLIMIT_NOFILE, &rl) == -1) {
94*7c478bd9Sstevel@tonic-gate 		error_msg("Failed to set file limit: %s", strerror(errno));
95*7c478bd9Sstevel@tonic-gate 		return (-1);
96*7c478bd9Sstevel@tonic-gate 	}
97*7c478bd9Sstevel@tonic-gate 
98*7c478bd9Sstevel@tonic-gate 	if ((method_pool = uu_list_pool_create("method_pool",
99*7c478bd9Sstevel@tonic-gate 	    sizeof (method_el_t), offsetof(method_el_t, link), NULL,
100*7c478bd9Sstevel@tonic-gate 	    UU_LIST_POOL_DEBUG)) == NULL) {
101*7c478bd9Sstevel@tonic-gate 		error_msg("%s: %s", gettext("Failed to create method pool"),
102*7c478bd9Sstevel@tonic-gate 		    uu_strerror(uu_error()));
103*7c478bd9Sstevel@tonic-gate 		return (-1);
104*7c478bd9Sstevel@tonic-gate 	}
105*7c478bd9Sstevel@tonic-gate 
106*7c478bd9Sstevel@tonic-gate 	if ((method_list = uu_list_create(method_pool, NULL, 0)) == NULL) {
107*7c478bd9Sstevel@tonic-gate 		error_msg("%s: %s",
108*7c478bd9Sstevel@tonic-gate 		    gettext("Failed to create method list"),
109*7c478bd9Sstevel@tonic-gate 		    uu_strerror(uu_error()));
110*7c478bd9Sstevel@tonic-gate 		/* let method_fini() clean-up */
111*7c478bd9Sstevel@tonic-gate 		return (-1);
112*7c478bd9Sstevel@tonic-gate 	}
113*7c478bd9Sstevel@tonic-gate 
114*7c478bd9Sstevel@tonic-gate 	return (0);
115*7c478bd9Sstevel@tonic-gate }
116*7c478bd9Sstevel@tonic-gate 
117*7c478bd9Sstevel@tonic-gate /*
118*7c478bd9Sstevel@tonic-gate  * Tear-down structures created in method_init().
119*7c478bd9Sstevel@tonic-gate  */
120*7c478bd9Sstevel@tonic-gate void
121*7c478bd9Sstevel@tonic-gate method_fini(void)
122*7c478bd9Sstevel@tonic-gate {
123*7c478bd9Sstevel@tonic-gate 	debug_msg("Entering method_fini");
124*7c478bd9Sstevel@tonic-gate 
125*7c478bd9Sstevel@tonic-gate 	if (method_list != NULL) {
126*7c478bd9Sstevel@tonic-gate 		method_el_t *me;
127*7c478bd9Sstevel@tonic-gate 
128*7c478bd9Sstevel@tonic-gate 		while ((me = uu_list_first(method_list)) != NULL)
129*7c478bd9Sstevel@tonic-gate 			unregister_method(me);
130*7c478bd9Sstevel@tonic-gate 
131*7c478bd9Sstevel@tonic-gate 		(void) uu_list_destroy(method_list);
132*7c478bd9Sstevel@tonic-gate 		method_list = NULL;
133*7c478bd9Sstevel@tonic-gate 	}
134*7c478bd9Sstevel@tonic-gate 	if (method_pool != NULL) {
135*7c478bd9Sstevel@tonic-gate 		(void) uu_list_pool_destroy(method_pool);
136*7c478bd9Sstevel@tonic-gate 		method_pool = NULL;
137*7c478bd9Sstevel@tonic-gate 	}
138*7c478bd9Sstevel@tonic-gate 
139*7c478bd9Sstevel@tonic-gate 	/* revert file limit */
140*7c478bd9Sstevel@tonic-gate 	method_preexec();
141*7c478bd9Sstevel@tonic-gate }
142*7c478bd9Sstevel@tonic-gate 
143*7c478bd9Sstevel@tonic-gate /*
144*7c478bd9Sstevel@tonic-gate  * Revert file limit back to pre-initialization one. This shouldn't fail as
145*7c478bd9Sstevel@tonic-gate  * long as its called *after* descriptor cleanup.
146*7c478bd9Sstevel@tonic-gate  */
147*7c478bd9Sstevel@tonic-gate void
148*7c478bd9Sstevel@tonic-gate method_preexec(void)
149*7c478bd9Sstevel@tonic-gate {
150*7c478bd9Sstevel@tonic-gate 	(void) setrlimit(RLIMIT_NOFILE, &saved_file_limit);
151*7c478bd9Sstevel@tonic-gate }
152*7c478bd9Sstevel@tonic-gate 
153*7c478bd9Sstevel@tonic-gate 
154*7c478bd9Sstevel@tonic-gate /*
155*7c478bd9Sstevel@tonic-gate  * Callback function that handles the timeout of an instance's method.
156*7c478bd9Sstevel@tonic-gate  * 'arg' points at the method_el_t representing the method.
157*7c478bd9Sstevel@tonic-gate  */
158*7c478bd9Sstevel@tonic-gate /* ARGSUSED0 */
159*7c478bd9Sstevel@tonic-gate static void
160*7c478bd9Sstevel@tonic-gate method_timeout(iu_tq_t *tq, void *arg)
161*7c478bd9Sstevel@tonic-gate {
162*7c478bd9Sstevel@tonic-gate 	method_el_t *mp = arg;
163*7c478bd9Sstevel@tonic-gate 
164*7c478bd9Sstevel@tonic-gate 	error_msg(gettext("The %s method of instance %s timed-out"),
165*7c478bd9Sstevel@tonic-gate 	    methods[mp->method].name, mp->inst->fmri);
166*7c478bd9Sstevel@tonic-gate 
167*7c478bd9Sstevel@tonic-gate 	mp->inst->timer_id = -1;
168*7c478bd9Sstevel@tonic-gate 
169*7c478bd9Sstevel@tonic-gate 	if (mp->method == IM_START) {
170*7c478bd9Sstevel@tonic-gate 		process_start_term(mp->inst);
171*7c478bd9Sstevel@tonic-gate 	} else {
172*7c478bd9Sstevel@tonic-gate 		process_non_start_term(mp->inst, IMRET_FAILURE);
173*7c478bd9Sstevel@tonic-gate 	}
174*7c478bd9Sstevel@tonic-gate 
175*7c478bd9Sstevel@tonic-gate 	unregister_method(mp);
176*7c478bd9Sstevel@tonic-gate }
177*7c478bd9Sstevel@tonic-gate 
178*7c478bd9Sstevel@tonic-gate /*
179*7c478bd9Sstevel@tonic-gate  * Registers the attributes of a running method passed as arguments so that
180*7c478bd9Sstevel@tonic-gate  * the method's termination is noticed and any further processing of the
181*7c478bd9Sstevel@tonic-gate  * associated instance is carried out. The function also sets up any
182*7c478bd9Sstevel@tonic-gate  * necessary timers so we can detect hung methods.
183*7c478bd9Sstevel@tonic-gate  * Returns -1 if either it failed to open the /proc psinfo file which is used
184*7c478bd9Sstevel@tonic-gate  * to monitor the method process, it failed to setup a required timer or
185*7c478bd9Sstevel@tonic-gate  * memory allocation failed; else 0.
186*7c478bd9Sstevel@tonic-gate  */
187*7c478bd9Sstevel@tonic-gate int
188*7c478bd9Sstevel@tonic-gate register_method(instance_t *ins, pid_t pid, ctid_t cid, instance_method_t mthd)
189*7c478bd9Sstevel@tonic-gate {
190*7c478bd9Sstevel@tonic-gate 	char		path[MAXPATHLEN];
191*7c478bd9Sstevel@tonic-gate 	int		fd;
192*7c478bd9Sstevel@tonic-gate 	method_el_t	*me;
193*7c478bd9Sstevel@tonic-gate 
194*7c478bd9Sstevel@tonic-gate 	debug_msg("Entering register_method: inst: %s, pid: %d, mthd: %s",
195*7c478bd9Sstevel@tonic-gate 	    ins->fmri, pid, methods[mthd].name);
196*7c478bd9Sstevel@tonic-gate 
197*7c478bd9Sstevel@tonic-gate 	/* open /proc psinfo file of process to listen for POLLHUP events on */
198*7c478bd9Sstevel@tonic-gate 	(void) snprintf(path, sizeof (path), "/proc/%u/psinfo", pid);
199*7c478bd9Sstevel@tonic-gate 	for (;;) {
200*7c478bd9Sstevel@tonic-gate 		if ((fd = open(path, O_RDONLY)) >= 0) {
201*7c478bd9Sstevel@tonic-gate 			break;
202*7c478bd9Sstevel@tonic-gate 		} else if (errno != EINTR) {
203*7c478bd9Sstevel@tonic-gate 			/*
204*7c478bd9Sstevel@tonic-gate 			 * Don't output an error for ENOENT; we get this
205*7c478bd9Sstevel@tonic-gate 			 * if a method has gone away whilst we were stopped,
206*7c478bd9Sstevel@tonic-gate 			 * and we're now trying to re-listen for it.
207*7c478bd9Sstevel@tonic-gate 			 */
208*7c478bd9Sstevel@tonic-gate 			if (errno != ENOENT) {
209*7c478bd9Sstevel@tonic-gate 				error_msg(gettext("Failed to open %s: %s"),
210*7c478bd9Sstevel@tonic-gate 				    path, strerror(errno));
211*7c478bd9Sstevel@tonic-gate 			}
212*7c478bd9Sstevel@tonic-gate 			return (-1);
213*7c478bd9Sstevel@tonic-gate 		}
214*7c478bd9Sstevel@tonic-gate 	}
215*7c478bd9Sstevel@tonic-gate 
216*7c478bd9Sstevel@tonic-gate 	/* add method record to in-memory list */
217*7c478bd9Sstevel@tonic-gate 	if ((me = calloc(1, sizeof (method_el_t))) == NULL) {
218*7c478bd9Sstevel@tonic-gate 		error_msg(strerror(errno));
219*7c478bd9Sstevel@tonic-gate 		(void) close(fd);
220*7c478bd9Sstevel@tonic-gate 		return (-1);
221*7c478bd9Sstevel@tonic-gate 	}
222*7c478bd9Sstevel@tonic-gate 	me->fd = fd;
223*7c478bd9Sstevel@tonic-gate 	me->inst = (instance_t *)ins;
224*7c478bd9Sstevel@tonic-gate 	me->method = mthd;
225*7c478bd9Sstevel@tonic-gate 	me->pid = pid;
226*7c478bd9Sstevel@tonic-gate 	me->cid = cid;
227*7c478bd9Sstevel@tonic-gate 
228*7c478bd9Sstevel@tonic-gate 	/* register a timeout for the method, if required */
229*7c478bd9Sstevel@tonic-gate 	if (mthd != IM_START) {
230*7c478bd9Sstevel@tonic-gate 		method_info_t *mi = ins->config->methods[mthd];
231*7c478bd9Sstevel@tonic-gate 
232*7c478bd9Sstevel@tonic-gate 		if (mi->timeout > 0) {
233*7c478bd9Sstevel@tonic-gate 			assert(ins->timer_id == -1);
234*7c478bd9Sstevel@tonic-gate 			ins->timer_id = iu_schedule_timer(timer_queue,
235*7c478bd9Sstevel@tonic-gate 			    mi->timeout, method_timeout, me);
236*7c478bd9Sstevel@tonic-gate 			if (ins->timer_id == -1) {
237*7c478bd9Sstevel@tonic-gate 				error_msg(gettext(
238*7c478bd9Sstevel@tonic-gate 				    "Failed to schedule method timeout"));
239*7c478bd9Sstevel@tonic-gate 				free(me);
240*7c478bd9Sstevel@tonic-gate 				(void) close(fd);
241*7c478bd9Sstevel@tonic-gate 				return (-1);
242*7c478bd9Sstevel@tonic-gate 			}
243*7c478bd9Sstevel@tonic-gate 		}
244*7c478bd9Sstevel@tonic-gate 	}
245*7c478bd9Sstevel@tonic-gate 
246*7c478bd9Sstevel@tonic-gate 	/*
247*7c478bd9Sstevel@tonic-gate 	 * Add fd of psinfo file to poll set, but pass 0 for events to
248*7c478bd9Sstevel@tonic-gate 	 * poll for, so we should only get a POLLHUP event on the fd.
249*7c478bd9Sstevel@tonic-gate 	 */
250*7c478bd9Sstevel@tonic-gate 	if (set_pollfd(fd, 0) == -1) {
251*7c478bd9Sstevel@tonic-gate 		cancel_inst_timer(ins);
252*7c478bd9Sstevel@tonic-gate 		free(me);
253*7c478bd9Sstevel@tonic-gate 		(void) close(fd);
254*7c478bd9Sstevel@tonic-gate 		return (-1);
255*7c478bd9Sstevel@tonic-gate 	}
256*7c478bd9Sstevel@tonic-gate 
257*7c478bd9Sstevel@tonic-gate 	uu_list_node_init(me, &me->link, method_pool);
258*7c478bd9Sstevel@tonic-gate 	(void) uu_list_insert_after(method_list, NULL, me);
259*7c478bd9Sstevel@tonic-gate 
260*7c478bd9Sstevel@tonic-gate 	return (0);
261*7c478bd9Sstevel@tonic-gate }
262*7c478bd9Sstevel@tonic-gate 
263*7c478bd9Sstevel@tonic-gate /*
264*7c478bd9Sstevel@tonic-gate  * A counterpart to register_method(), this function stops the monitoring of a
265*7c478bd9Sstevel@tonic-gate  * method process for its termination.
266*7c478bd9Sstevel@tonic-gate  */
267*7c478bd9Sstevel@tonic-gate static void
268*7c478bd9Sstevel@tonic-gate unregister_method(method_el_t *me)
269*7c478bd9Sstevel@tonic-gate {
270*7c478bd9Sstevel@tonic-gate 	debug_msg("Entering unregister_method: inst: %s, pid: %d, mthd: %s",
271*7c478bd9Sstevel@tonic-gate 	    me->inst->fmri, me->pid, methods[me->method].name);
272*7c478bd9Sstevel@tonic-gate 
273*7c478bd9Sstevel@tonic-gate 	/* cancel any timer associated with the method */
274*7c478bd9Sstevel@tonic-gate 	if (me->inst->timer_id != -1)
275*7c478bd9Sstevel@tonic-gate 		cancel_inst_timer(me->inst);
276*7c478bd9Sstevel@tonic-gate 
277*7c478bd9Sstevel@tonic-gate 	/* stop polling on the psinfo file fd */
278*7c478bd9Sstevel@tonic-gate 	clear_pollfd(me->fd);
279*7c478bd9Sstevel@tonic-gate 	(void) close(me->fd);
280*7c478bd9Sstevel@tonic-gate 
281*7c478bd9Sstevel@tonic-gate 	/* remove method record from list */
282*7c478bd9Sstevel@tonic-gate 	uu_list_remove(method_list, me);
283*7c478bd9Sstevel@tonic-gate 
284*7c478bd9Sstevel@tonic-gate 	free(me);
285*7c478bd9Sstevel@tonic-gate }
286*7c478bd9Sstevel@tonic-gate 
287*7c478bd9Sstevel@tonic-gate /*
288*7c478bd9Sstevel@tonic-gate  * Unregister all methods associated with instance 'inst'.
289*7c478bd9Sstevel@tonic-gate  */
290*7c478bd9Sstevel@tonic-gate void
291*7c478bd9Sstevel@tonic-gate unregister_instance_methods(const instance_t *inst)
292*7c478bd9Sstevel@tonic-gate {
293*7c478bd9Sstevel@tonic-gate 	method_el_t *me = uu_list_first(method_list);
294*7c478bd9Sstevel@tonic-gate 
295*7c478bd9Sstevel@tonic-gate 	while (me != NULL) {
296*7c478bd9Sstevel@tonic-gate 		if (me->inst == inst) {
297*7c478bd9Sstevel@tonic-gate 			method_el_t *tmp = me;
298*7c478bd9Sstevel@tonic-gate 
299*7c478bd9Sstevel@tonic-gate 			me = uu_list_next(method_list, me);
300*7c478bd9Sstevel@tonic-gate 			unregister_method(tmp);
301*7c478bd9Sstevel@tonic-gate 		} else  {
302*7c478bd9Sstevel@tonic-gate 			me = uu_list_next(method_list, me);
303*7c478bd9Sstevel@tonic-gate 		}
304*7c478bd9Sstevel@tonic-gate 	}
305*7c478bd9Sstevel@tonic-gate }
306*7c478bd9Sstevel@tonic-gate 
307*7c478bd9Sstevel@tonic-gate /*
308*7c478bd9Sstevel@tonic-gate  * Process any terminated methods. For each method determined to have
309*7c478bd9Sstevel@tonic-gate  * terminated, the function determines its return value and calls the
310*7c478bd9Sstevel@tonic-gate  * appropriate handling function, depending on the type of the method.
311*7c478bd9Sstevel@tonic-gate  */
312*7c478bd9Sstevel@tonic-gate void
313*7c478bd9Sstevel@tonic-gate process_terminated_methods(void)
314*7c478bd9Sstevel@tonic-gate {
315*7c478bd9Sstevel@tonic-gate 	method_el_t	*me = uu_list_first(method_list);
316*7c478bd9Sstevel@tonic-gate 
317*7c478bd9Sstevel@tonic-gate 	debug_msg("Entering process_terminated_methods");
318*7c478bd9Sstevel@tonic-gate 
319*7c478bd9Sstevel@tonic-gate 	while (me != NULL) {
320*7c478bd9Sstevel@tonic-gate 		struct pollfd	*pfd;
321*7c478bd9Sstevel@tonic-gate 		pid_t		pid;
322*7c478bd9Sstevel@tonic-gate 		int		status;
323*7c478bd9Sstevel@tonic-gate 		int		ret;
324*7c478bd9Sstevel@tonic-gate 		method_el_t	*tmp;
325*7c478bd9Sstevel@tonic-gate 
326*7c478bd9Sstevel@tonic-gate 		pfd = find_pollfd(me->fd);
327*7c478bd9Sstevel@tonic-gate 
328*7c478bd9Sstevel@tonic-gate 		/*
329*7c478bd9Sstevel@tonic-gate 		 * We expect to get a POLLHUP back on the fd of the process's
330*7c478bd9Sstevel@tonic-gate 		 * open psinfo file from /proc when the method terminates.
331*7c478bd9Sstevel@tonic-gate 		 * A POLLERR could(?) mask a POLLHUP, so handle this
332*7c478bd9Sstevel@tonic-gate 		 * also.
333*7c478bd9Sstevel@tonic-gate 		 */
334*7c478bd9Sstevel@tonic-gate 		if ((pfd->revents & (POLLHUP|POLLERR)) == 0) {
335*7c478bd9Sstevel@tonic-gate 			me = uu_list_next(method_list, me);
336*7c478bd9Sstevel@tonic-gate 			continue;
337*7c478bd9Sstevel@tonic-gate 		}
338*7c478bd9Sstevel@tonic-gate 
339*7c478bd9Sstevel@tonic-gate 		/* get the method's exit code (no need to loop for EINTR) */
340*7c478bd9Sstevel@tonic-gate 		pid = waitpid(me->pid, &status, WNOHANG);
341*7c478bd9Sstevel@tonic-gate 
342*7c478bd9Sstevel@tonic-gate 		switch (pid) {
343*7c478bd9Sstevel@tonic-gate 		case 0:					/* child still around */
344*7c478bd9Sstevel@tonic-gate 			/*
345*7c478bd9Sstevel@tonic-gate 			 * Either poll() is sending us invalid POLLHUP events
346*7c478bd9Sstevel@tonic-gate 			 * or is flagging a POLLERR on the fd. Neither should
347*7c478bd9Sstevel@tonic-gate 			 * happen, but in the event they do, ignore this fd
348*7c478bd9Sstevel@tonic-gate 			 * this time around and wait out the termination
349*7c478bd9Sstevel@tonic-gate 			 * of its associated method. This may result in
350*7c478bd9Sstevel@tonic-gate 			 * inetd swiftly looping in event_loop(), but means
351*7c478bd9Sstevel@tonic-gate 			 * we don't miss the termination of a method.
352*7c478bd9Sstevel@tonic-gate 			 */
353*7c478bd9Sstevel@tonic-gate 			me = uu_list_next(method_list, me);
354*7c478bd9Sstevel@tonic-gate 			continue;
355*7c478bd9Sstevel@tonic-gate 
356*7c478bd9Sstevel@tonic-gate 		case -1:				/* non-existent child */
357*7c478bd9Sstevel@tonic-gate 			assert(errno == ECHILD);
358*7c478bd9Sstevel@tonic-gate 			/*
359*7c478bd9Sstevel@tonic-gate 			 * the method must not be owned by inetd due to it
360*7c478bd9Sstevel@tonic-gate 			 * persisting over an inetd restart. Let's assume the
361*7c478bd9Sstevel@tonic-gate 			 * best, that it was successful.
362*7c478bd9Sstevel@tonic-gate 			 */
363*7c478bd9Sstevel@tonic-gate 			ret = IMRET_SUCCESS;
364*7c478bd9Sstevel@tonic-gate 			break;
365*7c478bd9Sstevel@tonic-gate 
366*7c478bd9Sstevel@tonic-gate 		default:				/* child terminated */
367*7c478bd9Sstevel@tonic-gate 			if (WIFEXITED(status)) {
368*7c478bd9Sstevel@tonic-gate 				ret = WEXITSTATUS(status);
369*7c478bd9Sstevel@tonic-gate 				debug_msg("process %d of instance %s returned "
370*7c478bd9Sstevel@tonic-gate 				    "%d", pid, me->inst->fmri, ret);
371*7c478bd9Sstevel@tonic-gate 			} else if (WIFSIGNALED(status)) {
372*7c478bd9Sstevel@tonic-gate 				/*
373*7c478bd9Sstevel@tonic-gate 				 * Terminated by signal.  This may be due
374*7c478bd9Sstevel@tonic-gate 				 * to a kill that we sent from a disable or
375*7c478bd9Sstevel@tonic-gate 				 * offline event. We flag it as a failure, but
376*7c478bd9Sstevel@tonic-gate 				 * this flagged failure will only be processed
377*7c478bd9Sstevel@tonic-gate 				 * in the case of non-start methods, or when
378*7c478bd9Sstevel@tonic-gate 				 * the instance is still enabled.
379*7c478bd9Sstevel@tonic-gate 				 */
380*7c478bd9Sstevel@tonic-gate 				debug_msg("process %d of instance %s exited "
381*7c478bd9Sstevel@tonic-gate 				    "due to signal %d", pid, me->inst->fmri,
382*7c478bd9Sstevel@tonic-gate 				    WTERMSIG(status));
383*7c478bd9Sstevel@tonic-gate 				ret = IMRET_FAILURE;
384*7c478bd9Sstevel@tonic-gate 			} else {
385*7c478bd9Sstevel@tonic-gate 				/*
386*7c478bd9Sstevel@tonic-gate 				 * Can we actually get here?  Don't think so.
387*7c478bd9Sstevel@tonic-gate 				 * Treat it as a failure, anyway.
388*7c478bd9Sstevel@tonic-gate 				 */
389*7c478bd9Sstevel@tonic-gate 				debug_msg("waitpid() for %s method of "
390*7c478bd9Sstevel@tonic-gate 				    "instance %s returned %d",
391*7c478bd9Sstevel@tonic-gate 				    methods[me->method].name, me->inst->fmri,
392*7c478bd9Sstevel@tonic-gate 				    status);
393*7c478bd9Sstevel@tonic-gate 				ret = IMRET_FAILURE;
394*7c478bd9Sstevel@tonic-gate 			}
395*7c478bd9Sstevel@tonic-gate 		}
396*7c478bd9Sstevel@tonic-gate 
397*7c478bd9Sstevel@tonic-gate 		remove_method_ids(me->inst, me->pid, me->cid, me->method);
398*7c478bd9Sstevel@tonic-gate 
399*7c478bd9Sstevel@tonic-gate 		/* continue state transition processing of the instance */
400*7c478bd9Sstevel@tonic-gate 		if (me->method != IM_START) {
401*7c478bd9Sstevel@tonic-gate 			process_non_start_term(me->inst, ret);
402*7c478bd9Sstevel@tonic-gate 		} else {
403*7c478bd9Sstevel@tonic-gate 			process_start_term(me->inst);
404*7c478bd9Sstevel@tonic-gate 		}
405*7c478bd9Sstevel@tonic-gate 
406*7c478bd9Sstevel@tonic-gate 		if (me->cid != -1)
407*7c478bd9Sstevel@tonic-gate 			(void) abandon_contract(me->cid);
408*7c478bd9Sstevel@tonic-gate 
409*7c478bd9Sstevel@tonic-gate 		tmp = me;
410*7c478bd9Sstevel@tonic-gate 		me = uu_list_next(method_list, me);
411*7c478bd9Sstevel@tonic-gate 		unregister_method(tmp);
412*7c478bd9Sstevel@tonic-gate 	}
413*7c478bd9Sstevel@tonic-gate }
414