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