diff options
Diffstat (limited to 'usr/src/cmd/truss/ipc.c')
-rw-r--r-- | usr/src/cmd/truss/ipc.c | 417 |
1 files changed, 417 insertions, 0 deletions
diff --git a/usr/src/cmd/truss/ipc.c b/usr/src/cmd/truss/ipc.c new file mode 100644 index 0000000..2bb3465 --- /dev/null +++ b/usr/src/cmd/truss/ipc.c @@ -0,0 +1,417 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2010 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <ctype.h> +#include <string.h> +#include <signal.h> +#include <stropts.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/termio.h> +#include <libproc.h> +#include "ramdata.h" +#include "proto.h" + +/* + * Routines related to interprocess communication + * among the truss processes which are controlling + * multiple traced processes. + */ + +/* + * Function prototypes for static routines in this module. + */ +void Ecritical(int); +void Xcritical(int); + +/* + * Ensure everyone keeps out of each other's way + * while writing lines of trace output. + */ +void +Flush() +{ + /* + * Except for regions bounded by Eserialize()/Xserialize(), + * this is the only place anywhere in the program where a + * write() to the trace output file takes place, so here + * is where we detect errors writing to the output. + */ + + errno = 0; + + Ecritical(0); + (void) fflush(stdout); + Xcritical(0); + + if (ferror(stdout) && errno) /* error on write(), probably EPIPE */ + interrupt = SIGTERM; /* post an interrupt */ +} + +/* + * Eserialize() and Xserialize() are used to bracket + * a region which may produce large amounts of output, + * such as showargs()/dumpargs(). + */ + +void +Eserialize() +{ + /* serialize output */ + Ecritical(0); +} + +void +Xserialize() +{ + (void) fflush(stdout); + Xcritical(0); +} + +/* + * Enter critical region --- Wait on mutex, lock out other processes. + * Lock zero is used to serialize output in situations where multiple processes + * may be writing to stdout/stderr and order must be preserved. Most of these + * are in expound.c + * Lock one is used to protect the table of processes currently being traced + * every time a pid is added or removed from the table Ecritical(1)/Xcritical(1) + * get called. + */ +void +Ecritical(int num) +{ + int rv; + + if (num == 0) + rv = mutex_lock(&gps->ps_mutex0); + else if (num == 1) + rv = mutex_lock(&gps->ps_mutex1); + else + abend("Invalid mutex specified", NULL); + + if (rv != 0) { + char mnum[2]; + mnum[0] = '0' + num; + mnum[1] = '\0'; + errno = rv; + perror(command); + errmsg("cannot grab mutex #", mnum); + } +} + +/* + * Exit critical region --- + * Release other processes waiting on mutex. + */ +void +Xcritical(int num) +{ + int rv; + + if (num == 0) + rv = mutex_unlock(&gps->ps_mutex0); + else if (num == 1) + rv = mutex_unlock(&gps->ps_mutex1); + else + abend("Invalid mutex specified", NULL); + + + if (rv != 0) { + char mnum[2]; + mnum[0] = '0' + num; + mnum[1] = '\0'; + errno = rv; + perror(command); + errmsg("cannot release mutex #", mnum); + } +} + +/* + * Add process to set of those being traced. + */ +void +procadd(pid_t spid, const char *lwplist) +{ + int i; + int j = -1; + + if (gps == NULL) + return; + + Ecritical(1); + for (i = 0; i < sizeof (gps->tpid) / sizeof (gps->tpid[0]); i++) { + if (gps->tpid[i] == 0) { + if (j == -1) /* remember first vacant slot */ + j = i; + if (gps->spid[i] == 0) /* this slot is better */ + break; + } + } + if (i < sizeof (gps->tpid) / sizeof (gps->tpid[0])) + j = i; + if (j >= 0) { + gps->tpid[j] = getpid(); + gps->spid[j] = spid; + gps->lwps[j] = lwplist; + } + Xcritical(1); +} + +/* + * Delete process from set of those being traced. + */ +void +procdel() +{ + int i; + pid_t tpid; + + if (gps == NULL) + return; + + tpid = getpid(); + + Ecritical(1); + for (i = 0; i < sizeof (gps->tpid) / sizeof (gps->tpid[0]); i++) { + if (gps->tpid[i] == tpid) { + gps->tpid[i] = 0; + break; + } + } + Xcritical(1); +} + +/* + * Determine if the lwp for this process should be traced. + */ +int +lwptrace(pid_t spid, lwpid_t lwpid) +{ + int i; + pid_t tpid; + const char *lwps; + + if (gps == NULL) + return (0); + + tpid = getpid(); + + Ecritical(1); + for (i = 0; i < sizeof (gps->tpid) / sizeof (gps->tpid[0]); i++) { + if (gps->tpid[i] == tpid && + gps->spid[i] == spid) + break; + } + lwps = gps->lwps[i]; + Xcritical(1); + + return (proc_lwp_in_set(lwps, lwpid)); +} + +/* + * Check for open of a /proc/nnnnn file. + * Return 0 if this is not an open of a /proc file. + * Return 1 if the process opened itself. + * Return 2 if the process failed to open another process + * in truss's set of controlled processes. + * Return 3 if the process successfully opened another process + * in truss's set of controlled processes. + * We notify and wait for the other controlling truss process + * to terminate before returning in cases 2 and 3. + */ +/* ARGSUSED */ +int +checkproc(private_t *pri) +{ + char *path = pri->sys_path; + const pstatus_t *Psp = Pstatus(Proc); + struct ps_lwphandle *Lwp = pri->Lwp; + const lwpstatus_t *Lsp = pri->lwpstat; + int what = Lsp->pr_what; /* one of the SYS_open* syscalls */ + int err = Lsp->pr_errno; + int pid; + int i; + const char *dirname; + char *next; + char *sp1; + char *sp2; + prgreg_t pc; + + /* + * A bit heuristic ... + * Test for the cases: + * 1234 + * 1234/as + * 1234/ctl + * 1234/lwp/24/lwpctl + * .../1234 + * .../1234/as + * .../1234/ctl + * .../1234/lwp/24/lwpctl + * Insert a '\0', if necessary, so the path becomes ".../1234". + * + * Along the way, watch out for /proc/self and /proc/1234/lwp/agent + */ + if ((sp1 = strrchr(path, '/')) == NULL) /* last component */ + /* EMPTY */; + else if (isdigit(*(sp1+1))) { + sp1 += strlen(sp1); + while (--sp1 > path && isdigit(*sp1)) + ; + if (*sp1 != '/') + return (0); + } else if (strcmp(sp1+1, "as") == 0 || + strcmp(sp1+1, "ctl") == 0) { + *sp1 = '\0'; + } else if (strcmp(sp1+1, "lwpctl") == 0) { + /* + * .../1234/lwp/24/lwpctl + * ............ ^-- sp1 + */ + if (sp1-6 >= path && strncmp(sp1-6, "/agent", 6) == 0) + sp1 -= 6; + else { + while (--sp1 > path && isdigit(*sp1)) + ; + } + if (*sp1 != '/' || + (sp1 -= 4) <= path || + strncmp(sp1, "/lwp", 4) != 0) + return (0); + *sp1 = '\0'; + } else if (strcmp(sp1+1, "self") != 0) { + return (0); + } + + if ((sp2 = strrchr(path, '/')) == NULL) + dirname = path; + else + dirname = sp2 + 1; + + if (strcmp(dirname, "self") == 0) { + pid = Psp->pr_pid; + } else if ((pid = strtol(dirname, &next, 10)) < 0 || + *next != '\0') { /* dirname not a number */ + if (sp1 != NULL) + *sp1 = '/'; + return (0); + } + if (sp2 == NULL) + dirname = "."; + else { + *sp2 = '\0'; + dirname = path; + } + + if (!Pisprocdir(Proc, dirname) || /* file not in a /proc directory */ + pid == getpid() || /* process opened truss's /proc file */ + pid == 0) { /* process opened process 0 */ + if (sp1 != NULL) + *sp1 = '/'; + if (sp2 != NULL) + *sp2 = '/'; + return (0); + } + if (sp1 != NULL) + *sp1 = '/'; + if (sp2 != NULL) + *sp2 = '/'; + + /* + * Process did open a /proc file --- + */ + if (pid == Psp->pr_pid) { /* process opened its own /proc file */ + /* + * In SunOS 5.6 and beyond, self-opens always succeed. + */ + return (1); + } + + /* + * Search for a matching pid in our set of controlled processes. + */ + for (i = 0; i < sizeof (gps->tpid)/sizeof (gps->tpid[0]); i++) { + if (gps->spid[i] == pid) { + pid = gps->tpid[i]; + break; + } + } + if (i >= sizeof (gps->tpid) / sizeof (gps->tpid[0])) { + /* + * The process opened a /proc file, but not one we care about. + */ + return (0); + } + + /* + * Notify and wait for the controlling process to terminate. + */ + while (pid && gps->tpid[i] == pid) { + if (kill(pid, SIGUSR1) == -1) + break; + (void) usleep(1000000); + } + Ecritical(1); + if (gps->tpid[i] == 0) + gps->spid[i] = 0; + Xcritical(1); + + if (err) { /* prepare to reissue the failed open() system call */ +#if defined(__sparc) + (void) Lgetareg(Lwp, R_PC, &pc); + if (pri->sys_indirect) { + (void) Lputareg(Lwp, R_G1, (prgreg_t)SYS_syscall); + (void) Lputareg(Lwp, R_O0, (prgreg_t)what); + for (i = 0; i < 5; i++) + (void) Lputareg(Lwp, R_O1+i, pri->sys_args[i]); + } else { + (void) Lputareg(Lwp, R_G1, (prgreg_t)what); + for (i = 0; i < 6; i++) + (void) Lputareg(Lwp, R_O0+i, pri->sys_args[i]); + } + (void) Lputareg(Lwp, R_nPC, pc); +#elif defined(__amd64) + (void) Lgetareg(Lwp, R_PC, &pc); + (void) Lputareg(Lwp, REG_RAX, (prgreg_t)what); +#elif defined(__i386) + (void) Lgetareg(Lwp, R_PC, &pc); + (void) Lputareg(Lwp, EAX, (prgreg_t)what); +#else +#error "unrecognized architecture" +#endif + (void) Pissyscall_prev(Proc, pc, (uintptr_t *)&pc); + (void) Lputareg(Lwp, R_PC, pc); + return (2); + } + + return (3); +} |