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