log: Add support for backtrace symbole resolving
[platform/upstream/connman.git] / src / log.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #define _GNU_SOURCE
27 #include <stdio.h>
28 #include <unistd.h>
29 #include <stdarg.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <syslog.h>
33 #include <execinfo.h>
34 #include <dlfcn.h>
35
36 #include "connman.h"
37
38 static const char *program_exec;
39 static const char *program_path;
40
41 /**
42  * connman_info:
43  * @format: format string
44  * @Varargs: list of arguments
45  *
46  * Output general information
47  */
48 void connman_info(const char *format, ...)
49 {
50         va_list ap;
51
52         va_start(ap, format);
53
54         vsyslog(LOG_INFO, format, ap);
55
56         va_end(ap);
57 }
58
59 /**
60  * connman_warn:
61  * @format: format string
62  * @Varargs: list of arguments
63  *
64  * Output warning messages
65  */
66 void connman_warn(const char *format, ...)
67 {
68         va_list ap;
69
70         va_start(ap, format);
71
72         vsyslog(LOG_WARNING, format, ap);
73
74         va_end(ap);
75 }
76
77 /**
78  * connman_error:
79  * @format: format string
80  * @varargs: list of arguments
81  *
82  * Output error messages
83  */
84 void connman_error(const char *format, ...)
85 {
86         va_list ap;
87
88         va_start(ap, format);
89
90         vsyslog(LOG_ERR, format, ap);
91
92         va_end(ap);
93 }
94
95 /**
96  * connman_debug:
97  * @format: format string
98  * @varargs: list of arguments
99  *
100  * Output debug message
101  */
102 void connman_debug(const char *format, ...)
103 {
104         va_list ap;
105
106         va_start(ap, format);
107
108         vsyslog(LOG_DEBUG, format, ap);
109
110         va_end(ap);
111 }
112
113 static void print_backtrace(unsigned int offset)
114 {
115         void *frames[99];
116         size_t n_ptrs;
117         unsigned int i;
118         int outfd[2], infd[2];
119         int pathlen;
120         pid_t pid;
121
122         if (program_exec == NULL)
123                 return;
124
125         pathlen = strlen(program_path);
126
127         n_ptrs = backtrace(frames, G_N_ELEMENTS(frames));
128         if (n_ptrs < offset)
129                 return;
130
131         if (pipe(outfd) < 0)
132                 return;
133
134         if (pipe(infd) < 0) {
135                 close(outfd[0]);
136                 close(outfd[1]);
137                 return;
138         }
139
140         pid = fork();
141         if (pid < 0) {
142                 close(outfd[0]);
143                 close(outfd[1]);
144                 close(infd[0]);
145                 close(infd[1]);
146                 return;
147         }
148
149         if (pid == 0) {
150                 close(outfd[1]);
151                 close(infd[0]);
152
153                 dup2(outfd[0], STDIN_FILENO);
154                 dup2(infd[1], STDOUT_FILENO);
155
156                 execlp("addr2line", "-C", "-f", "-e", program_exec, NULL);
157
158                 exit(EXIT_FAILURE);
159         }
160
161         close(outfd[0]);
162         close(infd[1]);
163
164         connman_error("++++++++ backtrace ++++++++");
165
166         for (i = offset; i < n_ptrs - 1; i++) {
167                 Dl_info info;
168                 char addr[20], buf[PATH_MAX * 2];
169                 int len, written;
170                 char *ptr, *pos;
171
172                 dladdr(frames[i], &info);
173
174                 len = snprintf(addr, sizeof(addr), "%p\n", frames[i]);
175                 if (len < 0)
176                         break;
177
178                 written = write(outfd[1], addr, len);
179                 if (written < 0)
180                         break;
181
182                 len = read(infd[0], buf, sizeof(buf));
183                 if (len < 0)
184                         break;
185
186                 buf[len] = '\0';
187
188                 pos = strchr(buf, '\n');
189                 *pos++ = '\0';
190
191                 if (strcmp(buf, "??") == 0) {
192                         connman_error("#%-2u %p in %s", i - offset,
193                                                 frames[i], info.dli_fname);
194                         continue;
195                 }
196
197                 ptr = strchr(pos, '\n');
198                 *ptr++ = '\0';
199
200                 if (strncmp(pos, program_path, pathlen) == 0)
201                         pos += pathlen + 1;
202
203                 connman_error("#%-2u %p in %s() at %s", i - offset,
204                                                 frames[i], buf, pos);
205         }
206
207         connman_error("+++++++++++++++++++++++++++");
208
209         kill(pid, SIGTERM);
210
211         close(outfd[1]);
212         close(infd[0]);
213 }
214
215 static void signal_handler(int signo)
216 {
217         connman_error("Aborting (signal %d) [%s]", signo, program_exec);
218
219         print_backtrace(2);
220
221         exit(EXIT_FAILURE);
222 }
223
224 static void signal_setup(sighandler_t handler)
225 {
226         struct sigaction sa;
227         sigset_t mask;
228
229         sigemptyset(&mask);
230         sa.sa_handler = handler;
231         sa.sa_mask = mask;
232         sa.sa_flags = 0;
233         sigaction(SIGBUS, &sa, NULL);
234         sigaction(SIGILL, &sa, NULL);
235         sigaction(SIGFPE, &sa, NULL);
236         sigaction(SIGSEGV, &sa, NULL);
237         sigaction(SIGABRT, &sa, NULL);
238         sigaction(SIGPIPE, &sa, NULL);
239 }
240
241 extern struct connman_debug_desc __start___debug[];
242 extern struct connman_debug_desc __stop___debug[];
243
244 void __connman_debug_list_available(DBusMessageIter *iter, void *user_data)
245 {
246         struct connman_debug_desc *desc;
247
248         for (desc = __start___debug; desc < __stop___debug; desc++) {
249                 if ((desc->flags & CONNMAN_DEBUG_FLAG_ALIAS) &&
250                                                 desc->name != NULL)
251                         dbus_message_iter_append_basic(iter,
252                                         DBUS_TYPE_STRING, &desc->name);
253         }
254 }
255
256 static gchar **enabled = NULL;
257
258 void __connman_debug_list_enabled(DBusMessageIter *iter, void *user_data)
259 {
260         int i;
261
262         if (enabled == NULL)
263                 return;
264
265         for (i = 0; enabled[i] != NULL; i++)
266                 dbus_message_iter_append_basic(iter,
267                                         DBUS_TYPE_STRING, &enabled[i]);
268 }
269
270 static connman_bool_t is_enabled(struct connman_debug_desc *desc)
271 {
272         int i;
273
274         if (enabled == NULL)
275                 return FALSE;
276
277         for (i = 0; enabled[i] != NULL; i++) {
278                 if (desc->name != NULL && g_pattern_match_simple(enabled[i],
279                                                         desc->name) == TRUE)
280                         return TRUE;
281                 if (desc->file != NULL && g_pattern_match_simple(enabled[i],
282                                                         desc->file) == TRUE)
283                         return TRUE;
284         }
285
286         return FALSE;
287 }
288
289 void __connman_log_enable(struct connman_debug_desc *start,
290                                         struct connman_debug_desc *stop)
291 {
292         struct connman_debug_desc *desc;
293         const char *name = NULL, *file = NULL;
294
295         if (start == NULL || stop == NULL)
296                 return;
297
298         for (desc = start; desc < stop; desc++) {
299                 if (desc->flags & CONNMAN_DEBUG_FLAG_ALIAS) {
300                         file = desc->file;
301                         name = desc->name;
302                         continue;
303                 }
304
305                 if (file != NULL || name != NULL) {
306                         if (g_strcmp0(desc->file, file) == 0) {
307                                 if (desc->name == NULL)
308                                         desc->name = name;
309                         } else
310                                 file = NULL;
311                 }
312
313                 if (is_enabled(desc) == TRUE)
314                         desc->flags |= CONNMAN_DEBUG_FLAG_PRINT;
315         }
316 }
317
318 int __connman_log_init(const char *program, const char *debug,
319                                                 connman_bool_t detach)
320 {
321         static char path[PATH_MAX];
322         int option = LOG_NDELAY | LOG_PID;
323
324         program_exec = program;
325         program_path = getcwd(path, sizeof(path));
326
327         if (debug != NULL)
328                 enabled = g_strsplit_set(debug, ":, ", 0);
329
330         __connman_log_enable(__start___debug, __stop___debug);
331
332         if (detach == FALSE)
333                 option |= LOG_PERROR;
334
335         signal_setup(signal_handler);
336
337         openlog(basename(program), option, LOG_DAEMON);
338
339         syslog(LOG_INFO, "Connection Manager version %s", VERSION);
340
341         return 0;
342 }
343
344 void __connman_log_cleanup(void)
345 {
346         syslog(LOG_INFO, "Exit");
347
348         closelog();
349
350         signal_setup(SIG_DFL);
351
352         g_strfreev(enabled);
353 }