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