/* Copyright Yann Weber This file is part of asmsh. asmsh is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. asmsh 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 General Public License for more details. You should have received a copy of the GNU General Public License along with asmsh. If not, see . */ #include "logger.h" asmsh_logger_t *_default_logger=NULL; int asmsh_logger_setup(asmsh_logger_t *logger) { if(logger) { _default_logger = logger; return 0; } _default_logger = asmsh_logger_new(ASMSH_WARN); return (_default_logger == NULL) ? -1 : 0; } asmsh_logger_t* asmsh_logger_new(asmsh_loglevel_t min_level) { asmsh_logger_t *res; int err; if((res = malloc(sizeof(*res))) == NULL) { err = errno; dprintf(2, "Error allocating logger"); errno = err; return NULL; } if((res->msgs = malloc(ASMSH_LOG_BUFFER_ALLOC)) == NULL) { err = errno; dprintf(2, "Error allocating logger's buffer"); goto err_msgs; } res->min_level = min_level; res->msgs_sz = 0; res->msgs_alloc = ASMSH_LOG_BUFFER_ALLOC; res->nxt = res->msgs; res->fmt = asmsh_log_default_fmt; return res; err_msgs: free(res); errno = err; return NULL; } void asmsh_logger_free(asmsh_logger_t* logger) { if(logger->msgs) { free(logger->msgs); } free(logger); } int asmsh_logger_dprint_fmt(int fd, asmsh_logger_t *logger, asmsh_log_fmt_f *custom_fmt) { const int BUF_ALLOC = 4096; char *buf; int bufsz, ret, res; asmsh_log_msg_t *cur; asmsh_log_fmt_f *fmt; fmt = custom_fmt?custom_fmt:logger->fmt; if((buf = malloc(BUF_ALLOC)) == NULL) { return -1; } bufsz = BUF_ALLOC; cur = (asmsh_log_msg_t*)logger->msgs; res = 0; while(cur != logger->nxt) { ret = fmt(cur, buf, bufsz); if(ret < 0) { perror("Unable to format log message"); continue; } else if (ret == BUF_ALLOC) { buf[BUF_ALLOC-1] = '\0'; for(int i=0; i<3; i++) { buf[BUF_ALLOC-(2+i)] = '.'; } } res += dprintf(fd, buf); /// TODO use write & check result cur = cur->nxt; } free(buf); logger->nxt = logger->msgs; return res; } int asmsh_logger_strdup(asmsh_logger_t *logger, char **result, size_t *res_sz) { const int BUF_ALLOC = 4096; size_t buf_sz; asmsh_log_msg_t *cur; char *buf_ptr; int ret; *res_sz = 0; buf_ptr = *result = malloc(buf_sz = BUF_ALLOC); if(*result == NULL) { perror("Enable to allocate buffer for log messages"); return -1; } cur = (asmsh_log_msg_t*)logger->msgs; while(cur != logger->nxt) { ret = logger->fmt(cur, buf_ptr, buf_sz - *res_sz); if(ret == (*res_sz - buf_sz)) { buf_sz += BUF_ALLOC; void *tmp = realloc(*result, buf_sz); } else { *res_sz += ret; cur = cur->nxt; } } } int asmsh_log(asmsh_logger_t *logger, asmsh_loglevel_t lvl, const char caller[], const char *msg) { if(!logger) { logger = _default_logger = asmsh_logger_new(ASMSH_TRACE); } if(lvl < logger->min_level) { return 0; } size_t caller_len, msg_len, total_len; caller_len = strlen(caller); msg_len = strlen(msg); total_len = msg_len + caller_len + 2 + sizeof(asmsh_log_msg_t); if(logger->msgs_alloc <= logger->msgs_sz + total_len) { void *tmp; logger->msgs_alloc += ASMSH_LOG_BUFFER_ALLOC; tmp = realloc(logger->msgs, logger->msgs_alloc); if(tmp <= 0) { return -1; } if(tmp != logger->msgs) { logger->nxt = tmp + ((void*)logger->nxt - (void*)logger->msgs); } logger->msgs = tmp; } if(logger->msgs_alloc <= logger->msgs_sz + total_len) { // still to short just after realloc, more than // ASMSH_LOG_BUFFER_ALLOC is needed, something seems // to be wrong errno = EMSGSIZE; return -1; } logger->msgs_sz += total_len; logger->nxt->level = lvl; if(time(&(logger->nxt->timestamp)) < 0) { return -1; } logger->nxt->caller = (char*)((logger->nxt)+1); logger->nxt->msg = logger->nxt->caller + caller_len + 1; strncpy(logger->nxt->caller, caller, caller_len+1); strncpy(logger->nxt->msg, msg, msg_len+1); logger->nxt->nxt = (asmsh_log_msg_t*)((void*)logger->nxt->msg+total_len); logger->nxt = logger->nxt->nxt; return 0; } const char * asmsh_loglevel_name(asmsh_loglevel_t lvl) { switch(lvl) { case ASMSH_TRACE: return "TRACE"; case ASMSH_DEBUG: return "DEBUG"; case ASMSH_INFO: return "INFO"; case ASMSH_WARN: return "WARN"; case ASMSH_ERR: return "ERR"; case ASMSH_FATAL: return "FATAL"; default: return "UNKNW"; } } int asmsh_log_default_fmt(asmsh_log_msg_t *msg, char *res, int sz) { int ret; struct tm msg_tm; char dtstr[32]; if(gmtime_r(&msg->timestamp, &msg_tm) == NULL) { strncpy(res, "DATETIME ERROR", sz); return -1; } if(strftime(dtstr, sizeof(dtstr), "%FT%H:%M:%S+00:00", &msg_tm) == 0) { strncpy(res, "DATETIME FMT ERROR", sz); return -1; } ret = snprintf(res, sz, "%s [%s](%s) : %s\n", dtstr, asmsh_loglevel_name(msg->level), msg->caller, msg->msg); return ret; } int asmsh_log_lvl_fmt(asmsh_log_msg_t *msg, char *res, int sz) { int ret; ret = snprintf(res, sz, "%s: %s\n", asmsh_loglevel_name(msg->level), msg->msg); return ret; }