4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Interfaces that return a tnfctl handle back to client (except for
29 * tnfctl_internal_open()) and helper functions for these interfaces.
30 * Also has buffer alloc, buffer dealloc, and trace attributes retrieval
34 #include "tnfctl_int.h"
35 #include "kernel_int.h"
43 static tnfctl_errcode_t
attach_pid(pid_t pid
, prb_proc_ctl_t
**proc_pp
);
44 static tnfctl_errcode_t
step_to_end_of_exec(tnfctl_handle_t
*hndl
);
47 * invokes the target program and executes it till the run time linker (rtld)
48 * has loaded in the shared objects (but before any .init sections are
49 * executed). Returns a pointer to a tnfctl handle.
52 tnfctl_exec_open(const char *pgm_name
, char * const *args
, char * const *envp
,
53 const char *ld_preload
,
54 const char *libtnfprobe_path
,
55 tnfctl_handle_t
**ret_val
)
58 prb_proc_ctl_t
*proc_p
= NULL
;
61 tnfctl_errcode_t prexstat
;
63 prbstat
= prb_child_create(pgm_name
, args
, ld_preload
, libtnfprobe_path
,
66 return (_tnfctl_map_to_errcode(prbstat
));
69 /* allocate hdl and zero fill */
70 hdl
= calloc(1, sizeof (*hdl
));
72 (void) prb_proc_close(proc_p
);
73 return (TNFCTL_ERR_ALLOCFAIL
);
77 hdl
->mode
= DIRECT_MODE
;
78 hdl
->called_exit
= B_FALSE
;
80 /* use native /proc on this target */
81 hdl
->p_read
= _tnfctl_read_targ
;
82 hdl
->p_write
= _tnfctl_write_targ
;
83 hdl
->p_obj_iter
= _tnfctl_loadobj_iter
;
84 hdl
->p_getpid
= _tnfctl_pid_get
;
87 * get the address of DT_DEBUG and send it in to prb_ layer.
88 * This is needed before before prb_rtld_sync() can be called.
90 prexstat
= _tnfctl_elf_dbgent(hdl
, &dbgaddr
);
94 prb_dbgaddr(proc_p
, dbgaddr
);
96 /* sync up to rtld sync point */
97 prbstat
= prb_rtld_sync_if_needed(proc_p
);
99 prexstat
= _tnfctl_map_to_errcode(prbstat
);
103 /* initialize state in handle */
104 prexstat
= _tnfctl_set_state(hdl
);
108 prexstat
= _tnfctl_external_getlock(hdl
);
113 /* Successful return */
114 return (TNFCTL_ERR_NONE
);
117 (void) prb_proc_close(proc_p
);
124 * attaches to a running process. If the process is in the beginning
125 * of an exec(2) system call (which is how tnfctl_continue() returns on exec),
126 * it steps the process till the end of the the exec. If the process hasn't
127 * reached the rtld sync point, the process is continued until it does
128 * reach it. Returns a pointer to a tnfctl handle.
131 tnfctl_pid_open(pid_t pid
, tnfctl_handle_t
**ret_val
)
133 tnfctl_handle_t
*hdl
;
134 prb_proc_ctl_t
*proc_p
= NULL
;
136 prb_status_t prbstat
;
137 tnfctl_errcode_t prexstat
;
139 prexstat
= attach_pid(pid
, &proc_p
);
144 /* allocate hdl and zero fill */
145 hdl
= calloc(1, sizeof (*hdl
));
147 (void) prb_proc_close(proc_p
);
148 return (TNFCTL_ERR_ALLOCFAIL
);
151 hdl
->proc_p
= proc_p
;
152 hdl
->mode
= DIRECT_MODE
;
153 hdl
->called_exit
= B_FALSE
;
155 /* use native /proc on this target */
156 hdl
->p_read
= _tnfctl_read_targ
;
157 hdl
->p_write
= _tnfctl_write_targ
;
158 hdl
->p_obj_iter
= _tnfctl_loadobj_iter
;
159 hdl
->p_getpid
= _tnfctl_pid_get
;
162 * Since tnfctl_continue() returns when a process does an exec
163 * and leaves the process stopped at the beginning of exec, we
164 * have to be sure to catch this case.
166 prexstat
= step_to_end_of_exec(hdl
);
167 /* proc_p could be side effected by step_to_end_of_exec() */
168 proc_p
= hdl
->proc_p
;
173 * get the address of DT_DEBUG and send it in to prb_ layer.
175 prexstat
= _tnfctl_elf_dbgent(hdl
, &dbgaddr
);
179 prb_dbgaddr(proc_p
, dbgaddr
);
181 /* sync up to rtld sync point if target is not there yet */
182 prbstat
= prb_rtld_sync_if_needed(proc_p
);
184 prexstat
= _tnfctl_map_to_errcode(prbstat
);
188 /* initialize state in handle */
189 prexstat
= _tnfctl_set_state(hdl
);
193 /* set state in target indicating we're tracing externally */
194 prexstat
= _tnfctl_external_getlock(hdl
);
200 /* Sucessful return */
201 return (TNFCTL_ERR_NONE
);
204 (void) prb_proc_close(proc_p
);
210 * open a process for tracing without using native /proc on it. The client
211 * provides a set of callback functions which encapsulate the /proc
212 * functionality we need. Returns a pointer to a tnfctl handle.
215 tnfctl_indirect_open(void *prochandle
, tnfctl_ind_config_t
*config
,
216 tnfctl_handle_t
**ret_val
)
218 tnfctl_handle_t
*hdl
;
219 tnfctl_errcode_t prexstat
;
221 /* allocate hdl and zero fill */
222 hdl
= calloc(1, sizeof (*hdl
));
224 return (TNFCTL_ERR_ALLOCFAIL
);
227 hdl
->proc_p
= prochandle
;
228 hdl
->mode
= INDIRECT_MODE
;
229 hdl
->called_exit
= B_FALSE
;
231 /* initialize callback functions */
232 hdl
->p_read
= config
->p_read
;
233 hdl
->p_write
= config
->p_write
;
234 hdl
->p_obj_iter
= config
->p_obj_iter
;
235 hdl
->p_getpid
= config
->p_getpid
;
237 /* initialize state in handle */
238 prexstat
= _tnfctl_set_state(hdl
);
243 /* set state in target indicating we're tracing externally */
244 prexstat
= _tnfctl_external_getlock(hdl
);
250 return (TNFCTL_ERR_NONE
);
254 * Returns a pointer to a tnfctl handle that can do kernel trace control
255 * and kernel probe control.
258 tnfctl_kernel_open(tnfctl_handle_t
**ret_val
)
260 tnfctl_handle_t
*hdl
;
261 tnfctl_errcode_t prexstat
;
263 /* allocate hdl and zero fill */
264 hdl
= calloc(1, sizeof (*hdl
));
266 return (TNFCTL_ERR_ALLOCFAIL
);
269 /* initialize kernel tracing */
270 prexstat
= _tnfctl_prbk_init(hdl
);
274 hdl
->mode
= KERNEL_MODE
;
277 /* initialize function pointers that can be stuffed into a probe */
278 _tnfctl_prbk_get_other_funcs(&hdl
->allocfunc
, &hdl
->commitfunc
,
279 &hdl
->rollbackfunc
, &hdl
->endfunc
);
280 _tnfctl_prbk_test_func(&hdl
->testfunc
);
282 /* find the probes in the kernel */
283 prexstat
= _tnfctl_refresh_kernel(hdl
);
288 return (TNFCTL_ERR_NONE
);
292 * Returns the trace attributes to the client. Since there can be
293 * only one controlling agent on a target at a time, our cached information
294 * is correct and we don't have to actually retrieve any information
298 tnfctl_trace_attrs_get(tnfctl_handle_t
*hdl
, tnfctl_trace_attrs_t
*attrs
)
300 boolean_t release_lock
;
301 tnfctl_errcode_t prexstat
;
303 /*LINTED statement has no consequent: else*/
304 LOCK_SYNC(hdl
, prexstat
, release_lock
);
306 attrs
->targ_pid
= hdl
->targ_pid
;
307 attrs
->trace_file_name
= hdl
->trace_file_name
;
308 attrs
->trace_buf_size
= hdl
->trace_buf_size
;
309 attrs
->trace_min_size
= hdl
->trace_min_size
;
310 attrs
->trace_buf_state
= hdl
->trace_buf_state
;
311 attrs
->trace_state
= hdl
->trace_state
;
312 attrs
->filter_state
= hdl
->kpidfilter_state
;
314 /*LINTED statement has no consequent: else*/
315 UNLOCK(hdl
, release_lock
);
317 return (TNFCTL_ERR_NONE
);
322 * Allocate a trace buffer of the specified name and size.
325 tnfctl_buffer_alloc(tnfctl_handle_t
*hdl
, const char *trace_file_name
,
326 uint_t trace_file_size
)
328 tnfctl_errcode_t prexstat
;
330 if (hdl
->mode
== KERNEL_MODE
) {
331 /* trace_file_name is ignored in kernel mode */
332 prexstat
= _tnfctl_prbk_buffer_alloc(hdl
, trace_file_size
);
335 return (TNFCTL_ERR_NONE
);
338 /* Not KERNEL_MODE */
339 if (hdl
->trace_file_name
!= NULL
) {
340 /* buffer already allocated */
341 return (TNFCTL_ERR_BUFEXISTS
);
344 prexstat
= _tnfctl_create_tracefile(hdl
, trace_file_name
,
350 return (TNFCTL_ERR_NONE
);
354 * Deallocate the trace buffer - only works for kernel mode
357 tnfctl_buffer_dealloc(tnfctl_handle_t
*hdl
)
359 tnfctl_errcode_t prexstat
;
361 if (hdl
->mode
!= KERNEL_MODE
)
362 return (TNFCTL_ERR_BADARG
);
365 prexstat
= _tnfctl_prbk_buffer_dealloc(hdl
);
368 return (TNFCTL_ERR_NONE
);
373 * Helper function for attaching to a target process
375 static tnfctl_errcode_t
376 attach_pid(pid_t pid
, prb_proc_ctl_t
**proc_pp
)
378 prb_status_t prbstat
;
379 prb_proc_ctl_t
*proc_p
;
382 return (TNFCTL_ERR_BADARG
);
384 /* check if pid is valid */
385 if ((kill(pid
, 0) == -1) && errno
== ESRCH
) {
386 return (TNFCTL_ERR_NOPROCESS
);
388 /* open up /proc fd */
389 prbstat
= prb_proc_open(pid
, proc_pp
);
391 return (_tnfctl_map_to_errcode(prbstat
));
395 * default is to run-on-last-close. In case we cannot sync with
396 * target, we don't want to kill the target.
398 prbstat
= prb_proc_setrlc(proc_p
, B_TRUE
);
401 prbstat
= prb_proc_setklc(proc_p
, B_FALSE
);
406 prbstat
= prb_proc_stop(proc_p
);
410 /* Sucessful return */
411 return (TNFCTL_ERR_NONE
);
414 (void) prb_proc_close(proc_p
);
415 return (_tnfctl_map_to_errcode(prbstat
));
419 * Checks if target is at the beginning of an exec system call. If so,
420 * it runs it till the end of the exec system call. It takes care of
421 * the case where you're about to exec a setuid program.
422 * CAUTION: could side effect hndl->proc_p
424 static tnfctl_errcode_t
425 step_to_end_of_exec(tnfctl_handle_t
*hndl
)
427 prb_proc_ctl_t
*proc_p
, *oldproc_p
;
428 prb_status_t prbstat
, tempstat
;
430 prb_proc_state_t pstate
;
432 proc_p
= hndl
->proc_p
;
433 pid
= hndl
->p_getpid(proc_p
);
435 prbstat
= prb_proc_state(proc_p
, &pstate
);
437 return (_tnfctl_map_to_errcode(prbstat
));
438 if (!(pstate
.ps_issysentry
&& pstate
.ps_syscallnum
== SYS_execve
)) {
439 /* not stopped at beginning of exec system call */
440 return (TNFCTL_ERR_NONE
);
443 /* we are stopped at beginning of exec system call */
445 prbstat
= prb_proc_exit(proc_p
, SYS_execve
, PRB_SYS_ADD
);
447 return (_tnfctl_map_to_errcode(prbstat
));
449 prbstat
= prb_proc_cont(proc_p
);
451 return (_tnfctl_map_to_errcode(prbstat
));
453 prbstat
= prb_proc_wait(proc_p
, B_FALSE
, NULL
);
457 case PRB_STATUS_EAGAIN
:
459 * If we had exec'ed a setuid/setgid program PIOCWSTOP
460 * will return EAGAIN. Reopen the 'fd' and try again.
461 * Read the last section of /proc man page - we reopen first
462 * and then close the old fd.
465 tempstat
= prb_proc_reopen(pid
, &proc_p
);
467 /* here EACCES means exec'ed a setuid/setgid program */
468 return (_tnfctl_map_to_errcode(tempstat
));
471 prb_proc_close(oldproc_p
);
472 hndl
->proc_p
= proc_p
;
475 return (_tnfctl_map_to_errcode(prbstat
));
478 prbstat
= prb_proc_state(proc_p
, &pstate
);
480 return (_tnfctl_map_to_errcode(prbstat
));
482 if (!(pstate
.ps_issysexit
&& pstate
.ps_syscallnum
== SYS_execve
)) {
483 /* unexpected condition */
484 return (tnfctl_status_map(ENOENT
));
487 /* clear old interest mask */
488 prbstat
= prb_proc_exit(proc_p
, SYS_execve
, PRB_SYS_DEL
);
490 return (_tnfctl_map_to_errcode(prbstat
));
491 return (TNFCTL_ERR_NONE
);
496 _tnfctl_external_getlock(tnfctl_handle_t
*hdl
)
499 tnfctl_errcode_t prexstat
;
500 prb_status_t prbstat
;
501 uintptr_t targ_symbol_ptr
;
502 int internal_tracing_on
;
504 prexstat
= _tnfctl_sym_find(hdl
, TNFCTL_INTERNAL_TRACEFLAG
,
507 /* no libtnfctl in target: success */
508 return (TNFCTL_ERR_NONE
);
510 prbstat
= hdl
->p_read(hdl
->proc_p
, targ_symbol_ptr
,
511 &internal_tracing_on
, sizeof (internal_tracing_on
));
514 prexstat
= _tnfctl_map_to_errcode(prbstat
);
517 if (internal_tracing_on
) {
518 /* target process being traced internally */
519 prexstat
= TNFCTL_ERR_BUSY
;
522 prexstat
= _tnfctl_sym_find(hdl
, TNFCTL_EXTERNAL_TRACEDPID
,
525 /* this shouldn't happen. we know we have libtnfctl */
528 prbstat
= hdl
->p_write(hdl
->proc_p
, targ_symbol_ptr
,
529 &(hdl
->targ_pid
), sizeof (hdl
->targ_pid
));
531 prexstat
= _tnfctl_map_to_errcode(prbstat
);
535 DBG((void) fprintf(stderr
, "_tnfctl_external_getlock: ok to trace %d\n",
537 return (TNFCTL_ERR_NONE
);