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 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <unistd.h> 33 #include <ctype.h> 34 #include <string.h> 35 #include <signal.h> 36 #include <stropts.h> 37 #include <errno.h> 38 #include <sys/types.h> 39 #include <sys/termio.h> 40 #include <libproc.h> 41 #include "ramdata.h" 42 #include "proto.h" 43 44 /* 45 * Routines related to interprocess communication 46 * among the truss processes which are controlling 47 * multiple traced processes. 48 */ 49 50 /* 51 * Function prototypes for static routines in this module. 52 */ 53 void Ecritical(int); 54 void Xcritical(int); 55 56 /* 57 * Ensure everyone keeps out of each other's way 58 * while writing lines of trace output. 59 */ 60 void 61 Flush() 62 { 63 /* 64 * Except for regions bounded by Eserialize()/Xserialize(), 65 * this is the only place anywhere in the program where a 66 * write() to the trace output file takes place, so here 67 * is where we detect errors writing to the output. 68 */ 69 70 errno = 0; 71 72 Ecritical(0); 73 (void) fflush(stdout); 74 Xcritical(0); 75 76 if (ferror(stdout) && errno) /* error on write(), probably EPIPE */ 77 interrupt = SIGTERM; /* post an interrupt */ 78 } 79 80 /* 81 * Eserialize() and Xserialize() are used to bracket 82 * a region which may produce large amounts of output, 83 * such as showargs()/dumpargs(). 84 */ 85 86 void 87 Eserialize() 88 { 89 /* serialize output */ 90 Ecritical(0); 91 } 92 93 void 94 Xserialize() 95 { 96 (void) fflush(stdout); 97 Xcritical(0); 98 } 99 100 /* 101 * Enter critical region --- Wait on mutex, lock out other processes. 102 * Lock zero is used to serialize output in situations where multiple processes 103 * may be writing to stdout/stderr and order must be preserved. Most of these 104 * are in expound.c 105 * Lock one is used to protect the table of processes currently being traced 106 * every time a pid is added or removed from the table Ecritical(1)/Xcritical(1) 107 * get called. 108 */ 109 void 110 Ecritical(int num) 111 { 112 int rv; 113 114 if (num == 0) 115 rv = mutex_lock(&gps->ps_mutex0); 116 else if (num == 1) 117 rv = mutex_lock(&gps->ps_mutex1); 118 else 119 abend("Invalid mutex specified", NULL); 120 121 if (rv != 0) { 122 char mnum[2]; 123 mnum[0] = '0' + num; 124 mnum[1] = '\0'; 125 errno = rv; 126 perror(command); 127 errmsg("cannot grab mutex #", mnum); 128 } 129 } 130 131 /* 132 * Exit critical region --- 133 * Release other processes waiting on mutex. 134 */ 135 void 136 Xcritical(int num) 137 { 138 int rv; 139 140 if (num == 0) 141 rv = mutex_unlock(&gps->ps_mutex0); 142 else if (num == 1) 143 rv = mutex_unlock(&gps->ps_mutex1); 144 else 145 abend("Invalid mutex specified", NULL); 146 147 148 if (rv != 0) { 149 char mnum[2]; 150 mnum[0] = '0' + num; 151 mnum[1] = '\0'; 152 errno = rv; 153 perror(command); 154 errmsg("cannot release mutex #", mnum); 155 } 156 } 157 158 /* 159 * Add process to set of those being traced. 160 */ 161 void 162 procadd(pid_t spid, const char *lwplist) 163 { 164 int i; 165 int j = -1; 166 167 if (gps == NULL) 168 return; 169 170 Ecritical(1); 171 for (i = 0; i < sizeof (gps->tpid) / sizeof (gps->tpid[0]); i++) { 172 if (gps->tpid[i] == 0) { 173 if (j == -1) /* remember first vacant slot */ 174 j = i; 175 if (gps->spid[i] == 0) /* this slot is better */ 176 break; 177 } 178 } 179 if (i < sizeof (gps->tpid) / sizeof (gps->tpid[0])) 180 j = i; 181 if (j >= 0) { 182 gps->tpid[j] = getpid(); 183 gps->spid[j] = spid; 184 gps->lwps[j] = lwplist; 185 } 186 Xcritical(1); 187 } 188 189 /* 190 * Delete process from set of those being traced. 191 */ 192 void 193 procdel() 194 { 195 int i; 196 pid_t tpid; 197 198 if (gps == NULL) 199 return; 200 201 tpid = getpid(); 202 203 Ecritical(1); 204 for (i = 0; i < sizeof (gps->tpid) / sizeof (gps->tpid[0]); i++) { 205 if (gps->tpid[i] == tpid) { 206 gps->tpid[i] = 0; 207 break; 208 } 209 } 210 Xcritical(1); 211 } 212 213 /* 214 * Determine if the lwp for this process should be traced. 215 */ 216 int 217 lwptrace(pid_t spid, lwpid_t lwpid) 218 { 219 int i; 220 pid_t tpid; 221 const char *lwps; 222 223 if (gps == NULL) 224 return (0); 225 226 tpid = getpid(); 227 228 Ecritical(1); 229 for (i = 0; i < sizeof (gps->tpid) / sizeof (gps->tpid[0]); i++) { 230 if (gps->tpid[i] == tpid && 231 gps->spid[i] == spid) 232 break; 233 } 234 lwps = gps->lwps[i]; 235 Xcritical(1); 236 237 return (proc_lwp_in_set(lwps, lwpid)); 238 } 239 240 /* 241 * Check for open of a /proc/nnnnn file. 242 * Return 0 if this is not an open of a /proc file. 243 * Return 1 if the process opened itself. 244 * Return 2 if the process failed to open another process 245 * in truss's set of controlled processes. 246 * Return 3 if the process successfully opened another process 247 * in truss's set of controlled processes. 248 * We notify and wait for the other controlling truss process 249 * to terminate before returning in cases 2 and 3. 250 */ 251 /* ARGSUSED */ 252 int 253 checkproc(private_t *pri) 254 { 255 char *path = pri->sys_path; 256 const pstatus_t *Psp = Pstatus(Proc); 257 struct ps_lwphandle *Lwp = pri->Lwp; 258 const lwpstatus_t *Lsp = pri->lwpstat; 259 int what = Lsp->pr_what; /* one of the SYS_open* syscalls */ 260 int err = Lsp->pr_errno; 261 int pid; 262 int i; 263 const char *dirname; 264 char *next; 265 char *sp1; 266 char *sp2; 267 prgreg_t pc; 268 269 /* 270 * A bit heuristic ... 271 * Test for the cases: 272 * 1234 273 * 1234/as 274 * 1234/ctl 275 * 1234/lwp/24/lwpctl 276 * .../1234 277 * .../1234/as 278 * .../1234/ctl 279 * .../1234/lwp/24/lwpctl 280 * Insert a '\0', if necessary, so the path becomes ".../1234". 281 * 282 * Along the way, watch out for /proc/self and /proc/1234/lwp/agent 283 */ 284 if ((sp1 = strrchr(path, '/')) == NULL) /* last component */ 285 /* EMPTY */; 286 else if (isdigit(*(sp1+1))) { 287 sp1 += strlen(sp1); 288 while (--sp1 > path && isdigit(*sp1)) 289 ; 290 if (*sp1 != '/') 291 return (0); 292 } else if (strcmp(sp1+1, "as") == 0 || 293 strcmp(sp1+1, "ctl") == 0) { 294 *sp1 = '\0'; 295 } else if (strcmp(sp1+1, "lwpctl") == 0) { 296 /* 297 * .../1234/lwp/24/lwpctl 298 * ............ ^-- sp1 299 */ 300 if (sp1-6 >= path && strncmp(sp1-6, "/agent", 6) == 0) 301 sp1 -= 6; 302 else { 303 while (--sp1 > path && isdigit(*sp1)) 304 ; 305 } 306 if (*sp1 != '/' || 307 (sp1 -= 4) <= path || 308 strncmp(sp1, "/lwp", 4) != 0) 309 return (0); 310 *sp1 = '\0'; 311 } else if (strcmp(sp1+1, "self") != 0) { 312 return (0); 313 } 314 315 if ((sp2 = strrchr(path, '/')) == NULL) 316 dirname = path; 317 else 318 dirname = sp2 + 1; 319 320 if (strcmp(dirname, "self") == 0) { 321 pid = Psp->pr_pid; 322 } else if ((pid = strtol(dirname, &next, 10)) < 0 || 323 *next != '\0') { /* dirname not a number */ 324 if (sp1 != NULL) 325 *sp1 = '/'; 326 return (0); 327 } 328 if (sp2 == NULL) 329 dirname = "."; 330 else { 331 *sp2 = '\0'; 332 dirname = path; 333 } 334 335 if (!Pisprocdir(Proc, dirname) || /* file not in a /proc directory */ 336 pid == getpid() || /* process opened truss's /proc file */ 337 pid == 0) { /* process opened process 0 */ 338 if (sp1 != NULL) 339 *sp1 = '/'; 340 if (sp2 != NULL) 341 *sp2 = '/'; 342 return (0); 343 } 344 if (sp1 != NULL) 345 *sp1 = '/'; 346 if (sp2 != NULL) 347 *sp2 = '/'; 348 349 /* 350 * Process did open a /proc file --- 351 */ 352 if (pid == Psp->pr_pid) { /* process opened its own /proc file */ 353 /* 354 * In SunOS 5.6 and beyond, self-opens always succeed. 355 */ 356 return (1); 357 } 358 359 /* 360 * Search for a matching pid in our set of controlled processes. 361 */ 362 for (i = 0; i < sizeof (gps->tpid)/sizeof (gps->tpid[0]); i++) { 363 if (gps->spid[i] == pid) { 364 pid = gps->tpid[i]; 365 break; 366 } 367 } 368 if (i >= sizeof (gps->tpid) / sizeof (gps->tpid[0])) { 369 /* 370 * The process opened a /proc file, but not one we care about. 371 */ 372 return (0); 373 } 374 375 /* 376 * Notify and wait for the controlling process to terminate. 377 */ 378 while (pid && gps->tpid[i] == pid) { 379 if (kill(pid, SIGUSR1) == -1) 380 break; 381 (void) usleep(1000000); 382 } 383 Ecritical(1); 384 if (gps->tpid[i] == 0) 385 gps->spid[i] = 0; 386 Xcritical(1); 387 388 if (err) { /* prepare to reissue the failed open() system call */ 389 #if defined(__sparc) 390 (void) Lgetareg(Lwp, R_PC, &pc); 391 if (pri->sys_indirect) { 392 (void) Lputareg(Lwp, R_G1, (prgreg_t)SYS_syscall); 393 (void) Lputareg(Lwp, R_O0, (prgreg_t)what); 394 for (i = 0; i < 5; i++) 395 (void) Lputareg(Lwp, R_O1+i, pri->sys_args[i]); 396 } else { 397 (void) Lputareg(Lwp, R_G1, (prgreg_t)what); 398 for (i = 0; i < 6; i++) 399 (void) Lputareg(Lwp, R_O0+i, pri->sys_args[i]); 400 } 401 (void) Lputareg(Lwp, R_nPC, pc); 402 #elif defined(__amd64) 403 (void) Lgetareg(Lwp, R_PC, &pc); 404 (void) Lputareg(Lwp, REG_RAX, (prgreg_t)what); 405 #elif defined(__i386) 406 (void) Lgetareg(Lwp, R_PC, &pc); 407 (void) Lputareg(Lwp, EAX, (prgreg_t)what); 408 #else 409 #error "unrecognized architecture" 410 #endif 411 (void) Pissyscall_prev(Proc, pc, (uintptr_t *)&pc); 412 (void) Lputareg(Lwp, R_PC, pc); 413 return (2); 414 } 415 416 return (3); 417 } 418