aboutsummaryrefslogtreecommitdiff
path: root/src/util/log.c
blob: c28bc695eb544d161554ea70482ead0eab82969c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.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

/**
 * 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);

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, ...)
{
    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 */
            fprintf (stderr, "%s\n", buffer);
            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, ...)
{
    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);
    vfprintf (stderr, fmt,
        /* log_strings[loglevel], module, file, func, line, */
        vaargs);
    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;
    char *envlevel = getenv("TSS2_LOG");
    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;
}