| /* |
| * logging.c -- Logging functions |
| * |
| * (c) Copyright IBM Corporation 2014. |
| * |
| * Author: Stefan Berger <stefanb@us.ibm.com> |
| * |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * |
| * Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * Neither the names of the IBM Corporation nor the names of its |
| * contributors may be used to endorse or promote products derived from |
| * this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| |
| #include "sys_dependencies.h" |
| |
| #include <errno.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <unistd.h> |
| #include <stdbool.h> |
| |
| #include "logging.h" |
| #include "utils.h" |
| |
| #include <libtpms/tpm_library.h> |
| |
| #define CONSOLE_LOGGING 2 /* stderr */ |
| #define SUPPRESS_LOGGING -1 |
| |
| static int logfd = CONSOLE_LOGGING; |
| static unsigned int log_level = 1; |
| static char *log_prefix; |
| |
| static void log_prefix_clear(void) |
| { |
| free(log_prefix); |
| log_prefix = NULL; |
| } |
| |
| /* |
| * log_init: |
| * Initialize the logging to log into a file with the given name. |
| * @filename: the log filename; use '-' to suppress logging |
| * |
| * Returns 0 on success, -1 on failure with errno set. |
| */ |
| int log_init(const char *filename, bool truncate) |
| { |
| int flags; |
| |
| if (!strcmp(filename, "-")) { |
| logfd = SUPPRESS_LOGGING; |
| return 0; |
| } |
| |
| if (!truncate) { |
| flags = O_WRONLY|O_CREAT|O_APPEND|O_NOFOLLOW; |
| } else { |
| flags = O_WRONLY|O_CREAT|O_TRUNC|O_NOFOLLOW; |
| } |
| |
| logfd = open(filename, flags, S_IRUSR|S_IWUSR); |
| if (logfd < 0) { |
| logfd = CONSOLE_LOGGING; |
| return -1; |
| } |
| |
| log_prefix_clear(); |
| |
| return 0; |
| } |
| |
| /* |
| * log_init_fd: |
| * Initialize the logging and have the logs written to the given file |
| * descriptor. |
| * @fd: file descriptor to log to |
| * |
| * Returns 0 on success, -1 on failure with errno set. |
| */ |
| int log_init_fd(int fd) |
| { |
| int flags; |
| |
| close(logfd); |
| logfd = fd; |
| |
| if (logfd >= 0) { |
| flags = fcntl(logfd, F_GETFL); |
| if (flags == -1) |
| return -1; |
| if ((flags & (O_RDWR|O_WRONLY)) == 0) { |
| errno = EPERM; |
| return -1; |
| } |
| } |
| |
| log_prefix_clear(); |
| |
| return 0; |
| } |
| |
| /* |
| * log_set_level |
| * Set the log level; the higher the level, the more is printed |
| * @level: the log level |
| */ |
| int log_set_level(unsigned int level) |
| { |
| log_level = level; |
| char *prefixbuf = NULL; |
| int ret = 0; |
| |
| if (level >= 5) { |
| TPMLIB_SetDebugLevel(level - 4); |
| |
| if (asprintf(&prefixbuf, "%s%s", |
| log_prefix ? log_prefix : "", |
| " ") < 0) { |
| ret = -1; |
| goto err_exit; |
| } |
| TPMLIB_SetDebugPrefix(prefixbuf); |
| free(prefixbuf); |
| } |
| |
| if (logfd != SUPPRESS_LOGGING) |
| TPMLIB_SetDebugFD(logfd); |
| |
| err_exit: |
| return ret; |
| } |
| |
| /* |
| * log_set_prefix |
| * Set the logging prefix; this function should be |
| * call before log_set_level. |
| * |
| * @prefix: string to print before every line |
| */ |
| int log_set_prefix(const char *prefix) |
| { |
| free(log_prefix); |
| |
| if (prefix) { |
| log_prefix = strdup(prefix); |
| if (!log_prefix) |
| return -1; |
| } else { |
| log_prefix = NULL; |
| } |
| return 0; |
| } |
| |
| /* |
| * Check whether to write the string to the log following |
| * the log level |
| * @string: the string to print |
| * |
| * Returns -1 in case the string must not be printed following |
| * the log level, the number of bytes used for indentation otherwise. |
| */ |
| int log_check_string(const char *string) |
| { |
| unsigned int level, i; |
| |
| if (log_level == 0) |
| return -1; |
| |
| level = log_level - 1; |
| i = 0; |
| while (1) { |
| if (string[i] == 0) |
| return -1; |
| if (string[i] != ' ') |
| return i; |
| if (i == level) |
| return -1; |
| i++; |
| } |
| } |
| |
| /* |
| * log_global_free: free memory allocated for global variables |
| */ |
| void log_global_free(void) |
| { |
| free(log_prefix); |
| log_prefix = NULL; |
| TPMLIB_SetDebugPrefix(NULL); |
| } |
| |
| /* |
| * _logprintf: |
| * Format a log line and output it to the given file descriptor. |
| * @fd: file descriptor to log to |
| * @format: printf type of format for the string |
| * @ap: list of var args to format |
| * @check_indent: whether to check the level or force printing |
| * |
| * Returns the number of bytes written on success, a value < 0 on error. |
| */ |
| static int _logprintf(int fd, const char *format, va_list ap, bool check_indent) |
| { |
| char *buf = NULL; |
| int ret = 0, len = 0; |
| |
| if (logfd == SUPPRESS_LOGGING) |
| return 0; |
| |
| if (logfd > 0) |
| fd = logfd; |
| |
| ret = vasprintf(&buf, format, ap); |
| if (ret < 0) |
| goto cleanup; |
| |
| if (!check_indent || log_check_string(buf) >= 0) { |
| if (log_prefix) { |
| ret = write_full(fd, log_prefix, strlen(log_prefix)); |
| if (ret < 0) |
| goto err_exit; |
| len = ret; |
| } |
| ret = write_full(fd, buf, strlen(buf)); |
| if (ret < 0) |
| goto err_exit; |
| ret += len; |
| } else |
| ret = 0; |
| err_exit: |
| free(buf); |
| |
| cleanup: |
| return ret; |
| } |
| |
| /* |
| * logprintf: |
| * log to stderr or logfile |
| * @fd : the foile descriptor to log to |
| * @format: the printf style format to format the var args into |
| * @... : var args list of parameters to format |
| * |
| * Returns the number of bytes written on success, a value < 0 on error. |
| */ |
| int logprintf(int fd, const char *format, ...) |
| { |
| int ret; |
| va_list ap; |
| |
| va_start(ap, format); |
| ret = _logprintf(fd, format, ap, true); |
| va_end(ap); |
| |
| return ret; |
| } |
| |
| /* |
| * logprintfA: |
| * log to stderr or logfile without checking the log level; indent each |
| * line by a number of spaces |
| * |
| * @fd : the foile descriptor to log to |
| * @indent: number of bytes to indent the string |
| * @format: the printf style format to format the var args into |
| * @... : var args list of parameters to format |
| * |
| * Returns the number of bytes written on success, a value < 0 on error. |
| */ |
| int logprintfA(int fd, unsigned int indent, const char *format, ...) |
| { |
| int ret; |
| va_list ap; |
| char spaces[20]; |
| |
| if (indent) { |
| if (indent > sizeof(spaces) - 1) |
| indent = sizeof(spaces) - 1; |
| memset(spaces, ' ', indent); |
| spaces[indent] = 0; |
| logprintfA(fd, 0, spaces, ""); |
| } |
| |
| va_start(ap, format); |
| ret = _logprintf(fd, format, ap, false); |
| va_end(ap); |
| |
| return ret; |
| } |