1 /* 2 * CDDL HEADER START 3 * 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. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * interface to continue a target process (DIRECT_MODE) and helper 31 * functions needed by this routine. 32 */ 33 34 #include "tnfctl_int.h" 35 #include "prb_proc.h" 36 #include "dbg.h" 37 38 39 #include <stdlib.h> 40 #include <errno.h> 41 42 static tnfctl_errcode_t _tnfctl_continue(tnfctl_handle_t *hndl, 43 tnfctl_event_t *evt, sigset_t *oldmask, boolean_t watch_forks); 44 static tnfctl_errcode_t enable_target_state(tnfctl_handle_t *hndl, 45 boolean_t watch_forks); 46 static tnfctl_errcode_t disable_target_state(tnfctl_handle_t *hndl); 47 48 /* 49 * continue the target process and return the evt it stopped on. 50 * If child_hndl is set and we see a fork, return a handle on child 51 * process. 52 */ 53 tnfctl_errcode_t 54 tnfctl_continue(tnfctl_handle_t *hndl, tnfctl_event_t *evt, 55 tnfctl_handle_t **child_hndl) 56 { 57 tnfctl_errcode_t prexstat; 58 prb_status_t prbstat; 59 boolean_t lmapok = B_FALSE; 60 boolean_t watch_forks; 61 /* set my_evt to something other than TNFCTL_EVENT_TARGGONE */ 62 tnfctl_event_t my_evt = TNFCTL_EVENT_EINTR; 63 enum event_op_t dl_evt; 64 sigset_t newmask, oldmask; 65 prb_proc_ctl_t *proc_p; 66 prgreg_t reg0, reg1; 67 68 /* this interface only works for DIRECT_MODE clients */ 69 if (hndl->mode != DIRECT_MODE) 70 return (TNFCTL_ERR_BADARG); 71 72 proc_p = hndl->proc_p; 73 74 if (sigfillset(&newmask) == -1) 75 return (tnfctl_status_map(errno)); 76 77 watch_forks = (child_hndl != NULL); 78 79 /* 80 * XXXX block all signals. Synchronous signals like SEGV that 81 * the user could catch and handle will now result in a core dump. 82 * But, this is very unlikely for 2 reasons - most users don't try 83 * to handle synchronous signals - it usually just aborts the process. 84 * And, secondly, the code until we return the original mask is the 85 * place where this synchronous signal would be generated - and, it 86 * is not very much code. 87 */ 88 if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) == -1) 89 return (tnfctl_status_map(errno)); 90 91 /* 92 * Target is stopped on entry because tnfctl_continue() 93 * only returns with a stopped target. 94 */ 95 96 /* target process shouldn't be stopped when link maps are incosistent */ 97 while (lmapok == B_FALSE) { 98 prexstat = _tnfctl_continue(hndl, &my_evt, &oldmask, 99 watch_forks); 100 if (prexstat) { 101 if (my_evt == TNFCTL_EVENT_TARGGONE || 102 my_evt == TNFCTL_EVENT_EXIT) { 103 /* 104 * target exited - free obj list and probe 105 * list so that we keep our internal state 106 * correct, else probe control interfaces will 107 * have wrong information. 108 */ 109 DBG(fprintf(stderr, "target is gone\n")); 110 _tnfctl_free_objs_and_probes(hndl); 111 *evt = my_evt; 112 return (TNFCTL_ERR_NONE); 113 } else if (my_evt == TNFCTL_EVENT_EXEC) { 114 *evt = my_evt; 115 return (TNFCTL_ERR_NONE); 116 } else if (prexstat == TNFCTL_ERR_FILENOTFOUND) { 117 return (TNFCTL_ERR_NOPROCESS); 118 } else { 119 return (prexstat); 120 } 121 } 122 if (my_evt == TNFCTL_EVENT_FORK) { 123 /* 124 * sanity check. we should only get here if child_hndl is set 125 */ 126 if (child_hndl) { 127 *evt = my_evt; 128 prbstat = prb_proc_get_r0_r1(proc_p, 129 ®0, ®1); 130 if (prbstat) { 131 prexstat = _tnfctl_map_to_errcode(prbstat); 132 return (prexstat); 133 } 134 prexstat = tnfctl_pid_open((pid_t)reg0, 135 child_hndl); 136 disable_target_state(*child_hndl); 137 return (prexstat); 138 } 139 return (TNFCTL_ERR_NONE); 140 } 141 142 /* 143 * update state in handle 144 * REMIND: Only need to call _tnfctl_refresh_process on 145 * dlopen or dlclose. Need to take out other functionality 146 * of refresh_process into a separate function that should 147 * be called here. 148 */ 149 prexstat = _tnfctl_refresh_process(hndl, &lmapok, &dl_evt); 150 if (prexstat && (lmapok == B_TRUE)) 151 return (prexstat); 152 prexstat = TNFCTL_ERR_NONE; 153 } 154 *evt = my_evt; 155 /* see if we have more detail about the event */ 156 if (dl_evt == EVT_OPEN) 157 *evt = TNFCTL_EVENT_DLOPEN; 158 else if (dl_evt == EVT_CLOSE) 159 *evt = TNFCTL_EVENT_DLCLOSE; 160 161 return (TNFCTL_ERR_NONE); 162 } 163 164 /* 165 * Continues target and waits for it to stop. 166 * warning: This routine returns TNFCTL_EVENT_DLOPEN for any kind of 167 * dl activity. Up to the caller to determine the actual DL event. 168 */ 169 static tnfctl_errcode_t 170 _tnfctl_continue(tnfctl_handle_t *hndl, tnfctl_event_t *evt, sigset_t *oldmask, 171 boolean_t watch_forks) 172 { 173 tnfctl_errcode_t prexstat; 174 tnfctl_errcode_t ret_prexstat = TNFCTL_ERR_NONE; 175 prb_status_t prbstat, prbstat2; 176 prb_proc_ctl_t *proc_p; 177 prb_proc_state_t state; 178 179 proc_p = hndl->proc_p; 180 181 /* set up state before we run process */ 182 prexstat = enable_target_state(hndl, watch_forks); 183 if (prexstat) 184 return (prexstat); 185 186 again: 187 188 /* resume target */ 189 prbstat = prb_proc_cont(proc_p); 190 if (prbstat) { 191 ret_prexstat = _tnfctl_map_to_errcode(prbstat); 192 goto end_of_func; 193 } 194 195 /* wait on target to stop (standby) */ 196 prbstat = prb_proc_wait(proc_p, B_TRUE, oldmask); 197 if (prbstat) { 198 if (prbstat == EINTR) { 199 *evt = TNFCTL_EVENT_EINTR; 200 prbstat2 = prb_proc_stop(proc_p); 201 if (prbstat2) { 202 ret_prexstat = _tnfctl_map_to_errcode(prbstat2); 203 goto end_of_func; 204 } 205 } else if (prbstat == ENOENT) { 206 /* target process finished */ 207 if (hndl->called_exit) 208 *evt = TNFCTL_EVENT_EXIT; 209 else 210 *evt = TNFCTL_EVENT_TARGGONE; 211 /* return directly - process no longer around */ 212 return (TNFCTL_ERR_INTERNAL); 213 } else { 214 ret_prexstat = _tnfctl_map_to_errcode(prbstat); 215 goto end_of_func; 216 } 217 } 218 219 prbstat = prb_proc_state(proc_p, &state); 220 if (prbstat) { 221 ret_prexstat = _tnfctl_map_to_errcode(prbstat); 222 goto end_of_func; 223 } 224 if (state.ps_isbptfault) { 225 /* dlopen or dlclose */ 226 prbstat = prb_rtld_advance(proc_p); 227 if (prbstat) { 228 ret_prexstat = _tnfctl_map_to_errcode(prbstat); 229 goto end_of_func; 230 } 231 /* 232 * actually don't know if it is a dlopen or dlclose yet. 233 * But, we return dlopen here. Up to the caller to determine 234 * which one it actually is. 235 */ 236 *evt = TNFCTL_EVENT_DLOPEN; 237 } else 238 if (state.ps_issysentry) { 239 switch (state.ps_syscallnum) { 240 case SYS_exec: 241 case SYS_execve: 242 *evt = TNFCTL_EVENT_EXEC; 243 ret_prexstat = TNFCTL_ERR_INTERNAL; 244 break; 245 case SYS_exit: 246 hndl->called_exit = B_TRUE; 247 goto again; 248 default: 249 break; 250 } 251 } else if (state.ps_issysexit) { 252 switch (state.ps_syscallnum) { 253 case SYS_forkall: 254 case SYS_vfork: 255 case SYS_fork1: 256 case SYS_forksys: 257 *evt = TNFCTL_EVENT_FORK; 258 break; 259 default: 260 break; 261 } 262 } 263 end_of_func: 264 /* 265 * disable all our sycall tracing and bpt setup in process when it 266 * is stopped, so that even if the controlling process aborts, 267 * the target could continue running 268 */ 269 prexstat = disable_target_state(hndl); 270 if (prexstat) 271 return (prexstat); 272 return (ret_prexstat); 273 } 274 275 /* 276 * enable the system call tracing and dl activity tracing 277 */ 278 static tnfctl_errcode_t 279 enable_target_state(tnfctl_handle_t *hndl, boolean_t watch_forks) 280 { 281 prb_status_t prbstat; 282 prb_proc_ctl_t *proc_p; 283 284 proc_p = hndl->proc_p; 285 286 /* trace exec */ 287 prbstat = prb_proc_entry(proc_p, SYS_execve, PRB_SYS_ADD); 288 if (prbstat) 289 return (_tnfctl_map_to_errcode(prbstat)); 290 prbstat = prb_proc_entry(proc_p, SYS_exec, PRB_SYS_ADD); 291 if (prbstat) 292 return (_tnfctl_map_to_errcode(prbstat)); 293 /* trace exit */ 294 prbstat = prb_proc_entry(proc_p, SYS_exit, PRB_SYS_ADD); 295 if (prbstat) 296 return (_tnfctl_map_to_errcode(prbstat)); 297 /* trace fork if the caller requests */ 298 if (watch_forks) { 299 prbstat = prb_proc_exit(proc_p, SYS_forkall, PRB_SYS_ADD); 300 if (prbstat) 301 return (_tnfctl_map_to_errcode(prbstat)); 302 303 prbstat = prb_proc_exit(proc_p, SYS_vfork, PRB_SYS_ADD); 304 if (prbstat) 305 return (_tnfctl_map_to_errcode(prbstat)); 306 307 prbstat = prb_proc_exit(proc_p, SYS_fork1, PRB_SYS_ADD); 308 if (prbstat) 309 return (_tnfctl_map_to_errcode(prbstat)); 310 311 prbstat = prb_proc_exit(proc_p, SYS_forksys, PRB_SYS_ADD); 312 if (prbstat) 313 return (_tnfctl_map_to_errcode(prbstat)); 314 315 prbstat = prb_proc_setfork(proc_p, B_TRUE); 316 if (prbstat) 317 return (_tnfctl_map_to_errcode(prbstat)); 318 } 319 /* 320 * tracing flags for fork and exec will get unset when 321 * process stops. see disable_target_state() 322 */ 323 324 /* setup process to stop during dlopen() or dlclose() */ 325 prbstat = prb_rtld_stalk(proc_p); 326 return (_tnfctl_map_to_errcode(prbstat)); 327 } 328 329 /* 330 * disable the system call tracing and dl activity tracing 331 */ 332 static tnfctl_errcode_t 333 disable_target_state(tnfctl_handle_t *hndl) 334 { 335 prb_status_t prbstat; 336 prb_proc_ctl_t *proc_p; 337 338 proc_p = hndl->proc_p; 339 340 /* remove the stalking breakpoint while the process is stopped */ 341 prbstat = prb_rtld_unstalk(proc_p); 342 if (prbstat) 343 return (_tnfctl_map_to_errcode(prbstat)); 344 345 /* remove the exec, exit and fork tracing while stopped */ 346 prbstat = prb_proc_entry(proc_p, SYS_execve, PRB_SYS_DEL); 347 if (prbstat) 348 return (_tnfctl_map_to_errcode(prbstat)); 349 prbstat = prb_proc_entry(proc_p, SYS_exec, PRB_SYS_DEL); 350 if (prbstat) 351 return (_tnfctl_map_to_errcode(prbstat)); 352 prbstat = prb_proc_entry(proc_p, SYS_exit, PRB_SYS_DEL); 353 if (prbstat) 354 return (_tnfctl_map_to_errcode(prbstat)); 355 prbstat = prb_proc_exit(proc_p, SYS_forkall, PRB_SYS_DEL); 356 if (prbstat) 357 return (_tnfctl_map_to_errcode(prbstat)); 358 prbstat = prb_proc_exit(proc_p, SYS_vfork, PRB_SYS_DEL); 359 if (prbstat) 360 return (_tnfctl_map_to_errcode(prbstat)); 361 prbstat = prb_proc_exit(proc_p, SYS_fork1, PRB_SYS_DEL); 362 if (prbstat) 363 return (_tnfctl_map_to_errcode(prbstat)); 364 prbstat = prb_proc_exit(proc_p, SYS_forksys, PRB_SYS_DEL); 365 if (prbstat) 366 return (_tnfctl_map_to_errcode(prbstat)); 367 prbstat = prb_proc_setfork(proc_p, B_FALSE); 368 if (prbstat) 369 return (_tnfctl_map_to_errcode(prbstat)); 370 371 return (TNFCTL_ERR_NONE); 372 } 373