diff options
Diffstat (limited to 'usr/src/lib/libtnfctl/open.c')
-rw-r--r-- | usr/src/lib/libtnfctl/open.c | 541 |
1 files changed, 0 insertions, 541 deletions
diff --git a/usr/src/lib/libtnfctl/open.c b/usr/src/lib/libtnfctl/open.c deleted file mode 100644 index f3087aa40a..0000000000 --- a/usr/src/lib/libtnfctl/open.c +++ /dev/null @@ -1,541 +0,0 @@ -/* - * 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 - * 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 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> - -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_execve)) { - /* not stopped at beginning of exec system call */ - return (TNFCTL_ERR_NONE); - } - - /* we are stopped at beginning of exec system call */ - - 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 PRB_STATUS_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); -} |