blob: d2781f4893de9f7bfe8fd3f705ea979ceec68dca [file] [log] [blame]
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#define LOGMODULE log
#include "log.h"
#if !defined(_MSC_VER) || defined(__INTEL_COMPILER)
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#else
/* Microsoft Visual Studio gives internal error C1001 with _builtin_expect */
#define likely(x) (x)
#define unlikely(x) (x)
#endif
#if MAXLOGLEVEL != LOGL_NONE
static const char *log_strings[] = {
"none",
"(unused)",
"ERROR",
"WARNING",
"info",
"debug",
"trace"
};
/**
* Compares two strings byte by byte and ignores the
* character's case. Stops at the n-th byte of both
* strings.
*
* This is basically a replacement of the POSIX-function
* _strncasecmp_. Since tpm2-tss is supposed to be compatible
* with ISO C99 and not with POSIX, _strncasecmp_ had to be
* replaced. This function creates lowercase representations
* of the strings and compares them bytewise.
*
* @param string1 The first of the two strings to compare
* @param string2 The second of the two strings to compare
* @param n The maximum number of bytes to compare
* @return 0 if both strings are equal (case insensitive),
* an integer greater than zero if string1 is greater than
* string 2 and an integer smaller than zero if string1 is
* smaller than string2
*
*/
static int
case_insensitive_strncmp(const char *string1,
const char *string2,
size_t n)
{
if ((string1 == NULL) && (string2 == NULL)) {
return 0;
}
if ((string1 == NULL) && (string2 != NULL)) {
return -1;
}
if ((string1 != NULL) && (string2 == NULL)) {
return 1;
}
if (n == 0) { // Zero bytes are always equal
return 0;
}
if (string1 == string2) { // return equal if they point to same location
return 0;
}
int result;
do {
result = tolower((unsigned char) *string1) - tolower((unsigned char) *string2);
if (result != 0) {
break;
}
} while (*string1++ != '\0' && *string2++ != '\0' && --n );
return result;
}
static log_level
getLogLevel(const char *module, log_level logdefault);
static FILE *
getLogFile(void)
{
#ifdef LOG_FILE_ENABLED
const char *envpath;
static FILE *file = NULL;
if (file) {
return file;
}
envpath = getenv("TSS2_LOGFILE");
if (envpath == NULL || !case_insensitive_strncmp(envpath, "stderr", 7)) {
file = stderr;
} else if (!strcmp(envpath, "-") || !case_insensitive_strncmp(envpath, "stdout", 7)) {
file = stdout;
} else {
file = fopen(envpath, "a+");
if (file == NULL) {
file = stderr;
fprintf(file, "Failed to open logging file %s: %s\n", envpath, strerror(errno));
fflush(file);
}
}
return file;
#else
return stderr;
#endif
}
void
doLogBlob(log_level loglevel, const char *module, log_level logdefault,
log_level *status,
const char *file, const char *func, int line,
const uint8_t *blob, size_t size, const char *fmt, ...)
{
FILE *logfile;
if (unlikely(*status == LOGLEVEL_UNDEFINED))
*status = getLogLevel(module, logdefault);
if (loglevel > *status)
return;
va_list vaargs;
va_start(vaargs, fmt);
/* TODO: Unfortunately, vsnprintf(NULL, 0, ...) do not behave the same as
snprintf(NULL, 0, ...). Until there is an alternative, messages on
logblob are restricted to 255 characters
int msg_len = vsnprintf(NULL, 0, fmt, vaargs); */
int msg_len = 255;
char msg[msg_len+1];
vsnprintf(msg, sizeof(msg), fmt, vaargs);
va_end(vaargs);
doLog(loglevel, module, logdefault, status, file, func, line,
"%s (size=%zi):", msg, size);
unsigned int i, y, x, off, off2;
unsigned int width = 16;
#define LINE_LEN 64
char buffer[LINE_LEN];
for (i = 1, off = 0, off2 = 0; i <= size; i++) {
if (i == 1) {
sprintf(&buffer[off], "%04x: ", i - 1);
off += 6;
}
/* data output */
sprintf(&buffer[off], "%02x", blob[i-1]);
off += 2;
/* ASCII output */
if ((i % width == 0 && i > 1) || i == size) {
sprintf(&buffer[off], " ");
off += 2;
/* Align to the right */
for (x = off; x < width * 2 + 8; x++) {
sprintf(&buffer[off], " ");
off++;
}
/* Account for a line that is not 'full' */
unsigned int less = width - (i % width);
if (less == width)
less = 0;
for (y = 0; y < width - less; y++) {
if (isgraph(blob[off2 + y])) {
sprintf(&buffer[y + off], "%c", blob[off2 + y]);
} else {
sprintf(&buffer[y + off], "%c", '.');
}
}
/* print the line and restart */
logfile = getLogFile();
fprintf (logfile, "%s\n", buffer);
fflush(logfile);
off2 = i;
off = 0;
memset(buffer, '\0', LINE_LEN);
sprintf(&buffer[off], "%04x: ", i);
off += 6;
}
}
}
void
doLog(log_level loglevel, const char *module, log_level logdefault,
log_level *status,
const char *file, const char *func, int line,
const char *msg, ...)
{
FILE *logfile;
if (unlikely(*status == LOGLEVEL_UNDEFINED))
*status = getLogLevel(module, logdefault);
if (loglevel > *status)
return;
int size = snprintf(NULL, 0, "%s:%s:%s:%d:%s() %s \n",
log_strings[loglevel], module, file, line, func, msg);
char fmt[size+1];
snprintf(fmt, sizeof(fmt), "%s:%s:%s:%d:%s() %s \n",
log_strings[loglevel], module, file, line, func, msg);
va_list vaargs;
va_start(vaargs, msg);
logfile = getLogFile();
vfprintf (logfile, fmt,
/* log_strings[loglevel], module, file, func, line, */
vaargs);
fflush(logfile);
va_end(vaargs);
}
static log_level
log_stringlevel(const char *n)
{
log_level i;
for(i = 0; i < sizeof(log_strings)/sizeof(log_strings[0]); i++) {
if (case_insensitive_strncmp(log_strings[i], n, strlen(log_strings[i])) == 0) {
return i;
}
}
return LOGLEVEL_UNDEFINED;
}
static log_level
getLogLevel(const char *module, log_level logdefault)
{
log_level loglevel = logdefault;
const char *envlevel = getenv("TSS2_LOG");
const char *i = envlevel;
if (envlevel == NULL)
return loglevel;
while ((i = strchr(i, '+')) != NULL) {
if ((envlevel <= i - strlen("all") &&
case_insensitive_strncmp(i - 3, "all", 3) == 0) ||
(envlevel <= i - strlen(module) &&
case_insensitive_strncmp(i - strlen(module), module, strlen(module)) == 0)) {
log_level tmp = log_stringlevel(i+1);
if (tmp != LOGLEVEL_UNDEFINED)
loglevel = tmp;
}
i = i + 1;
}
return loglevel;
}
#endif