4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2012, Joyent, Inc. All rights reserved.
28 * wait.c - asynchronous monitoring of "wait registered" start methods
30 * Use event ports to poll on the set of fds representing the /proc/[pid]/psinfo
31 * files. If one of these fds returns an event, then we inform the restarter
32 * that it has stopped.
34 * The wait_info_list holds the series of processes currently being monitored
35 * for exit. The wi_fd member, which contains the file descriptor of the psinfo
36 * file being polled upon ("event ported upon"), will be set to -1 if the file
37 * descriptor is inactive (already closed or not yet opened).
40 #ifdef _FILE_OFFSET_BITS
41 #undef _FILE_OFFSET_BITS
42 #endif /* _FILE_OFFSET_BITS */
44 #include <sys/resource.h>
46 #include <sys/types.h>
64 #define WAIT_FILES 262144 /* reasonably high maximum */
67 static scf_handle_t
*wait_hndl
;
68 static struct rlimit init_fd_rlimit
;
70 static uu_list_pool_t
*wait_info_pool
;
71 static uu_list_t
*wait_info_list
;
73 static pthread_mutex_t wait_info_lock
;
76 * void wait_remove(wait_info_t *, int)
77 * Remove the given wait_info structure from our list, performing various
78 * cleanup operations along the way. If the direct flag is false (meaning
79 * that we are being called with from restarter instance list context) and
80 * the instance should not be ignored, then notify the restarter that the
81 * associated instance has exited. If the wi_ignore flag is true then it
82 * means that the stop was initiated from within svc.startd, rather than
85 * Since we may no longer be the startd that started this process, we only are
86 * concerned with a waitpid(3C) failure if the wi_parent field is non-zero.
89 wait_remove(wait_info_t
*wi
, int direct
)
92 stop_cause_t cause
= RSTOP_EXIT
;
94 if (waitpid(wi
->wi_pid
, &status
, 0) == -1) {
96 log_framework(LOG_INFO
,
97 "instance %s waitpid failure: %s\n", wi
->wi_fmri
,
100 if (WEXITSTATUS(status
) != 0) {
101 log_framework(LOG_NOTICE
,
102 "instance %s exited with status %d\n", wi
->wi_fmri
,
103 WEXITSTATUS(status
));
104 if (WEXITSTATUS(status
) == SMF_EXIT_ERR_CONFIG
)
105 cause
= RSTOP_ERR_CFG
;
107 cause
= RSTOP_ERR_EXIT
;
111 MUTEX_LOCK(&wait_info_lock
);
112 if (wi
->wi_fd
!= -1) {
113 startd_close(wi
->wi_fd
);
116 uu_list_remove(wait_info_list
, wi
);
117 MUTEX_UNLOCK(&wait_info_lock
);
120 * Make an attempt to clear out any utmpx record associated with this
123 utmpx_mark_dead(wi
->wi_pid
, status
, B_FALSE
);
125 if (!direct
&& !wi
->wi_ignore
) {
127 * Bind wait_hndl lazily.
129 if (wait_hndl
== NULL
) {
131 libscf_handle_create_bound(SCF_VERSION
);
134 libscf_handle_create_bound(SCF_VERSION
)) {
135 log_error(LOG_INFO
, "[wait_remove] Unable to "
136 "bind a new repository handle: %s\n",
137 scf_strerror(scf_error()));
142 log_framework(LOG_DEBUG
,
143 "wait_remove requesting stop of %s\n", wi
->wi_fmri
);
144 (void) stop_instance_fmri(wait_hndl
, wi
->wi_fmri
, cause
);
147 uu_list_node_fini(wi
, &wi
->wi_link
, wait_info_pool
);
148 startd_free(wi
, sizeof (wait_info_t
));
152 * void wait_ignore_by_fmri(const char *)
153 * wait_ignore_by_fmri is called when svc.startd is going to stop the
154 * instance. Since we need to wait on the process and close the utmpx record,
155 * we're going to set the wi_ignore flag, so that when the process exits we
156 * clean up, but don't tell the restarter to stop it.
159 wait_ignore_by_fmri(const char *fmri
)
163 MUTEX_LOCK(&wait_info_lock
);
165 for (wi
= uu_list_first(wait_info_list
); wi
!= NULL
;
166 wi
= uu_list_next(wait_info_list
, wi
)) {
167 if (strcmp(wi
->wi_fmri
, fmri
) == 0)
175 MUTEX_UNLOCK(&wait_info_lock
);
179 * int wait_register(pid_t, char *, int, int)
180 * wait_register is called after we have called fork(2), and know which pid we
181 * wish to monitor. However, since the child may have already exited by the
182 * time we are called, we must handle the error cases from open(2)
183 * appropriately. The am_parent flag is recorded to handle waitpid(2)
184 * behaviour on removal; similarly, the direct flag is passed through to a
185 * potential call to wait_remove() to govern its behaviour in different
188 * Returns 0 if registration successful, 1 if child pid did not exist, and -1
189 * if a different error occurred.
192 wait_register(pid_t pid
, const char *inst_fmri
, int am_parent
, int direct
)
194 char *fname
= uu_msprintf("/proc/%ld/psinfo", pid
);
203 wi
= startd_alloc(sizeof (wait_info_t
));
205 uu_list_node_init(wi
, &wi
->wi_link
, wait_info_pool
);
209 wi
->wi_fmri
= inst_fmri
;
210 wi
->wi_parent
= am_parent
;
213 MUTEX_LOCK(&wait_info_lock
);
214 (void) uu_list_insert_before(wait_info_list
, NULL
, wi
);
215 MUTEX_UNLOCK(&wait_info_lock
);
217 if ((fd
= open(fname
, O_RDONLY
)) == -1) {
218 if (errno
== ENOENT
) {
220 * Child has already exited.
222 wait_remove(wi
, direct
);
226 log_error(LOG_WARNING
,
227 "open %s failed; not monitoring %s: %s\n", fname
,
228 inst_fmri
, strerror(errno
));
238 if (port_associate(port_fd
, PORT_SOURCE_FD
, fd
, 0, wi
)) {
239 log_error(LOG_WARNING
,
240 "initial port_association of %d / %s failed: %s\n", fd
,
241 inst_fmri
, strerror(errno
));
245 log_framework(LOG_DEBUG
, "monitoring PID %ld on fd %d (%s)\n", pid
, fd
,
253 wait_thread(void *args
)
260 if (port_get(port_fd
, &pe
, NULL
) != 0) {
264 log_error(LOG_WARNING
,
265 "port_get() failed with %s\n",
267 bad_error("port_get", errno
);
271 fd
= pe
.portev_object
;
274 assert(fd
== wi
->wi_fd
);
276 if ((pe
.portev_events
& POLLHUP
) == POLLHUP
) {
279 if (lseek(fd
, 0, SEEK_SET
) != 0 ||
280 read(fd
, &psi
, sizeof (psinfo_t
)) !=
282 log_framework(LOG_WARNING
,
283 "couldn't get psinfo data for %s (%s); "
284 "assuming failed\n", wi
->wi_fmri
,
289 if (psi
.pr_nlwp
!= 0 ||
291 psi
.pr_lwp
.pr_lwpid
!= 0) {
293 * We have determined, in accordance with the
294 * definition in proc(4), this process is not a
295 * zombie. Reassociate.
297 if (port_associate(port_fd
, PORT_SOURCE_FD
, fd
,
299 log_error(LOG_WARNING
,
300 "port_association of %d / %s "
301 "failed\n", fd
, wi
->wi_fmri
);
305 (pe
.portev_events
& POLLERR
) == 0) {
306 if (port_associate(port_fd
, PORT_SOURCE_FD
, fd
, 0, wi
))
307 log_error(LOG_WARNING
,
308 "port_association of %d / %s "
309 "failed\n", fd
, wi
->wi_fmri
);
317 /*LINTED E_FUNC_HAS_NO_RETURN_STMT*/
323 MUTEX_LOCK(&wait_info_lock
);
327 wait_postfork(pid_t pid
)
331 MUTEX_UNLOCK(&wait_info_lock
);
337 * Close all of the child's wait-related fds. The wait_thread() is
338 * gone, so no need to worry about returning events. We always exec(2)
339 * after a fork request, so we needn't free the list elements
343 for (wi
= uu_list_first(wait_info_list
);
345 wi
= uu_list_next(wait_info_list
, wi
)) {
347 startd_close(wi
->wi_fd
);
350 startd_close(port_fd
);
352 (void) setrlimit(RLIMIT_NOFILE
, &init_fd_rlimit
);
358 struct rlimit fd_new
;
360 (void) getrlimit(RLIMIT_NOFILE
, &init_fd_rlimit
);
361 (void) getrlimit(RLIMIT_NOFILE
, &fd_new
);
363 fd_new
.rlim_max
= fd_new
.rlim_cur
= WAIT_FILES
;
365 (void) setrlimit(RLIMIT_NOFILE
, &fd_new
);
367 if ((port_fd
= port_create()) == -1)
368 uu_die("wait_init couldn't port_create");
370 wait_info_pool
= uu_list_pool_create("wait_info", sizeof (wait_info_t
),
371 offsetof(wait_info_t
, wi_link
), NULL
, UU_LIST_POOL_DEBUG
);
372 if (wait_info_pool
== NULL
)
373 uu_die("wait_init couldn't create wait_info_pool");
375 wait_info_list
= uu_list_create(wait_info_pool
, wait_info_list
, 0);
376 if (wait_info_list
== NULL
)
377 uu_die("wait_init couldn't create wait_info_list");
379 (void) pthread_mutex_init(&wait_info_lock
, &mutex_attrs
);