diff options
Diffstat (limited to 'src/log.c')
-rw-r--r-- | src/log.c | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/src/log.c b/src/log.c new file mode 100644 index 0000000..e6a3c8a --- /dev/null +++ b/src/log.c @@ -0,0 +1,210 @@ +/* + * log.c + * + * Copyright (c) 2024 Nikias Bassen. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <time.h> +#ifdef _WIN32 +#include <windows.h> +#else +#include <sys/time.h> +#endif +#include <errno.h> + +#include <libimobiledevice-glue/thread.h> +#include <plist/plist.h> + +#include "log.h" + +static int stderr_enabled = 1; + +int log_level = LL_VERBOSE; +int print_level = LL_INFO; + +static void (*print_func)(int level, const char* fmt, va_list) = NULL; + +const char *_level_label[6] = { + " <Error>", + "<Warning>", + " <Notice>", + " <Info>", + "<Verbose>", + " <Debug>" +}; + +// Reference: https://stackoverflow.com/a/2390626/1806760 +// Initializer/finalizer sample for MSVC and GCC/Clang. +// 2010-2016 Joe Lowe. Released into the public domain. + +#ifdef __cplusplus + #define INITIALIZER(f) \ + static void f(void); \ + struct f##_t_ { f##_t_(void) { f(); } }; static f##_t_ f##_; \ + static void f(void) +#elif defined(_MSC_VER) + #pragma section(".CRT$XCU",read) + #define INITIALIZER2_(f,p) \ + static void f(void); \ + __declspec(allocate(".CRT$XCU")) void (*f##_)(void) = f; \ + __pragma(comment(linker,"/include:" p #f "_")) \ + static void f(void) + #ifdef _WIN64 + #define INITIALIZER(f) INITIALIZER2_(f,"") + #else + #define INITIALIZER(f) INITIALIZER2_(f,"_") + #endif +#else + #define INITIALIZER(f) \ + static void f(void) __attribute__((__constructor__)); \ + static void f(void) +#endif + +static mutex_t log_mutex; + +static void logger_deinit(void) +{ + mutex_destroy(&log_mutex); +} + +INITIALIZER(logger_init) +{ + mutex_init(&log_mutex); + atexit(logger_deinit); +} + +void logger(enum loglevel level, const char *fmt, ...) +{ + va_list ap; + char *fs; + + if (level > log_level) + return; + + mutex_lock(&log_mutex); + + size_t fslen = 24 + strlen(fmt); + fs = malloc(fslen); + +#ifdef _WIN32 + SYSTEMTIME lt; + GetLocalTime(<); + snprintf(fs, 24, "%02d:%02d:%02d.%03d", lt.wHour, lt.wMinute, lt.wSecond, lt.wMilliseconds); +#else + struct timeval ts; + struct tm tp_; + struct tm *tp; + + gettimeofday(&ts, NULL); +#ifdef HAVE_LOCALTIME_R + tp = localtime_r(&ts.tv_sec, &tp_); +#else + tp = localtime(&ts.tv_sec); +#endif + + strftime(fs, 9, "%H:%M:%S", tp); + snprintf(fs+8, fslen-8, ".%03d %s %s", (int)(ts.tv_usec / 1000), _level_label[level], fmt); +#endif + + va_start(ap, fmt); + if (print_func) { + if (stderr_enabled) { + vfprintf(stderr, fs, ap); + fflush(stderr); + } + if (level <= print_level) { + // skip the timestamp and log level string + print_func(level, fs+23, ap); + } + } else { + vprintf(fs, ap); + } + + va_end(ap); + + free(fs); + + mutex_unlock(&log_mutex); +} + +void logger_dump_hex(enum loglevel level, const void* buf, unsigned int len) +{ + char *fs; + + if (level > log_level) + return; + + mutex_lock(&log_mutex); + + fs = (char*)malloc(len * 3 + 1); + for (unsigned int i = 0; i < len; i++) { + snprintf(fs + i*3, 3, "%02x%c", ((unsigned char*)buf)[i], (i < len-1) ? ' ' : '\n'); + } + if (print_func) { + if (stderr_enabled) { + fprintf(stderr, "%s", fs); + fflush(stderr); + } + if (level <= print_level) { + print_func(level, "%s", fs); + } + } else { + printf("%s", fs); + } + free(fs); + + mutex_unlock(&log_mutex); +} + +void logger_dump_plist(enum loglevel level, plist_t plist, int human_readable) +{ + if (level > log_level) + return; + mutex_lock(&log_mutex); + plist_write_to_stream(plist, stderr_enabled ? stderr : stdout, (human_readable) ? PLIST_FORMAT_PRINT : PLIST_FORMAT_XML, PLIST_OPT_NONE); + mutex_unlock(&log_mutex); +} + +int logger_set_logfile(const char* path) +{ + if (!path || !strcasecmp(path, "NULL") || !strcasecmp(path, "NONE")) { + stderr_enabled = 0; + return 0; + } + stderr_enabled = 1; + if (strcmp(path, "-")) { + FILE* newf = freopen(path, "w", stderr); + if (!newf) { + logger(LL_ERROR, "Could not open logfile '%s': %s\n", path, strerror(errno)); + return -1; + } + } + return 0; +} + +void logger_set_print_func(void (*func)(int, const char*, va_list)) +{ + print_func = func; +} |