blob: 385bcaa6ec020a30bc6c1b01855d0f87fedf84f9
1 | /** |
2 | * logging.c - Centralised logging. Originated from the Linux-NTFS project. |
3 | * |
4 | * Copyright (c) 2005 Richard Russon |
5 | * Copyright (c) 2005-2008 Szabolcs Szakacsits |
6 | * |
7 | * This program/include file is free software; you can redistribute it and/or |
8 | * modify it under the terms of the GNU General Public License as published |
9 | * by the Free Software Foundation; either version 2 of the License, or |
10 | * (at your option) any later version. |
11 | * |
12 | * This program/include file is distributed in the hope that it will be |
13 | * useful, but WITHOUT ANY WARRANTY; without even the implied warranty |
14 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | * GNU General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU General Public License |
18 | * along with this program (in the main directory of the NTFS-3G |
19 | * distribution in the file COPYING); if not, write to the Free Software |
20 | * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
21 | */ |
22 | |
23 | #ifdef HAVE_CONFIG_H |
24 | #include "config.h" |
25 | #endif |
26 | |
27 | #ifdef HAVE_STDIO_H |
28 | #include <stdio.h> |
29 | #endif |
30 | #ifdef HAVE_ERRNO_H |
31 | #include <errno.h> |
32 | #endif |
33 | #ifdef HAVE_STDARG_H |
34 | #include <stdarg.h> |
35 | #endif |
36 | #ifdef HAVE_STRING_H |
37 | #include <string.h> |
38 | #endif |
39 | #ifdef HAVE_STDLIB_H |
40 | #include <stdlib.h> |
41 | #endif |
42 | #ifdef HAVE_SYSLOG_H |
43 | #include <syslog.h> |
44 | #endif |
45 | |
46 | #include "logging.h" |
47 | #include "misc.h" |
48 | |
49 | #ifndef PATH_SEP |
50 | #define PATH_SEP '/' |
51 | #endif |
52 | |
53 | #ifdef DEBUG |
54 | static int tab; |
55 | #endif |
56 | |
57 | /* Some gcc 3.x, 4.[01].X crash with internal compiler error. */ |
58 | #if __GNUC__ <= 3 || (__GNUC__ == 4 && __GNUC_MINOR__ <= 1) |
59 | # define BROKEN_GCC_FORMAT_ATTRIBUTE |
60 | #else |
61 | # define BROKEN_GCC_FORMAT_ATTRIBUTE __attribute__((format(printf, 6, 0))) |
62 | #endif |
63 | |
64 | /** |
65 | * struct ntfs_logging - Control info for the logging system |
66 | * @levels: Bitfield of logging levels |
67 | * @flags: Flags which affect the output style |
68 | * @handler: Function to perform the actual logging |
69 | */ |
70 | struct ntfs_logging { |
71 | u32 levels; |
72 | u32 flags; |
73 | ntfs_log_handler *handler BROKEN_GCC_FORMAT_ATTRIBUTE; |
74 | }; |
75 | |
76 | /** |
77 | * ntfs_log |
78 | * This struct controls all the logging within the library and tools. |
79 | */ |
80 | static struct ntfs_logging ntfs_log = { |
81 | #ifdef DEBUG |
82 | NTFS_LOG_LEVEL_DEBUG | NTFS_LOG_LEVEL_TRACE | NTFS_LOG_LEVEL_ENTER | |
83 | NTFS_LOG_LEVEL_LEAVE | |
84 | #endif |
85 | NTFS_LOG_LEVEL_INFO | NTFS_LOG_LEVEL_QUIET | NTFS_LOG_LEVEL_WARNING | |
86 | NTFS_LOG_LEVEL_ERROR | NTFS_LOG_LEVEL_PERROR | NTFS_LOG_LEVEL_CRITICAL | |
87 | NTFS_LOG_LEVEL_PROGRESS, |
88 | NTFS_LOG_FLAG_ONLYNAME, |
89 | #ifdef DEBUG |
90 | ntfs_log_handler_outerr |
91 | #else |
92 | ntfs_log_handler_null |
93 | #endif |
94 | }; |
95 | |
96 | |
97 | /** |
98 | * ntfs_log_get_levels - Get a list of the current logging levels |
99 | * |
100 | * Find out which logging levels are enabled. |
101 | * |
102 | * Returns: Log levels in a 32-bit field |
103 | */ |
104 | u32 ntfs_log_get_levels(void) |
105 | { |
106 | return ntfs_log.levels; |
107 | } |
108 | |
109 | /** |
110 | * ntfs_log_set_levels - Enable extra logging levels |
111 | * @levels: 32-bit field of log levels to set |
112 | * |
113 | * Enable one or more logging levels. |
114 | * The logging levels are named: NTFS_LOG_LEVEL_*. |
115 | * |
116 | * Returns: Log levels that were enabled before the call |
117 | */ |
118 | u32 ntfs_log_set_levels(u32 levels) |
119 | { |
120 | u32 old; |
121 | old = ntfs_log.levels; |
122 | ntfs_log.levels |= levels; |
123 | return old; |
124 | } |
125 | |
126 | /** |
127 | * ntfs_log_clear_levels - Disable some logging levels |
128 | * @levels: 32-bit field of log levels to clear |
129 | * |
130 | * Disable one or more logging levels. |
131 | * The logging levels are named: NTFS_LOG_LEVEL_*. |
132 | * |
133 | * Returns: Log levels that were enabled before the call |
134 | */ |
135 | u32 ntfs_log_clear_levels(u32 levels) |
136 | { |
137 | u32 old; |
138 | old = ntfs_log.levels; |
139 | ntfs_log.levels &= (~levels); |
140 | return old; |
141 | } |
142 | |
143 | |
144 | /** |
145 | * ntfs_log_get_flags - Get a list of logging style flags |
146 | * |
147 | * Find out which logging flags are enabled. |
148 | * |
149 | * Returns: Logging flags in a 32-bit field |
150 | */ |
151 | u32 ntfs_log_get_flags(void) |
152 | { |
153 | return ntfs_log.flags; |
154 | } |
155 | |
156 | /** |
157 | * ntfs_log_set_flags - Enable extra logging style flags |
158 | * @flags: 32-bit field of logging flags to set |
159 | * |
160 | * Enable one or more logging flags. |
161 | * The log flags are named: NTFS_LOG_LEVEL_*. |
162 | * |
163 | * Returns: Logging flags that were enabled before the call |
164 | */ |
165 | u32 ntfs_log_set_flags(u32 flags) |
166 | { |
167 | u32 old; |
168 | old = ntfs_log.flags; |
169 | ntfs_log.flags |= flags; |
170 | return old; |
171 | } |
172 | |
173 | /** |
174 | * ntfs_log_clear_flags - Disable some logging styles |
175 | * @flags: 32-bit field of logging flags to clear |
176 | * |
177 | * Disable one or more logging flags. |
178 | * The log flags are named: NTFS_LOG_LEVEL_*. |
179 | * |
180 | * Returns: Logging flags that were enabled before the call |
181 | */ |
182 | u32 ntfs_log_clear_flags(u32 flags) |
183 | { |
184 | u32 old; |
185 | old = ntfs_log.flags; |
186 | ntfs_log.flags &= (~flags); |
187 | return old; |
188 | } |
189 | |
190 | |
191 | /** |
192 | * ntfs_log_get_stream - Default output streams for logging levels |
193 | * @level: Log level |
194 | * |
195 | * By default, urgent messages are sent to "stderr". |
196 | * Other messages are sent to "stdout". |
197 | * |
198 | * Returns: "string" Prefix to be used |
199 | */ |
200 | static FILE * ntfs_log_get_stream(u32 level) |
201 | { |
202 | FILE *stream; |
203 | |
204 | switch (level) { |
205 | case NTFS_LOG_LEVEL_INFO: |
206 | case NTFS_LOG_LEVEL_QUIET: |
207 | case NTFS_LOG_LEVEL_PROGRESS: |
208 | case NTFS_LOG_LEVEL_VERBOSE: |
209 | stream = stdout; |
210 | break; |
211 | |
212 | case NTFS_LOG_LEVEL_DEBUG: |
213 | case NTFS_LOG_LEVEL_TRACE: |
214 | case NTFS_LOG_LEVEL_ENTER: |
215 | case NTFS_LOG_LEVEL_LEAVE: |
216 | case NTFS_LOG_LEVEL_WARNING: |
217 | case NTFS_LOG_LEVEL_ERROR: |
218 | case NTFS_LOG_LEVEL_CRITICAL: |
219 | case NTFS_LOG_LEVEL_PERROR: |
220 | default: |
221 | stream = stderr; |
222 | break; |
223 | } |
224 | |
225 | return stream; |
226 | } |
227 | |
228 | /** |
229 | * ntfs_log_get_prefix - Default prefixes for logging levels |
230 | * @level: Log level to be prefixed |
231 | * |
232 | * Prefixing the logging output can make it easier to parse. |
233 | * |
234 | * Returns: "string" Prefix to be used |
235 | */ |
236 | static const char * ntfs_log_get_prefix(u32 level) |
237 | { |
238 | const char *prefix; |
239 | |
240 | switch (level) { |
241 | case NTFS_LOG_LEVEL_DEBUG: |
242 | prefix = "DEBUG: "; |
243 | break; |
244 | case NTFS_LOG_LEVEL_TRACE: |
245 | prefix = "TRACE: "; |
246 | break; |
247 | case NTFS_LOG_LEVEL_QUIET: |
248 | prefix = "QUIET: "; |
249 | break; |
250 | case NTFS_LOG_LEVEL_INFO: |
251 | prefix = "INFO: "; |
252 | break; |
253 | case NTFS_LOG_LEVEL_VERBOSE: |
254 | prefix = "VERBOSE: "; |
255 | break; |
256 | case NTFS_LOG_LEVEL_PROGRESS: |
257 | prefix = "PROGRESS: "; |
258 | break; |
259 | case NTFS_LOG_LEVEL_WARNING: |
260 | prefix = "WARNING: "; |
261 | break; |
262 | case NTFS_LOG_LEVEL_ERROR: |
263 | prefix = "ERROR: "; |
264 | break; |
265 | case NTFS_LOG_LEVEL_PERROR: |
266 | prefix = "ERROR: "; |
267 | break; |
268 | case NTFS_LOG_LEVEL_CRITICAL: |
269 | prefix = "CRITICAL: "; |
270 | break; |
271 | default: |
272 | prefix = ""; |
273 | break; |
274 | } |
275 | |
276 | return prefix; |
277 | } |
278 | |
279 | |
280 | /** |
281 | * ntfs_log_set_handler - Provide an alternate logging handler |
282 | * @handler: function to perform the logging |
283 | * |
284 | * This alternate handler will be called for all future logging requests. |
285 | * If no @handler is specified, logging will revert to the default handler. |
286 | */ |
287 | void ntfs_log_set_handler(ntfs_log_handler *handler) |
288 | { |
289 | if (handler) { |
290 | ntfs_log.handler = handler; |
291 | #ifdef HAVE_SYSLOG_H |
292 | if (handler == ntfs_log_handler_syslog) |
293 | openlog("ntfs-3g", LOG_PID, LOG_USER); |
294 | #endif |
295 | } else |
296 | ntfs_log.handler = ntfs_log_handler_null; |
297 | } |
298 | |
299 | /** |
300 | * ntfs_log_redirect - Pass on the request to the real handler |
301 | * @function: Function in which the log line occurred |
302 | * @file: File in which the log line occurred |
303 | * @line: Line number on which the log line occurred |
304 | * @level: Level at which the line is logged |
305 | * @data: User specified data, possibly specific to a handler |
306 | * @format: printf-style formatting string |
307 | * @...: Arguments to be formatted |
308 | * |
309 | * This is just a redirector function. The arguments are simply passed to the |
310 | * main logging handler (as defined in the global logging struct @ntfs_log). |
311 | * |
312 | * Returns: -1 Error occurred |
313 | * 0 Message wasn't logged |
314 | * num Number of output characters |
315 | */ |
316 | int ntfs_log_redirect(const char *function, const char *file, |
317 | int line, u32 level, void *data, const char *format, ...) |
318 | { |
319 | int olderr = errno; |
320 | int ret; |
321 | va_list args; |
322 | |
323 | if (!(ntfs_log.levels & level)) /* Don't log this message */ |
324 | return 0; |
325 | |
326 | va_start(args, format); |
327 | errno = olderr; |
328 | ret = ntfs_log.handler(function, file, line, level, data, format, args); |
329 | va_end(args); |
330 | |
331 | errno = olderr; |
332 | return ret; |
333 | } |
334 | |
335 | |
336 | /** |
337 | * ntfs_log_handler_syslog - syslog logging handler |
338 | * @function: Function in which the log line occurred |
339 | * @file: File in which the log line occurred |
340 | * @line: Line number on which the log line occurred |
341 | * @level: Level at which the line is logged |
342 | * @data: User specified data, possibly specific to a handler |
343 | * @format: printf-style formatting string |
344 | * @args: Arguments to be formatted |
345 | * |
346 | * A simple syslog logging handler. Ignores colors. |
347 | * |
348 | * Returns: -1 Error occurred |
349 | * 0 Message wasn't logged |
350 | * num Number of output characters |
351 | */ |
352 | |
353 | |
354 | #ifdef HAVE_SYSLOG_H |
355 | |
356 | #define LOG_LINE_LEN 512 |
357 | |
358 | int ntfs_log_handler_syslog(const char *function __attribute__((unused)), |
359 | const char *file __attribute__((unused)), |
360 | int line __attribute__((unused)), u32 level, |
361 | void *data __attribute__((unused)), |
362 | const char *format, va_list args) |
363 | { |
364 | char logbuf[LOG_LINE_LEN]; |
365 | int ret, olderr = errno; |
366 | |
367 | #ifndef DEBUG |
368 | if ((level & NTFS_LOG_LEVEL_PERROR) && errno == ENOSPC) |
369 | return 1; |
370 | #endif |
371 | ret = vsnprintf(logbuf, LOG_LINE_LEN, format, args); |
372 | if (ret < 0) { |
373 | vsyslog(LOG_NOTICE, format, args); |
374 | ret = 1; |
375 | goto out; |
376 | } |
377 | |
378 | if ((LOG_LINE_LEN > ret + 3) && (level & NTFS_LOG_LEVEL_PERROR)) { |
379 | strncat(logbuf, ": ", LOG_LINE_LEN - ret - 1); |
380 | strncat(logbuf, strerror(olderr), LOG_LINE_LEN - (ret + 3)); |
381 | ret = strlen(logbuf); |
382 | } |
383 | |
384 | syslog(LOG_NOTICE, "%s", logbuf); |
385 | out: |
386 | errno = olderr; |
387 | return ret; |
388 | } |
389 | #endif |
390 | |
391 | /** |
392 | * ntfs_log_handler_fprintf - Basic logging handler |
393 | * @function: Function in which the log line occurred |
394 | * @file: File in which the log line occurred |
395 | * @line: Line number on which the log line occurred |
396 | * @level: Level at which the line is logged |
397 | * @data: User specified data, possibly specific to a handler |
398 | * @format: printf-style formatting string |
399 | * @args: Arguments to be formatted |
400 | * |
401 | * A simple logging handler. This is where the log line is finally displayed. |
402 | * It is more likely that you will want to set the handler to either |
403 | * ntfs_log_handler_outerr or ntfs_log_handler_stderr. |
404 | * |
405 | * Note: For this handler, @data is a pointer to a FILE output stream. |
406 | * If @data is NULL, nothing will be displayed. |
407 | * |
408 | * Returns: -1 Error occurred |
409 | * 0 Message wasn't logged |
410 | * num Number of output characters |
411 | */ |
412 | int ntfs_log_handler_fprintf(const char *function, const char *file, |
413 | int line, u32 level, void *data, const char *format, va_list args) |
414 | { |
415 | #ifdef DEBUG |
416 | int i; |
417 | #endif |
418 | int ret = 0; |
419 | int olderr = errno; |
420 | FILE *stream; |
421 | |
422 | if (!data) /* Interpret data as a FILE stream. */ |
423 | return 0; /* If it's NULL, we can't do anything. */ |
424 | stream = (FILE*)data; |
425 | |
426 | #ifdef DEBUG |
427 | if (level == NTFS_LOG_LEVEL_LEAVE) { |
428 | if (tab) |
429 | tab--; |
430 | return 0; |
431 | } |
432 | |
433 | for (i = 0; i < tab; i++) |
434 | ret += fprintf(stream, " "); |
435 | #endif |
436 | if ((ntfs_log.flags & NTFS_LOG_FLAG_ONLYNAME) && |
437 | (strchr(file, PATH_SEP))) /* Abbreviate the filename */ |
438 | file = strrchr(file, PATH_SEP) + 1; |
439 | |
440 | if (ntfs_log.flags & NTFS_LOG_FLAG_PREFIX) /* Prefix the output */ |
441 | ret += fprintf(stream, "%s", ntfs_log_get_prefix(level)); |
442 | |
443 | if (ntfs_log.flags & NTFS_LOG_FLAG_FILENAME) /* Source filename */ |
444 | ret += fprintf(stream, "%s ", file); |
445 | |
446 | if (ntfs_log.flags & NTFS_LOG_FLAG_LINE) /* Source line number */ |
447 | ret += fprintf(stream, "(%d) ", line); |
448 | |
449 | if ((ntfs_log.flags & NTFS_LOG_FLAG_FUNCTION) || /* Source function */ |
450 | (level & NTFS_LOG_LEVEL_TRACE) || (level & NTFS_LOG_LEVEL_ENTER)) |
451 | ret += fprintf(stream, "%s(): ", function); |
452 | |
453 | ret += vfprintf(stream, format, args); |
454 | |
455 | if (level & NTFS_LOG_LEVEL_PERROR) |
456 | ret += fprintf(stream, ": %s\n", strerror(olderr)); |
457 | |
458 | #ifdef DEBUG |
459 | if (level == NTFS_LOG_LEVEL_ENTER) |
460 | tab++; |
461 | #endif |
462 | fflush(stream); |
463 | errno = olderr; |
464 | return ret; |
465 | } |
466 | |
467 | /** |
468 | * ntfs_log_handler_null - Null logging handler (no output) |
469 | * @function: Function in which the log line occurred |
470 | * @file: File in which the log line occurred |
471 | * @line: Line number on which the log line occurred |
472 | * @level: Level at which the line is logged |
473 | * @data: User specified data, possibly specific to a handler |
474 | * @format: printf-style formatting string |
475 | * @args: Arguments to be formatted |
476 | * |
477 | * This handler produces no output. It provides a way to temporarily disable |
478 | * logging, without having to change the levels and flags. |
479 | * |
480 | * Returns: 0 Message wasn't logged |
481 | */ |
482 | int ntfs_log_handler_null(const char *function __attribute__((unused)), const char *file __attribute__((unused)), |
483 | int line __attribute__((unused)), u32 level __attribute__((unused)), void *data __attribute__((unused)), |
484 | const char *format __attribute__((unused)), va_list args __attribute__((unused))) |
485 | { |
486 | return 0; |
487 | } |
488 | |
489 | /** |
490 | * ntfs_log_handler_stdout - All logs go to stdout |
491 | * @function: Function in which the log line occurred |
492 | * @file: File in which the log line occurred |
493 | * @line: Line number on which the log line occurred |
494 | * @level: Level at which the line is logged |
495 | * @data: User specified data, possibly specific to a handler |
496 | * @format: printf-style formatting string |
497 | * @args: Arguments to be formatted |
498 | * |
499 | * Display a log message to stdout. |
500 | * |
501 | * Note: For this handler, @data is a pointer to a FILE output stream. |
502 | * If @data is NULL, then stdout will be used. |
503 | * |
504 | * Note: This function calls ntfs_log_handler_fprintf to do the main work. |
505 | * |
506 | * Returns: -1 Error occurred |
507 | * 0 Message wasn't logged |
508 | * num Number of output characters |
509 | */ |
510 | int ntfs_log_handler_stdout(const char *function, const char *file, |
511 | int line, u32 level, void *data, const char *format, va_list args) |
512 | { |
513 | if (!data) |
514 | data = stdout; |
515 | |
516 | return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); |
517 | } |
518 | |
519 | /** |
520 | * ntfs_log_handler_outerr - Logs go to stdout/stderr depending on level |
521 | * @function: Function in which the log line occurred |
522 | * @file: File in which the log line occurred |
523 | * @line: Line number on which the log line occurred |
524 | * @level: Level at which the line is logged |
525 | * @data: User specified data, possibly specific to a handler |
526 | * @format: printf-style formatting string |
527 | * @args: Arguments to be formatted |
528 | * |
529 | * Display a log message. The output stream will be determined by the log |
530 | * level. |
531 | * |
532 | * Note: For this handler, @data is a pointer to a FILE output stream. |
533 | * If @data is NULL, the function ntfs_log_get_stream will be called |
534 | * |
535 | * Note: This function calls ntfs_log_handler_fprintf to do the main work. |
536 | * |
537 | * Returns: -1 Error occurred |
538 | * 0 Message wasn't logged |
539 | * num Number of output characters |
540 | */ |
541 | int ntfs_log_handler_outerr(const char *function, const char *file, |
542 | int line, u32 level, void *data, const char *format, va_list args) |
543 | { |
544 | if (!data) |
545 | data = ntfs_log_get_stream(level); |
546 | |
547 | return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); |
548 | } |
549 | |
550 | /** |
551 | * ntfs_log_handler_stderr - All logs go to stderr |
552 | * @function: Function in which the log line occurred |
553 | * @file: File in which the log line occurred |
554 | * @line: Line number on which the log line occurred |
555 | * @level: Level at which the line is logged |
556 | * @data: User specified data, possibly specific to a handler |
557 | * @format: printf-style formatting string |
558 | * @args: Arguments to be formatted |
559 | * |
560 | * Display a log message to stderr. |
561 | * |
562 | * Note: For this handler, @data is a pointer to a FILE output stream. |
563 | * If @data is NULL, then stdout will be used. |
564 | * |
565 | * Note: This function calls ntfs_log_handler_fprintf to do the main work. |
566 | * |
567 | * Returns: -1 Error occurred |
568 | * 0 Message wasn't logged |
569 | * num Number of output characters |
570 | */ |
571 | int ntfs_log_handler_stderr(const char *function, const char *file, |
572 | int line, u32 level, void *data, const char *format, va_list args) |
573 | { |
574 | if (!data) |
575 | data = stderr; |
576 | |
577 | return ntfs_log_handler_fprintf(function, file, line, level, data, format, args); |
578 | } |
579 | |
580 | |
581 | /** |
582 | * ntfs_log_parse_option - Act upon command line options |
583 | * @option: Option flag |
584 | * |
585 | * Delegate some of the work of parsing the command line. All the options begin |
586 | * with "--log-". Options cause log levels to be enabled in @ntfs_log (the |
587 | * global logging structure). |
588 | * |
589 | * Note: The "colour" option changes the logging handler. |
590 | * |
591 | * Returns: TRUE Option understood |
592 | * FALSE Invalid log option |
593 | */ |
594 | BOOL ntfs_log_parse_option(const char *option) |
595 | { |
596 | if (strcmp(option, "--log-debug") == 0) { |
597 | ntfs_log_set_levels(NTFS_LOG_LEVEL_DEBUG); |
598 | return TRUE; |
599 | } else if (strcmp(option, "--log-verbose") == 0) { |
600 | ntfs_log_set_levels(NTFS_LOG_LEVEL_VERBOSE); |
601 | return TRUE; |
602 | } else if (strcmp(option, "--log-quiet") == 0) { |
603 | ntfs_log_clear_levels(NTFS_LOG_LEVEL_QUIET); |
604 | return TRUE; |
605 | } else if (strcmp(option, "--log-trace") == 0) { |
606 | ntfs_log_set_levels(NTFS_LOG_LEVEL_TRACE); |
607 | return TRUE; |
608 | } |
609 | |
610 | ntfs_log_debug("Unknown logging option '%s'\n", option); |
611 | return FALSE; |
612 | } |
613 | |
614 |