Skip to content
Snippets Groups Projects
debug.c 2.91 KiB
Newer Older
  • Learn to ignore specific revisions
  • /* SPDX-License-Identifier: GPL-2.0-only */
    
    /*
     * debug.c - for debug and logging.
     *
     * Copyright (C) 2025 Genexis AB.
     *
     * Author: anjan.chanda@iopsys.eu
     *
     */
    
    #include <stdlib.h>
    #include <stdio.h>
    #include <stdbool.h>
    #include <stdarg.h>
    #include <string.h>
    #include <syslog.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <time.h>
    
    #include <easy/easy.h>
    
    #include "wifimngr_opts.h"
    #include "debug.h"
    
    extern const char *PROG_NAME;
    
    static int loglevel;
    static uint32_t features;
    static const char *logfile;
    static FILE *outfile;
    static int ffd = -1;
    static int ofd = -1;
    static bool syslogging;
    static bool logfile_isfifo;
    static const int syslog_level[] = { LOG_ERR, LOG_WARNING, LOG_INFO, LOG_DEBUG };
    
    uint32_t logfeature_to_enum(const char *s)
    {
    	if (!strncmp(s, "cmd", 3))
    		return BIT(LOG_CMD);
    
    	if (!strncmp(s, "event", 5))
    		return BIT(LOG_EVENT);
    
    	if (!strncmp(s, "default", 7))
    		return BIT(LOG_DEFAULT);
    
    	if (!strncmp(s, "all", 3))
    		return LOG_FEATURE_ALL;
    
    	return 0;
    }
    
    const char *logfeature_to_string(uint32_t e)
    {
    	switch (e) {
    	case LOG_CMD:     return "cmd";
    	case LOG_EVENT:   return "event";
    	case LOG_DEFAULT: return "default";
    	}
    
    	return "";
    }
    
    void restart_logging(void *args)
    {
    	struct wifimngr_cmdline_opts *opts = args;
    
    	syslogging = opts->syslogging;
    	logfile = opts->logfile;
    	logfile_isfifo = opts->logfile_isfifo;
    	loglevel = opts->loglevel;
    	features = opts->features;
    
    	if (syslogging)
    		openlog(PROG_NAME, 0, LOG_DAEMON);
    
    	if (!logfile) {
    		outfile = stderr;
    		ofd = fileno(stderr);
    		return;
    	}
    
    	if (logfile_isfifo) {
    		struct stat st;
    		int rfd;
    
    		if (stat(logfile, &st))
    			unlink(logfile);
    
    		mkfifo(logfile, 0600);
    		if (stat(logfile, &st) == -1 || !S_ISFIFO(st.st_mode))
    			return;
    
    		rfd = open(logfile, O_RDONLY | O_NONBLOCK);
    		if (rfd) {
    			ffd = open(logfile, O_WRONLY | O_NONBLOCK);
    			close(rfd);
    		}
    	} else {
    		ofd = open(logfile, O_CREAT | O_WRONLY | O_APPEND | O_NONBLOCK);
    	}
    }
    
    void stop_logging(void)
    {
    	if (syslogging)
    		closelog();
    
    	if (outfile)
    		fclose(outfile);
    
    	if (ofd > 0) {
    		close(ofd);
    		ofd = -1;
    	}
    
    	if (ffd > 0) {
    		close(ffd);
    		unlink(logfile);
    	}
    }
    
    static void log_timestamp(int fd)
    {
    	time_t now = time(NULL);
    	struct tm *tm_now = localtime(&now);
    	const char *tm_fmt = "[%d-%02d-%02d %02d:%02d:%02d] ";
    
    	dprintf(fd, tm_fmt,
    		tm_now->tm_year + 1900,
    		tm_now->tm_mon + 1,
    		tm_now->tm_mday,
    		tm_now->tm_hour,
    		tm_now->tm_min,
    		tm_now->tm_sec);
    }
    
    void log_message(int feature, int level, const char *fmt, ...)
    {
    	va_list args;
    	int fd = -1;
    
    	if (!(BIT(feature) & features))
    		return;
    
    	if (level > loglevel)
    		return;
    
    	va_start(args, fmt);
    	if (syslogging && level >= 0)
    		vsyslog(syslog_level[level > 3 ? 3 : level], fmt, args);
    
    	if (logfile_isfifo && ffd > 0)
    		fd = ffd;
    	else if (ofd >= 0)
    		fd = ofd;
    
    	if (fd != -1) {
    		log_timestamp(fd);
    		dprintf(fd, "[%d]: ", getpid());
    		vdprintf(fd, fmt, args);
    	}
    
    	va_end(args);
    }