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