Handle multi-threaded attach/detach gracefully
diff --git a/ChangeLog b/ChangeLog
index 9e93ebd..3d68baa 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
 2011-07-08  Petr Machata  <pmachata@redhat.com>
 
+	* common.h (ltrace_exiting): New interface called when ^C is hit.
+	* ltrace.c, handle_event.c: Remove current exit handling.
+	* sysdeps/linux-gnu/events.c: Implement exit handling using custom
+	event handler.
+	* proc.c (open_one_pid): trace_set_options so that attach to
+	multi-threaded process works.
+
+2011-07-08  Petr Machata  <pmachata@redhat.com>
+
 	* common.h (struct Process.breakpoint_being_enabled): Drop.
 	* handle_event.c: Adapt to above.
 	* sysdeps/linux-gnu/events.c: Implement breakpoint re-enablement
diff --git a/common.h b/common.h
index c8f012b..a83e497 100644
--- a/common.h
+++ b/common.h
@@ -330,6 +330,7 @@
 extern void continue_process(pid_t pid);
 extern void continue_after_signal(pid_t pid, int signum);
 extern void continue_after_breakpoint(Process * proc, Breakpoint * sbp);
+extern void ltrace_exiting(void);
 extern long gimme_arg(enum tof type, Process * proc, int arg_num, arg_type_info * info);
 extern void save_register_args(enum tof type, Process * proc);
 extern int umovestr(Process * proc, void * addr, int len, void * laddr);
diff --git a/handle_event.c b/handle_event.c
index 0886846..0a7407c 100644
--- a/handle_event.c
+++ b/handle_event.c
@@ -37,6 +37,11 @@
 
 void
 handle_event(Event *event) {
+	if (exiting == 1) {
+		exiting = 2;
+		debug(1, "ltrace about to exit");
+		ltrace_exiting();
+	}
 	debug(DEBUG_FUNCTION, "handle_event(pid=%d, type=%d)",
 	      event->proc ? event->proc->pid : -1, event->type);
 	/* If the thread group defines an overriding event handler,
@@ -330,13 +335,6 @@
 static void
 handle_signal(Event *event) {
 	debug(DEBUG_FUNCTION, "handle_signal(pid=%d, signum=%d)", event->proc->pid, event->e_un.signum);
-	if (exiting && event->e_un.signum == SIGSTOP) {
-		pid_t pid = event->proc->pid;
-		disable_all_breakpoints(event->proc);
-		untrace_pid(pid);
-		remove_process(event->proc);
-		return;
-	}
 	if (event->proc->state != STATE_IGNORED && !options.no_signals) {
 		output_line(event->proc, "--- %s (%s) ---",
 				shortsignal(event->proc, event->e_un.signum),
diff --git a/libltrace.c b/libltrace.c
index caccb48..e731fe1 100644
--- a/libltrace.c
+++ b/libltrace.c
@@ -54,15 +54,7 @@
 	signal(SIGINT, SIG_IGN);
 	signal(SIGTERM, SIG_IGN);
 	signal(SIGALRM, signal_alarm);
-	if (opt_p) {
-		struct opt_p_t *tmp = opt_p;
-		while (tmp) {
-			debug(2, "Sending SIGSTOP to process %u\n", tmp->pid);
-			kill(tmp->pid, SIGSTOP);
-			tmp = tmp->next;
-		}
-	}
-	alarm(1);
+	//alarm(1);
 }
 
 static void
diff --git a/proc.c b/proc.c
index 87b5a89..f896bac 100644
--- a/proc.c
+++ b/proc.c
@@ -63,6 +63,7 @@
 	}
 
 	proc = open_program(filename, pid, 1);
+	trace_set_options(proc, pid);
 	continue_process(pid);
 	proc->breakpoints_enabled = 1;
 }
diff --git a/sysdeps/linux-gnu/trace.c b/sysdeps/linux-gnu/trace.c
index 86741de..e678536 100644
--- a/sysdeps/linux-gnu/trace.c
+++ b/sysdeps/linux-gnu/trace.c
@@ -536,6 +536,171 @@
 	}
 }
 
+/**
+ * Ltrace exit.  When we are about to exit, we have to go through all
+ * the processes, stop them all, remove all the breakpoints, and then
+ * detach the processes that we attached to using -p.  If we left the
+ * other tasks running, they might hit stray return breakpoints and
+ * produce artifacts, so we better stop everyone, even if it's a bit
+ * of extra work.
+ */
+struct ltrace_exiting_handler
+{
+	Event_Handler super;
+	struct pid_set pids;
+};
+
+static enum pcb_status
+remove_task(Process * task, void * data)
+{
+	/* Don't untrace leader just yet.  */
+	if (task != data)
+		remove_process(task);
+	return pcb_cont;
+}
+
+static enum pcb_status
+untrace_task(Process * task, void * data)
+{
+	untrace_pid(task->pid);
+	return pcb_cont;
+}
+
+static Event *
+ltrace_exiting_on_event(Event_Handler * super, Event * event)
+{
+	struct ltrace_exiting_handler * self = (void *)super;
+	Process * task = event->proc;
+	Process * leader = task->leader;
+
+	debug(DEBUG_PROCESS, "pid %d; event type %d", task->pid, event->type);
+
+	struct pid_task * task_info = get_task_info(&self->pids, task->pid);
+	handle_stopping_event(task_info, &event);
+
+	if (await_sigstop_delivery(&self->pids, task_info, event)) {
+		debug(DEBUG_PROCESS, "all SIGSTOPs delivered %d", leader->pid);
+		disable_all_breakpoints(leader);
+
+		/* Now untrace the process, if it was attached to by -p.  */
+		struct opt_p_t * it;
+		for (it = opt_p; it != NULL; it = it->next) {
+			Process * proc = pid2proc(it->pid);
+			if (proc == NULL)
+				continue;
+			if (proc->leader == leader) {
+				each_task(leader, &untrace_task, NULL);
+				break;
+			}
+		}
+
+		each_task(leader, &remove_task, leader);
+		destroy_event_handler(leader);
+		remove_task(leader, NULL);
+		return NULL;
+	}
+
+	/* Sink all non-exit events.  We are about to exit, so we
+	 * don't bother with queuing them. */
+	if (event_exit_or_none_p(event))
+		return event;
+	else
+		return NULL;
+}
+
+static void
+ltrace_exiting_destroy(Event_Handler * super)
+{
+	struct ltrace_exiting_handler * self = (void *)super;
+	free(self->pids.tasks);
+}
+
+static int
+ltrace_exiting_install_handler(Process * proc)
+{
+	/* Only install to leader.  */
+	if (proc->leader != proc)
+		return 0;
+
+	/* Perhaps we are already installed, if the user passed
+	 * several -p options that are tasks of one process.  */
+	if (proc->event_handler != NULL
+	    && proc->event_handler->on_event == &ltrace_exiting_on_event)
+		return 0;
+
+	struct ltrace_exiting_handler * handler
+		= calloc(sizeof(*handler), 1);
+	if (handler == NULL) {
+		perror("malloc exiting handler");
+	fatal:
+		/* XXXXXXXXXXXXXXXXXXX fixme */
+		return -1;
+	}
+
+	/* If we are in the middle of breakpoint, extract the
+	 * pid-state information from that handler so that we can take
+	 * over the SIGSTOP handling.  */
+	if (proc->event_handler != NULL) {
+		debug(DEBUG_PROCESS, "taking over breakpoint handling");
+		assert(proc->event_handler->on_event
+		       == &process_stopping_on_event);
+		struct process_stopping_handler * other
+			= (void *)proc->event_handler;
+		size_t i;
+		for (i = 0; i < other->pids.count; ++i) {
+			struct pid_task * oti = &other->pids.tasks[i];
+			struct pid_task * task_info
+				= add_task_info(&handler->pids, oti->pid);
+			if (task_info == NULL) {
+				perror("ltrace_exiting_install_handler"
+				       ":add_task_info");
+				goto fatal;
+			}
+			/* Copy over the state.  */
+			*task_info = *oti;
+		}
+
+		/* And destroy the original handler.  */
+		destroy_event_handler(proc);
+	}
+
+	handler->super.on_event = ltrace_exiting_on_event;
+	handler->super.destroy = ltrace_exiting_destroy;
+	install_event_handler(proc->leader, &handler->super);
+
+	if (each_task(proc->leader, &send_sigstop,
+		      &handler->pids) != NULL)
+		goto fatal;
+
+	return 0;
+}
+
+/* If ltrace gets SIGINT, the processes directly or indirectly run by
+ * ltrace get it too.  We just have to wait long enough for the signal
+ * to be delivered and the process terminated, which we notice and
+ * exit ltrace, too.  So there's not much we need to do there.  We
+ * want to keep tracing those processes as usual, in case they just
+ * SIG_IGN the SIGINT to do their shutdown etc.
+ *
+ * For processes ran on the background, we want to install an exit
+ * handler that stops all the threads, removes all breakpoints, and
+ * detaches.
+ */
+void
+ltrace_exiting(void)
+{
+	struct opt_p_t * it;
+	for (it = opt_p; it != NULL; it = it->next) {
+		Process * proc = pid2proc(it->pid);
+		if (proc == NULL || proc->leader == NULL)
+			continue;
+		if (ltrace_exiting_install_handler(proc->leader) < 0)
+			fprintf(stderr,
+				"Couldn't install exiting handler for %d.\n",
+				proc->pid);
+	}
+}
+
 size_t
 umovebytes(Process *proc, void *addr, void *laddr, size_t len) {