Home | History | Annotate | Download | only in truss
      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