Adding connman-test subpackage
[framework/connectivity/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 #if defined TIZEN_EXT
42 #include <sys/stat.h>
43
44 #define LOG_FILE_PATH "/var/log/connman.log"
45 #define MAX_LOG_SIZE    2 * 1024 * 1024
46 #define MAX_LOG_COUNT   9
47
48 #define openlog __connman_log_open
49 #define closelog __connman_log_close
50 #define vsyslog __connman_log
51 #define syslog __connman_log_s
52
53 static FILE *log_file = NULL;
54
55 void __connman_log_open(const char *ident, int option, int facility)
56 {
57         if (log_file == NULL)
58                 log_file = (FILE *)fopen(LOG_FILE_PATH, "a+");
59 }
60
61 void __connman_log_close(void)
62 {
63         fclose(log_file);
64         log_file = NULL;
65 }
66
67 static void __connman_log_update_file_revision(int rev)
68 {
69         int next_log_rev = 0;
70         char *log_file = NULL;
71         char *next_log_file = NULL;
72
73         next_log_rev = rev + 1;
74
75         log_file = g_strdup_printf("%s.%d", LOG_FILE_PATH, rev);
76         next_log_file = g_strdup_printf("%s.%d", LOG_FILE_PATH, next_log_rev);
77
78         if (next_log_rev >= MAX_LOG_COUNT)
79                 remove(next_log_file);
80
81         if (access(next_log_file, F_OK) == 0)
82                 __connman_log_update_file_revision(next_log_rev);
83
84         if (rename(log_file, next_log_file) != 0)
85                 remove(log_file);
86
87         g_free(log_file);
88         g_free(next_log_file);
89 }
90
91 static void __connman_log_make_backup(void)
92 {
93         const int rev = 0;
94         char *backup = NULL;
95
96         backup = g_strdup_printf("%s.%d", LOG_FILE_PATH, rev);
97
98         if (access(backup, F_OK) == 0)
99                 __connman_log_update_file_revision(rev);
100
101         if (rename(LOG_FILE_PATH, backup) != 0)
102                 remove(LOG_FILE_PATH);
103
104         g_free(backup);
105 }
106
107 static void __connman_log_get_local_time(char *strtime, const int size)
108 {
109         time_t buf;
110         struct tm *local_ptm;
111
112         time(&buf);
113         buf = time(NULL);
114         local_ptm = localtime(&buf);
115
116         strftime(strtime, size, "%D %H:%M:%S", local_ptm);
117 }
118
119 void __connman_log(const int log_priority, const char *format, va_list ap)
120 {
121         int log_size = 0;
122         struct stat buf;
123         char str[256];
124         char strtime[40];
125
126         if (log_file == NULL)
127                 log_file = (FILE *)fopen(LOG_FILE_PATH, "a+");
128
129         if (log_file == NULL)
130                 return;
131
132         fstat(fileno(log_file), &buf);
133         log_size = buf.st_size;
134
135         if (log_size >= MAX_LOG_SIZE) {
136                 fclose(log_file);
137                 log_file = NULL;
138
139                 __connman_log_make_backup();
140
141                 log_file = (FILE *)fopen(LOG_FILE_PATH, "a+");
142
143                 if (log_file == NULL)
144                         return;
145         }
146
147         __connman_log_get_local_time(strtime, sizeof(strtime));
148
149         if (vsnprintf(str, sizeof(str), format, ap) > 0)
150                 fprintf(log_file, "%s %s\n", strtime, str);
151 }
152
153 void __connman_log_s(int log_priority, const char *format, ...)
154 {
155         va_list ap;
156
157         va_start(ap, format);
158
159         vsyslog(LOG_DEBUG, format, ap);
160
161         va_end(ap);
162 }
163 #endif
164
165 /**
166  * connman_info:
167  * @format: format string
168  * @Varargs: list of arguments
169  *
170  * Output general information
171  */
172 void connman_info(const char *format, ...)
173 {
174         va_list ap;
175
176         va_start(ap, format);
177
178         vsyslog(LOG_INFO, format, ap);
179
180         va_end(ap);
181 }
182
183 /**
184  * connman_warn:
185  * @format: format string
186  * @Varargs: list of arguments
187  *
188  * Output warning messages
189  */
190 void connman_warn(const char *format, ...)
191 {
192         va_list ap;
193
194         va_start(ap, format);
195
196         vsyslog(LOG_WARNING, format, ap);
197
198         va_end(ap);
199 }
200
201 /**
202  * connman_error:
203  * @format: format string
204  * @varargs: list of arguments
205  *
206  * Output error messages
207  */
208 void connman_error(const char *format, ...)
209 {
210         va_list ap;
211
212         va_start(ap, format);
213
214         vsyslog(LOG_ERR, format, ap);
215
216         va_end(ap);
217 }
218
219 /**
220  * connman_debug:
221  * @format: format string
222  * @varargs: list of arguments
223  *
224  * Output debug message
225  */
226 void connman_debug(const char *format, ...)
227 {
228         va_list ap;
229
230         va_start(ap, format);
231
232         vsyslog(LOG_DEBUG, format, ap);
233
234         va_end(ap);
235 }
236
237 #if !defined TIZEN_EXT
238 static void print_backtrace(unsigned int offset)
239 {
240         void *frames[99];
241         size_t n_ptrs;
242         unsigned int i;
243         int outfd[2], infd[2];
244         int pathlen;
245         pid_t pid;
246
247         if (program_exec == NULL)
248                 return;
249
250         pathlen = strlen(program_path);
251
252         n_ptrs = backtrace(frames, G_N_ELEMENTS(frames));
253         if (n_ptrs < offset)
254                 return;
255
256         if (pipe(outfd) < 0)
257                 return;
258
259         if (pipe(infd) < 0) {
260                 close(outfd[0]);
261                 close(outfd[1]);
262                 return;
263         }
264
265         pid = fork();
266         if (pid < 0) {
267                 close(outfd[0]);
268                 close(outfd[1]);
269                 close(infd[0]);
270                 close(infd[1]);
271                 return;
272         }
273
274         if (pid == 0) {
275                 close(outfd[1]);
276                 close(infd[0]);
277
278                 dup2(outfd[0], STDIN_FILENO);
279                 dup2(infd[1], STDOUT_FILENO);
280
281                 execlp("addr2line", "-C", "-f", "-e", program_exec, NULL);
282
283                 exit(EXIT_FAILURE);
284         }
285
286         close(outfd[0]);
287         close(infd[1]);
288
289         connman_error("++++++++ backtrace ++++++++");
290
291         for (i = offset; i < n_ptrs - 1; i++) {
292                 Dl_info info;
293                 char addr[20], buf[PATH_MAX * 2];
294                 int len, written;
295                 char *ptr, *pos;
296
297                 dladdr(frames[i], &info);
298
299                 len = snprintf(addr, sizeof(addr), "%p\n", frames[i]);
300                 if (len < 0)
301                         break;
302
303                 written = write(outfd[1], addr, len);
304                 if (written < 0)
305                         break;
306
307                 len = read(infd[0], buf, sizeof(buf));
308                 if (len < 0)
309                         break;
310
311                 buf[len] = '\0';
312
313                 pos = strchr(buf, '\n');
314                 *pos++ = '\0';
315
316                 if (strcmp(buf, "??") == 0) {
317                         connman_error("#%-2u %p in %s", i - offset,
318                                                 frames[i], info.dli_fname);
319                         continue;
320                 }
321
322                 ptr = strchr(pos, '\n');
323                 *ptr++ = '\0';
324
325                 if (strncmp(pos, program_path, pathlen) == 0)
326                         pos += pathlen + 1;
327
328                 connman_error("#%-2u %p in %s() at %s", i - offset,
329                                                 frames[i], buf, pos);
330         }
331
332         connman_error("+++++++++++++++++++++++++++");
333
334         kill(pid, SIGTERM);
335
336         close(outfd[1]);
337         close(infd[0]);
338 }
339
340 static void signal_handler(int signo)
341 {
342         connman_error("Aborting (signal %d) [%s]", signo, program_exec);
343
344         print_backtrace(2);
345
346         exit(EXIT_FAILURE);
347 }
348
349 static void signal_setup(sighandler_t handler)
350 {
351         struct sigaction sa;
352         sigset_t mask;
353
354         sigemptyset(&mask);
355         sa.sa_handler = handler;
356         sa.sa_mask = mask;
357         sa.sa_flags = 0;
358         sigaction(SIGBUS, &sa, NULL);
359         sigaction(SIGILL, &sa, NULL);
360         sigaction(SIGFPE, &sa, NULL);
361         sigaction(SIGSEGV, &sa, NULL);
362         sigaction(SIGABRT, &sa, NULL);
363         sigaction(SIGPIPE, &sa, NULL);
364 }
365 #endif
366
367 extern struct connman_debug_desc __start___debug[];
368 extern struct connman_debug_desc __stop___debug[];
369
370 void __connman_debug_list_available(DBusMessageIter *iter, void *user_data)
371 {
372         struct connman_debug_desc *desc;
373
374         for (desc = __start___debug; desc < __stop___debug; desc++) {
375                 if ((desc->flags & CONNMAN_DEBUG_FLAG_ALIAS) &&
376                                                 desc->name != NULL)
377                         dbus_message_iter_append_basic(iter,
378                                         DBUS_TYPE_STRING, &desc->name);
379         }
380 }
381
382 static gchar **enabled = NULL;
383
384 void __connman_debug_list_enabled(DBusMessageIter *iter, void *user_data)
385 {
386         int i;
387
388         if (enabled == NULL)
389                 return;
390
391         for (i = 0; enabled[i] != NULL; i++)
392                 dbus_message_iter_append_basic(iter,
393                                         DBUS_TYPE_STRING, &enabled[i]);
394 }
395
396 static connman_bool_t is_enabled(struct connman_debug_desc *desc)
397 {
398         int i;
399
400         if (enabled == NULL)
401                 return FALSE;
402
403         for (i = 0; enabled[i] != NULL; i++) {
404                 if (desc->name != NULL && g_pattern_match_simple(enabled[i],
405                                                         desc->name) == TRUE)
406                         return TRUE;
407                 if (desc->file != NULL && g_pattern_match_simple(enabled[i],
408                                                         desc->file) == TRUE)
409                         return TRUE;
410         }
411
412         return FALSE;
413 }
414
415 void __connman_log_enable(struct connman_debug_desc *start,
416                                         struct connman_debug_desc *stop)
417 {
418         struct connman_debug_desc *desc;
419         const char *name = NULL, *file = NULL;
420
421         if (start == NULL || stop == NULL)
422                 return;
423
424         for (desc = start; desc < stop; desc++) {
425                 if (desc->flags & CONNMAN_DEBUG_FLAG_ALIAS) {
426                         file = desc->file;
427                         name = desc->name;
428                         continue;
429                 }
430
431                 if (file != NULL || name != NULL) {
432                         if (g_strcmp0(desc->file, file) == 0) {
433                                 if (desc->name == NULL)
434                                         desc->name = name;
435                         } else
436                                 file = NULL;
437                 }
438
439                 if (is_enabled(desc) == TRUE)
440                         desc->flags |= CONNMAN_DEBUG_FLAG_PRINT;
441         }
442 }
443
444 int __connman_log_init(const char *program, const char *debug,
445                                                 connman_bool_t detach)
446 {
447         static char path[PATH_MAX];
448         int option = LOG_NDELAY | LOG_PID;
449
450         program_exec = program;
451         program_path = getcwd(path, sizeof(path));
452
453         if (debug != NULL)
454                 enabled = g_strsplit_set(debug, ":, ", 0);
455
456         __connman_log_enable(__start___debug, __stop___debug);
457
458         if (detach == FALSE)
459                 option |= LOG_PERROR;
460
461 #if !defined TIZEN_EXT
462         signal_setup(signal_handler);
463 #endif
464
465         openlog(basename(program), option, LOG_DAEMON);
466
467         syslog(LOG_INFO, "Connection Manager version %s", VERSION);
468
469         return 0;
470 }
471
472 void __connman_log_cleanup(void)
473 {
474         syslog(LOG_INFO, "Exit");
475
476         closelog();
477
478 #if !defined TIZEN_EXT
479         signal_setup(SIG_DFL);
480 #endif
481
482         g_strfreev(enabled);
483 }