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
55static 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 */
63prb_status_t
64prb_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		 */
164ContChild:
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 */
211void
212prb_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 */
221static prb_status_t
222sync_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
322ret_failure:
323	(void) prb_proc_close(proc_p);
324	return (prbstat);
325}
326