root/usr.bin/sndiod/utils.c
/*      $OpenBSD: utils.c,v 1.9 2024/12/20 07:35:56 ratchov Exp $       */
/*
 * Copyright (c) 2003-2012 Alexandre Ratchov <alex@caoua.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*
 * logx() quickly stores traces into a trace buffer.
 * This allows traces to be collected during time sensitive operations without
 * disturbing them. The buffer can be flushed on standard error later, when
 * slow syscalls are no longer disruptive, e.g. at the end of the poll() loop.
 */
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "utils.h"

/*
 * log buffer size
 */
#define LOG_BUFSZ       8192

char log_buf[LOG_BUFSZ];        /* buffer where traces are stored */
size_t log_used = 0;            /* bytes used in the buffer */
unsigned int log_sync = 1;      /* if true, flush after each '\n' */

/*
 * write the log buffer on stderr
 */
void
log_flush(void)
{
        if (log_used == 0)
                return;
        write(STDERR_FILENO, log_buf, log_used);
        log_used = 0;
}

/*
 * log a single line to stderr
 */
void
log_do(const char *fmt, ...)
{
        va_list ap;
        int n, save_errno = errno;

        va_start(ap, fmt);
        n = vsnprintf(log_buf + log_used, sizeof(log_buf) - log_used, fmt, ap);
        va_end(ap);

        if (n != -1) {
                log_used += n;

                if (log_used >= sizeof(log_buf))
                        log_used = sizeof(log_buf) - 1;
                log_buf[log_used++] = '\n';

                if (log_sync)
                        log_flush();
        }
        errno = save_errno;
}

/*
 * abort program execution after a fatal error
 */
void
panic(void)
{
        log_flush();
        (void)kill(getpid(), SIGABRT);
        _exit(1);
}

/*
 * allocate 'size' bytes of memory (with size > 0). This functions never
 * fails (and never returns NULL), if there isn't enough memory then
 * abort the program.
 */
void *
xmalloc(size_t size)
{
        void *p;

        p = malloc(size);
        if (p == NULL) {
                logx(0, "failed to allocate %zu bytes", size);
                panic();
        }
        return p;
}

/*
 * free memory allocated with xmalloc()
 */
void
xfree(void *p)
{
#ifdef DEBUG
        if (p == NULL) {
                logx(0, "xfree with NULL arg");
                panic();
        }
#endif
        free(p);
}

/*
 * xmalloc-style strdup(3)
 */
char *
xstrdup(char *s)
{
        size_t size;
        void *p;

        size = strlen(s) + 1;
        p = xmalloc(size);
        memcpy(p, s, size);
        return p;
}