libltrace.c revision f670eea50e959eeb9da53d70cad8d43c19494ef0
1#include "config.h"
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <unistd.h>
6#include <string.h>
7#include <errno.h>
8#include <sys/param.h>
9#include <signal.h>
10#include <sys/wait.h>
11
12#include "common.h"
13
14char *command = NULL;
15Process *list_of_processes = NULL;
16
17int exiting = 0;		/* =1 if a SIGINT or SIGTERM has been received */
18
19static void
20signal_alarm(int sig) {
21	Process *tmp = list_of_processes;
22
23	signal(SIGALRM, SIG_DFL);
24	while (tmp) {
25		struct opt_p_t *tmp2 = opt_p;
26		while (tmp2) {
27			if (tmp->pid == tmp2->pid) {
28				tmp = tmp->next;
29				if (!tmp) {
30					return;
31				}
32				tmp2 = opt_p;
33				continue;
34			}
35			tmp2 = tmp2->next;
36		}
37		debug(2, "Sending SIGSTOP to process %u\n", tmp->pid);
38		kill(tmp->pid, SIGSTOP);
39		tmp = tmp->next;
40	}
41}
42
43static void
44signal_exit(int sig) {
45	exiting = 1;
46	debug(1, "Received interrupt signal; exiting...");
47	signal(SIGINT, SIG_IGN);
48	signal(SIGTERM, SIG_IGN);
49	signal(SIGALRM, signal_alarm);
50	if (opt_p) {
51		struct opt_p_t *tmp = opt_p;
52		while (tmp) {
53			debug(2, "Sending SIGSTOP to process %u\n", tmp->pid);
54			kill(tmp->pid, SIGSTOP);
55			tmp = tmp->next;
56		}
57	}
58	alarm(1);
59}
60
61static void
62normal_exit(void) {
63	output_line(0, 0);
64	if (options.summary) {
65		show_summary();
66	}
67	if (options.output) {
68		fclose(options.output);
69		options.output = NULL;
70	}
71}
72
73void
74ltrace_init(int argc, char **argv) {
75	struct opt_p_t *opt_p_tmp;
76
77	atexit(normal_exit);
78	signal(SIGINT, signal_exit);	/* Detach processes when interrupted */
79	signal(SIGTERM, signal_exit);	/*  ... or killed */
80
81	argv = process_options(argc, argv);
82	while (opt_F) {
83		/* If filename begins with ~, expand it to the user's home */
84		/* directory. This does not correctly handle ~yoda, but that */
85		/* isn't as bad as it seems because the shell will normally */
86		/* be doing the expansion for us; only the hardcoded */
87		/* ~/.ltrace.conf should ever use this code. */
88		if (opt_F->filename[0] == '~') {
89			char path[PATH_MAX];
90			char *home_dir = getenv("HOME");
91			if (home_dir) {
92				strncpy(path, home_dir, PATH_MAX - 1);
93				path[PATH_MAX - 1] = '\0';
94				strncat(path, opt_F->filename + 1,
95						PATH_MAX - strlen(path) - 1);
96				read_config_file(path);
97			}
98		} else {
99			read_config_file(opt_F->filename);
100		}
101		opt_F = opt_F->next;
102	}
103	if (opt_e) {
104		struct opt_e_t *tmp = opt_e;
105		while (tmp) {
106			debug(1, "Option -e: %s\n", tmp->name);
107			tmp = tmp->next;
108		}
109	}
110	if (command) {
111		execute_program(open_program(command, 0), argv);
112	}
113	opt_p_tmp = opt_p;
114	while (opt_p_tmp) {
115		open_pid(opt_p_tmp->pid);
116		opt_p_tmp = opt_p_tmp->next;
117	}
118}
119
120static int num_ltrace_callbacks[EVENT_MAX];
121static callback_func * ltrace_callbacks[EVENT_MAX];
122
123void
124ltrace_add_callback(callback_func func, Event_type type) {
125	ltrace_callbacks[type] = realloc(ltrace_callbacks[type], (num_ltrace_callbacks[type]+1)*sizeof(callback_func));
126	ltrace_callbacks[type][num_ltrace_callbacks[type]++] = func;
127}
128
129static void
130dispatch_callbacks(Event * ev) {
131	int i;
132	/* Ignoring case 1: signal into a dying tracer */
133	if (ev->type==EVENT_SIGNAL &&
134			exiting && ev->e_un.signum == SIGSTOP) {
135		return;
136	}
137	/* Ignoring case 2: process being born before a clone event */
138	if (ev->proc && ev->proc->state == STATE_IGNORED) {
139		return;
140	}
141	for (i=0; i<num_ltrace_callbacks[ev->type]; i++) {
142		ltrace_callbacks[ev->type][i](ev);
143	}
144}
145
146void
147ltrace_main(void) {
148	Event * ev;
149	while (1) {
150		ev = next_event();
151		dispatch_callbacks(ev);
152		handle_event(ev);
153	}
154}
155