1: /*
2: * $Id: log.c,v 1.14 2025/05/14 12:22:04 snw Exp $
3: * freem error logging
4: *
5: *
6: * Author: Serena Willis <snw@coherent-logic.com>
7: * Copyright (C) 1998 MUG Deutschland
8: * Copyright (C) 2020, 2025 Coherent Logic Development LLC
9: *
10: *
11: * This file is part of FreeM.
12: *
13: * FreeM is free software: you can redistribute it and/or modify
14: * it under the terms of the GNU Affero Public License as published by
15: * the Free Software Foundation, either version 3 of the License, or
16: * (at your option) any later version.
17: *
18: * FreeM is distributed in the hope that it will be useful,
19: * but WITHOUT ANY WARRANTY; without even the implied warranty of
20: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21: * GNU Affero Public License for more details.
22: *
23: * You should have received a copy of the GNU Affero Public License
24: * along with FreeM. If not, see <https://www.gnu.org/licenses/>.
25: *
26: * $Log: log.c,v $
27: * Revision 1.14 2025/05/14 12:22:04 snw
28: * Further work on shared memory
29: *
30: * Revision 1.13 2025/04/16 17:36:12 snw
31: * Add FreeBSD shm cleanup script
32: *
33: * Revision 1.12 2025/04/15 16:49:36 snw
34: * Make use of logprintf throughout codebase
35: *
36: * Revision 1.11 2025/04/15 14:56:20 snw
37: * Fix broken build due to missing declarations
38: *
39: * Revision 1.10 2025/04/15 14:39:06 snw
40: * Further improvements to logging
41: *
42: * Revision 1.9 2025/04/15 02:24:43 snw
43: * Improve FreeM logging capabilities
44: *
45: * Revision 1.8 2025/04/04 02:12:25 snw
46: * Bump to 0.63.0-rc5 and make sure m_log function is never empty
47: *
48: * Revision 1.7 2025/04/04 01:18:21 snw
49: * Remove vestigial logging code and bump to 0.63.0-rc4
50: *
51: * Revision 1.6 2025/04/01 23:21:45 snw
52: * fmadm commands for stopping, starting, and restarting environments now functional
53: *
54: * Revision 1.5 2025/04/01 20:11:46 snw
55: * Further work on fmadm
56: *
57: * Revision 1.4 2025/03/09 19:50:47 snw
58: * Second phase of REUSE compliance and header reformat
59: *
60: *
61: * SPDX-FileCopyrightText: (C) 2025 Coherent Logic Development LLC
62: * SPDX-License-Identifier: AGPL-3.0-or-later
63: **/
64:
65: #include <stdio.h>
66: #include <string.h>
67: #if defined(__linux__) | defined(__FreeBSD__) | defined(__sun__)
68: # include <syslog.h>
69: #endif
70: #include <stdlib.h>
71: #include <stdarg.h>
72: #include <unistd.h>
73:
74: #if defined(USE_SYS_TIME_H) && !defined(MSDOS) && !defined(__osf__)
75: # include <sys/time.h>
76: #else
77: # include <time.h>
78: #endif
79:
80: #include "mpsdef.h"
81: #include "log.h" /* for logging constants */
82: #include "init.h" /* for cleanup() */
83: #include "iniconf.h" /* for read_profile_string() */
84: #include "shmmgr.h" /* for shm_exit() */
85:
86: short log_threshold_file;
87: short log_threshold_syslog;
88: short log_threshold_stderr;
89:
90:
91: void init_log(void)
92: {
93: char buf[4096];
94: char *env_value;
95:
96: if (read_profile_string (env_config_file, shm_env, "log_threshold_file", buf) == TRUE) {
97: if (strcmp (buf, "debug") == 0) {
98: log_threshold_file = FM_LOG_DEBUG;
99: }
100: else if (strcmp (buf, "info") == 0) {
101: log_threshold_file = FM_LOG_INFO;
102: }
103: else if (strcmp (buf, "warning") == 0) {
104: log_threshold_file = FM_LOG_WARNING;
105: }
106: else if (strcmp (buf, "error") == 0) {
107: log_threshold_file = FM_LOG_ERROR;
108: }
109: else if (strcmp (buf, "fatal") == 0) {
110: log_threshold_file = FM_LOG_FATAL;
111: }
112: else {
113: log_threshold_file = FM_LOG_INFO;
114: }
115: }
116: else {
117: log_threshold_file = FM_LOG_INFO;
118: }
119:
120: if (read_profile_string (env_config_file, shm_env, "log_threshold_syslog", buf) == TRUE) {
121: if (strcmp (buf, "debug") == 0) {
122: log_threshold_syslog = FM_LOG_DEBUG;
123: }
124: else if (strcmp (buf, "info") == 0) {
125: log_threshold_syslog = FM_LOG_INFO;
126: }
127: else if (strcmp (buf, "warning") == 0) {
128: log_threshold_syslog = FM_LOG_WARNING;
129: }
130: else if (strcmp (buf, "error") == 0) {
131: log_threshold_syslog = FM_LOG_ERROR;
132: }
133: else if (strcmp (buf, "fatal") == 0) {
134: log_threshold_syslog = FM_LOG_FATAL;
135: }
136: else {
137: log_threshold_syslog = FM_LOG_INFO;
138: }
139: }
140: else {
141: log_threshold_syslog = FM_LOG_INFO;
142: }
143:
144: if (read_profile_string (env_config_file, shm_env, "log_threshold_stderr", buf) == TRUE) {
145: if (strcmp (buf, "debug") == 0) {
146: log_threshold_stderr = FM_LOG_DEBUG;
147: }
148: else if (strcmp (buf, "info") == 0) {
149: log_threshold_stderr = FM_LOG_INFO;
150: }
151: else if (strcmp (buf, "warning") == 0) {
152: log_threshold_stderr = FM_LOG_WARNING;
153: }
154: else if (strcmp (buf, "error") == 0) {
155: log_threshold_stderr = FM_LOG_ERROR;
156: }
157: else if (strcmp (buf, "fatal") == 0) {
158: log_threshold_stderr = FM_LOG_FATAL;
159: }
160: else {
161: log_threshold_stderr = FM_LOG_INFO;
162: }
163: }
164: else {
165: log_threshold_stderr = FM_LOG_WARNING;
166: }
167:
168: if ((env_value = getenv ("FREEM_LOG_THRESHOLD_FILE")) != NULL) {
169: if (strcmp (env_value, "debug") == 0) {
170: log_threshold_file = FM_LOG_DEBUG;
171: }
172: else if (strcmp (env_value, "info") == 0) {
173: log_threshold_file = FM_LOG_INFO;
174: }
175: else if (strcmp (env_value, "warning") == 0) {
176: log_threshold_file = FM_LOG_WARNING;
177: }
178: else if (strcmp (env_value, "error") == 0) {
179: log_threshold_file = FM_LOG_ERROR;
180: }
181: else if (strcmp (env_value, "fatal") == 0) {
182: log_threshold_file = FM_LOG_FATAL;
183: }
184: else {
185: log_threshold_file = FM_LOG_INFO;
186: }
187: }
188:
189: if ((env_value = getenv ("FREEM_LOG_THRESHOLD_SYSLOG")) != NULL) {
190: if (strcmp (env_value, "debug") == 0) {
191: log_threshold_syslog = FM_LOG_DEBUG;
192: }
193: else if (strcmp (env_value, "info") == 0) {
194: log_threshold_syslog = FM_LOG_INFO;
195: }
196: else if (strcmp (env_value, "warning") == 0) {
197: log_threshold_syslog = FM_LOG_WARNING;
198: }
199: else if (strcmp (env_value, "error") == 0) {
200: log_threshold_syslog = FM_LOG_ERROR;
201: }
202: else if (strcmp (env_value, "fatal") == 0) {
203: log_threshold_syslog = FM_LOG_FATAL;
204: }
205: else {
206: log_threshold_syslog = FM_LOG_INFO;
207: }
208: }
209:
210: if ((env_value = getenv ("FREEM_LOG_THRESHOLD_STDERR")) != NULL) {
211: if (strcmp (env_value, "debug") == 0) {
212: log_threshold_stderr = FM_LOG_DEBUG;
213: }
214: else if (strcmp (env_value, "info") == 0) {
215: log_threshold_stderr = FM_LOG_INFO;
216: }
217: else if (strcmp (env_value, "warning") == 0) {
218: log_threshold_stderr = FM_LOG_WARNING;
219: }
220: else if (strcmp (env_value, "error") == 0) {
221: log_threshold_stderr = FM_LOG_ERROR;
222: }
223: else if (strcmp (env_value, "fatal") == 0) {
224: log_threshold_stderr = FM_LOG_FATAL;
225: }
226: else {
227: log_threshold_stderr = FM_LOG_INFO;
228: }
229: }
230:
231:
232:
233: }
234:
235: void log_level_to_text(int level, char *buf)
236: {
237: switch (level) {
238:
239: case FM_LOG_DEBUG:
240: sprintf (buf, "DEBUG");
241: break;
242:
243: case FM_LOG_INFO:
244: sprintf (buf, "INFO");
245: break;
246:
247: case FM_LOG_WARNING:
248: sprintf (buf, "WARNING");
249: break;
250:
251: case FM_LOG_ERROR:
252: sprintf (buf, "ERROR");
253: break;
254:
255: case FM_LOG_FATAL:
256: sprintf (buf, "FATAL");
257: break;
258:
259: default:
260: sprintf (buf, "LEVEL %d", level);
261: break;
262:
263: }
264: }
265:
266: void m_log(int level, const char *msg)
267: {
268:
269: FILE *fp;
270: time_t unix_epoch;
271: char timeval[255];
272: char filename[4096];
273: char lvl[20];
274: struct tm *logtime;
275:
276: log_level_to_text (level, lvl);
277:
278: if (level >= log_threshold_file) {
279: snprintf (filename, sizeof (filename) - 1, "/var/log/freem/%s.log", shm_env);
280:
281: if ((fp = fopen (filename, "a+")) != NULL) {
282: unix_epoch = time (0L);
283: logtime = localtime (&unix_epoch);
284: strftime (timeval, sizeof (timeval) - 1, "%F %T", logtime);
285: fprintf (fp, "%s [LEVEL %s PID %ld]: %s\n", timeval, lvl, getpid (), msg);
286: fclose (fp);
287: }
288: }
289:
290: #if defined(__linux__) | defined(__FreeBSD__) | defined(__sun__)
291: if (level >= log_threshold_syslog) {
292: syslog (level, "%s", msg);
293: }
294: #endif
295:
296: if (level >= log_threshold_stderr) fprintf (stderr, "%s [%s]\r\n", msg, lvl);
297:
298: if (level >= FM_LOG_FATAL) {
299: cleanup ();
300: exit (FM_LOG_FATAL);
301: }
302:
303: return;
304: }
305:
306: void logprintf(int level, char *fmt, ...)
307: {
308: va_list ptr;
309: va_start (ptr, fmt);
310:
311: char logmsg[BIGSTR];
312: char tmps[BIGSTR];
313:
314: char ch;
315: char typ;
316: char subtyp;
317:
318: register int i;
319:
320: logmsg[0] = '\0';
321:
322: for (i = 0; fmt[i] != '\0'; i++) {
323: ch = fmt[i];
324:
325: tmps[0] = '\0';
326:
327: switch (ch) {
328:
329: case '%':
330: typ = fmt[++i];
331:
332: switch (typ) {
333:
334: case '%': /* literal percent sign */
335: strcat (logmsg, "%");
336: continue;
337:
338: case 'c': /* char */
339: sprintf (tmps, "%c", va_arg (ptr, int));
340: strcat (logmsg, tmps);
341: continue;
342:
343: case 's': /* C string */
344: strcat (logmsg, va_arg (ptr, char *));
345: continue;
346:
347: case 'S': /* FreeM string */
348: stcpy (tmps, va_arg (ptr, char *));
349: stcnv_m2c (tmps);
350: strcat (logmsg, tmps);
351: continue;
352:
353: case 'd': /* int */
354: sprintf (tmps, "%d", va_arg (ptr, int));
355: strcat (logmsg, tmps);
356: continue;
357:
358: case 'p': /* pointer */
359: sprintf (tmps, "%p", va_arg (ptr, void *));
360: strcat (logmsg, tmps);
361: continue;
362:
363: case 'l': /* long... */
364: subtyp = fmt[++i];
365: switch (subtyp) {
366:
367: case 'd': /* long int */
368: sprintf (tmps, "%ld", va_arg (ptr, long));
369: strcat (logmsg, tmps);
370: continue;
371:
372: case 'f': /* float */
373: sprintf (tmps, "%lf", va_arg (ptr, double));
374: strcat (logmsg, tmps);
375: continue;
376:
377: }
378: continue;
379: }
380:
381: case '\\':
382: typ = fmt[++i];
383: switch (typ) {
384: case 'n':
385: sprintf (tmps, "\n");
386: strcat (logmsg, tmps);
387: continue;
388:
389: case 'r':
390: sprintf (tmps, "\r");
391: strcat (logmsg, tmps);
392: continue;
393:
394: case 't':
395: sprintf (tmps, "\t");
396: strcat (logmsg, tmps);
397: continue;
398:
399: case '\\':
400: sprintf (tmps, "\\");
401: strcat (logmsg, tmps);
402: continue;
403: }
404:
405: default:
406: sprintf (tmps, "%c", ch);
407: strcat (logmsg, tmps);
408: break;
409: }
410: }
411:
412: m_log (level, logmsg);
413:
414: va_end (ptr);
415: return;
416: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>