summaryrefslogtreecommitdiff
path: root/usr/src/lib/libtnfctl/open.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libtnfctl/open.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libtnfctl/open.c')
-rw-r--r--usr/src/lib/libtnfctl/open.c544
1 files changed, 544 insertions, 0 deletions
diff --git a/usr/src/lib/libtnfctl/open.c b/usr/src/lib/libtnfctl/open.c
new file mode 100644
index 0000000000..641ef808b1
--- /dev/null
+++ b/usr/src/lib/libtnfctl/open.c
@@ -0,0 +1,544 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 (c) 1994, by Sun Microsytems, Inc.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * 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>
+
+static tnfctl_errcode_t attach_pid(pid_t pid, prb_proc_ctl_t **proc_pp);
+static tnfctl_errcode_t step_to_end_of_exec(tnfctl_handle_t *hndl);
+
+/*
+ * 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.
+ */
+tnfctl_errcode_t
+tnfctl_exec_open(const char *pgm_name, char * const *args, char * const *envp,
+ const char *ld_preload,
+ const char *libtnfprobe_path,
+ tnfctl_handle_t **ret_val)
+{
+ tnfctl_handle_t *hdl;
+ prb_proc_ctl_t *proc_p = NULL;
+ prb_status_t prbstat;
+ uintptr_t dbgaddr;
+ tnfctl_errcode_t prexstat;
+
+ prbstat = prb_child_create(pgm_name, args, ld_preload, libtnfprobe_path,
+ envp, &proc_p);
+ if (prbstat) {
+ return (_tnfctl_map_to_errcode(prbstat));
+ }
+
+ /* allocate hdl and zero fill */
+ hdl = calloc(1, sizeof (*hdl));
+ if (hdl == NULL) {
+ (void) prb_proc_close(proc_p);
+ return (TNFCTL_ERR_ALLOCFAIL);
+ }
+
+ hdl->proc_p = proc_p;
+ hdl->mode = DIRECT_MODE;
+ hdl->called_exit = B_FALSE;
+
+ /* use native /proc on this target */
+ hdl->p_read = _tnfctl_read_targ;
+ hdl->p_write = _tnfctl_write_targ;
+ hdl->p_obj_iter = _tnfctl_loadobj_iter;
+ hdl->p_getpid = _tnfctl_pid_get;
+
+ /*
+ * get the address of DT_DEBUG and send it in to prb_ layer.
+ * This is needed before before prb_rtld_sync() can be called.
+ */
+ prexstat = _tnfctl_elf_dbgent(hdl, &dbgaddr);
+ if (prexstat)
+ goto failure_ret;
+
+ prb_dbgaddr(proc_p, dbgaddr);
+
+ /* sync up to rtld sync point */
+ prbstat = prb_rtld_sync_if_needed(proc_p);
+ if (prbstat) {
+ prexstat = _tnfctl_map_to_errcode(prbstat);
+ goto failure_ret;
+ }
+
+ /* initialize state in handle */
+ prexstat = _tnfctl_set_state(hdl);
+ if (prexstat)
+ goto failure_ret;
+
+ prexstat = _tnfctl_external_getlock(hdl);
+ if (prexstat)
+ goto failure_ret;
+
+ *ret_val = hdl;
+ /* Successful return */
+ return (TNFCTL_ERR_NONE);
+
+failure_ret:
+ (void) prb_proc_close(proc_p);
+ free(hdl);
+ 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.
+ */
+tnfctl_errcode_t
+tnfctl_pid_open(pid_t pid, tnfctl_handle_t **ret_val)
+{
+ tnfctl_handle_t *hdl;
+ prb_proc_ctl_t *proc_p = NULL;
+ uintptr_t dbgaddr;
+ prb_status_t prbstat;
+ tnfctl_errcode_t prexstat;
+
+ prexstat = attach_pid(pid, &proc_p);
+ if (prexstat) {
+ return (prexstat);
+ }
+
+ /* allocate hdl and zero fill */
+ hdl = calloc(1, sizeof (*hdl));
+ if (hdl == NULL) {
+ (void) prb_proc_close(proc_p);
+ return (TNFCTL_ERR_ALLOCFAIL);
+ }
+
+ hdl->proc_p = proc_p;
+ hdl->mode = DIRECT_MODE;
+ hdl->called_exit = B_FALSE;
+
+ /* use native /proc on this target */
+ hdl->p_read = _tnfctl_read_targ;
+ hdl->p_write = _tnfctl_write_targ;
+ hdl->p_obj_iter = _tnfctl_loadobj_iter;
+ hdl->p_getpid = _tnfctl_pid_get;
+
+ /*
+ * 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.
+ */
+ prexstat = step_to_end_of_exec(hdl);
+ /* proc_p could be side effected by step_to_end_of_exec() */
+ proc_p = hdl->proc_p;
+ if (prexstat)
+ goto failure_ret;
+
+ /*
+ * get the address of DT_DEBUG and send it in to prb_ layer.
+ */
+ prexstat = _tnfctl_elf_dbgent(hdl, &dbgaddr);
+ if (prexstat)
+ goto failure_ret;
+
+ prb_dbgaddr(proc_p, dbgaddr);
+
+ /* sync up to rtld sync point if target is not there yet */
+ prbstat = prb_rtld_sync_if_needed(proc_p);
+ if (prbstat) {
+ prexstat = _tnfctl_map_to_errcode(prbstat);
+ goto failure_ret;
+ }
+
+ /* initialize state in handle */
+ prexstat = _tnfctl_set_state(hdl);
+ if (prexstat)
+ goto failure_ret;
+
+ /* set state in target indicating we're tracing externally */
+ prexstat = _tnfctl_external_getlock(hdl);
+ if (prexstat)
+ goto failure_ret;
+
+ *ret_val = hdl;
+
+ /* Sucessful return */
+ return (TNFCTL_ERR_NONE);
+
+failure_ret:
+ (void) prb_proc_close(proc_p);
+ free(hdl);
+ 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.
+ */
+tnfctl_errcode_t
+tnfctl_indirect_open(void *prochandle, tnfctl_ind_config_t *config,
+ tnfctl_handle_t **ret_val)
+{
+ tnfctl_handle_t *hdl;
+ tnfctl_errcode_t prexstat;
+
+ /* allocate hdl and zero fill */
+ hdl = calloc(1, sizeof (*hdl));
+ if (hdl == NULL) {
+ return (TNFCTL_ERR_ALLOCFAIL);
+ }
+
+ hdl->proc_p = prochandle;
+ hdl->mode = INDIRECT_MODE;
+ hdl->called_exit = B_FALSE;
+
+ /* initialize callback functions */
+ hdl->p_read = config->p_read;
+ hdl->p_write = config->p_write;
+ hdl->p_obj_iter = config->p_obj_iter;
+ hdl->p_getpid = config->p_getpid;
+
+ /* initialize state in handle */
+ prexstat = _tnfctl_set_state(hdl);
+ if (prexstat) {
+ free(hdl);
+ return (prexstat);
+ }
+ /* set state in target indicating we're tracing externally */
+ prexstat = _tnfctl_external_getlock(hdl);
+ if (prexstat) {
+ free(hdl);
+ return (prexstat);
+ }
+ *ret_val = hdl;
+ return (TNFCTL_ERR_NONE);
+}
+
+/*
+ * Returns a pointer to a tnfctl handle that can do kernel trace control
+ * and kernel probe control.
+ */
+tnfctl_errcode_t
+tnfctl_kernel_open(tnfctl_handle_t **ret_val)
+{
+ tnfctl_handle_t *hdl;
+ tnfctl_errcode_t prexstat;
+
+ /* allocate hdl and zero fill */
+ hdl = calloc(1, sizeof (*hdl));
+ if (hdl == NULL) {
+ return (TNFCTL_ERR_ALLOCFAIL);
+ }
+
+ /* initialize kernel tracing */
+ prexstat = _tnfctl_prbk_init(hdl);
+ if (prexstat)
+ return (prexstat);
+
+ hdl->mode = KERNEL_MODE;
+ hdl->targ_pid = 0;
+
+ /* initialize function pointers that can be stuffed into a probe */
+ _tnfctl_prbk_get_other_funcs(&hdl->allocfunc, &hdl->commitfunc,
+ &hdl->rollbackfunc, &hdl->endfunc);
+ _tnfctl_prbk_test_func(&hdl->testfunc);
+
+ /* find the probes in the kernel */
+ prexstat = _tnfctl_refresh_kernel(hdl);
+ if (prexstat)
+ return (prexstat);
+
+ *ret_val = hdl;
+ 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.
+ */
+tnfctl_errcode_t
+tnfctl_trace_attrs_get(tnfctl_handle_t *hdl, tnfctl_trace_attrs_t *attrs)
+{
+ boolean_t release_lock;
+ tnfctl_errcode_t prexstat;
+
+ /*LINTED statement has no consequent: else*/
+ LOCK_SYNC(hdl, prexstat, release_lock);
+
+ attrs->targ_pid = hdl->targ_pid;
+ attrs->trace_file_name = hdl->trace_file_name;
+ attrs->trace_buf_size = hdl->trace_buf_size;
+ attrs->trace_min_size = hdl->trace_min_size;
+ attrs->trace_buf_state = hdl->trace_buf_state;
+ attrs->trace_state = hdl->trace_state;
+ attrs->filter_state = hdl->kpidfilter_state;
+
+ /*LINTED statement has no consequent: else*/
+ UNLOCK(hdl, release_lock);
+
+ return (TNFCTL_ERR_NONE);
+}
+
+
+/*
+ * Allocate a trace buffer of the specified name and size.
+ */
+tnfctl_errcode_t
+tnfctl_buffer_alloc(tnfctl_handle_t *hdl, const char *trace_file_name,
+ uint_t trace_file_size)
+{
+ tnfctl_errcode_t prexstat;
+
+ if (hdl->mode == KERNEL_MODE) {
+ /* trace_file_name is ignored in kernel mode */
+ prexstat = _tnfctl_prbk_buffer_alloc(hdl, trace_file_size);
+ if (prexstat)
+ return (prexstat);
+ return (TNFCTL_ERR_NONE);
+ }
+
+ /* Not KERNEL_MODE */
+ if (hdl->trace_file_name != NULL) {
+ /* buffer already allocated */
+ return (TNFCTL_ERR_BUFEXISTS);
+ }
+
+ prexstat = _tnfctl_create_tracefile(hdl, trace_file_name,
+ trace_file_size);
+ if (prexstat) {
+ return (prexstat);
+ }
+
+ return (TNFCTL_ERR_NONE);
+}
+
+/*
+ * Deallocate the trace buffer - only works for kernel mode
+ */
+tnfctl_errcode_t
+tnfctl_buffer_dealloc(tnfctl_handle_t *hdl)
+{
+ tnfctl_errcode_t prexstat;
+
+ if (hdl->mode != KERNEL_MODE)
+ return (TNFCTL_ERR_BADARG);
+
+ /* KERNEL_MODE */
+ prexstat = _tnfctl_prbk_buffer_dealloc(hdl);
+ if (prexstat)
+ return (prexstat);
+ return (TNFCTL_ERR_NONE);
+}
+
+
+/*
+ * Helper function for attaching to a target process
+ */
+static tnfctl_errcode_t
+attach_pid(pid_t pid, prb_proc_ctl_t **proc_pp)
+{
+ prb_status_t prbstat;
+ prb_proc_ctl_t *proc_p;
+
+ if (getpid() == pid)
+ return (TNFCTL_ERR_BADARG);
+
+ /* check if pid is valid */
+ if ((kill(pid, 0) == -1) && errno == ESRCH) {
+ return (TNFCTL_ERR_NOPROCESS);
+ }
+ /* open up /proc fd */
+ prbstat = prb_proc_open(pid, proc_pp);
+ if (prbstat)
+ return (_tnfctl_map_to_errcode(prbstat));
+
+ proc_p = *proc_pp;
+ /*
+ * default is to run-on-last-close. In case we cannot sync with
+ * target, we don't want to kill the target.
+ */
+ prbstat = prb_proc_setrlc(proc_p, B_TRUE);
+ if (prbstat)
+ goto failure_ret;
+ prbstat = prb_proc_setklc(proc_p, B_FALSE);
+ if (prbstat)
+ goto failure_ret;
+
+ /* stop process */
+ prbstat = prb_proc_stop(proc_p);
+ if (prbstat)
+ goto failure_ret;
+
+ /* Sucessful return */
+ return (TNFCTL_ERR_NONE);
+
+failure_ret:
+ (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
+step_to_end_of_exec(tnfctl_handle_t *hndl)
+{
+ prb_proc_ctl_t *proc_p, *oldproc_p;
+ prb_status_t prbstat, tempstat;
+ int pid;
+ prb_proc_state_t pstate;
+
+ proc_p = hndl->proc_p;
+ pid = hndl->p_getpid(proc_p);
+
+ prbstat = prb_proc_state(proc_p, &pstate);
+ if (prbstat)
+ return (_tnfctl_map_to_errcode(prbstat));
+ if (!(pstate.ps_issysentry && (pstate.ps_syscallnum == SYS_exec ||
+ pstate.ps_syscallnum == SYS_execve))) {
+ /* not stopped at beginning of exec system call */
+ return (TNFCTL_ERR_NONE);
+ }
+
+ /* we are stopped at beginning of exec system call */
+
+ /* REMIND: do we have to wait on SYS_exec also ? */
+ prbstat = prb_proc_exit(proc_p, SYS_execve, PRB_SYS_ADD);
+ if (prbstat)
+ return (_tnfctl_map_to_errcode(prbstat));
+
+ prbstat = prb_proc_cont(proc_p);
+ if (prbstat)
+ return (_tnfctl_map_to_errcode(prbstat));
+
+ 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(pid, &proc_p);
+ if (tempstat) {
+ /* here EACCES means exec'ed a setuid/setgid program */
+ return (_tnfctl_map_to_errcode(tempstat));
+ }
+
+ prb_proc_close(oldproc_p);
+ hndl->proc_p = proc_p;
+ break;
+ default:
+ return (_tnfctl_map_to_errcode(prbstat));
+ }
+
+ prbstat = prb_proc_state(proc_p, &pstate);
+ if (prbstat)
+ return (_tnfctl_map_to_errcode(prbstat));
+
+ if (!(pstate.ps_issysexit && (pstate.ps_syscallnum == SYS_execve))) {
+ /* unexpected condition */
+ return (tnfctl_status_map(ENOENT));
+ }
+
+ /* clear old interest mask */
+ prbstat = prb_proc_exit(proc_p, SYS_execve, PRB_SYS_DEL);
+ if (prbstat)
+ return (_tnfctl_map_to_errcode(prbstat));
+ return (TNFCTL_ERR_NONE);
+}
+
+
+tnfctl_errcode_t
+_tnfctl_external_getlock(tnfctl_handle_t *hdl)
+{
+
+ tnfctl_errcode_t prexstat;
+ prb_status_t prbstat;
+ uintptr_t targ_symbol_ptr;
+ int internal_tracing_on;
+
+ prexstat = _tnfctl_sym_find(hdl, TNFCTL_INTERNAL_TRACEFLAG,
+ &targ_symbol_ptr);
+ if (prexstat) {
+ /* no libtnfctl in target: success */
+ return (TNFCTL_ERR_NONE);
+ }
+ prbstat = hdl->p_read(hdl->proc_p, targ_symbol_ptr,
+ &internal_tracing_on, sizeof (internal_tracing_on));
+
+ if (prbstat) {
+ prexstat = _tnfctl_map_to_errcode(prbstat);
+ goto failure_ret;
+ }
+ if (internal_tracing_on) {
+ /* target process being traced internally */
+ prexstat = TNFCTL_ERR_BUSY;
+ goto failure_ret;
+ }
+ prexstat = _tnfctl_sym_find(hdl, TNFCTL_EXTERNAL_TRACEDPID,
+ &targ_symbol_ptr);
+ if (prexstat) {
+ /* this shouldn't happen. we know we have libtnfctl */
+ goto failure_ret;
+ }
+ prbstat = hdl->p_write(hdl->proc_p, targ_symbol_ptr,
+ &(hdl->targ_pid), sizeof (hdl->targ_pid));
+ if (prbstat) {
+ prexstat = _tnfctl_map_to_errcode(prbstat);
+ goto failure_ret;
+ }
+ /* success */
+ DBG((void) fprintf(stderr, "_tnfctl_external_getlock: ok to trace %d\n",
+ hdl->targ_pid));
+ return (TNFCTL_ERR_NONE);
+
+failure_ret:
+ return (prexstat);
+}