1/*
2 * Copyright (c) 2018-2021 Yubico AB. All rights reserved.
3 * Use of this source code is governed by a BSD-style
4 * license that can be found in the LICENSE file.
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8#undef _GNU_SOURCE /* XSI strerror_r() */
9
10#include <stdarg.h>
11#include <stdio.h>
12
13#include "fido.h"
14
15#ifndef FIDO_NO_DIAGNOSTIC
16
17#define XXDLEN	32
18#define XXDROW	128
19#define LINELEN	256
20
21#ifndef TLS
22#define TLS
23#endif
24
25static TLS int logging;
26static TLS fido_log_handler_t *log_handler;
27
28static void
29log_on_stderr(const char *str)
30{
31	fprintf(stderr, "%s", str);
32}
33
34static void
35do_log(const char *suffix, const char *fmt, va_list args)
36{
37	char line[LINELEN], body[LINELEN];
38
39	vsnprintf(body, sizeof(body), fmt, args);
40
41	if (suffix != NULL)
42		snprintf(line, sizeof(line), "%.180s: %.70s\n", body, suffix);
43	else
44		snprintf(line, sizeof(line), "%.180s\n", body);
45
46	log_handler(line);
47}
48
49void
50fido_log_init(void)
51{
52	logging = 1;
53	log_handler = log_on_stderr;
54}
55
56void
57fido_log_debug(const char *fmt, ...)
58{
59	va_list args;
60
61	if (!logging || log_handler == NULL)
62		return;
63
64	va_start(args, fmt);
65	do_log(NULL, fmt, args);
66	va_end(args);
67}
68
69void
70fido_log_xxd(const void *buf, size_t count, const char *fmt, ...)
71{
72	const uint8_t *ptr = buf;
73	char row[XXDROW], xxd[XXDLEN];
74	va_list args;
75
76	if (!logging || log_handler == NULL)
77		return;
78
79	snprintf(row, sizeof(row), "buf=%p, len=%zu", buf, count);
80	va_start(args, fmt);
81	do_log(row, fmt, args);
82	va_end(args);
83	*row = '\0';
84
85	for (size_t i = 0; i < count; i++) {
86		*xxd = '\0';
87		if (i % 16 == 0)
88			snprintf(xxd, sizeof(xxd), "%04zu: %02x", i, *ptr++);
89		else
90			snprintf(xxd, sizeof(xxd), " %02x", *ptr++);
91		strlcat(row, xxd, sizeof(row));
92		if (i % 16 == 15 || i == count - 1) {
93			fido_log_debug("%s", row);
94			*row = '\0';
95		}
96	}
97}
98
99void
100fido_log_error(int errnum, const char *fmt, ...)
101{
102	char errstr[LINELEN];
103	va_list args;
104
105	if (!logging || log_handler == NULL)
106		return;
107	if (strerror_r(errnum, errstr, sizeof(errstr)) != 0)
108		snprintf(errstr, sizeof(errstr), "error %d", errnum);
109
110	va_start(args, fmt);
111	do_log(errstr, fmt, args);
112	va_end(args);
113}
114
115void
116fido_set_log_handler(fido_log_handler_t *handler)
117{
118	if (handler != NULL)
119		log_handler = handler;
120}
121
122#endif /* !FIDO_NO_DIAGNOSTIC */
123