File:  [Coherent Logic Development] / freem / src / log.c
Revision 1.14: download - view: text, annotated - select for diffs
Wed May 14 12:22:04 2025 UTC (3 weeks, 3 days ago) by snw
Branches: MAIN
CVS tags: HEAD
Further work on shared memory

/*
 *   $Id: log.c,v 1.14 2025/05/14 12:22:04 snw Exp $
 *    freem error logging
 *
 *  
 *   Author: Serena Willis <snw@coherent-logic.com>
 *    Copyright (C) 1998 MUG Deutschland
 *    Copyright (C) 2020, 2025 Coherent Logic Development LLC
 *
 *
 *   This file is part of FreeM.
 *
 *   FreeM is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   FreeM is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Affero Public License for more details.
 *
 *   You should have received a copy of the GNU Affero Public License
 *   along with FreeM.  If not, see <https://www.gnu.org/licenses/>.
 *
 *   $Log: log.c,v $
 *   Revision 1.14  2025/05/14 12:22:04  snw
 *   Further work on shared memory
 *
 *   Revision 1.13  2025/04/16 17:36:12  snw
 *   Add FreeBSD shm cleanup script
 *
 *   Revision 1.12  2025/04/15 16:49:36  snw
 *   Make use of logprintf throughout codebase
 *
 *   Revision 1.11  2025/04/15 14:56:20  snw
 *   Fix broken build due to missing declarations
 *
 *   Revision 1.10  2025/04/15 14:39:06  snw
 *   Further improvements to logging
 *
 *   Revision 1.9  2025/04/15 02:24:43  snw
 *   Improve FreeM logging capabilities
 *
 *   Revision 1.8  2025/04/04 02:12:25  snw
 *   Bump to 0.63.0-rc5 and make sure m_log function is never empty
 *
 *   Revision 1.7  2025/04/04 01:18:21  snw
 *   Remove vestigial logging code and bump to 0.63.0-rc4
 *
 *   Revision 1.6  2025/04/01 23:21:45  snw
 *   fmadm commands for stopping, starting, and restarting environments now functional
 *
 *   Revision 1.5  2025/04/01 20:11:46  snw
 *   Further work on fmadm
 *
 *   Revision 1.4  2025/03/09 19:50:47  snw
 *   Second phase of REUSE compliance and header reformat
 *
 *
 * SPDX-FileCopyrightText:  (C) 2025 Coherent Logic Development LLC
 * SPDX-License-Identifier: AGPL-3.0-or-later
 **/

#include <stdio.h>
#include <string.h>
#if defined(__linux__) | defined(__FreeBSD__) | defined(__sun__)
# include <syslog.h>
#endif
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>

#if defined(USE_SYS_TIME_H) && !defined(MSDOS) && !defined(__osf__)
# include <sys/time.h>
#else
# include <time.h> 
#endif

#include "mpsdef.h"
#include "log.h" /* for logging constants */
#include "init.h" /* for cleanup() */
#include "iniconf.h" /* for read_profile_string() */
#include "shmmgr.h" /* for shm_exit() */

short log_threshold_file;
short log_threshold_syslog;
short log_threshold_stderr;


void init_log(void)
{
    char buf[4096];
    char *env_value;
    
    if (read_profile_string (env_config_file, shm_env, "log_threshold_file", buf) == TRUE) {
        if (strcmp (buf, "debug") == 0) {
            log_threshold_file = FM_LOG_DEBUG;
        }
        else if (strcmp (buf, "info") == 0) {
            log_threshold_file = FM_LOG_INFO;
        }
        else if (strcmp (buf, "warning") == 0) {
            log_threshold_file = FM_LOG_WARNING;
        }
        else if (strcmp (buf, "error") == 0) {
            log_threshold_file = FM_LOG_ERROR;
        }
        else if (strcmp (buf, "fatal") == 0) {
            log_threshold_file = FM_LOG_FATAL;
        }
        else {
            log_threshold_file = FM_LOG_INFO;
        }
    }
    else {
        log_threshold_file = FM_LOG_INFO;
    }

    if (read_profile_string (env_config_file, shm_env, "log_threshold_syslog", buf) == TRUE) {
        if (strcmp (buf, "debug") == 0) {
            log_threshold_syslog = FM_LOG_DEBUG;
        }
        else if (strcmp (buf, "info") == 0) {
            log_threshold_syslog = FM_LOG_INFO;
        }
        else if (strcmp (buf, "warning") == 0) {
            log_threshold_syslog = FM_LOG_WARNING;
        }
        else if (strcmp (buf, "error") == 0) {
            log_threshold_syslog = FM_LOG_ERROR;
        }
        else if (strcmp (buf, "fatal") == 0) {
            log_threshold_syslog = FM_LOG_FATAL;
        }
        else {
            log_threshold_syslog = FM_LOG_INFO;
        }
    }
    else {
        log_threshold_syslog = FM_LOG_INFO;
    }

    if (read_profile_string (env_config_file, shm_env, "log_threshold_stderr", buf) == TRUE) {
        if (strcmp (buf, "debug") == 0) {
            log_threshold_stderr = FM_LOG_DEBUG;
        }
        else if (strcmp (buf, "info") == 0) {
            log_threshold_stderr = FM_LOG_INFO;
        }
        else if (strcmp (buf, "warning") == 0) {
            log_threshold_stderr = FM_LOG_WARNING;
        }
        else if (strcmp (buf, "error") == 0) {
            log_threshold_stderr = FM_LOG_ERROR;
        }
        else if (strcmp (buf, "fatal") == 0) {
            log_threshold_stderr = FM_LOG_FATAL;
        }
        else {
            log_threshold_stderr = FM_LOG_INFO;
        }
    }
    else {
        log_threshold_stderr = FM_LOG_WARNING;
    }

    if ((env_value = getenv ("FREEM_LOG_THRESHOLD_FILE")) != NULL) {
        if (strcmp (env_value, "debug") == 0) {
            log_threshold_file = FM_LOG_DEBUG;
        }
        else if (strcmp (env_value, "info") == 0) {
            log_threshold_file = FM_LOG_INFO;
        }
        else if (strcmp (env_value, "warning") == 0) {
            log_threshold_file = FM_LOG_WARNING;
        }
        else if (strcmp (env_value, "error") == 0) {
            log_threshold_file = FM_LOG_ERROR;
        }
        else if (strcmp (env_value, "fatal") == 0) {
            log_threshold_file = FM_LOG_FATAL;
        }
        else {
            log_threshold_file = FM_LOG_INFO;
        }
    }

    if ((env_value = getenv ("FREEM_LOG_THRESHOLD_SYSLOG")) != NULL) {
        if (strcmp (env_value, "debug") == 0) {
            log_threshold_syslog = FM_LOG_DEBUG;
        }
        else if (strcmp (env_value, "info") == 0) {
            log_threshold_syslog = FM_LOG_INFO;
        }
        else if (strcmp (env_value, "warning") == 0) {
            log_threshold_syslog = FM_LOG_WARNING;
        }
        else if (strcmp (env_value, "error") == 0) {
            log_threshold_syslog = FM_LOG_ERROR;
        }
        else if (strcmp (env_value, "fatal") == 0) {
            log_threshold_syslog = FM_LOG_FATAL;
        }
        else {
            log_threshold_syslog = FM_LOG_INFO;
        }
    }

    if ((env_value = getenv ("FREEM_LOG_THRESHOLD_STDERR")) != NULL) {
        if (strcmp (env_value, "debug") == 0) {
            log_threshold_stderr = FM_LOG_DEBUG;
        }
        else if (strcmp (env_value, "info") == 0) {
            log_threshold_stderr = FM_LOG_INFO;
        }
        else if (strcmp (env_value, "warning") == 0) {
            log_threshold_stderr = FM_LOG_WARNING;
        }
        else if (strcmp (env_value, "error") == 0) {
            log_threshold_stderr = FM_LOG_ERROR;
        }
        else if (strcmp (env_value, "fatal") == 0) {
            log_threshold_stderr = FM_LOG_FATAL;
        }
        else {
            log_threshold_stderr = FM_LOG_INFO;
        }
    }    



}

void log_level_to_text(int level, char *buf)
{
    switch (level) {

        case FM_LOG_DEBUG:
            sprintf (buf, "DEBUG");
            break;

        case FM_LOG_INFO:
            sprintf (buf, "INFO");
            break;

        case FM_LOG_WARNING:
            sprintf (buf, "WARNING");
            break;

        case FM_LOG_ERROR:
            sprintf (buf, "ERROR");
            break;

        case FM_LOG_FATAL:
            sprintf (buf, "FATAL");
            break;

        default:
            sprintf (buf, "LEVEL %d", level);
            break;

    }
}

void m_log(int level, const char *msg)
{

    FILE *fp;
    time_t unix_epoch;
    char timeval[255];
    char filename[4096];    
    char lvl[20];
    struct tm *logtime;

    log_level_to_text (level, lvl);
    
    if (level >= log_threshold_file) {
        snprintf (filename, sizeof (filename) - 1, "/var/log/freem/%s.log", shm_env);
        
        if ((fp = fopen (filename, "a+")) != NULL) {
            unix_epoch = time (0L);
            logtime = localtime (&unix_epoch);
            strftime (timeval, sizeof (timeval) - 1, "%F %T", logtime);
            fprintf (fp, "%s [LEVEL %s PID %ld]:  %s\n", timeval, lvl, getpid (), msg);
            fclose (fp);
        }        
    }
    
#if defined(__linux__) | defined(__FreeBSD__) | defined(__sun__)
    if (level >= log_threshold_syslog) {
        syslog (level, "%s", msg);
    }
#endif

    if (level >= log_threshold_stderr) fprintf (stderr, "%s [%s]\r\n", msg, lvl);

    if (level >= FM_LOG_FATAL) {
        cleanup ();
        exit (FM_LOG_FATAL);
    }
    
    return;    
}

void logprintf(int level, char *fmt, ...)
{
    va_list ptr;
    va_start (ptr, fmt);

    char logmsg[BIGSTR];
    char tmps[BIGSTR];
    
    char ch;
    char typ;
    char subtyp;

    register int i;

    logmsg[0] = '\0';
    
    for (i = 0; fmt[i] != '\0'; i++) {
        ch = fmt[i];

        tmps[0] = '\0';
        
        switch (ch) {
            
            case '%':
                typ = fmt[++i];

                switch (typ) {
                    
                    case '%': /* literal percent sign */
                        strcat (logmsg, "%");
                        continue;

                    case 'c': /* char */
                        sprintf (tmps, "%c", va_arg (ptr, int));
                        strcat (logmsg, tmps);
                        continue;

                    case 's': /* C string */
                        strcat (logmsg, va_arg (ptr, char *));
                        continue;

                    case 'S': /* FreeM string */
                        stcpy (tmps, va_arg (ptr, char *));
                        stcnv_m2c (tmps);
                        strcat (logmsg, tmps);
                        continue;

                    case 'd': /* int */
                        sprintf (tmps, "%d", va_arg (ptr, int));
                        strcat (logmsg, tmps);
                        continue;

                    case 'p': /* pointer */
                        sprintf (tmps, "%p", va_arg (ptr, void *));
                        strcat (logmsg, tmps);
                        continue;
                        
                    case 'l': /* long... */
                        subtyp = fmt[++i];
                        switch (subtyp) {
                            
                            case 'd': /* long int */
                                sprintf (tmps, "%ld", va_arg (ptr, long));
                                strcat (logmsg, tmps);
                                continue;

                            case 'f': /* float */
                                sprintf (tmps, "%lf", va_arg (ptr, double));
                                strcat (logmsg, tmps);
                                continue;
                                
                        }
                        continue;
                }
                
            case '\\':
                typ = fmt[++i];
                switch (typ) {
                    case 'n':
                        sprintf (tmps, "\n");
                        strcat (logmsg, tmps);
                        continue;
                        
                    case 'r':
                        sprintf (tmps, "\r");
                        strcat (logmsg, tmps);
                        continue;

                    case 't':
                        sprintf (tmps, "\t");
                        strcat (logmsg, tmps);
                        continue;
                        
                    case '\\':
                        sprintf (tmps, "\\");
                        strcat (logmsg, tmps);
                        continue;
                }
                
            default:
                sprintf (tmps, "%c", ch);
                strcat (logmsg, tmps);
                break;
        }
    }

    m_log (level, logmsg);
        
    va_end (ptr);
    return;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>