/*
* $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>