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