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