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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 1994,2003 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 *evt = TNFCTL_EVENT_FORK; 257 break; 258 default: 259 break; 260 } 261 } 262 end_of_func: 263 /* 264 * disable all our sycall tracing and bpt setup in process when it 265 * is stopped, so that even if the controlling process aborts, 266 * the target could continue running 267 */ 268 prexstat = disable_target_state(hndl); 269 if (prexstat) 270 return (prexstat); 271 return (ret_prexstat); 272 } 273 274 /* 275 * enable the system call tracing and dl activity tracing 276 */ 277 static tnfctl_errcode_t 278 enable_target_state(tnfctl_handle_t *hndl, boolean_t watch_forks) 279 { 280 prb_status_t prbstat; 281 prb_proc_ctl_t *proc_p; 282 283 proc_p = hndl->proc_p; 284 285 /* trace exec */ 286 prbstat = prb_proc_entry(proc_p, SYS_execve, PRB_SYS_ADD); 287 if (prbstat) 288 return (_tnfctl_map_to_errcode(prbstat)); 289 prbstat = prb_proc_entry(proc_p, SYS_exec, PRB_SYS_ADD); 290 if (prbstat) 291 return (_tnfctl_map_to_errcode(prbstat)); 292 /* trace exit */ 293 prbstat = prb_proc_entry(proc_p, SYS_exit, PRB_SYS_ADD); 294 if (prbstat) 295 return (_tnfctl_map_to_errcode(prbstat)); 296 /* trace fork if the caller requests */ 297 if (watch_forks) { 298 prbstat = prb_proc_exit(proc_p, SYS_forkall, PRB_SYS_ADD); 299 if (prbstat) 300 return (_tnfctl_map_to_errcode(prbstat)); 301 302 prbstat = prb_proc_exit(proc_p, SYS_vfork, PRB_SYS_ADD); 303 if (prbstat) 304 return (_tnfctl_map_to_errcode(prbstat)); 305 306 prbstat = prb_proc_exit(proc_p, SYS_fork1, PRB_SYS_ADD); 307 if (prbstat) 308 return (_tnfctl_map_to_errcode(prbstat)); 309 prbstat = prb_proc_setfork(proc_p, B_TRUE); 310 if (prbstat) 311 return (_tnfctl_map_to_errcode(prbstat)); 312 } 313 /* 314 * tracing flags for fork and exec will get unset when 315 * process stops. see disable_target_state() 316 */ 317 318 /* setup process to stop during dlopen() or dlclose() */ 319 prbstat = prb_rtld_stalk(proc_p); 320 return (_tnfctl_map_to_errcode(prbstat)); 321 } 322 323 /* 324 * disable the system call tracing and dl activity tracing 325 */ 326 static tnfctl_errcode_t 327 disable_target_state(tnfctl_handle_t *hndl) 328 { 329 prb_status_t prbstat; 330 prb_proc_ctl_t *proc_p; 331 332 proc_p = hndl->proc_p; 333 334 /* remove the stalking breakpoint while the process is stopped */ 335 prbstat = prb_rtld_unstalk(proc_p); 336 if (prbstat) 337 return (_tnfctl_map_to_errcode(prbstat)); 338 339 /* remove the exec, exit and fork tracing while stopped */ 340 prbstat = prb_proc_entry(proc_p, SYS_execve, PRB_SYS_DEL); 341 if (prbstat) 342 return (_tnfctl_map_to_errcode(prbstat)); 343 prbstat = prb_proc_entry(proc_p, SYS_exec, PRB_SYS_DEL); 344 if (prbstat) 345 return (_tnfctl_map_to_errcode(prbstat)); 346 prbstat = prb_proc_entry(proc_p, SYS_exit, PRB_SYS_DEL); 347 if (prbstat) 348 return (_tnfctl_map_to_errcode(prbstat)); 349 prbstat = prb_proc_exit(proc_p, SYS_forkall, PRB_SYS_DEL); 350 if (prbstat) 351 return (_tnfctl_map_to_errcode(prbstat)); 352 prbstat = prb_proc_exit(proc_p, SYS_vfork, PRB_SYS_DEL); 353 if (prbstat) 354 return (_tnfctl_map_to_errcode(prbstat)); 355 prbstat = prb_proc_exit(proc_p, SYS_fork1, PRB_SYS_DEL); 356 if (prbstat) 357 return (_tnfctl_map_to_errcode(prbstat)); 358 prbstat = prb_proc_setfork(proc_p, B_FALSE); 359 if (prbstat) 360 return (_tnfctl_map_to_errcode(prbstat)); 361 362 return (TNFCTL_ERR_NONE); 363 } 364