Experimental support for -D option.
Unlike normal case, with -D *grandparent* process exec's,
becoming a traced process. Child exits (this prevents traced process
from having children it doesn't expect to have), and grandchild
attaches to grandparent similarly to strace -p PID.
This allows for more transparent interaction in cases
when process and its parent are communicating via signals,
wait() etc. Without -D, strace process gets lodged in between,
disrupting parent<->child link.
* strace.c: Add global flag variable daemonized_tracer for -D option.
(startup_attach): If -D, fork and block parent in pause().
In this case we are already a child, we in fact created a grandchild.
After attaching to grandparent, grandchild SIGKILLs parent.
(startup_child): If -D, parent blocks in wait(), then
execs the program to strace. Normally (w/o -D), it is child
who execs the program.
(main): Detect -D option, call startup_attach() if it is given.
diff --git a/ChangeLog b/ChangeLog
index e8ed5f4..1bc0dea 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2008-12-30 Denys Vlasenko <dvlasenk@redhat.com>
+
+ Experimental support for -D option.
+ Unlike normal case, with -D *grandparent* process exec's,
+ becoming a traced process. Child exits (this prevents traced process
+ from having children it doesn't expect to have), and grandchild
+ attaches to grandparent similarly to strace -p PID.
+ This allows for more transparent interaction in cases
+ when process and its parent are communicating via signals,
+ wait() etc. Without -D, strace process gets lodged in between,
+ disrupting parent<->child link.
+ * strace.c: Add global flag variable daemonized_tracer for -D option.
+ (startup_attach): If -D, fork and block parent in pause().
+ In this case we are already a child, we in fact created a grandchild.
+ After attaching to grandparent, grandchild SIGKILLs parent.
+ (startup_child): If -D, parent blocks in wait(), then
+ execs the program to strace. Normally (w/o -D), it is child
+ who execs the program.
+ (main): Detect -D option, call startup_attach() if it is given.
+
2008-12-30 Kirill A. Shutemov <kirill@shutemov.name>
Fix some warnings on ARM build.
diff --git a/strace.c b/strace.c
index 9bda553..9a0b2e5 100644
--- a/strace.c
+++ b/strace.c
@@ -83,6 +83,19 @@
int debug = 0, followfork = 0;
int dtime = 0, cflag = 0, xflag = 0, qflag = 0;
static int iflag = 0, interactive = 0, pflag_seen = 0, rflag = 0, tflag = 0;
+/*
+ * daemonized_tracer supports -D option.
+ * With this option, strace forks twice.
+ * Unlike normal case, with -D *grandparent* process exec's,
+ * becoming a traced process. Child exits (this prevents traced process
+ * from having children it doesn't expect to have), and grandchild
+ * attaches to grandparent similarly to strace -p PID.
+ * This allows for more transparent interaction in cases
+ * when process and its parent are communicating via signals,
+ * wait() etc. Without -D, strace process gets lodged in between,
+ * disrupting parent<->child link.
+ */
+static bool daemonized_tracer = 0;
/* Sometimes we want to print only succeeding syscalls. */
int not_failing_only = 0;
@@ -159,7 +172,7 @@
usage: strace [-dffhiqrtttTvVxx] [-a column] [-e expr] ... [-o file]\n\
[-p pid] ... [-s strsize] [-u username] [-E var=val] ...\n\
[command [arg ...]]\n\
- or: strace -c [-e expr] ... [-O overhead] [-S sortby] [-E var=val] ...\n\
+ or: strace -c -D [-e expr] ... [-O overhead] [-S sortby] [-E var=val] ...\n\
[command [arg ...]]\n\
-c -- count time, calls, and errors for each syscall and report summary\n\
-f -- follow forks, -ff -- with output into separate files\n\
@@ -176,6 +189,7 @@
-o file -- send trace output to FILE instead of stderr\n\
-O overhead -- set overhead for tracing syscalls to OVERHEAD usecs\n\
-p pid -- trace process with process id PID, may be repeated\n\
+-D -- run tracer process as a detached grandchild, not as parent\n\
-s strsize -- limit length of print strings to STRSIZE chars (default %d)\n\
-S sortby -- sort syscall counts by: time, calls, name, nothing (default %s)\n\
-u username -- run command as username handling setuid and/or setgid\n\
@@ -362,6 +376,23 @@
if (interactive)
sigprocmask(SIG_BLOCK, &blocked_set, NULL);
+ if (daemonized_tracer) {
+ pid_t pid = fork();
+ if (pid < 0) {
+ _exit(1);
+ }
+ if (pid) { /* parent */
+ /*
+ * Wait for child to attach to straced process
+ * (our parent). Child SIGKILLs us after it attached.
+ * Parent's wait() is unblocked by our death,
+ * it proceeds to exec the straced program.
+ */
+ pause();
+ _exit(0); /* paranoia */
+ }
+ }
+
for (tcbi = 0; tcbi < tcbtabsize; tcbi++) {
tcp = tcbtab[tcbi];
if (!(tcp->flags & TCB_INUSE) || !(tcp->flags & TCB_ATTACHED))
@@ -383,7 +414,7 @@
}
#else /* !USE_PROCFS */
# ifdef LINUX
- if (followfork) {
+ if (followfork && !daemonized_tracer) {
char procdir[MAXPATHLEN];
DIR *dir;
@@ -452,6 +483,20 @@
continue;
}
/* INTERRUPTED is going to be checked at the top of TRACE. */
+
+ if (daemonized_tracer) {
+ /*
+ * It is our grandparent we trace, not a -p PID.
+ * Don't want to just detach on exit, so...
+ */
+ tcp->flags &= ~TCB_ATTACHED;
+ /*
+ * Make parent go away.
+ * Also makes grandparent's wait() unblock.
+ */
+ kill(getppid(), SIGKILL);
+ }
+
#endif /* !USE_PROCFS */
if (!qflag)
fprintf(stderr,
@@ -530,13 +575,15 @@
exit(1);
}
strace_child = pid = fork();
- switch (pid) {
- case -1:
+ if (pid < 0) {
perror("strace: fork");
cleanup();
exit(1);
- break;
- case 0: {
+ }
+ if ((pid != 0 && daemonized_tracer) /* parent: to become a traced process */
+ || (pid == 0 && !daemonized_tracer) /* child: to become a traced process */
+ ) {
+ pid = getpid();
#ifdef USE_PROCFS
if (outf != stderr) close (fileno (outf));
#ifdef MIPS
@@ -549,18 +596,20 @@
#ifndef FREEBSD
pause();
#else /* FREEBSD */
- kill(getpid(), SIGSTOP); /* stop HERE */
+ kill(pid, SIGSTOP); /* stop HERE */
#endif /* FREEBSD */
#else /* !USE_PROCFS */
if (outf!=stderr)
close(fileno (outf));
- if (ptrace(PTRACE_TRACEME, 0, (char *) 1, 0) < 0) {
- perror("strace: ptrace(PTRACE_TRACEME, ...)");
- exit(1);
+ if (!daemonized_tracer) {
+ if (ptrace(PTRACE_TRACEME, 0, (char *) 1, 0) < 0) {
+ perror("strace: ptrace(PTRACE_TRACEME, ...)");
+ exit(1);
+ }
+ if (debug)
+ kill(pid, SIGSTOP);
}
- if (debug)
- kill(getpid(), SIGSTOP);
if (username != NULL || geteuid() == 0) {
uid_t run_euid = run_uid;
@@ -593,33 +642,54 @@
else
setreuid(run_uid, run_uid);
- /*
- * Induce an immediate stop so that the parent
- * will resume us with PTRACE_SYSCALL and display
- * this execve call normally.
- */
- kill(getpid(), SIGSTOP);
+ if (!daemonized_tracer) {
+ /*
+ * Induce an immediate stop so that the parent
+ * will resume us with PTRACE_SYSCALL and display
+ * this execve call normally.
+ */
+ kill(getpid(), SIGSTOP);
+ } else {
+ struct sigaction sv_sigchld;
+ sigaction(SIGCHLD, NULL, &sv_sigchld);
+ /*
+ * Make sure it is not SIG_IGN, otherwise wait
+ * will not block.
+ */
+ signal(SIGCHLD, SIG_DFL);
+ /*
+ * Wait for grandchild to attach to us.
+ * It kills child after that, and wait() unblocks.
+ */
+ alarm(3);
+ wait(NULL);
+ alarm(0);
+ sigaction(SIGCHLD, &sv_sigchld, NULL);
+ }
#endif /* !USE_PROCFS */
execv(pathname, argv);
perror("strace: exec");
_exit(1);
- break;
}
- default:
- if ((tcp = alloctcb(pid)) == NULL) {
- cleanup();
- exit(1);
- }
+
+ /* We are the tracer. */
+ tcp = alloctcb(daemonized_tracer ? getppid() : pid);
+ if (tcp == NULL) {
+ cleanup();
+ exit(1);
+ }
+ if (daemonized_tracer) {
+ /* We want subsequent startup_attach() to attach to it. */
+ tcp->flags |= TCB_ATTACHED;
+ }
#ifdef USE_PROCFS
- if (proc_open(tcp, 0) < 0) {
- fprintf(stderr, "trouble opening proc file\n");
- cleanup();
- exit(1);
- }
-#endif /* USE_PROCFS */
- break;
+ if (proc_open(tcp, 0) < 0) {
+ fprintf(stderr, "trouble opening proc file\n");
+ cleanup();
+ exit(1);
}
+#endif /* USE_PROCFS */
}
int
@@ -658,7 +728,11 @@
qualify("verbose=all");
qualify("signal=all");
while ((c = getopt(argc, argv,
- "+cdfFhiqrtTvVxza:e:o:O:p:s:S:u:E:")) != EOF) {
+ "+cdfFhiqrtTvVxz"
+#ifndef USE_PROCFS
+ "D"
+#endif
+ "a:e:o:O:p:s:S:u:E:")) != EOF) {
switch (c) {
case 'c':
cflag++;
@@ -667,6 +741,12 @@
case 'd':
debug++;
break;
+#ifndef USE_PROCFS
+ /* Experimental, not documented in manpage yet. */
+ case 'D':
+ daemonized_tracer = 1;
+ break;
+#endif
case 'F':
optF = 1;
break;
@@ -880,7 +960,7 @@
sigaction(SIGCHLD, &sa, NULL);
#endif /* USE_PROCFS */
- if (pflag_seen)
+ if (pflag_seen || daemonized_tracer)
startup_attach();
if (trace() < 0)