/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Interfaces that return a tnfctl handle back to client (except for
* tnfctl_internal_open()) and helper functions for these interfaces.
* Also has buffer alloc, buffer dealloc, and trace attributes retrieval
* interfaces.
*/
#include "tnfctl_int.h"
#include "kernel_int.h"
#include "dbg.h"
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
/*
* invokes the target program and executes it till the run time linker (rtld)
* has loaded in the shared objects (but before any .init sections are
* executed). Returns a pointer to a tnfctl handle.
*/
const char *ld_preload,
const char *libtnfprobe_path,
{
if (prbstat) {
return (_tnfctl_map_to_errcode(prbstat));
}
/* allocate hdl and zero fill */
(void) prb_proc_close(proc_p);
return (TNFCTL_ERR_ALLOCFAIL);
}
/* use native /proc on this target */
/*
* get the address of DT_DEBUG and send it in to prb_ layer.
* This is needed before before prb_rtld_sync() can be called.
*/
if (prexstat)
goto failure_ret;
/* sync up to rtld sync point */
if (prbstat) {
goto failure_ret;
}
/* initialize state in handle */
if (prexstat)
goto failure_ret;
if (prexstat)
goto failure_ret;
/* Successful return */
return (TNFCTL_ERR_NONE);
(void) prb_proc_close(proc_p);
return (prexstat);
}
/*
* attaches to a running process. If the process is in the beginning
* of an exec(2) system call (which is how tnfctl_continue() returns on exec),
* it steps the process till the end of the the exec. If the process hasn't
* reached the rtld sync point, the process is continued until it does
* reach it. Returns a pointer to a tnfctl handle.
*/
{
if (prexstat) {
return (prexstat);
}
/* allocate hdl and zero fill */
(void) prb_proc_close(proc_p);
return (TNFCTL_ERR_ALLOCFAIL);
}
/* use native /proc on this target */
/*
* Since tnfctl_continue() returns when a process does an exec
* and leaves the process stopped at the beginning of exec, we
* have to be sure to catch this case.
*/
/* proc_p could be side effected by step_to_end_of_exec() */
if (prexstat)
goto failure_ret;
/*
* get the address of DT_DEBUG and send it in to prb_ layer.
*/
if (prexstat)
goto failure_ret;
/* sync up to rtld sync point if target is not there yet */
if (prbstat) {
goto failure_ret;
}
/* initialize state in handle */
if (prexstat)
goto failure_ret;
/* set state in target indicating we're tracing externally */
if (prexstat)
goto failure_ret;
/* Sucessful return */
return (TNFCTL_ERR_NONE);
(void) prb_proc_close(proc_p);
return (prexstat);
}
/*
* open a process for tracing without using native /proc on it. The client
* provides a set of callback functions which encapsulate the /proc
* functionality we need. Returns a pointer to a tnfctl handle.
*/
{
/* allocate hdl and zero fill */
return (TNFCTL_ERR_ALLOCFAIL);
}
/* initialize callback functions */
/* initialize state in handle */
if (prexstat) {
return (prexstat);
}
/* set state in target indicating we're tracing externally */
if (prexstat) {
return (prexstat);
}
return (TNFCTL_ERR_NONE);
}
/*
* Returns a pointer to a tnfctl handle that can do kernel trace control
* and kernel probe control.
*/
{
/* allocate hdl and zero fill */
return (TNFCTL_ERR_ALLOCFAIL);
}
/* initialize kernel tracing */
if (prexstat)
return (prexstat);
/* initialize function pointers that can be stuffed into a probe */
/* find the probes in the kernel */
if (prexstat)
return (prexstat);
return (TNFCTL_ERR_NONE);
}
/*
* Returns the trace attributes to the client. Since there can be
* only one controlling agent on a target at a time, our cached information
* is correct and we don't have to actually retrieve any information
* from the target.
*/
{
/*LINTED statement has no consequent: else*/
/*LINTED statement has no consequent: else*/
return (TNFCTL_ERR_NONE);
}
/*
* Allocate a trace buffer of the specified name and size.
*/
{
/* trace_file_name is ignored in kernel mode */
if (prexstat)
return (prexstat);
return (TNFCTL_ERR_NONE);
}
/* Not KERNEL_MODE */
/* buffer already allocated */
return (TNFCTL_ERR_BUFEXISTS);
}
if (prexstat) {
return (prexstat);
}
return (TNFCTL_ERR_NONE);
}
/*
* Deallocate the trace buffer - only works for kernel mode
*/
{
return (TNFCTL_ERR_BADARG);
/* KERNEL_MODE */
if (prexstat)
return (prexstat);
return (TNFCTL_ERR_NONE);
}
/*
* Helper function for attaching to a target process
*/
static tnfctl_errcode_t
{
return (TNFCTL_ERR_BADARG);
/* check if pid is valid */
return (TNFCTL_ERR_NOPROCESS);
}
/* open up /proc fd */
if (prbstat)
return (_tnfctl_map_to_errcode(prbstat));
/*
* default is to run-on-last-close. In case we cannot sync with
* target, we don't want to kill the target.
*/
if (prbstat)
goto failure_ret;
if (prbstat)
goto failure_ret;
/* stop process */
if (prbstat)
goto failure_ret;
/* Sucessful return */
return (TNFCTL_ERR_NONE);
(void) prb_proc_close(proc_p);
return (_tnfctl_map_to_errcode(prbstat));
}
/*
* Checks if target is at the beginning of an exec system call. If so,
* it runs it till the end of the exec system call. It takes care of
* the case where you're about to exec a setuid program.
* CAUTION: could side effect hndl->proc_p
*/
static tnfctl_errcode_t
{
int pid;
if (prbstat)
return (_tnfctl_map_to_errcode(prbstat));
/* not stopped at beginning of exec system call */
return (TNFCTL_ERR_NONE);
}
/* we are stopped at beginning of exec system call */
if (prbstat)
return (_tnfctl_map_to_errcode(prbstat));
if (prbstat)
return (_tnfctl_map_to_errcode(prbstat));
switch (prbstat) {
case PRB_STATUS_OK:
break;
case EAGAIN:
/*
* 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.
*/
if (tempstat) {
return (_tnfctl_map_to_errcode(tempstat));
}
break;
default:
return (_tnfctl_map_to_errcode(prbstat));
}
if (prbstat)
return (_tnfctl_map_to_errcode(prbstat));
/* unexpected condition */
return (tnfctl_status_map(ENOENT));
}
/* clear old interest mask */
if (prbstat)
return (_tnfctl_map_to_errcode(prbstat));
return (TNFCTL_ERR_NONE);
}
{
int internal_tracing_on;
if (prexstat) {
/* no libtnfctl in target: success */
return (TNFCTL_ERR_NONE);
}
&internal_tracing_on, sizeof (internal_tracing_on));
if (prbstat) {
goto failure_ret;
}
if (internal_tracing_on) {
/* target process being traced internally */
goto failure_ret;
}
if (prexstat) {
/* this shouldn't happen. we know we have libtnfctl */
goto failure_ret;
}
if (prbstat) {
goto failure_ret;
}
/* success */
return (TNFCTL_ERR_NONE);
return (prexstat);
}