strace/pathtrace.c
Denys Vlasenko b63256e69b Whitespace cleanups. no code changes.
* bjm.c: Fix tabulation (such as extra spaces before tabs),
convert punctuation where it deviates from prevalent form
elsewhere in strace code, convert sizeof and offsetof where
it deviates from from prevalent form, remove space between
function/macro/array names and (parameters) or [index],
add space between "if" and (condition), correct non-standard
or wrong indentaion.
* defs.h: Likewise
* desc.c: Likewise
* file.c: Likewise
* ipc.c: Likewise
* linux/arm/syscallent.h: Likewise
* linux/avr32/syscallent.h: Likewise
* linux/hppa/syscallent.h: Likewise
* linux/i386/syscallent.h: Likewise
* linux/ioctlsort.c: Likewise
* linux/m68k/syscallent.h: Likewise
* linux/microblaze/syscallent.h: Likewise
* linux/powerpc/syscallent.h: Likewise
* linux/s390/syscallent.h: Likewise
* linux/s390x/syscallent.h: Likewise
* linux/sh/syscallent.h: Likewise
* linux/sh64/syscallent.h: Likewise
* linux/tile/syscallent.h: Likewise
* linux/x86_64/syscallent.h: Likewise
* mem.c: Likewise
* net.c: Likewise
* pathtrace.c: Likewise
* process.c: Likewise
* signal.c: Likewise
* sock.c: Likewise
* strace.c: Likewise
* stream.c: Likewise
* sunos4/syscall.h: Likewise
* sunos4/syscallent.h: Likewise
* svr4/syscall.h: Likewise
* svr4/syscallent.h: Likewise
* syscall.c: Likewise
* system.c: Likewise
* test/childthread.c: Likewise
* test/leaderkill.c: Likewise
* test/skodic.c: Likewise
* time.c: Likewise
* util.c: Likewise

Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
2011-06-07 12:13:24 +02:00

391 lines
8.5 KiB
C

/*
* Copyright (c) 2011, Comtrol Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include "defs.h"
#include <ctype.h>
#include <limits.h>
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#ifdef HAVE_SYS_POLL_H
#include <sys/poll.h>
#endif
#include "syscall.h"
#define NumElem(a) (int)(sizeof(a) / sizeof((a)[0]))
#define MAXSELECTED 256 /* max number of "selected" paths */
static const char *selected[MAXSELECTED]; /* paths selected for tracing */
/*
* Return true if specified path matches one that we're tracing.
*/
static int
pathmatch(const char *path)
{
int i;
for (i = 0; i < NumElem(selected); ++i)
{
if (selected[i] == NULL)
return 0;
if (!strcmp(path, selected[i]))
return 1;
}
return 0;
}
/*
* Return true if specified path (in user-space) matches.
*/
static int
upathmatch(struct tcb *tcp, unsigned long upath)
{
char path[PATH_MAX + 1];
return umovestr(tcp, upath, sizeof path, path) == 0 &&
pathmatch(path);
}
/*
* Return true if specified fd maps to a path we're tracing.
*/
static int
fdmatch(struct tcb *tcp, int fd)
{
const char *path = getfdpath(tcp, fd);
return path && pathmatch(path);
}
/*
* Add a path to the set we're tracing.
* Secifying NULL will delete all paths.
*/
static int
storepath(const char *path)
{
int i;
if (path == NULL)
{
for (i = 0; i < NumElem(selected); ++i)
if (selected[i])
{
free((char *) selected[i]);
selected[i] = NULL;
}
return 0;
}
for (i = 0; i < NumElem(selected); ++i)
if (!selected[i])
{
selected[i] = path;
return 0;
}
fprintf(stderr, "Max trace paths exceeded, only using first %d\n",
NumElem(selected));
return -1;
}
/*
* Get path associated with fd.
*/
const char *getfdpath(struct tcb *tcp, int fd)
{
#ifdef LINUX
static char path[PATH_MAX+1];
char linkpath[64];
ssize_t n;
if (fd < 0)
return NULL;
snprintf(linkpath, sizeof linkpath, "/proc/%d/fd/%d", tcp->pid, fd);
n = readlink(linkpath, path, (sizeof path) - 1);
if (n <= 0)
return NULL;
path[n] = '\0';
return path;
#else
return NULL;
#endif
}
/*
* Add a path to the set we're tracing. Also add the canonicalized
* version of the path. Secifying NULL will delete all paths.
*/
int
pathtrace_select(const char *path)
{
char *rpath;
if (path == NULL)
return storepath(path);
if (storepath(path))
return -1;
rpath = realpath(path, NULL);
if (rpath == NULL)
return 0;
/* if realpath and specified path are same, we're done */
if (!strcmp(path, rpath))
{
free(rpath);
return 0;
}
fprintf(stderr, "Requested path '%s' resolved into '%s'\n",
path, rpath);
return storepath(rpath);
}
/*
* Return true if syscall accesses a selected path
* (or if no paths have been specified for tracing).
*/
int
pathtrace_match(struct tcb *tcp)
{
const struct sysent *s;
if (selected[0] == NULL)
return 1;
s = &sysent[tcp->scno];
if (!(s->sys_flags & (TRACE_FILE | TRACE_DESC)))
return 0;
/*
* Check for special cases where we need to do something
* other than test arg[0].
*/
#ifdef LINUX
if (s->sys_func == sys_dup2 ||
s->sys_func == sys_dup3 ||
s->sys_func == sys_sendfile ||
s->sys_func == sys_sendfile64 ||
!strcmp(s->sys_name, "tee"))
{
/* fd, fd */
return fdmatch(tcp, tcp->u_arg[0]) ||
fdmatch(tcp, tcp->u_arg[1]);
}
if (s->sys_func == sys_inotify_add_watch ||
s->sys_func == sys_faccessat ||
s->sys_func == sys_fchmodat ||
s->sys_func == sys_futimesat ||
s->sys_func == sys_mkdirat ||
s->sys_func == sys_unlinkat ||
s->sys_func == sys_newfstatat ||
s->sys_func == sys_mknodat ||
s->sys_func == sys_openat ||
s->sys_func == sys_readlinkat ||
s->sys_func == sys_utimensat ||
s->sys_func == sys_fchownat ||
s->sys_func == sys_pipe2)
{
/* fd, path */
return fdmatch(tcp, tcp->u_arg[0]) ||
upathmatch(tcp, tcp->u_arg[1]);
}
if (s->sys_func == sys_link ||
s->sys_func == sys_pivotroot ||
s->sys_func == sys_rename ||
s->sys_func == sys_symlink ||
s->sys_func == sys_mount)
{
/* path, path */
return upathmatch(tcp, tcp->u_arg[0]) ||
upathmatch(tcp, tcp->u_arg[1]);
}
if (s->sys_func == sys_renameat ||
s->sys_func == sys_linkat)
{
/* fd, path, fd, path */
return fdmatch(tcp, tcp->u_arg[0]) ||
fdmatch(tcp, tcp->u_arg[2]) ||
upathmatch(tcp, tcp->u_arg[1]) ||
upathmatch(tcp, tcp->u_arg[3]);
}
if (s->sys_func == sys_old_mmap || s->sys_func == sys_mmap)
{
/* x, x, x, x, fd */
return fdmatch(tcp, tcp->u_arg[4]);
}
if (s->sys_func == sys_symlinkat)
{
/* path, fd, path */
return fdmatch(tcp, tcp->u_arg[1]) ||
upathmatch(tcp, tcp->u_arg[0]) ||
upathmatch(tcp, tcp->u_arg[2]);
}
if (!strcmp(s->sys_name, "splice"))
{
/* fd, x, fd, x, x */
return fdmatch(tcp, tcp->u_arg[0]) ||
fdmatch(tcp, tcp->u_arg[2]);
}
if (s->sys_func == sys_epoll_ctl)
{
/* x, x, fd, x */
return fdmatch(tcp, tcp->u_arg[2]);
}
if (s->sys_func == sys_select ||
s->sys_func == sys_oldselect ||
s->sys_func == sys_pselect6)
{
int i, j, nfds;
long *args, oldargs[5];
unsigned fdsize;
fd_set *fds;
if (s->sys_func == sys_oldselect)
{
if (umoven(tcp, tcp->u_arg[0], sizeof oldargs,
(char*) oldargs) < 0)
{
fprintf(stderr, "umoven() failed\n");
return 0;
}
args = oldargs;
} else
args = tcp->u_arg;
nfds = args[0];
fdsize = ((((nfds + 7) / 8) + sizeof(long) - 1)
& -sizeof(long));
fds = malloc(fdsize);
if (fds == NULL)
{
fprintf(stderr, "out of memory\n");
return 0;
}
for (i = 1; i <= 3; ++i)
{
if (args[i] == 0)
continue;
if (umoven(tcp, args[i], fdsize, (char *) fds) < 0)
{
fprintf(stderr, "umoven() failed\n");
continue;
}
for (j = 0; j < nfds; ++j)
if (FD_ISSET(j, fds) && fdmatch(tcp, j))
{
free(fds);
return 1;
}
}
free(fds);
return 0;
}
if (s->sys_func == sys_poll ||
s->sys_func == sys_ppoll)
{
struct pollfd fds;
unsigned nfds;
unsigned long start, cur, end;
start = tcp->u_arg[0];
nfds = tcp->u_arg[1];
end = start + sizeof(fds) * nfds;
if (nfds == 0 || end < start)
return 0;
for (cur = start; cur < end; cur += sizeof(fds))
if ((umoven(tcp, cur, sizeof fds, (char *) &fds) == 0)
&& fdmatch(tcp, fds.fd))
return 1;
return 0;
}
if (s->sys_func == printargs ||
s->sys_func == sys_pipe ||
s->sys_func == sys_pipe2 ||
s->sys_func == sys_eventfd2 ||
s->sys_func == sys_eventfd ||
s->sys_func == sys_inotify_init1 ||
s->sys_func == sys_timerfd_create ||
s->sys_func == sys_timerfd_settime ||
s->sys_func == sys_timerfd_gettime ||
s->sys_func == sys_epoll_create ||
!strcmp(s->sys_name, "fanotify_init"))
{
/*
* These have TRACE_FILE or TRACE_DESCRIPTOR set, but they
* don't have any file descriptor or path args to test.
*/
return 0;
}
#else
#warning "path tracing only using arg[0]"
#endif
/*
* Our fallback position for calls that haven't already
* been handled is to just check arg[0].
*/
if (s->sys_flags & TRACE_FILE)
return upathmatch(tcp, tcp->u_arg[0]);
if (s->sys_flags & TRACE_DESC)
return fdmatch(tcp, tcp->u_arg[0]);
return 0;
}