aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common.h18
-rw-r--r--print.c2
-rw-r--r--process.c20
-rw-r--r--sctrace.c314
-rw-r--r--tests/Makefile2
-rw-r--r--tests/abort-nodump.c9
-rw-r--r--tests/abort.c2
-rw-r--r--tests/signal-interrupt.c26
-rw-r--r--util.c2
9 files changed, 258 insertions, 137 deletions
diff --git a/common.h b/common.h
index 3f899fe..99497f9 100644
--- a/common.h
+++ b/common.h
@@ -1,6 +1,8 @@
/* See LICENSE file for copyright and license details. */
#include <asm/unistd.h>
+#include <sys/prctl.h>
#include <sys/uio.h>
+#include <asm/unistd.h>
#include <sys/wait.h>
#include <ctype.h>
#include <errno.h>
@@ -65,15 +67,9 @@ enum type {
};
enum state {
- Normal,
- Syscall,
- CloneChild,
- ForkChild,
- VforkChild,
- CloneParent,
- ForkParent,
- VforkParent,
- Exec
+ UserSpace,
+ KernelSpace,
+ Zombie
};
struct output {
@@ -89,7 +85,7 @@ struct process {
struct process *next;
struct process *prev;
enum state state;
- int silent_until_execed; /* 2 until exec, 1 until "= 0", 0 afterwards */
+ int ignore_until_execed; /* 2 until exec, 1 until "= 0", 0 afterwards */
/* Syscall data */
unsigned long long int scall;
@@ -131,7 +127,7 @@ void print_systemcall_exit(struct process *proc);
/* process.c */
void init_process_list(void);
struct process *find_process(pid_t pid);
-struct process *add_process(pid_t pid, unsigned long int trace_options);
+struct process *add_process(pid_t pid, pid_t leader, unsigned long int trace_options);
void remove_process(struct process *proc);
/* util.c */
diff --git a/print.c b/print.c
index 77f8f48..01d19c8 100644
--- a/print.c
+++ b/print.c
@@ -864,7 +864,7 @@ print_nonconst_sockaddr(struct process *proc, size_t arg_index)
socklen_t saved_len;
void *mem;
const char *err;
- if (proc->state == Syscall) {
+ if (proc->state == KernelSpace) {
/* on return */
saved_len = (socklen_t)proc->save[arg_index + 1];
len = len < saved_len ? len : saved_len;
diff --git a/process.c b/process.c
index 7e7a54f..5d88a04 100644
--- a/process.c
+++ b/process.c
@@ -26,7 +26,7 @@ find_process(pid_t pid)
struct process *
-add_process(pid_t pid, unsigned long int trace_options)
+add_process(pid_t pid, pid_t leader, unsigned long int trace_options)
{
struct process *proc;
int status, sig;
@@ -35,12 +35,16 @@ add_process(pid_t pid, unsigned long int trace_options)
if (!proc)
eprintf("calloc: %s\n");
proc->pid = pid;
+ proc->thread_leader = leader;
proc->next = &tail;
proc->prev = tail.prev;
proc->prev->next = proc;
tail.prev = proc;
- while (waitpid(pid, &status, WSTOPPED) != pid) {
+ if (!leader)
+ leader = pid;
+
+ while (waitpid(pid, &status, WUNTRACED) != pid) {
if (errno == EINTR)
continue;
eprintf_and_kill(pid, "waitpid %ju <buffer> WSTOPPED:", (uintmax_t)pid);
@@ -48,15 +52,15 @@ add_process(pid_t pid, unsigned long int trace_options)
sig = WIFSTOPPED(status) ? WSTOPSIG(status) : 0;
if (sig == SIGSTOP) {
- if (ptrace(PTRACE_SEIZE, pid, 0, trace_options))
- eprintf_and_kill(pid, "ptrace PTRACE_SEIZE %ju 0 ...:", (uintmax_t)pid);
+ if (ptrace(PTRACE_SEIZE, pid, NULL, trace_options))
+ eprintf_and_kill(pid, "ptrace PTRACE_SEIZE %ju NULL ...:", (uintmax_t)pid);
if (ptrace(PTRACE_INTERRUPT, pid, NULL, 0))
eprintf_and_kill(pid, "ptrace PTRACE_INTERRUPT %ju NULL 0:", (uintmax_t)pid);
- if (kill(pid, SIGCONT) < 0)
- eprintf_and_kill(pid, "kill &ju SIGCONT:", (uintmax_t)pid);
+ if (tgkill(leader, pid, SIGCONT) < 0)
+ eprintf_and_kill(pid, "tgkill %ju %ju SIGCONT:", (uintmax_t)leader, (uintmax_t)pid);
} else if (sig == SIGTRAP && status & PTRACE_EVENT_STOP << 16) {
- if (ptrace(PTRACE_SYSCALL, pid, NULL, 0))
- eprintf_and_kill(pid, "ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)pid);
+ if (ptrace(PTRACE_SETOPTIONS, pid, NULL, trace_options))
+ eprintf_and_kill(pid, "ptrace PTRACE_SETOPTIONS %ju NULL ...:", (uintmax_t)pid);
} else {
eprintf_and_kill(pid, "unexpected return of waitpid %ju <buffer> WSTOPPED: %#x\n", (uintmax_t)pid, status);
}
diff --git a/sctrace.c b/sctrace.c
index 976624d..fe1ff5e 100644
--- a/sctrace.c
+++ b/sctrace.c
@@ -3,7 +3,7 @@
char *argv0;
-static unsigned long int trace_options = PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC;
+static unsigned long int trace_options = PTRACE_O_EXITKILL | PTRACE_O_TRACESYSGOOD | PTRACE_O_TRACEEXEC | PTRACE_O_TRACEEXIT;
_Noreturn static void
@@ -16,129 +16,208 @@ usage(void)
static void
-handle_syscall(struct process *proc)
+fetch_systemcall(struct process *proc, struct user_regs_struct *regs)
{
- struct user_regs_struct regs;
-
- switch ((int)proc->state) {
- default:
- /* Get system call arguments */
- if (ptrace(PTRACE_GETREGS, proc->pid, REGARGS(NULL, &regs)))
- eprintf("ptrace PTRACE_GETREGS %ju NULL <buffer>:", (uintmax_t)proc->pid);
- proc->scall = regs.SYSCALL_NUM_REG;
+ if (ptrace(PTRACE_GETREGS, proc->pid, REGARGS(NULL, regs)))
+ eprintf("ptrace PTRACE_GETREGS %ju NULL <buffer>:", (uintmax_t)proc->pid);
+ proc->scall = regs->SYSCALL_NUM_REG;
#ifdef CHECK_ARCHITECTURE
- CHECK_ARCHITECTURE(proc, &regs);
- proc->scall ^= proc->scall_xor;
+ CHECK_ARCHITECTURE(proc, regs);
+ proc->scall ^= proc->scall_xor;
#endif
- GET_SYSCALL_ARGUMENTS(proc, &regs);
- memset(proc->save, 0, sizeof(proc->save));
+ GET_SYSCALL_ARGUMENTS(proc, regs);
+ memset(proc->save, 0, sizeof(proc->save));
+}
- /* Print system call */
- print_systemcall(proc);
- /* Run system call */
- if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0))
- eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid);
+static void
+fetch_systemcall_result(struct process *proc, struct user_regs_struct *regs)
+{
+ if (ptrace(PTRACE_GETREGS, proc->pid, REGARGS(NULL, regs)))
+ eprintf("ptrace PTRACE_GETREGS %ju NULL <buffer>:", (uintmax_t)proc->pid);
+ proc->ret = regs->SYSCALL_RET_REG;
+}
- proc->state = Syscall;
- break;
- case Syscall:
- case CloneParent:
- case ForkParent:
- /* Get system call result */
- if (ptrace(PTRACE_GETREGS, proc->pid, REGARGS(NULL, &regs)))
- eprintf("ptrace PTRACE_GETREGS %ju NULL <buffer>:", (uintmax_t)proc->pid);
+static void
+enter_systemcall(struct process *proc)
+{
+ if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0))
+ eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid);
+}
- /* Get or set return */
- if (proc->state == Syscall) {
- proc->ret = regs.SYSCALL_RET_REG;
- } else {
- regs.SYSCALL_RET_REG = proc->ret;
- if (ptrace(PTRACE_SETREGS, proc->pid, REGARGS(NULL, &regs)))
- eprintf("ptrace PTRACE_SETREGS %ju NULL <buffer>:", (uintmax_t)proc->pid);
- if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0))
- eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid);
- }
- /* Print system call result */
- print_systemcall_exit(proc);
+static void
+leave_systemcall(struct process *proc)
+{
+ if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0))
+ eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid);
+}
- proc->silent_until_execed -= (proc->silent_until_execed == 1);
- /* Make process continue and stop at next syscall */
- if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0))
- eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid);
+static void
+restart_systemcall(struct process *proc, int sig)
+{
+ if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, sig))
+ eprintf("ptrace PTRACE_SYSCALL %ju NULL %i:", (uintmax_t)proc->pid, sig);
+}
- proc->state = Normal;
- break;
- case Exec:
- proc->silent_until_execed -= (proc->silent_until_execed == 2);
- FALL_THROUGH
- /* fall through */
- case VforkParent:
- if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0))
- eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid);
- proc->state = Syscall;
+static void
+vfork_start(struct process *proc)
+{
+ tprintf(proc, "\nProcess stopped by vfork until child exits or exec(2)s\n");
+}
+
+
+static void
+vfork_done(struct process *proc, const char *reason)
+{
+ tprintf(proc, "\nProcess continues due to %s of vfork child\n", reason);
+}
+
+
+static void
+process_exiting(struct process *proc, int status)
+{
+ (void) proc;
+ (void) status;
+}
+
+
+static void
+process_exited(struct process *proc, int status)
+{
+ if (WIFEXITED(status)) {
+ tprintf(proc, "\nProcess exited%s with value %i%s\n",
+ proc->state == Zombie ? "" : ", without knell",
+ WEXITSTATUS(status), WCOREDUMP(status) ? ", core dumped" : "");
+ } else {
+ tprintf(proc, "\nProcess terminated%s by signal %i (%s: %s)%s\n",
+ proc->state == Zombie ? "" : ", without knell",
+ WTERMSIG(status), get_signum_name(WTERMSIG(status)),
+ strsignal(WTERMSIG(status)), WCOREDUMP(status) ? ", core dumped" : "");
+ }
+}
+
+
+static void
+process_signalled(struct process *proc, int sig, int stopped)
+{
+ tprintf(proc, "\nProcess %s signal %i (%s: %s)\n",
+ stopped ? "stopped by" : "received",
+ sig, get_signum_name(sig), strsignal(sig));
+}
+
+
+static void
+process_continued(struct process *proc, int status)
+{
+ tprintf(proc, "\n%s continued, presumably by signal %i (SIGCONT: %s)\n",
+ proc->state == Zombie ? "Zombie process" : "Process", SIGCONT, strsignal(SIGCONT));
+ (void) status;
+}
+
+
+static void
+restart_process(struct process *proc, int cmd, int sig)
+{
+ if (ptrace(cmd, proc->pid, NULL, sig)) {
+ eprintf("ptrace %s %ju NULL %i:",
+ cmd == PTRACE_CONT ? "PTRACE_CONT" :
+ cmd == PTRACE_LISTEN ? "PTRACE_LISTN" :
+ cmd == PTRACE_SYSEMU ? "PTRACE_SYSEMU" :
+ cmd == PTRACE_SYSCALL ? "PTRACE_SYSCALL" : "???",
+ (uintmax_t)proc->pid, sig);
+ }
+}
+
+
+static void
+process_created(struct process *proc, struct process *parent)
+{
+ tprintf(proc, "\nTracing new process with parent %ju\n", (uintmax_t)parent->pid);
+ tprintf(proc, "= 0\n");
+}
+
+
+static void
+zombie_stopped(struct process *proc, int status)
+{
+ tprintf(proc, "\nReceived unexpected event on zombie process\n");
+ (void) status;
+}
+
+
+static void
+handle_syscall(struct process *proc)
+{
+ struct user_regs_struct regs;
+
+ switch ((int)proc->state) {
+ case UserSpace:
+ fetch_systemcall(proc, &regs);
+ print_systemcall(proc);
+ enter_systemcall(proc);
+ proc->state = KernelSpace;
break;
- case CloneChild:
- case ForkChild:
- case VforkChild:
- tprintf(proc, "= 0\n");
- proc->state = Normal;
+ case KernelSpace:
+ fetch_systemcall_result(proc, &regs);
+ if (!proc->ignore_until_execed)
+ print_systemcall_exit(proc);
+ else
+ proc->ignore_until_execed -= (proc->ignore_until_execed == 1);
+ leave_systemcall(proc);
+ proc->state = UserSpace;
break;
+
+ default:
+ abort();
}
}
+static unsigned long int
+get_event_msg(struct process *proc)
+{
+ unsigned long int event;
+ if (ptrace(PTRACE_GETEVENTMSG, proc->pid, NULL, &event))
+ eprintf("ptrace PTRACE_GETEVENTMSG %ju NULL <buffer>:", (uintmax_t)proc->pid);
+ return event;
+}
+
+
static void
handle_event(struct process *proc, int status)
{
- int trace_event, sig;
- unsigned long int event;
struct process *proc2;
-
- sig = WSTOPSIG(status);
- trace_event = status >> 16;
+ int sig = WSTOPSIG(status);
+ int trace_event = status >> 16;
switch (trace_event) {
case PTRACE_EVENT_VFORK:
- tprintf(proc, "\nProcess stopped by vfork until child exits or exec(2)s\n");
- FALL_THROUGH
- /* fall through */
case PTRACE_EVENT_FORK:
case PTRACE_EVENT_CLONE:
- if (ptrace(PTRACE_GETEVENTMSG, proc->pid, NULL, &event))
- eprintf("ptrace PTRACE_GETEVENTMSG %ju NULL <buffer>:", (uintmax_t)proc->pid);
- proc2 = add_process((pid_t)event, trace_options);
- if (trace_event == PTRACE_EVENT_CLONE)
- proc2->thread_leader = proc->pid;
- proc->ret = event;
+ proc2 = add_process((pid_t)get_event_msg(proc), trace_event == PTRACE_EVENT_CLONE ? proc->pid : 0, trace_options);
if (trace_event == PTRACE_EVENT_VFORK) {
+ vfork_start(proc);
proc2->continue_on_exit = proc;
proc->vfork_waiting_on = proc2;
- proc->state = VforkParent;
- } else {
- proc->state = trace_event == PTRACE_EVENT_CLONE ? CloneParent : ForkParent;
- handle_syscall(proc);
}
- tprintf(proc2, "\nTracing new process with parent %ju\n", (uintmax_t)proc->pid);
- proc2->state = trace_event == PTRACE_EVENT_FORK ? ForkChild :
- trace_event == PTRACE_EVENT_VFORK ? VforkChild : CloneChild;
- handle_syscall(proc2);
+ process_created(proc2, proc);
+ restart_systemcall(proc2, 0);
+ restart_systemcall(proc, 0);
break;
case PTRACE_EVENT_EXEC:
- proc->state = Exec;
- handle_syscall(proc);
+ proc->ignore_until_execed -= (proc->ignore_until_execed == 2);
+ restart_systemcall(proc, 0);
proc2 = proc->continue_on_exit;
if (proc2) {
proc->continue_on_exit = NULL;
proc2->vfork_waiting_on = NULL;
- tprintf(proc2, "\nProcess continues due to exec(2) of vfork child\n");
- handle_syscall(proc2);
+ vfork_done(proc2, "exec(2)");
}
break;
@@ -148,27 +227,34 @@ handle_event(struct process *proc, int status)
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
- stop_signal:
- tprintf(proc, "\nProcess stopped by signal %i (%s: %s)\n", sig, get_signum_name(sig), strsignal(sig));
- if (ptrace(PTRACE_LISTEN, proc->pid, NULL, 0))
- eprintf("ptrace PTRACE_LISTEN %ju NULL 0:", (uintmax_t)proc->pid);
+ process_signalled(proc, sig, 1);
+ restart_process(proc, PTRACE_LISTEN, 0);
break;
default:
- if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, 0))
- eprintf("ptrace PTRACE_SYSCALL %ju NULL 0:", (uintmax_t)proc->pid);
+ tprintf(proc, "\nTRACE_EVENT_STOP with signal %i (%s: %s)\n",
+ WTERMSIG(status), get_signum_name(WTERMSIG(status)),
+ strsignal(WTERMSIG(status)));
+ restart_systemcall(proc, 0);
break;
}
break;
+ case PTRACE_EVENT_EXIT:
+ process_exiting(proc, (int)get_event_msg(proc));
+ proc->state = Zombie;
+ restart_process(proc, PTRACE_CONT, 0);
+ break;
+
+ case PTRACE_EVENT_VFORK_DONE:
+ restart_systemcall(proc, 0);
+ break;
+
default:
abort();
case 0:
- if (ptrace(PTRACE_GETSIGINFO, proc->pid, 0, &(siginfo_t){0}))
- goto stop_signal;
- tprintf(proc, "\nProcess received signal %i (%s: %s)\n", sig, get_signum_name(sig), strsignal(sig));
- if (ptrace(PTRACE_SYSCALL, proc->pid, NULL, sig))
- eprintf("ptrace PTRACE_SYSCALL %ju NULL %i:", (uintmax_t)proc->pid, sig);
+ process_signalled(proc, sig, 0);
+ restart_systemcall(proc, sig);
break;
}
}
@@ -182,7 +268,7 @@ main(int argc, char **argv)
FILE *outfp = stderr;
int status, exit_code = 0, with_argv0 = 0, multiprocess = 0, i;
char *arg;
- struct process *proc, *proc2;
+ struct process *proc;
struct sigaction sa;
sigset_t sm;
@@ -212,6 +298,7 @@ main(int argc, char **argv)
case 'f':
trace_options |= PTRACE_O_TRACEFORK;
trace_options |= PTRACE_O_TRACEVFORK;
+ trace_options |= PTRACE_O_TRACEVFORKDONE;
FALL_THROUGH
/* fall through */
case 't':
@@ -224,6 +311,9 @@ main(int argc, char **argv)
if (!argc)
usage();
+ if (prctl(PR_SET_CHILD_SUBREAPER, 1))
+ weprintf("prctl PR_SET_CHILD_SUBREAPER 1:");
+
orig_pid = fork();
switch (orig_pid) {
case -1:
@@ -247,10 +337,12 @@ main(int argc, char **argv)
outfp = outfile ? xfopen(outfile, "wb") : stderr;
setup_trace_output(outfp, multiprocess);
init_process_list();
- add_process(orig_pid, trace_options)->silent_until_execed = 2;
+ proc = add_process(orig_pid, 0, trace_options);
+ proc->ignore_until_execed = 2;
+ restart_systemcall(proc, 0);
for (;;) {
- pid = waitpid(-1, &status, __WALL | WCONTINUED);
+ pid = waitpid(-1, &status, __WALL | WCONTINUED); /* TODO WCONTINUED should require waitid */
if (pid < 0) {
if (errno == ECHILD)
break;
@@ -264,32 +356,21 @@ main(int argc, char **argv)
continue;
if (WIFSTOPPED(status)) {
- if (WSTOPSIG(status) == (SIGTRAP | 0x80))
+ if (proc->state == Zombie)
+ zombie_stopped(proc, status);
+ else if (WSTOPSIG(status) == (SIGTRAP | 0x80))
handle_syscall(proc);
else
handle_event(proc, status);
} else if (WIFCONTINUED(status)) {
- tprintf(proc, "\nProcess continued, presumably by signal %i (SIGCONT: %s)\n", SIGCONT, strsignal(SIGCONT));
+ process_continued(proc, status);
} else {
if (pid == orig_pid)
exit_code = status;
- if (WIFEXITED(status)) {
- tprintf(proc, "\nProcess exited with value %i%s\n", WEXITSTATUS(status),
- WCOREDUMP(status) ? ", core dumped" : "");
- } else {
- tprintf(proc, "\nProcess terminated by signal %i (%s: %s)%s\n", WTERMSIG(status),
- get_signum_name(WTERMSIG(status)), strsignal(WTERMSIG(status)),
- WCOREDUMP(status) ? ", core dumped" : "");
- }
- proc2 = proc->continue_on_exit;
+ process_exited(proc, status);
+ if (proc->continue_on_exit)
+ vfork_done(proc->continue_on_exit, WIFEXITED(status) ? "exit" : "abnormal termination");
remove_process(proc);
- if (proc2) {
- if (WIFEXITED(status))
- tprintf(proc2, "\nProcess continues due to exit of vfork child\n");
- else
- tprintf(proc2, "\nProcess continues due to abnormal termination of vfork child\n");
- handle_syscall(proc2);
- }
}
fflush(outfp);
@@ -301,6 +382,7 @@ main(int argc, char **argv)
weprintf("Copying exit from %s\n", multiprocess ? "original tracee" : "tracee");
if (WIFSIGNALED(exit_code)) {
+ prctl(PR_SET_DUMPABLE, 0);
exit_code = WTERMSIG(exit_code);
raise(exit_code);
return exit_code + 128;
diff --git a/tests/Makefile b/tests/Makefile
index 7468bfa..527c690 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -2,6 +2,7 @@
BIN_64 =\
abort.64\
+ abort-nodump.64\
cont.64\
exec.64\
exit.64\
@@ -13,6 +14,7 @@ BIN_64 =\
raise.64\
siginfo.64\
signal.64\
+ signal-interrupt.64\
stop.64\
threads.64\
tstp.64\
diff --git a/tests/abort-nodump.c b/tests/abort-nodump.c
new file mode 100644
index 0000000..9e13595
--- /dev/null
+++ b/tests/abort-nodump.c
@@ -0,0 +1,9 @@
+#include <sys/prctl.h>
+#include <stdlib.h>
+
+int
+main(void)
+{
+ prctl(PR_SET_DUMPABLE, 0);
+ abort();
+}
diff --git a/tests/abort.c b/tests/abort.c
index 3afda7b..aa9dac9 100644
--- a/tests/abort.c
+++ b/tests/abort.c
@@ -1,7 +1,9 @@
+#include <sys/prctl.h>
#include <stdlib.h>
int
main(void)
{
+ prctl(PR_SET_DUMPABLE, 1);
abort();
}
diff --git a/tests/signal-interrupt.c b/tests/signal-interrupt.c
new file mode 100644
index 0000000..7b6988f
--- /dev/null
+++ b/tests/signal-interrupt.c
@@ -0,0 +1,26 @@
+#include <signal.h>
+#include <time.h>
+#include <unistd.h>
+
+static void
+interrupt()
+{
+ write(-2, "xyzzy\n", 6);
+}
+
+int
+main(void)
+{
+ struct timespec ts = {0, 100000000L};
+ pid_t pid = getpid();
+ signal(SIGINT, interrupt);
+ if (fork() == 0) {
+ ts.tv_nsec /= 2;
+ nanosleep(&ts, NULL);
+ kill(pid, SIGINT);
+ } else {
+ nanosleep(&ts, NULL);
+ write(-1, "qwerty\n", 7);
+ }
+ return 0;
+}
diff --git a/util.c b/util.c
index 7888133..c81d041 100644
--- a/util.c
+++ b/util.c
@@ -25,7 +25,7 @@ void
tprintf(struct process *proc, const char *fmt, ...)
{
va_list ap;
- if (proc->silent_until_execed)
+ if (proc->ignore_until_execed)
return;
if (fmt[0] == '\n' && fmt[1]) {
last_pid = 0;