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 (c) 1994, by Sun Microsytems, Inc.
24*7c478bd9Sstevel@tonic-gate */
25*7c478bd9Sstevel@tonic-gate
26*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
27*7c478bd9Sstevel@tonic-gate
28*7c478bd9Sstevel@tonic-gate /*
29*7c478bd9Sstevel@tonic-gate * interfaces to exec a command and run it till all loadobjects have
30*7c478bd9Sstevel@tonic-gate * been loaded (rtld sync point).
31*7c478bd9Sstevel@tonic-gate */
32*7c478bd9Sstevel@tonic-gate
33*7c478bd9Sstevel@tonic-gate #include <unistd.h>
34*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
35*7c478bd9Sstevel@tonic-gate #include <string.h>
36*7c478bd9Sstevel@tonic-gate #include <errno.h>
37*7c478bd9Sstevel@tonic-gate #include <limits.h>
38*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/stat.h>
40*7c478bd9Sstevel@tonic-gate
41*7c478bd9Sstevel@tonic-gate #include "prb_proc_int.h"
42*7c478bd9Sstevel@tonic-gate #include "dbg.h"
43*7c478bd9Sstevel@tonic-gate
44*7c478bd9Sstevel@tonic-gate /*
45*7c478bd9Sstevel@tonic-gate * Defines
46*7c478bd9Sstevel@tonic-gate */
47*7c478bd9Sstevel@tonic-gate
48*7c478bd9Sstevel@tonic-gate #define PRELOAD "LD_PRELOAD"
49*7c478bd9Sstevel@tonic-gate #define LIBPROBE "libtnfprobe.so.1"
50*7c478bd9Sstevel@tonic-gate
51*7c478bd9Sstevel@tonic-gate /*
52*7c478bd9Sstevel@tonic-gate * Local declarations
53*7c478bd9Sstevel@tonic-gate */
54*7c478bd9Sstevel@tonic-gate
55*7c478bd9Sstevel@tonic-gate static prb_status_t sync_child(int pid, volatile shmem_msg_t *smp,
56*7c478bd9Sstevel@tonic-gate prb_proc_ctl_t **proc_pp);
57*7c478bd9Sstevel@tonic-gate
58*7c478bd9Sstevel@tonic-gate /*
59*7c478bd9Sstevel@tonic-gate * prb_child_create() - this routine instantiates and rendevous with the
60*7c478bd9Sstevel@tonic-gate * target child process. This routine returns an opaque handle for the
61*7c478bd9Sstevel@tonic-gate * childs /proc entry.
62*7c478bd9Sstevel@tonic-gate */
63*7c478bd9Sstevel@tonic-gate prb_status_t
prb_child_create(const char * cmdname,char * const * cmdargs,const char * loption,const char * libtnfprobe_path,char * const * envp,prb_proc_ctl_t ** ret_val)64*7c478bd9Sstevel@tonic-gate prb_child_create(const char *cmdname, char * const *cmdargs,
65*7c478bd9Sstevel@tonic-gate const char *loption, const char *libtnfprobe_path,
66*7c478bd9Sstevel@tonic-gate char * const *envp, prb_proc_ctl_t **ret_val)
67*7c478bd9Sstevel@tonic-gate {
68*7c478bd9Sstevel@tonic-gate prb_status_t prbstat;
69*7c478bd9Sstevel@tonic-gate pid_t childpid;
70*7c478bd9Sstevel@tonic-gate char executable_name[PATH_MAX + 2];
71*7c478bd9Sstevel@tonic-gate extern char **environ;
72*7c478bd9Sstevel@tonic-gate char * const * env_to_use;
73*7c478bd9Sstevel@tonic-gate size_t loptlen, probepathlen;
74*7c478bd9Sstevel@tonic-gate volatile shmem_msg_t *smp;
75*7c478bd9Sstevel@tonic-gate
76*7c478bd9Sstevel@tonic-gate /* initialize shmem communication buffer to cause child to wait */
77*7c478bd9Sstevel@tonic-gate prbstat = prb_shmem_init(&smp);
78*7c478bd9Sstevel@tonic-gate if (prbstat)
79*7c478bd9Sstevel@tonic-gate return (prbstat);
80*7c478bd9Sstevel@tonic-gate
81*7c478bd9Sstevel@tonic-gate /* fork to create the child process */
82*7c478bd9Sstevel@tonic-gate childpid = fork();
83*7c478bd9Sstevel@tonic-gate if (childpid == (pid_t) - 1) {
84*7c478bd9Sstevel@tonic-gate DBG(perror("prb_child_create: fork failed"));
85*7c478bd9Sstevel@tonic-gate return (prb_status_map(errno));
86*7c478bd9Sstevel@tonic-gate }
87*7c478bd9Sstevel@tonic-gate if (childpid == 0) {
88*7c478bd9Sstevel@tonic-gate char *oldenv;
89*7c478bd9Sstevel@tonic-gate char *newenv;
90*7c478bd9Sstevel@tonic-gate
91*7c478bd9Sstevel@tonic-gate /* ---- CHILD PROCESS ---- */
92*7c478bd9Sstevel@tonic-gate
93*7c478bd9Sstevel@tonic-gate DBG_TNF_PROBE_1(prb_child_create_1, "libtnfctl",
94*7c478bd9Sstevel@tonic-gate "sunw%verbosity 1; sunw%debug 'child process created'",
95*7c478bd9Sstevel@tonic-gate tnf_long, pid, getpid());
96*7c478bd9Sstevel@tonic-gate
97*7c478bd9Sstevel@tonic-gate if (envp) {
98*7c478bd9Sstevel@tonic-gate env_to_use = envp;
99*7c478bd9Sstevel@tonic-gate goto ContChild;
100*7c478bd9Sstevel@tonic-gate }
101*7c478bd9Sstevel@tonic-gate
102*7c478bd9Sstevel@tonic-gate /* append libtnfprobe.so to the LD_PRELOAD environment */
103*7c478bd9Sstevel@tonic-gate loptlen = (loption) ? strlen(loption) : 0;
104*7c478bd9Sstevel@tonic-gate /* probepathlen has a "/" added in ("+ 1") */
105*7c478bd9Sstevel@tonic-gate probepathlen = (libtnfprobe_path) ?
106*7c478bd9Sstevel@tonic-gate (strlen(libtnfprobe_path) + 1) : 0;
107*7c478bd9Sstevel@tonic-gate oldenv = getenv(PRELOAD);
108*7c478bd9Sstevel@tonic-gate if (oldenv) {
109*7c478bd9Sstevel@tonic-gate newenv = (char *) malloc(strlen(PRELOAD) +
110*7c478bd9Sstevel@tonic-gate 1 + /* "=" */
111*7c478bd9Sstevel@tonic-gate strlen(oldenv) +
112*7c478bd9Sstevel@tonic-gate 1 + /* " " */
113*7c478bd9Sstevel@tonic-gate probepathlen +
114*7c478bd9Sstevel@tonic-gate strlen(LIBPROBE) +
115*7c478bd9Sstevel@tonic-gate 1 + /* " " */
116*7c478bd9Sstevel@tonic-gate loptlen +
117*7c478bd9Sstevel@tonic-gate 1); /* NULL */
118*7c478bd9Sstevel@tonic-gate
119*7c478bd9Sstevel@tonic-gate if (!newenv)
120*7c478bd9Sstevel@tonic-gate goto ContChild;
121*7c478bd9Sstevel@tonic-gate (void) strcpy(newenv, PRELOAD);
122*7c478bd9Sstevel@tonic-gate (void) strcat(newenv, "=");
123*7c478bd9Sstevel@tonic-gate (void) strcat(newenv, oldenv);
124*7c478bd9Sstevel@tonic-gate (void) strcat(newenv, " ");
125*7c478bd9Sstevel@tonic-gate if (probepathlen) {
126*7c478bd9Sstevel@tonic-gate (void) strcat(newenv, libtnfprobe_path);
127*7c478bd9Sstevel@tonic-gate (void) strcat(newenv, "/");
128*7c478bd9Sstevel@tonic-gate }
129*7c478bd9Sstevel@tonic-gate (void) strcat(newenv, LIBPROBE);
130*7c478bd9Sstevel@tonic-gate if (loptlen) {
131*7c478bd9Sstevel@tonic-gate (void) strcat(newenv, " ");
132*7c478bd9Sstevel@tonic-gate (void) strcat(newenv, loption);
133*7c478bd9Sstevel@tonic-gate }
134*7c478bd9Sstevel@tonic-gate } else {
135*7c478bd9Sstevel@tonic-gate newenv = (char *) malloc(strlen(PRELOAD) +
136*7c478bd9Sstevel@tonic-gate 1 + /* "=" */
137*7c478bd9Sstevel@tonic-gate probepathlen +
138*7c478bd9Sstevel@tonic-gate strlen(LIBPROBE) +
139*7c478bd9Sstevel@tonic-gate 1 + /* " " */
140*7c478bd9Sstevel@tonic-gate loptlen +
141*7c478bd9Sstevel@tonic-gate 1); /* NULL */
142*7c478bd9Sstevel@tonic-gate if (!newenv)
143*7c478bd9Sstevel@tonic-gate goto ContChild;
144*7c478bd9Sstevel@tonic-gate (void) strcpy(newenv, PRELOAD);
145*7c478bd9Sstevel@tonic-gate (void) strcat(newenv, "=");
146*7c478bd9Sstevel@tonic-gate if (probepathlen) {
147*7c478bd9Sstevel@tonic-gate (void) strcat(newenv, libtnfprobe_path);
148*7c478bd9Sstevel@tonic-gate (void) strcat(newenv, "/");
149*7c478bd9Sstevel@tonic-gate }
150*7c478bd9Sstevel@tonic-gate (void) strcat(newenv, LIBPROBE);
151*7c478bd9Sstevel@tonic-gate if (loptlen) {
152*7c478bd9Sstevel@tonic-gate (void) strcat(newenv, " ");
153*7c478bd9Sstevel@tonic-gate (void) strcat(newenv, loption);
154*7c478bd9Sstevel@tonic-gate }
155*7c478bd9Sstevel@tonic-gate }
156*7c478bd9Sstevel@tonic-gate (void) putenv((char *) newenv);
157*7c478bd9Sstevel@tonic-gate env_to_use = environ;
158*7c478bd9Sstevel@tonic-gate /*
159*7c478bd9Sstevel@tonic-gate * We don't check the return value of putenv because the
160*7c478bd9Sstevel@tonic-gate * desired libraries might already be in the target, even
161*7c478bd9Sstevel@tonic-gate * if our effort to change the environment fails. We
162*7c478bd9Sstevel@tonic-gate * should continue either way ...
163*7c478bd9Sstevel@tonic-gate */
164*7c478bd9Sstevel@tonic-gate ContChild:
165*7c478bd9Sstevel@tonic-gate /* wait until the parent releases us */
166*7c478bd9Sstevel@tonic-gate (void) prb_shmem_wait(smp);
167*7c478bd9Sstevel@tonic-gate
168*7c478bd9Sstevel@tonic-gate DBG_TNF_PROBE_1(prb_child_create_2, "libtnfctl",
169*7c478bd9Sstevel@tonic-gate "sunw%verbosity 2; "
170*7c478bd9Sstevel@tonic-gate "sunw%debug 'child process about to exec'",
171*7c478bd9Sstevel@tonic-gate tnf_string, cmdname, cmdname);
172*7c478bd9Sstevel@tonic-gate
173*7c478bd9Sstevel@tonic-gate /*
174*7c478bd9Sstevel@tonic-gate * make the child it's own process group.
175*7c478bd9Sstevel@tonic-gate * This is so that signals delivered to parent are not
176*7c478bd9Sstevel@tonic-gate * also delivered to child.
177*7c478bd9Sstevel@tonic-gate */
178*7c478bd9Sstevel@tonic-gate (void) setpgrp();
179*7c478bd9Sstevel@tonic-gate prbstat = find_executable(cmdname, executable_name);
180*7c478bd9Sstevel@tonic-gate if (prbstat) {
181*7c478bd9Sstevel@tonic-gate DBG((void) fprintf(stderr, "prb_child_create: %s\n",
182*7c478bd9Sstevel@tonic-gate prb_status_str(prbstat)));
183*7c478bd9Sstevel@tonic-gate /* parent waits for exit */
184*7c478bd9Sstevel@tonic-gate _exit(1);
185*7c478bd9Sstevel@tonic-gate }
186*7c478bd9Sstevel@tonic-gate if (execve(executable_name, cmdargs, env_to_use) == -1) {
187*7c478bd9Sstevel@tonic-gate DBG(perror("prb_child_create: exec failed"));
188*7c478bd9Sstevel@tonic-gate _exit(1);
189*7c478bd9Sstevel@tonic-gate }
190*7c478bd9Sstevel@tonic-gate
191*7c478bd9Sstevel@tonic-gate /* Never reached */
192*7c478bd9Sstevel@tonic-gate _exit(1);
193*7c478bd9Sstevel@tonic-gate }
194*7c478bd9Sstevel@tonic-gate /* ---- PARENT PROCESS ---- */
195*7c478bd9Sstevel@tonic-gate /* child is waiting for us */
196*7c478bd9Sstevel@tonic-gate
197*7c478bd9Sstevel@tonic-gate prbstat = sync_child(childpid, smp, ret_val);
198*7c478bd9Sstevel@tonic-gate if (prbstat) {
199*7c478bd9Sstevel@tonic-gate return (prbstat);
200*7c478bd9Sstevel@tonic-gate }
201*7c478bd9Sstevel@tonic-gate
202*7c478bd9Sstevel@tonic-gate return (PRB_STATUS_OK);
203*7c478bd9Sstevel@tonic-gate
204*7c478bd9Sstevel@tonic-gate }
205*7c478bd9Sstevel@tonic-gate
206*7c478bd9Sstevel@tonic-gate /*
207*7c478bd9Sstevel@tonic-gate * interface that registers the address of the debug structure
208*7c478bd9Sstevel@tonic-gate * in the target process. This is where the linker maintains all
209*7c478bd9Sstevel@tonic-gate * the information about the loadobjects
210*7c478bd9Sstevel@tonic-gate */
211*7c478bd9Sstevel@tonic-gate void
prb_dbgaddr(prb_proc_ctl_t * proc_p,uintptr_t dbgaddr)212*7c478bd9Sstevel@tonic-gate prb_dbgaddr(prb_proc_ctl_t *proc_p, uintptr_t dbgaddr)
213*7c478bd9Sstevel@tonic-gate {
214*7c478bd9Sstevel@tonic-gate proc_p->dbgaddr = dbgaddr;
215*7c478bd9Sstevel@tonic-gate }
216*7c478bd9Sstevel@tonic-gate
217*7c478bd9Sstevel@tonic-gate /*
218*7c478bd9Sstevel@tonic-gate * continue the child until the run time linker has loaded in all
219*7c478bd9Sstevel@tonic-gate * the loadobjects (rtld sync point)
220*7c478bd9Sstevel@tonic-gate */
221*7c478bd9Sstevel@tonic-gate static prb_status_t
sync_child(int childpid,volatile shmem_msg_t * smp,prb_proc_ctl_t ** proc_pp)222*7c478bd9Sstevel@tonic-gate sync_child(int childpid, volatile shmem_msg_t *smp, prb_proc_ctl_t **proc_pp)
223*7c478bd9Sstevel@tonic-gate {
224*7c478bd9Sstevel@tonic-gate prb_proc_ctl_t *proc_p, *oldproc_p;
225*7c478bd9Sstevel@tonic-gate prb_status_t prbstat = PRB_STATUS_OK;
226*7c478bd9Sstevel@tonic-gate prb_status_t tempstat;
227*7c478bd9Sstevel@tonic-gate prb_proc_state_t pstate;
228*7c478bd9Sstevel@tonic-gate
229*7c478bd9Sstevel@tonic-gate prbstat = prb_proc_open(childpid, proc_pp);
230*7c478bd9Sstevel@tonic-gate if (prbstat)
231*7c478bd9Sstevel@tonic-gate return (prbstat);
232*7c478bd9Sstevel@tonic-gate
233*7c478bd9Sstevel@tonic-gate proc_p = *proc_pp;
234*7c478bd9Sstevel@tonic-gate
235*7c478bd9Sstevel@tonic-gate prbstat = prb_proc_stop(proc_p);
236*7c478bd9Sstevel@tonic-gate if (prbstat)
237*7c478bd9Sstevel@tonic-gate goto ret_failure;
238*7c478bd9Sstevel@tonic-gate
239*7c478bd9Sstevel@tonic-gate /*
240*7c478bd9Sstevel@tonic-gate * default is to kill-on-last-close. In case we cannot sync with
241*7c478bd9Sstevel@tonic-gate * target, we don't want the target to continue.
242*7c478bd9Sstevel@tonic-gate */
243*7c478bd9Sstevel@tonic-gate prbstat = prb_proc_setrlc(proc_p, B_FALSE);
244*7c478bd9Sstevel@tonic-gate if (prbstat)
245*7c478bd9Sstevel@tonic-gate goto ret_failure;
246*7c478bd9Sstevel@tonic-gate
247*7c478bd9Sstevel@tonic-gate prbstat = prb_proc_setklc(proc_p, B_TRUE);
248*7c478bd9Sstevel@tonic-gate if (prbstat)
249*7c478bd9Sstevel@tonic-gate goto ret_failure;
250*7c478bd9Sstevel@tonic-gate
251*7c478bd9Sstevel@tonic-gate /* REMIND: do we have to wait on SYS_exec also ? */
252*7c478bd9Sstevel@tonic-gate prbstat = prb_proc_exit(proc_p, SYS_execve, PRB_SYS_ADD);
253*7c478bd9Sstevel@tonic-gate if (prbstat)
254*7c478bd9Sstevel@tonic-gate goto ret_failure;
255*7c478bd9Sstevel@tonic-gate
256*7c478bd9Sstevel@tonic-gate prbstat = prb_proc_entry(proc_p, SYS_exit, PRB_SYS_ADD);
257*7c478bd9Sstevel@tonic-gate if (prbstat)
258*7c478bd9Sstevel@tonic-gate goto ret_failure;
259*7c478bd9Sstevel@tonic-gate
260*7c478bd9Sstevel@tonic-gate prbstat = prb_shmem_clear(smp);
261*7c478bd9Sstevel@tonic-gate if (prbstat)
262*7c478bd9Sstevel@tonic-gate goto ret_failure;
263*7c478bd9Sstevel@tonic-gate
264*7c478bd9Sstevel@tonic-gate prbstat = prb_proc_cont(proc_p);
265*7c478bd9Sstevel@tonic-gate if (prbstat)
266*7c478bd9Sstevel@tonic-gate goto ret_failure;
267*7c478bd9Sstevel@tonic-gate
268*7c478bd9Sstevel@tonic-gate prbstat = prb_proc_wait(proc_p, B_FALSE, NULL);
269*7c478bd9Sstevel@tonic-gate switch (prbstat) {
270*7c478bd9Sstevel@tonic-gate case PRB_STATUS_OK:
271*7c478bd9Sstevel@tonic-gate break;
272*7c478bd9Sstevel@tonic-gate case EAGAIN:
273*7c478bd9Sstevel@tonic-gate /*
274*7c478bd9Sstevel@tonic-gate * If we had exec'ed a setuid/setgid program PIOCWSTOP
275*7c478bd9Sstevel@tonic-gate * will return EAGAIN. Reopen the 'fd' and try again.
276*7c478bd9Sstevel@tonic-gate * Read the last section of /proc man page - we reopen first
277*7c478bd9Sstevel@tonic-gate * and then close the old fd.
278*7c478bd9Sstevel@tonic-gate */
279*7c478bd9Sstevel@tonic-gate oldproc_p = proc_p;
280*7c478bd9Sstevel@tonic-gate tempstat = prb_proc_reopen(childpid, proc_pp);
281*7c478bd9Sstevel@tonic-gate proc_p = *proc_pp;
282*7c478bd9Sstevel@tonic-gate if (tempstat) {
283*7c478bd9Sstevel@tonic-gate /* here EACCES means exec'ed a setuid/setgid program */
284*7c478bd9Sstevel@tonic-gate (void) prb_proc_close(oldproc_p);
285*7c478bd9Sstevel@tonic-gate return (tempstat);
286*7c478bd9Sstevel@tonic-gate }
287*7c478bd9Sstevel@tonic-gate
288*7c478bd9Sstevel@tonic-gate (void) prb_proc_close(oldproc_p);
289*7c478bd9Sstevel@tonic-gate break;
290*7c478bd9Sstevel@tonic-gate default:
291*7c478bd9Sstevel@tonic-gate goto ret_failure;
292*7c478bd9Sstevel@tonic-gate }
293*7c478bd9Sstevel@tonic-gate
294*7c478bd9Sstevel@tonic-gate prbstat = prb_shmem_free(smp);
295*7c478bd9Sstevel@tonic-gate if (prbstat)
296*7c478bd9Sstevel@tonic-gate goto ret_failure;
297*7c478bd9Sstevel@tonic-gate
298*7c478bd9Sstevel@tonic-gate prbstat = prb_proc_state(proc_p, &pstate);
299*7c478bd9Sstevel@tonic-gate if (prbstat)
300*7c478bd9Sstevel@tonic-gate goto ret_failure;
301*7c478bd9Sstevel@tonic-gate
302*7c478bd9Sstevel@tonic-gate if (pstate.ps_issysexit && (pstate.ps_syscallnum == SYS_execve)) {
303*7c478bd9Sstevel@tonic-gate /* expected condition */
304*7c478bd9Sstevel@tonic-gate prbstat = PRB_STATUS_OK;
305*7c478bd9Sstevel@tonic-gate } else {
306*7c478bd9Sstevel@tonic-gate prbstat = prb_status_map(ENOENT);
307*7c478bd9Sstevel@tonic-gate goto ret_failure;
308*7c478bd9Sstevel@tonic-gate }
309*7c478bd9Sstevel@tonic-gate
310*7c478bd9Sstevel@tonic-gate /* clear old interest mask */
311*7c478bd9Sstevel@tonic-gate prbstat = prb_proc_exit(proc_p, 0, PRB_SYS_NONE);
312*7c478bd9Sstevel@tonic-gate if (prbstat)
313*7c478bd9Sstevel@tonic-gate goto ret_failure;
314*7c478bd9Sstevel@tonic-gate
315*7c478bd9Sstevel@tonic-gate prbstat = prb_proc_entry(proc_p, 0, PRB_SYS_NONE);
316*7c478bd9Sstevel@tonic-gate if (prbstat)
317*7c478bd9Sstevel@tonic-gate goto ret_failure;
318*7c478bd9Sstevel@tonic-gate
319*7c478bd9Sstevel@tonic-gate /* Successful return */
320*7c478bd9Sstevel@tonic-gate return (PRB_STATUS_OK);
321*7c478bd9Sstevel@tonic-gate
322*7c478bd9Sstevel@tonic-gate ret_failure:
323*7c478bd9Sstevel@tonic-gate (void) prb_proc_close(proc_p);
324*7c478bd9Sstevel@tonic-gate return (prbstat);
325*7c478bd9Sstevel@tonic-gate }
326