1a74702c6SGeorge Wang/* Copyright (c) 2017 - 2022 LiteSpeed Technologies Inc.  See LICENSE. */
250aadb33SDmitri Tikhonov/*
350aadb33SDmitri Tikhonov * LSQUIC Logger implementation.
450aadb33SDmitri Tikhonov */
550aadb33SDmitri Tikhonov
6560db45bSDmitri Tikhonov#include <assert.h>
750aadb33SDmitri Tikhonov#include <errno.h>
850aadb33SDmitri Tikhonov#include <inttypes.h>
950aadb33SDmitri Tikhonov#include <stdarg.h>
1050aadb33SDmitri Tikhonov#include <stdio.h>
1150aadb33SDmitri Tikhonov#include <stdlib.h>
1250aadb33SDmitri Tikhonov#include <string.h>
13461e84d8SAmol Deshpande#ifndef WIN32
1450aadb33SDmitri Tikhonov#include <sys/time.h>
15461e84d8SAmol Deshpande#endif
1650aadb33SDmitri Tikhonov#include <time.h>
1750aadb33SDmitri Tikhonov
1850aadb33SDmitri Tikhonov#define LSQUIC_LOGGER_MODULE LSQLM_LOGGER /* Quis custodiet ipsos custodes? */
1950aadb33SDmitri Tikhonov#include "lsquic.h"
205392f7a3SLiteSpeed Tech#include "lsquic_logger.h"
2150aadb33SDmitri Tikhonov
225392f7a3SLiteSpeed Tech#define MAX_LINE_LEN 8192
2355f8042dSDmitri Tikhonov/* Expanded TRUNC_FMT should not exceed TRUNC_SZ bytes.  At the same time,
2455f8042dSDmitri Tikhonov * TRUNC_SZ should be significantly smaller than MAX_LINE_LEN.
2555f8042dSDmitri Tikhonov */
2655f8042dSDmitri Tikhonov#define TRUNC_FMT "<truncated, need %d bytes>"
2755f8042dSDmitri Tikhonov#define TRUNC_SZ 40
2855f8042dSDmitri Tikhonov#define FORMAT_PROBLEM(lb, len, max) ((lb < 0) || (lb + len >= max))
295392f7a3SLiteSpeed Tech
305392f7a3SLiteSpeed Tech/* TODO: display GQUIC CIDs in Chrome-compatible format */
313229dd11SDmitri Tikhonov
3250aadb33SDmitri Tikhonovstatic enum lsquic_logger_timestamp_style g_llts = LLTS_NONE;
3350aadb33SDmitri Tikhonov
3450aadb33SDmitri Tikhonovstatic int
355392f7a3SLiteSpeed Technull_log_buf (void *ctx, const char *buf, size_t len)
3650aadb33SDmitri Tikhonov{
3750aadb33SDmitri Tikhonov    return 0;
3850aadb33SDmitri Tikhonov}
3950aadb33SDmitri Tikhonov
4050aadb33SDmitri Tikhonovstatic int
415392f7a3SLiteSpeed Techfile_log_buf (void *ctx, const char *buf, size_t len)
4250aadb33SDmitri Tikhonov{
435392f7a3SLiteSpeed Tech    return (int)fwrite(buf, sizeof(char), len, (FILE *) ctx);
4450aadb33SDmitri Tikhonov}
4550aadb33SDmitri Tikhonov
4650aadb33SDmitri Tikhonovstatic const struct lsquic_logger_if file_logger_if = {
475392f7a3SLiteSpeed Tech    .log_buf    = file_log_buf,
4850aadb33SDmitri Tikhonov};
4950aadb33SDmitri Tikhonov
5050aadb33SDmitri Tikhonovstatic const struct lsquic_logger_if null_logger_if = {
515392f7a3SLiteSpeed Tech    .log_buf    = null_log_buf,
5250aadb33SDmitri Tikhonov};
5350aadb33SDmitri Tikhonov
5450aadb33SDmitri Tikhonovstatic void *logger_ctx = NULL;
5550aadb33SDmitri Tikhonovstatic const struct lsquic_logger_if *logger_if = &null_logger_if;
5650aadb33SDmitri Tikhonov
5750aadb33SDmitri Tikhonovenum lsq_log_level lsq_log_levels[N_LSQUIC_LOGGER_MODULES] = {
5850aadb33SDmitri Tikhonov    [LSQLM_NOMODULE]    = LSQ_LOG_WARN,
5950aadb33SDmitri Tikhonov    [LSQLM_LOGGER]      = LSQ_LOG_WARN,
6050aadb33SDmitri Tikhonov    [LSQLM_EVENT]       = LSQ_LOG_WARN,
6150aadb33SDmitri Tikhonov    [LSQLM_ENGINE]      = LSQ_LOG_WARN,
6250aadb33SDmitri Tikhonov    [LSQLM_CONN]        = LSQ_LOG_WARN,
6350aadb33SDmitri Tikhonov    [LSQLM_STREAM]      = LSQ_LOG_WARN,
6450aadb33SDmitri Tikhonov    [LSQLM_PARSE]       = LSQ_LOG_WARN,
6550aadb33SDmitri Tikhonov    [LSQLM_CFCW]        = LSQ_LOG_WARN,
6650aadb33SDmitri Tikhonov    [LSQLM_SFCW]        = LSQ_LOG_WARN,
6750aadb33SDmitri Tikhonov    [LSQLM_SENDCTL]     = LSQ_LOG_WARN,
6850aadb33SDmitri Tikhonov    [LSQLM_ALARMSET]    = LSQ_LOG_WARN,
6950aadb33SDmitri Tikhonov    [LSQLM_CRYPTO]      = LSQ_LOG_WARN,
7050aadb33SDmitri Tikhonov    [LSQLM_HANDSHAKE]   = LSQ_LOG_WARN,
7150aadb33SDmitri Tikhonov    [LSQLM_HSK_ADAPTER] = LSQ_LOG_WARN,
725392f7a3SLiteSpeed Tech    [LSQLM_BBR]         = LSQ_LOG_WARN,
7350aadb33SDmitri Tikhonov    [LSQLM_CUBIC]       = LSQ_LOG_WARN,
74b1a7c3f9SDmitri Tikhonov    [LSQLM_ADAPTIVE_CC] = LSQ_LOG_WARN,
7550aadb33SDmitri Tikhonov    [LSQLM_HEADERS]     = LSQ_LOG_WARN,
7650aadb33SDmitri Tikhonov    [LSQLM_FRAME_READER]= LSQ_LOG_WARN,
7750aadb33SDmitri Tikhonov    [LSQLM_FRAME_WRITER]= LSQ_LOG_WARN,
785392f7a3SLiteSpeed Tech    [LSQLM_MINI_CONN]   = LSQ_LOG_WARN,
795392f7a3SLiteSpeed Tech    [LSQLM_TOKGEN]      = LSQ_LOG_WARN,
8050aadb33SDmitri Tikhonov    [LSQLM_ENG_HIST]    = LSQ_LOG_WARN,
8150aadb33SDmitri Tikhonov    [LSQLM_SPI]         = LSQ_LOG_WARN,
82fbc6cc04SDmitri Tikhonov    [LSQLM_HPI]         = LSQ_LOG_WARN,
8350aadb33SDmitri Tikhonov    [LSQLM_DI]          = LSQ_LOG_WARN,
845392f7a3SLiteSpeed Tech    [LSQLM_PRQ]         = LSQ_LOG_WARN,
8550aadb33SDmitri Tikhonov    [LSQLM_PACER]       = LSQ_LOG_WARN,
863b55e6aeSDmitri Tikhonov    [LSQLM_HTTP1X]      = LSQ_LOG_WARN,
8755cd0b38SDmitri Tikhonov    [LSQLM_QLOG]        = LSQ_LOG_WARN,
885392f7a3SLiteSpeed Tech    [LSQLM_TRAPA]       = LSQ_LOG_WARN,
895392f7a3SLiteSpeed Tech    [LSQLM_PURGA]       = LSQ_LOG_WARN,
905392f7a3SLiteSpeed Tech    [LSQLM_HCSI_READER] = LSQ_LOG_WARN,
915392f7a3SLiteSpeed Tech    [LSQLM_HCSO_WRITER] = LSQ_LOG_WARN,
925392f7a3SLiteSpeed Tech    [LSQLM_QENC_HDL]    = LSQ_LOG_WARN,
935392f7a3SLiteSpeed Tech    [LSQLM_QDEC_HDL]    = LSQ_LOG_WARN,
945392f7a3SLiteSpeed Tech    [LSQLM_QPACK_ENC]    = LSQ_LOG_WARN,
955392f7a3SLiteSpeed Tech    [LSQLM_QPACK_DEC]    = LSQ_LOG_WARN,
965392f7a3SLiteSpeed Tech    [LSQLM_PRIO]        = LSQ_LOG_WARN,
975392f7a3SLiteSpeed Tech    [LSQLM_BW_SAMPLER]  = LSQ_LOG_WARN,
98b8fa6195SDmitri Tikhonov    [LSQLM_PACKET_RESIZE] = LSQ_LOG_WARN,
99758aff32SDmitri Tikhonov    [LSQLM_CONN_STATS]  = LSQ_LOG_WARN,
10050aadb33SDmitri Tikhonov};
10150aadb33SDmitri Tikhonov
10250aadb33SDmitri Tikhonovconst char *const lsqlm_to_str[N_LSQUIC_LOGGER_MODULES] = {
10350aadb33SDmitri Tikhonov    [LSQLM_NOMODULE]    = "",
10450aadb33SDmitri Tikhonov    [LSQLM_LOGGER]      = "logger",
10550aadb33SDmitri Tikhonov    [LSQLM_EVENT]       = "event",
10650aadb33SDmitri Tikhonov    [LSQLM_ENGINE]      = "engine",
10750aadb33SDmitri Tikhonov    [LSQLM_CONN]        = "conn",
10850aadb33SDmitri Tikhonov    [LSQLM_STREAM]      = "stream",
10950aadb33SDmitri Tikhonov    [LSQLM_PARSE]       = "parse",
11050aadb33SDmitri Tikhonov    [LSQLM_CFCW]        = "cfcw",
11150aadb33SDmitri Tikhonov    [LSQLM_SFCW]        = "sfcw",
11250aadb33SDmitri Tikhonov    [LSQLM_SENDCTL]     = "sendctl",
11350aadb33SDmitri Tikhonov    [LSQLM_ALARMSET]    = "alarmset",
11450aadb33SDmitri Tikhonov    [LSQLM_CRYPTO]      = "crypto",
11550aadb33SDmitri Tikhonov    [LSQLM_HANDSHAKE]   = "handshake",
11650aadb33SDmitri Tikhonov    [LSQLM_HSK_ADAPTER] = "hsk-adapter",
1175392f7a3SLiteSpeed Tech    [LSQLM_BBR]         = "bbr",
11850aadb33SDmitri Tikhonov    [LSQLM_CUBIC]       = "cubic",
119b1a7c3f9SDmitri Tikhonov    [LSQLM_ADAPTIVE_CC] = "adaptive-cc",
12050aadb33SDmitri Tikhonov    [LSQLM_HEADERS]     = "headers",
12150aadb33SDmitri Tikhonov    [LSQLM_FRAME_READER]= "frame-reader",
12250aadb33SDmitri Tikhonov    [LSQLM_FRAME_WRITER]= "frame-writer",
1235392f7a3SLiteSpeed Tech    [LSQLM_MINI_CONN]   = "mini-conn",
1245392f7a3SLiteSpeed Tech    [LSQLM_TOKGEN]      = "tokgen",
12550aadb33SDmitri Tikhonov    [LSQLM_ENG_HIST]    = "eng-hist",
12650aadb33SDmitri Tikhonov    [LSQLM_SPI]         = "spi",
127fbc6cc04SDmitri Tikhonov    [LSQLM_HPI]         = "hpi",
12850aadb33SDmitri Tikhonov    [LSQLM_DI]          = "di",
1295392f7a3SLiteSpeed Tech    [LSQLM_PRQ]         = "prq",
13050aadb33SDmitri Tikhonov    [LSQLM_PACER]       = "pacer",
1313b55e6aeSDmitri Tikhonov    [LSQLM_HTTP1X]      = "http1x",
13255cd0b38SDmitri Tikhonov    [LSQLM_QLOG]        = "qlog",
1335392f7a3SLiteSpeed Tech    [LSQLM_TRAPA]       = "trapa",
1345392f7a3SLiteSpeed Tech    [LSQLM_PURGA]       = "purga",
1355392f7a3SLiteSpeed Tech    [LSQLM_HCSI_READER] = "hcsi-reader",
1365392f7a3SLiteSpeed Tech    [LSQLM_HCSO_WRITER] = "hcso-writer",
1375392f7a3SLiteSpeed Tech    [LSQLM_QENC_HDL]    = "qenc-hdl",
1385392f7a3SLiteSpeed Tech    [LSQLM_QDEC_HDL]    = "qdec-hdl",
1395392f7a3SLiteSpeed Tech    [LSQLM_QPACK_ENC]    = "qpack-enc",
1405392f7a3SLiteSpeed Tech    [LSQLM_QPACK_DEC]    = "qpack-dec",
1415392f7a3SLiteSpeed Tech    [LSQLM_PRIO]        = "prio",
1425392f7a3SLiteSpeed Tech    [LSQLM_BW_SAMPLER]  = "bw-sampler",
143b8fa6195SDmitri Tikhonov    [LSQLM_PACKET_RESIZE] = "packet-resize",
144758aff32SDmitri Tikhonov    [LSQLM_CONN_STATS]  = "conn-stats",
14550aadb33SDmitri Tikhonov};
14650aadb33SDmitri Tikhonov
14750aadb33SDmitri Tikhonovconst char *const lsq_loglevel2str[N_LSQUIC_LOG_LEVELS] = {
14850aadb33SDmitri Tikhonov    [LSQ_LOG_ALERT]   =  "ALERT",
14950aadb33SDmitri Tikhonov    [LSQ_LOG_CRIT]    =  "CRIT",
15050aadb33SDmitri Tikhonov    [LSQ_LOG_DEBUG]   =  "DEBUG",
15150aadb33SDmitri Tikhonov    [LSQ_LOG_EMERG]   =  "EMERG",
15250aadb33SDmitri Tikhonov    [LSQ_LOG_ERROR]   =  "ERROR",
15350aadb33SDmitri Tikhonov    [LSQ_LOG_INFO]    =  "INFO",
15450aadb33SDmitri Tikhonov    [LSQ_LOG_NOTICE]  =  "NOTICE",
15550aadb33SDmitri Tikhonov    [LSQ_LOG_WARN]    =  "WARN",
15650aadb33SDmitri Tikhonov};
15750aadb33SDmitri Tikhonov
15850aadb33SDmitri Tikhonov
159b93f59beSBob Perper#ifdef WIN32
160560db45bSDmitri Tikhonov#define DELTA_EPOCH_IN_TICKS  116444736000000000Ui64
161b93f59beSBob Perperstruct timezone
162b93f59beSBob Perper{
163ab5c8df2SBob Perper    time_t tz_minuteswest;         /* minutes W of Greenwich */
164ab5c8df2SBob Perper    time_t tz_dsttime;             /* type of dst correction */
165b93f59beSBob Perper};
166b93f59beSBob Perper
167b93f59beSBob Perperstatic int
168b93f59beSBob Perpergettimeofday (struct timeval *tv, struct timezone *tz)
169b93f59beSBob Perper{
170b93f59beSBob Perper    FILETIME ft;
171560db45bSDmitri Tikhonov    uint64_t tmpres;
172b93f59beSBob Perper    static int tzflag;
173b93f59beSBob Perper
174b93f59beSBob Perper    if (NULL != tv)
175b93f59beSBob Perper    {
176b93f59beSBob Perper        GetSystemTimeAsFileTime(&ft);
177b93f59beSBob Perper
178560db45bSDmitri Tikhonov        tmpres = ((uint64_t) ft.dwHighDateTime << 32)
179560db45bSDmitri Tikhonov               | (ft.dwLowDateTime);
180b93f59beSBob Perper
181560db45bSDmitri Tikhonov        tmpres -= DELTA_EPOCH_IN_TICKS;
182560db45bSDmitri Tikhonov        tv->tv_sec = tmpres / 10000000;
183560db45bSDmitri Tikhonov        tv->tv_usec = tmpres % 1000000;
184b93f59beSBob Perper    }
185b93f59beSBob Perper
186b93f59beSBob Perper    if (NULL != tz)
187b93f59beSBob Perper    {
188b93f59beSBob Perper        if (!tzflag)
189b93f59beSBob Perper        {
190b93f59beSBob Perper            _tzset();
191b93f59beSBob Perper            tzflag++;
192b93f59beSBob Perper        }
193b93f59beSBob Perper        tz->tz_minuteswest = _timezone / 60;
194b93f59beSBob Perper        tz->tz_dsttime = _daylight;
195b93f59beSBob Perper    }
196b93f59beSBob Perper
197b93f59beSBob Perper    return 0;
198b93f59beSBob Perper}
199b93f59beSBob Perper#endif
200b93f59beSBob Perper
201b93f59beSBob Perper
2025392f7a3SLiteSpeed Techstatic size_t
2035392f7a3SLiteSpeed Techprint_timestamp (char *buf, size_t max)
20450aadb33SDmitri Tikhonov{
20550aadb33SDmitri Tikhonov    struct tm tm;
20650aadb33SDmitri Tikhonov    struct timeval tv;
2075392f7a3SLiteSpeed Tech    size_t len = 0;
2085392f7a3SLiteSpeed Tech
20950aadb33SDmitri Tikhonov    gettimeofday(&tv, NULL);
210ab5c8df2SBob Perper#ifdef WIN32
211ab5c8df2SBob Perper    {
2127b1586ddSDmitri Tikhonov        time_t t = tv.tv_sec;
213560db45bSDmitri Tikhonov#ifndef NDEBUG
2147b1586ddSDmitri Tikhonov        errno_t e =
215560db45bSDmitri Tikhonov#endif
2167b1586ddSDmitri Tikhonov	localtime_s(&tm, &t);
217560db45bSDmitri Tikhonov	assert(!e);
218ab5c8df2SBob Perper    }
219ab5c8df2SBob Perper#else
22050aadb33SDmitri Tikhonov    localtime_r(&tv.tv_sec, &tm);
221ab5c8df2SBob Perper#endif
222bfc7bfd8SDmitri Tikhonov    if (g_llts == LLTS_YYYYMMDD_HHMMSSUS)
2235392f7a3SLiteSpeed Tech        len = snprintf(buf, max, "%04d-%02d-%02d %02d:%02d:%02d.%06d ",
224bfc7bfd8SDmitri Tikhonov            tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
225bfc7bfd8SDmitri Tikhonov            tm.tm_hour, tm.tm_min, tm.tm_sec, (int) (tv.tv_usec));
226bfc7bfd8SDmitri Tikhonov    else if (g_llts == LLTS_YYYYMMDD_HHMMSSMS)
2275392f7a3SLiteSpeed Tech        len = snprintf(buf, max, "%04d-%02d-%02d %02d:%02d:%02d.%03d ",
22850aadb33SDmitri Tikhonov            tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
22950aadb33SDmitri Tikhonov            tm.tm_hour, tm.tm_min, tm.tm_sec, (int) (tv.tv_usec / 1000));
23050aadb33SDmitri Tikhonov    else if (g_llts == LLTS_HHMMSSMS)
2315392f7a3SLiteSpeed Tech        len = snprintf(buf, max, "%02d:%02d:%02d.%03d ",
2325392f7a3SLiteSpeed Tech            tm.tm_hour, tm.tm_min, tm.tm_sec, (int) (tv.tv_usec / 1000));
23350aadb33SDmitri Tikhonov    else if (g_llts == LLTS_HHMMSSUS)
2345392f7a3SLiteSpeed Tech        len = snprintf(buf, max, "%02d:%02d:%02d.%06d ",
2355392f7a3SLiteSpeed Tech            tm.tm_hour, tm.tm_min, tm.tm_sec, (int) tv.tv_usec);
23650aadb33SDmitri Tikhonov    else if (g_llts == LLTS_CHROMELIKE)
2375392f7a3SLiteSpeed Tech        len = snprintf(buf, max, "%02d%02d/%02d%02d%02d.%06d ",
2385392f7a3SLiteSpeed Tech            tm.tm_mon + 1, tm.tm_mday,tm.tm_hour, tm.tm_min,
2395392f7a3SLiteSpeed Tech            tm.tm_sec, (int) tv.tv_usec);
2405392f7a3SLiteSpeed Tech    return len;
24150aadb33SDmitri Tikhonov}
24250aadb33SDmitri Tikhonov
24350aadb33SDmitri Tikhonov
24450aadb33SDmitri Tikhonovvoid
24550aadb33SDmitri Tikhonovlsquic_logger_log3 (enum lsq_log_level log_level,
24650aadb33SDmitri Tikhonov                    enum lsquic_logger_module module,
2475392f7a3SLiteSpeed Tech                    const lsquic_cid_t *conn_id, lsquic_stream_id_t stream_id,
2485392f7a3SLiteSpeed Tech                    const char *fmt, ...)
24950aadb33SDmitri Tikhonov{
25050aadb33SDmitri Tikhonov    const int saved_errno = errno;
2515392f7a3SLiteSpeed Tech    char cidbuf_[MAX_CID_LEN * 2 + 1];
2525392f7a3SLiteSpeed Tech    size_t len = 0;
25355f8042dSDmitri Tikhonov    int lb;
2545392f7a3SLiteSpeed Tech    size_t max = MAX_LINE_LEN;
2555392f7a3SLiteSpeed Tech    char buf[MAX_LINE_LEN];
25650aadb33SDmitri Tikhonov
25750aadb33SDmitri Tikhonov    if (g_llts != LLTS_NONE)
2585392f7a3SLiteSpeed Tech    {
2595392f7a3SLiteSpeed Tech        lb = print_timestamp(buf, max);
2605392f7a3SLiteSpeed Tech        if (FORMAT_PROBLEM(lb, len, max))
2615392f7a3SLiteSpeed Tech            goto end;
2625392f7a3SLiteSpeed Tech        len += lb;
2635392f7a3SLiteSpeed Tech    }
2645392f7a3SLiteSpeed Tech    lb = snprintf(buf + len, max - len, "[%s] [QUIC:%"CID_FMT"-%"PRIu64"] %s: ",
2655392f7a3SLiteSpeed Tech        lsq_loglevel2str[log_level], CID_BITS(conn_id),
2665392f7a3SLiteSpeed Tech        stream_id, lsqlm_to_str[module]);
2675392f7a3SLiteSpeed Tech    if (FORMAT_PROBLEM(lb, len, max))
2685392f7a3SLiteSpeed Tech        goto end;
2695392f7a3SLiteSpeed Tech    len += lb;
27050aadb33SDmitri Tikhonov    va_list ap;
27150aadb33SDmitri Tikhonov    va_start(ap, fmt);
2725392f7a3SLiteSpeed Tech    lb = vsnprintf(buf + len, max - len, fmt, ap);
273a0e1aeeeSDmitri Tikhonov    va_end(ap);
27455f8042dSDmitri Tikhonov    if (lb > 0 && (size_t) lb >= max - len && max - len >= TRUNC_SZ)
27555f8042dSDmitri Tikhonov    {
27655f8042dSDmitri Tikhonov        len = max - TRUNC_SZ;
27755f8042dSDmitri Tikhonov        lb = snprintf(buf + max - TRUNC_SZ, TRUNC_SZ, TRUNC_FMT, lb);
27855f8042dSDmitri Tikhonov    }
2795392f7a3SLiteSpeed Tech    if (FORMAT_PROBLEM(lb, len, max))
2805392f7a3SLiteSpeed Tech        goto end;
2815392f7a3SLiteSpeed Tech    len += lb;
2825392f7a3SLiteSpeed Tech    lb = snprintf(buf + len, max - len, "\n");
2835392f7a3SLiteSpeed Tech    if (FORMAT_PROBLEM(lb, len, max))
2845392f7a3SLiteSpeed Tech        goto end;
2855392f7a3SLiteSpeed Tech    len += lb;
2865392f7a3SLiteSpeed Tech    logger_if->log_buf(logger_ctx, buf, len);
2875392f7a3SLiteSpeed Techend:
28850aadb33SDmitri Tikhonov    errno = saved_errno;
28950aadb33SDmitri Tikhonov}
29050aadb33SDmitri Tikhonov
29150aadb33SDmitri Tikhonov
29250aadb33SDmitri Tikhonovvoid
29350aadb33SDmitri Tikhonovlsquic_logger_log2 (enum lsq_log_level log_level,
29450aadb33SDmitri Tikhonov                    enum lsquic_logger_module module,
2955392f7a3SLiteSpeed Tech                    const struct lsquic_cid *conn_id, const char *fmt, ...)
29650aadb33SDmitri Tikhonov{
29750aadb33SDmitri Tikhonov    const int saved_errno = errno;
2985392f7a3SLiteSpeed Tech    char cidbuf_[MAX_CID_LEN * 2 + 1];
2995392f7a3SLiteSpeed Tech    size_t len = 0;
30055f8042dSDmitri Tikhonov    int lb;
3015392f7a3SLiteSpeed Tech    size_t max = MAX_LINE_LEN;
3025392f7a3SLiteSpeed Tech    char buf[MAX_LINE_LEN];
30350aadb33SDmitri Tikhonov
30450aadb33SDmitri Tikhonov    if (g_llts != LLTS_NONE)
3055392f7a3SLiteSpeed Tech    {
3065392f7a3SLiteSpeed Tech        lb = print_timestamp(buf, max);
3075392f7a3SLiteSpeed Tech        if (FORMAT_PROBLEM(lb, len, max))
3085392f7a3SLiteSpeed Tech            goto end;
3095392f7a3SLiteSpeed Tech        len += lb;
3105392f7a3SLiteSpeed Tech    }
31150aadb33SDmitri Tikhonov
3125392f7a3SLiteSpeed Tech    lb = snprintf(buf + len, max - len, "[%s] [QUIC:%"CID_FMT"] %s: ",
3135392f7a3SLiteSpeed Tech        lsq_loglevel2str[log_level], CID_BITS(conn_id), lsqlm_to_str[module]);
3145392f7a3SLiteSpeed Tech    if (FORMAT_PROBLEM(lb, len, max))
3155392f7a3SLiteSpeed Tech        goto end;
3165392f7a3SLiteSpeed Tech    len += lb;
31750aadb33SDmitri Tikhonov    va_list ap;
31850aadb33SDmitri Tikhonov    va_start(ap, fmt);
3195392f7a3SLiteSpeed Tech    lb = vsnprintf(buf + len, max - len, fmt, ap);
320a0e1aeeeSDmitri Tikhonov    va_end(ap);
32155f8042dSDmitri Tikhonov    if (lb > 0 && (size_t) lb >= max - len && max - len >= TRUNC_SZ)
32255f8042dSDmitri Tikhonov    {
32355f8042dSDmitri Tikhonov        len = max - TRUNC_SZ;
32455f8042dSDmitri Tikhonov        lb = snprintf(buf + max - TRUNC_SZ, TRUNC_SZ, TRUNC_FMT, lb);
32555f8042dSDmitri Tikhonov    }
3265392f7a3SLiteSpeed Tech    if (FORMAT_PROBLEM(lb, len, max))
3275392f7a3SLiteSpeed Tech        goto end;
3285392f7a3SLiteSpeed Tech    len += lb;
3295392f7a3SLiteSpeed Tech    lb = snprintf(buf + len, max - len, "\n");
3305392f7a3SLiteSpeed Tech    if (FORMAT_PROBLEM(lb, len, max))
3315392f7a3SLiteSpeed Tech        goto end;
3325392f7a3SLiteSpeed Tech    len += lb;
3335392f7a3SLiteSpeed Tech    logger_if->log_buf(logger_ctx, buf, len);
3345392f7a3SLiteSpeed Techend:
33550aadb33SDmitri Tikhonov    errno = saved_errno;
33650aadb33SDmitri Tikhonov}
33750aadb33SDmitri Tikhonov
33850aadb33SDmitri Tikhonov
33950aadb33SDmitri Tikhonovvoid
34050aadb33SDmitri Tikhonovlsquic_logger_log1 (enum lsq_log_level log_level,
34150aadb33SDmitri Tikhonov                    enum lsquic_logger_module module,
34250aadb33SDmitri Tikhonov                    const char *fmt, ...)
34350aadb33SDmitri Tikhonov{
34450aadb33SDmitri Tikhonov    const int saved_errno = errno;
3455392f7a3SLiteSpeed Tech    size_t len = 0;
34655f8042dSDmitri Tikhonov    int lb;
3475392f7a3SLiteSpeed Tech    size_t max = MAX_LINE_LEN;
3485392f7a3SLiteSpeed Tech    char buf[MAX_LINE_LEN];
34950aadb33SDmitri Tikhonov
35050aadb33SDmitri Tikhonov    if (g_llts != LLTS_NONE)
3515392f7a3SLiteSpeed Tech    {
3525392f7a3SLiteSpeed Tech        lb = print_timestamp(buf, max);
3535392f7a3SLiteSpeed Tech        if (FORMAT_PROBLEM(lb, len, max))
3545392f7a3SLiteSpeed Tech            goto end;
3555392f7a3SLiteSpeed Tech        len += lb;
3565392f7a3SLiteSpeed Tech    }
3575392f7a3SLiteSpeed Tech    lb = snprintf(buf + len, max - len, "[%s] %s: ", lsq_loglevel2str[log_level],
35850aadb33SDmitri Tikhonov                                                lsqlm_to_str[module]);
3595392f7a3SLiteSpeed Tech    if (FORMAT_PROBLEM(lb, len, max))
3605392f7a3SLiteSpeed Tech        goto end;
3615392f7a3SLiteSpeed Tech    len += lb;
36250aadb33SDmitri Tikhonov    va_list ap;
36350aadb33SDmitri Tikhonov    va_start(ap, fmt);
3645392f7a3SLiteSpeed Tech    lb = vsnprintf(buf + len, max - len, fmt, ap);
365a0e1aeeeSDmitri Tikhonov    va_end(ap);
36655f8042dSDmitri Tikhonov    if (lb > 0 && (size_t) lb >= max - len && max - len >= TRUNC_SZ)
36755f8042dSDmitri Tikhonov    {
36855f8042dSDmitri Tikhonov        len = max - TRUNC_SZ;
36955f8042dSDmitri Tikhonov        lb = snprintf(buf + max - TRUNC_SZ, TRUNC_SZ, TRUNC_FMT, lb);
37055f8042dSDmitri Tikhonov    }
3715392f7a3SLiteSpeed Tech    if (FORMAT_PROBLEM(lb, len, max))
3725392f7a3SLiteSpeed Tech        goto end;
3735392f7a3SLiteSpeed Tech    len += lb;
3745392f7a3SLiteSpeed Tech    lb = snprintf(buf + len, max - len, "\n");
3755392f7a3SLiteSpeed Tech    if (FORMAT_PROBLEM(lb, len, max))
3765392f7a3SLiteSpeed Tech        goto end;
3775392f7a3SLiteSpeed Tech    len += lb;
3785392f7a3SLiteSpeed Tech    logger_if->log_buf(logger_ctx, buf, len);
3795392f7a3SLiteSpeed Techend:
38050aadb33SDmitri Tikhonov    errno = saved_errno;
38150aadb33SDmitri Tikhonov}
38250aadb33SDmitri Tikhonov
38350aadb33SDmitri Tikhonov
38450aadb33SDmitri Tikhonovvoid
38550aadb33SDmitri Tikhonovlsquic_logger_log0 (enum lsq_log_level log_level, const char *fmt, ...)
38650aadb33SDmitri Tikhonov{
38750aadb33SDmitri Tikhonov    const int saved_errno = errno;
3885392f7a3SLiteSpeed Tech    size_t len = 0;
38955f8042dSDmitri Tikhonov    int lb;
3905392f7a3SLiteSpeed Tech    size_t max = MAX_LINE_LEN;
3915392f7a3SLiteSpeed Tech    char buf[MAX_LINE_LEN];
39250aadb33SDmitri Tikhonov
39350aadb33SDmitri Tikhonov    if (g_llts != LLTS_NONE)
3945392f7a3SLiteSpeed Tech    {
3955392f7a3SLiteSpeed Tech        lb = print_timestamp(buf, max);
3965392f7a3SLiteSpeed Tech        if (FORMAT_PROBLEM(lb, len, max))
3975392f7a3SLiteSpeed Tech            goto end;
3985392f7a3SLiteSpeed Tech        len += lb;
3995392f7a3SLiteSpeed Tech    }
40050aadb33SDmitri Tikhonov
4015392f7a3SLiteSpeed Tech    lb = snprintf(buf + len, max - len, "[%s] ", lsq_loglevel2str[log_level]);
4025392f7a3SLiteSpeed Tech    if (FORMAT_PROBLEM(lb, len, max))
4035392f7a3SLiteSpeed Tech        goto end;
4045392f7a3SLiteSpeed Tech    len += lb;
40550aadb33SDmitri Tikhonov    va_list ap;
40650aadb33SDmitri Tikhonov    va_start(ap, fmt);
4075392f7a3SLiteSpeed Tech    lb = vsnprintf(buf + len, max - len, fmt, ap);
40850aadb33SDmitri Tikhonov    va_end(ap);
40955f8042dSDmitri Tikhonov    if (lb > 0 && (size_t) lb >= max - len && max - len >= TRUNC_SZ)
41055f8042dSDmitri Tikhonov    {
41155f8042dSDmitri Tikhonov        len = max - TRUNC_SZ;
41255f8042dSDmitri Tikhonov        lb = snprintf(buf + max - TRUNC_SZ, TRUNC_SZ, TRUNC_FMT, lb);
41355f8042dSDmitri Tikhonov    }
4145392f7a3SLiteSpeed Tech    if (FORMAT_PROBLEM(lb, len, max))
4155392f7a3SLiteSpeed Tech        goto end;
4165392f7a3SLiteSpeed Tech    len += lb;
4175392f7a3SLiteSpeed Tech    lb = snprintf(buf + len, max - len, "\n");
4185392f7a3SLiteSpeed Tech    if (FORMAT_PROBLEM(lb, len, max))
4195392f7a3SLiteSpeed Tech        goto end;
4205392f7a3SLiteSpeed Tech    len += lb;
4215392f7a3SLiteSpeed Tech    logger_if->log_buf(logger_ctx, buf, len);
4225392f7a3SLiteSpeed Techend:
42350aadb33SDmitri Tikhonov    errno = saved_errno;
42450aadb33SDmitri Tikhonov}
42550aadb33SDmitri Tikhonov
42650aadb33SDmitri Tikhonov
42750aadb33SDmitri Tikhonovvoid
42850aadb33SDmitri Tikhonovlsquic_logger_init (const struct lsquic_logger_if *lif, void *lctx,
42950aadb33SDmitri Tikhonov                    unsigned llts)
43050aadb33SDmitri Tikhonov{
43150aadb33SDmitri Tikhonov    logger_if  = lif;
43250aadb33SDmitri Tikhonov    logger_ctx = lctx;
43350aadb33SDmitri Tikhonov    if (llts < N_LLTS)
43450aadb33SDmitri Tikhonov        g_llts = llts;
43550aadb33SDmitri Tikhonov    LSQ_DEBUG("%s called", __func__);
43650aadb33SDmitri Tikhonov}
43750aadb33SDmitri Tikhonov
43850aadb33SDmitri Tikhonov
43950aadb33SDmitri Tikhonovenum lsquic_logger_module
44050aadb33SDmitri Tikhonovlsquic_str_to_logger_module (const char *str)
44150aadb33SDmitri Tikhonov{
44250aadb33SDmitri Tikhonov    enum lsquic_logger_module i;
44350aadb33SDmitri Tikhonov    for (i = 0; i < sizeof(lsqlm_to_str) / sizeof(lsqlm_to_str[0]); ++i)
44450aadb33SDmitri Tikhonov        if (0 == strcasecmp(lsqlm_to_str[i], str))
44550aadb33SDmitri Tikhonov            return i;
44650aadb33SDmitri Tikhonov    return -1;
44750aadb33SDmitri Tikhonov}
44850aadb33SDmitri Tikhonov
44950aadb33SDmitri Tikhonov
45050aadb33SDmitri Tikhonovenum lsq_log_level
45150aadb33SDmitri Tikhonovlsquic_str_to_log_level (const char *str)
45250aadb33SDmitri Tikhonov{
45350aadb33SDmitri Tikhonov    if (0 == strcasecmp(str, "emerg"))
45450aadb33SDmitri Tikhonov        return LSQ_LOG_EMERG;
45550aadb33SDmitri Tikhonov    if (0 == strcasecmp(str, "alert"))
45650aadb33SDmitri Tikhonov        return LSQ_LOG_ALERT;
45750aadb33SDmitri Tikhonov    if (0 == strcasecmp(str, "crit"))
45850aadb33SDmitri Tikhonov        return LSQ_LOG_CRIT;
45950aadb33SDmitri Tikhonov    if (0 == strcasecmp(str, "error"))
46050aadb33SDmitri Tikhonov        return LSQ_LOG_ERROR;
46150aadb33SDmitri Tikhonov    if (0 == strcasecmp(str, "warn"))
46250aadb33SDmitri Tikhonov        return LSQ_LOG_WARN;
46350aadb33SDmitri Tikhonov    if (0 == strcasecmp(str, "notice"))
46450aadb33SDmitri Tikhonov        return LSQ_LOG_NOTICE;
46550aadb33SDmitri Tikhonov    if (0 == strcasecmp(str, "info"))
46650aadb33SDmitri Tikhonov        return LSQ_LOG_INFO;
46750aadb33SDmitri Tikhonov    if (0 == strcasecmp(str, "debug"))
46850aadb33SDmitri Tikhonov        return LSQ_LOG_DEBUG;
46950aadb33SDmitri Tikhonov    return -1;
47050aadb33SDmitri Tikhonov}
47150aadb33SDmitri Tikhonov
47250aadb33SDmitri Tikhonov
47350aadb33SDmitri Tikhonovvoid
47450aadb33SDmitri Tikhonovlsquic_log_to_fstream (FILE *file, unsigned llts)
47550aadb33SDmitri Tikhonov{
47650aadb33SDmitri Tikhonov    lsquic_logger_init(&file_logger_if, file, llts);
47750aadb33SDmitri Tikhonov}
47850aadb33SDmitri Tikhonov
47950aadb33SDmitri Tikhonov
48050aadb33SDmitri Tikhonovint
48150aadb33SDmitri Tikhonovlsquic_logger_lopt (const char *optarg_orig)
48250aadb33SDmitri Tikhonov{
48350aadb33SDmitri Tikhonov    char *const optarg = strdup(optarg_orig);
48450aadb33SDmitri Tikhonov    char *mod_str;
48550aadb33SDmitri Tikhonov    int i;
48650aadb33SDmitri Tikhonov    for (i = 0; (mod_str = strtok(i ? NULL : optarg, ",")); ++i) {
48750aadb33SDmitri Tikhonov        char *level_str = strchr(mod_str, '=');
48850aadb33SDmitri Tikhonov        if (!level_str) {
48950aadb33SDmitri Tikhonov            fprintf(stderr, "Invalid module specification `%s'\n", mod_str);
49050aadb33SDmitri Tikhonov            break;
49150aadb33SDmitri Tikhonov        }
49250aadb33SDmitri Tikhonov        *level_str = '\0';
49350aadb33SDmitri Tikhonov        ++level_str;
49450aadb33SDmitri Tikhonov        enum lsquic_logger_module mod = lsquic_str_to_logger_module(mod_str);
49550aadb33SDmitri Tikhonov        if (-1 == (int) mod) {
49650aadb33SDmitri Tikhonov            fprintf(stderr, "`%s' is not a valid module name\n", mod_str);
49750aadb33SDmitri Tikhonov            break;
49850aadb33SDmitri Tikhonov        }
49950aadb33SDmitri Tikhonov        enum lsq_log_level level = lsquic_str_to_log_level(level_str);
50050aadb33SDmitri Tikhonov        if (-1 == (int) level) {
50150aadb33SDmitri Tikhonov            fprintf(stderr, "`%s' is not a valid level\n", level_str);
50250aadb33SDmitri Tikhonov            break;
50350aadb33SDmitri Tikhonov        }
50450aadb33SDmitri Tikhonov        lsq_log_levels[mod] = level;
50550aadb33SDmitri Tikhonov        LSQ_INFO("set %s to %s", mod_str, level_str);
50650aadb33SDmitri Tikhonov    }
50750aadb33SDmitri Tikhonov    free(optarg);
50850aadb33SDmitri Tikhonov    return mod_str == NULL ? 0 : -1;
50950aadb33SDmitri Tikhonov}
51050aadb33SDmitri Tikhonov
51150aadb33SDmitri Tikhonov
51250aadb33SDmitri Tikhonovint
51350aadb33SDmitri Tikhonovlsquic_set_log_level (const char *level_str)
51450aadb33SDmitri Tikhonov{
51550aadb33SDmitri Tikhonov    enum lsq_log_level level;
51650aadb33SDmitri Tikhonov    unsigned i;
51750aadb33SDmitri Tikhonov
51850aadb33SDmitri Tikhonov    level = lsquic_str_to_log_level(level_str);
51950aadb33SDmitri Tikhonov    if ((int) level >= 0)
52050aadb33SDmitri Tikhonov    {
52150aadb33SDmitri Tikhonov        for (i = 0; i < sizeof(lsq_log_levels) / sizeof(lsq_log_levels[0]); ++i)
52250aadb33SDmitri Tikhonov            lsq_log_levels[i] = level;
52350aadb33SDmitri Tikhonov        return 0;
52450aadb33SDmitri Tikhonov    }
52550aadb33SDmitri Tikhonov    else
52650aadb33SDmitri Tikhonov        return -1;
52750aadb33SDmitri Tikhonov}
5285392f7a3SLiteSpeed Tech
5295392f7a3SLiteSpeed Tech
5305392f7a3SLiteSpeed Tech/* `out' must be at least MAX_CID_LEN * 2 + 1 characters long */
5315392f7a3SLiteSpeed Techvoid
5325392f7a3SLiteSpeed Techlsquic_cid2str (const lsquic_cid_t *cid, char *out)
5335392f7a3SLiteSpeed Tech{
5345392f7a3SLiteSpeed Tech    static const char hex[] = "0123456789ABCDEF";
5355392f7a3SLiteSpeed Tech    int i;
5365392f7a3SLiteSpeed Tech
5375392f7a3SLiteSpeed Tech    for (i = 0; i < (int) cid->len; ++i)
5385392f7a3SLiteSpeed Tech    {
5395392f7a3SLiteSpeed Tech        *out++ = hex[ cid->idbuf[i] >> 4 ];
5405392f7a3SLiteSpeed Tech        *out++ = hex[ cid->idbuf[i] & 0xF ];
5415392f7a3SLiteSpeed Tech    }
5425392f7a3SLiteSpeed Tech    *out = '\0';
5435392f7a3SLiteSpeed Tech}
544