Пример #1
0
/*
 * continue the child until the run time linker has loaded in all
 * the loadobjects (rtld sync point)
 */
static prb_status_t
sync_child(int childpid, volatile shmem_msg_t *smp, prb_proc_ctl_t **proc_pp)
{
    prb_proc_ctl_t		*proc_p, *oldproc_p;
    prb_status_t		prbstat = PRB_STATUS_OK;
    prb_status_t		tempstat;
    prb_proc_state_t	pstate;

    prbstat = prb_proc_open(childpid, proc_pp);
    if (prbstat)
        return (prbstat);

    proc_p = *proc_pp;

    prbstat = prb_proc_stop(proc_p);
    if (prbstat)
        goto ret_failure;

    /*
     * default is to kill-on-last-close.  In case we cannot sync with
     * target, we don't want the target to continue.
     */
    prbstat = prb_proc_setrlc(proc_p, B_FALSE);
    if (prbstat)
        goto ret_failure;

    prbstat = prb_proc_setklc(proc_p, B_TRUE);
    if (prbstat)
        goto ret_failure;

    /* REMIND: do we have to wait on SYS_exec also ? */
    prbstat = prb_proc_exit(proc_p, SYS_execve, PRB_SYS_ADD);
    if (prbstat)
        goto ret_failure;

    prbstat = prb_proc_entry(proc_p, SYS_exit, PRB_SYS_ADD);
    if (prbstat)
        goto ret_failure;

    prbstat = prb_shmem_clear(smp);
    if (prbstat)
        goto ret_failure;

    prbstat = prb_proc_cont(proc_p);
    if (prbstat)
        goto ret_failure;

    prbstat = prb_proc_wait(proc_p, B_FALSE, NULL);
    switch (prbstat) {
    case PRB_STATUS_OK:
        break;
    case EAGAIN:
        /*
         * If we had exec'ed a setuid/setgid program PIOCWSTOP
         * will return EAGAIN.  Reopen the 'fd' and try again.
         * Read the last section of /proc man page - we reopen first
         * and then close the old fd.
         */
        oldproc_p = proc_p;
        tempstat = prb_proc_reopen(childpid, proc_pp);
        proc_p = *proc_pp;
        if (tempstat) {
            /* here EACCES means exec'ed a setuid/setgid program */
            (void) prb_proc_close(oldproc_p);
            return (tempstat);
        }

        (void) prb_proc_close(oldproc_p);
        break;
    default:
        goto ret_failure;
    }

    prbstat = prb_shmem_free(smp);
    if (prbstat)
        goto ret_failure;

    prbstat = prb_proc_state(proc_p, &pstate);
    if (prbstat)
        goto ret_failure;

    if (pstate.ps_issysexit && (pstate.ps_syscallnum == SYS_execve)) {
        /* expected condition */
        prbstat = PRB_STATUS_OK;
    } else {
        prbstat = prb_status_map(ENOENT);
        goto ret_failure;
    }

    /* clear old interest mask */
    prbstat = prb_proc_exit(proc_p, 0, PRB_SYS_NONE);
    if (prbstat)
        goto ret_failure;

    prbstat = prb_proc_entry(proc_p, 0, PRB_SYS_NONE);
    if (prbstat)
        goto ret_failure;

    /* Successful return */
    return (PRB_STATUS_OK);

ret_failure:
    (void) prb_proc_close(proc_p);
    return (prbstat);
}
Пример #2
0
/*
 * prb_child_create()  - this routine instantiates and rendevous with the
 * target child process.  This routine returns an opaque handle for the
 * childs /proc entry.
 */
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)
{
    prb_status_t	prbstat;
    pid_t		childpid;
    char		executable_name[PATH_MAX + 2];
    extern char 	**environ;
    char * const *	env_to_use;
    size_t		loptlen, probepathlen;
    volatile shmem_msg_t *smp;

    /* initialize shmem communication buffer to cause child to wait */
    prbstat = prb_shmem_init(&smp);
    if (prbstat)
        return (prbstat);

    /* fork to create the child process */
    childpid = fork();
    if (childpid == (pid_t) - 1) {
        DBG(perror("prb_child_create: fork failed"));
        return (prb_status_map(errno));
    }
    if (childpid == 0) {
        char		   *oldenv;
        char		   *newenv;

        /* ---- CHILD PROCESS ---- */

        DBG_TNF_PROBE_1(prb_child_create_1, "libtnfctl",
                        "sunw%verbosity 1; sunw%debug 'child process created'",
                        tnf_long, pid, getpid());

        if (envp) {
            env_to_use = envp;
            goto ContChild;
        }

        /* append libtnfprobe.so to the LD_PRELOAD environment */
        loptlen = (loption) ? strlen(loption) : 0;
        /* probepathlen has a "/" added in ("+ 1") */
        probepathlen = (libtnfprobe_path) ?
                       (strlen(libtnfprobe_path) + 1) : 0;
        oldenv = getenv(PRELOAD);
        if (oldenv) {
            newenv = (char *) malloc(strlen(PRELOAD) +
                                     1 +	/* "=" */
                                     strlen(oldenv) +
                                     1 +	/* " " */
                                     probepathlen +
                                     strlen(LIBPROBE) +
                                     1 +	/* " " */
                                     loptlen +
                                     1);	/* NULL */

            if (!newenv)
                goto ContChild;
            (void) strcpy(newenv, PRELOAD);
            (void) strcat(newenv, "=");
            (void) strcat(newenv, oldenv);
            (void) strcat(newenv, " ");
            if (probepathlen) {
                (void) strcat(newenv, libtnfprobe_path);
                (void) strcat(newenv, "/");
            }
            (void) strcat(newenv, LIBPROBE);
            if (loptlen) {
                (void) strcat(newenv, " ");
                (void) strcat(newenv, loption);
            }
        } else {
            newenv = (char *) malloc(strlen(PRELOAD) +
                                     1 +	/* "=" */
                                     probepathlen +
                                     strlen(LIBPROBE) +
                                     1 +	/* " " */
                                     loptlen +
                                     1);	/* NULL */
            if (!newenv)
                goto ContChild;
            (void) strcpy(newenv, PRELOAD);
            (void) strcat(newenv, "=");
            if (probepathlen) {
                (void) strcat(newenv, libtnfprobe_path);
                (void) strcat(newenv, "/");
            }
            (void) strcat(newenv, LIBPROBE);
            if (loptlen) {
                (void) strcat(newenv, " ");
                (void) strcat(newenv, loption);
            }
        }
        (void) putenv((char *) newenv);
        env_to_use = environ;
        /*
         * We don't check the return value of putenv because the
         * desired libraries might already be in the target, even
         * if our effort to change the environment fails.  We
         * should continue either way ...
         */
ContChild:
        /* wait until the parent releases us */
        (void) prb_shmem_wait(smp);

        DBG_TNF_PROBE_1(prb_child_create_2, "libtnfctl",
                        "sunw%verbosity 2; "
                        "sunw%debug 'child process about to exec'",
                        tnf_string, cmdname, cmdname);

        /*
         * make the child it's own process group.
         * This is so that signals delivered to parent are not
         * also delivered to child.
         */
        (void) setpgrp();
        prbstat = find_executable(cmdname, executable_name);
        if (prbstat) {
            DBG((void) fprintf(stderr, "prb_child_create: %s\n",
                               prb_status_str(prbstat)));
            /* parent waits for exit */
            _exit(1);
        }
        if (execve(executable_name, cmdargs, env_to_use) == -1) {
            DBG(perror("prb_child_create: exec failed"));
            _exit(1);
        }

        /* Never reached */
        _exit(1);
    }
    /* ---- PARENT PROCESS ---- */
    /* child is waiting for us */

    prbstat = sync_child(childpid, smp, ret_val);
    if (prbstat) {
        return (prbstat);
    }

    return (PRB_STATUS_OK);

}
Пример #3
0
/*
 * prb_rtld_wait() - waits on target to execute getpid()
 */
static prb_status_t
prb_rtld_wait(prb_proc_ctl_t *proc_p)
{
	prb_proc_state_t pstate;
	prb_status_t	prbstat;

	DBG_TNF_PROBE_0(prb_rtld_wait_1, "libtnfctl", "sunw%verbosity 2");

	/* stop on exit of getpid() */
	prbstat = prb_proc_exit(proc_p, SYS_getpid, PRB_SYS_ADD);
	if (prbstat) {
		DBG((void) fprintf(stderr,
			"prb_rtld_wait: couldn't set up child to stop on "
			"exit of getpid(): %s\n", prb_status_str(prbstat)));
		return (prbstat);
	}
	/* stop on entry of exit() - i.e. exec failed */
	prbstat = prb_proc_entry(proc_p, SYS_exit, PRB_SYS_ADD);
	if (prbstat) {
		DBG((void) fprintf(stderr,
			"prb_rtld_wait: couldn't set up child to stop on "
			"entry of exit(): %s\n", prb_status_str(prbstat)));
		return (prbstat);
	}
	/* continue target and wait for it to stop */
	prbstat = prb_proc_cont(proc_p);
	if (prbstat) {
		DBG((void) fprintf(stderr,
			"prb_rtld_wait: couldn't continue target process: %s\n",
				prb_status_str(prbstat)));
		return (prbstat);
	}
	/* wait for target to stop */
	prbstat = prb_proc_wait(proc_p, B_FALSE, NULL);
	if (prbstat) {
		DBG((void) fprintf(stderr,
			"prb_rtld_wait: couldn't wait on target process: %s\n",
			prb_status_str(prbstat)));
		return (prbstat);
	}
	/* make sure it did stop on getpid() */
	prbstat = prb_proc_state(proc_p, &pstate);
	if (prbstat) {
		DBG((void) fprintf(stderr,
			"prb_rtld_wait: couldn't get state of target: %s\n",
				prb_status_str(prbstat)));
		return (prbstat);
	}
	if (pstate.ps_issysentry && (pstate.ps_syscallnum == SYS_exit)) {
		DBG((void) fprintf(stderr, "prb_rtld_wait: target exited\n"));
		return (prb_status_map(EACCES));
	}
	/* catch any other errors */
	if (!(pstate.ps_issysexit && (pstate.ps_syscallnum == SYS_getpid))) {
		DBG((void) fprintf(stderr,
			"prb_rtld_wait: target didn't stop on getpid\n"));
		return (PRB_STATUS_BADSYNC);
	}
	/* clear wait on getpid */
	prbstat = prb_proc_exit(proc_p, SYS_getpid, PRB_SYS_DEL);
	if (prbstat) {
		DBG((void) fprintf(stderr,
			"prb_rtld_wait: couldn't clear child to stop on "
			"exit of getpid(): %s\n", prb_status_str(prbstat)));
		return (prbstat);
	}
	/* clear wait on exit */
	prbstat = prb_proc_entry(proc_p, SYS_exit, PRB_SYS_DEL);
	if (prbstat) {
		DBG((void) fprintf(stderr,
			"prb_rtld_wait: couldn't clear child to stop on "
			"entry of exit(): %s\n", prb_status_str(prbstat)));
		return (prbstat);
	}
	/* start-stop the process to clear it out of the system call */
	prbstat = prb_proc_prstop(proc_p);
	if (prbstat) {
		DBG((void) fprintf(stderr,
			"prb_rtld_wait: couldn't prstop child: %s\n",
			prb_status_str(prbstat)));
		return (prbstat);
	}
	return (PRB_STATUS_OK);
}