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