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