1 #ifdef HAVE_CONFIG_H
2 #include <config.h>
3 #endif
4 
5 #include <string.h>
6 #include <stdio.h>
7 #include <stdarg.h>
8 #include <stdlib.h>
9 #include <ctype.h>
10 
11 #define LOGMODULE log
12 #include "log.h"
13 
14 #include <android-base/logging.h>
15 
16 #if !defined(_MSC_VER) || defined(__INTEL_COMPILER)
17 #define likely(x)       __builtin_expect(!!(x), 1)
18 #define unlikely(x)     __builtin_expect(!!(x), 0)
19 #else
20 /* Microsoft Visual Studio gives internal error C1001 with _builtin_expect */
21 #define likely(x)       (x)
22 #define unlikely(x)     (x)
23 #endif
24 
25 extern "C" {
26 
27 /**
28  * Compares two strings byte by byte and ignores the
29  * character's case. Stops at the n-th byte of both
30  * strings.
31  *
32  * This is basically a replacement of the POSIX-function
33  * _strncasecmp_. Since tpm2-tss is supposed to be compatible
34  * with ISO C99 and not with POSIX, _strncasecmp_ had to be
35  * replaced. This function creates lowercase representations
36  * of the strings and compares them bytewise.
37  *
38  * @param string1 The first of the two strings to compare
39  * @param string2 The second of the two strings to compare
40  * @param n The maximum number of bytes to compare
41  * @return 0 if both strings are equal (case insensitive),
42  *  an integer greater than zero if string1 is greater than
43  *  string 2 and an integer smaller than zero if string1 is
44  *  smaller than string2
45  *
46  */
47 static int
case_insensitive_strncmp(const char * string1,const char * string2,size_t n)48 case_insensitive_strncmp(const char *string1,
49         const char *string2,
50         size_t n)
51 {
52     if ((string1 == NULL) && (string2 == NULL)) {
53         return 0;
54     }
55     if ((string1 == NULL) && (string2 != NULL)) {
56         return -1;
57     }
58     if ((string1 != NULL) && (string2 == NULL)) {
59         return 1;
60     }
61     if (n == 0) { // Zero bytes are always equal
62         return 0;
63     }
64     if (string1 == string2) { // return equal if they point to same location
65         return 0;
66     }
67 
68     int result;
69     do {
70         result = tolower((unsigned char) *string1) - tolower((unsigned char) *string2);
71         if (result != 0) {
72                 break;
73         }
74     } while (*string1++ != '\0' && *string2++ != '\0' && --n );
75     return result;
76 }
77 
78 static log_level
79 getLogLevel(const char *module, log_level logdefault);
80 
81 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,...)82 doLogBlob(log_level loglevel, const char *module, log_level logdefault,
83            log_level *status,
84            const char *file, const char *func, int line,
85            const uint8_t *blob, size_t size, const char *fmt, ...)
86 {
87     if (unlikely(*status == LOGLEVEL_UNDEFINED))
88         *status = getLogLevel(module, logdefault);
89     if (loglevel > *status)
90         return;
91 
92     va_list vaargs;
93     va_start(vaargs, fmt);
94     /* TODO: Unfortunately, vsnprintf(NULL, 0, ...) do not behave the same as
95        snprintf(NULL, 0, ...). Until there is an alternative, messages on
96        logblob are restricted to 255 characters
97     int msg_len = vsnprintf(NULL, 0, fmt, vaargs); */
98     int msg_len = 255;
99     char msg[msg_len+1];
100     vsnprintf(msg, sizeof(msg), fmt, vaargs);
101     va_end(vaargs);
102 
103     doLog(loglevel, module, logdefault, status, file, func, line,
104           "%s (size=%zi):", msg, size);
105 
106     unsigned int i, y, x, off, off2;
107     unsigned int width = 16;
108 #define LINE_LEN 64
109     char buffer[LINE_LEN];
110 
111     for (i = 1, off = 0, off2 = 0; i <= size; i++) {
112         if (i == 1) {
113             sprintf(&buffer[off], "%04x: ", i - 1);
114             off += 6;
115         }
116 
117         /* data output */
118         sprintf(&buffer[off], "%02x", blob[i-1]);
119         off += 2;
120 
121         /* ASCII output */
122         if ((i % width == 0 && i > 1) || i == size) {
123             sprintf(&buffer[off], "  ");
124             off += 2;
125             /* Align to the right */
126             for (x = off; x < width * 2 + 8; x++) {
127                 sprintf(&buffer[off], " ");
128                 off++;
129             }
130 
131             /* Account for a line that is not 'full' */
132             unsigned int less = width - (i % width);
133             if (less == width)
134                 less = 0;
135 
136             for (y = 0; y < width - less; y++) {
137                 if (isgraph(blob[off2 + y])) {
138                     sprintf(&buffer[y + off], "%c", blob[off2 + y]);
139                 } else {
140                     sprintf(&buffer[y + off], "%c", '.');
141                 }
142             }
143             /* print the line and restart */
144             fprintf (stderr, "%s\n", buffer);
145             off2 = i;
146             off = 0;
147             memset(buffer, '\0', LINE_LEN);
148             sprintf(&buffer[off], "%04x: ", i);
149             off += 6;
150         }
151     }
152 }
153 
154 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,...)155 doLog(log_level loglevel, const char *module, log_level logdefault,
156            log_level *status,
157            const char *file, const char *func, int line,
158            const char *msg, ...)
159 {
160     if (unlikely(*status == LOGLEVEL_UNDEFINED))
161         *status = getLogLevel(module, logdefault);
162 
163     if (loglevel > *status)
164         return;
165 
166     int size = snprintf(NULL, 0, "%s:%s:%s:%d:%s() %s ",
167                 log_strings[loglevel], module, file, line, func, msg);
168     char fmt[size+1];
169     snprintf(fmt, sizeof(fmt), "%s:%s:%s:%d:%s() %s ",
170                 log_strings[loglevel], module, file, line, func, msg);
171 
172     va_list vaargs;
173     va_start(vaargs, msg);
174     int complete_size = vsnprintf(NULL, 0, fmt, vaargs);
175     va_end(vaargs);
176 
177     va_start(vaargs, msg);
178     char complete[complete_size+1];
179     vsnprintf(complete, sizeof(complete), fmt, vaargs);
180     va_end(vaargs);
181 
182     fprintf(stderr, "%s\n", complete);
183 
184     switch (loglevel) {
185       case LOGLEVEL_NONE:
186         LOG(ERROR) << complete;
187         break;
188       case LOGLEVEL_ERROR:
189         LOG(ERROR) << complete;
190         break;
191       case LOGLEVEL_WARNING:
192         LOG(WARNING) << complete;
193         break;
194       case LOGLEVEL_INFO:
195         LOG(INFO) << complete;
196         break;
197       case LOGLEVEL_DEBUG:
198         LOG(DEBUG) << complete;
199         break;
200       case LOGLEVEL_TRACE:
201         LOG(VERBOSE) << complete;
202         break;
203       case LOGLEVEL_UNDEFINED:
204       default:
205         LOG(WARNING) << complete;
206         break;
207     }
208 }
209 
210 static log_level
log_stringlevel(const char * n)211 log_stringlevel(const char *n)
212 {
213     log_level i;
214     for(i = (log_level) 0; i < sizeof(log_strings)/sizeof(log_strings[0]); i = (log_level) ((int) i + 1)) {
215         if (case_insensitive_strncmp(log_strings[i], n, strlen(log_strings[i])) == 0) {
216             return i;
217         }
218     }
219     return LOGLEVEL_UNDEFINED;
220 }
221 
222 static log_level
getLogLevel(const char * module,log_level logdefault)223 getLogLevel(const char *module, log_level logdefault)
224 {
225     log_level loglevel = logdefault;
226     char *envlevel = getenv("TSS2_LOG");
227     char *i = envlevel;
228     if (envlevel == NULL)
229         return loglevel;
230     while ((i = strchr(i, '+')) != NULL) {
231         if ((envlevel <= i - strlen("all") &&
232 	     case_insensitive_strncmp(i - 3, "all", 3) == 0) ||
233             (envlevel <= i - strlen(module) &&
234              case_insensitive_strncmp(i - strlen(module), module, strlen(module)) == 0)) {
235             log_level tmp = log_stringlevel(i+1);
236             if (tmp != LOGLEVEL_UNDEFINED)
237                 loglevel = tmp;
238         }
239         i = i + 1;
240     }
241     return loglevel;
242 }
243 
244 } // extern "C"
245