1 /************************************************************************
2 * IRC - Internet Relay Chat, src/ircd_log.c
3 * Copyright (C) 1999 Thomas Helvey (BleepSoft)
4 * Copyright (C) 2000 Kevin L. Mitchell <klmitch@mit.edu>
6 * See file AUTHORS in IRC package for additional names of
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 1, or (at your option)
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28 #include "ircd_alloc.h"
29 #include "ircd_snprintf.h"
30 #include "ircd_string.h"
42 #include <sys/types.h>
48 #define LOG_BUFSIZE 2048
50 /* select default log level cutoff */
52 # define L_DEFAULT L_DEBUG
54 # define L_DEFAULT L_INFO
57 #define LOG_DOSYSLOG 0x10
58 #define LOG_DOFILELOG 0x20
59 #define LOG_DOSNOTICE 0x40
61 #define LOG_DOMASK (LOG_DOSYSLOG | LOG_DOFILELOG | LOG_DOSNOTICE)
63 /* Map severity levels to strings and syslog levels */
64 static struct LevelData {
68 unsigned int snomask; /* 0 means use default in LogDesc */
70 #define L(level, syslog, mask) { L_ ## level, #level, (syslog), (mask) }
71 L(CRIT, LOG_CRIT, SNO_OLDSNO),
73 L(WARNING, LOG_WARNING, 0),
74 L(NOTICE, LOG_NOTICE, 0),
75 L(TRACE, LOG_INFO, 0),
77 L(DEBUG, LOG_INFO, SNO_DEBUG),
79 { L_LAST_LEVEL, 0, 0, 0 }
82 /* Just in case some implementation of syslog has them... */
87 #define LOG_NONE -1 /* don't syslog */
88 #define LOG_DEFAULT 0 /* syslog to logInfo.facility */
89 #define LOG_NOTFOUND -2 /* didn't find a facility corresponding to name */
91 /* Map names to syslog facilities--allows syslog configuration from .conf */
96 #define F(fac) { #fac, LOG_ ## fac }
97 F(NONE), F(DEFAULT), F(AUTH),
101 F(CRON), F(DAEMON), F(LOCAL0), F(LOCAL1), F(LOCAL2), F(LOCAL3),
102 F(LOCAL4), F(LOCAL5), F(LOCAL6), F(LOCAL7), F(LPR), F(MAIL),
103 F(NEWS), F(USER), F(UUCP),
108 #define SNO_NONE 0x00000000 /* don't send server notices */
109 #define SNO_NOTFOUND 0xffffffff /* didn't find a SNO_MASK value for name */
111 /* Map names to snomask values--allows configuration from .conf */
114 unsigned int snomask;
116 #define M(mask) { #mask, SNO_ ## mask }
117 M(NONE), M(OLDSNO), M(SERVKILL), M(OPERKILL), M(HACK2),
118 M(HACK3), M(UNAUTH), M(TCPCOMMON), M(TOOMANY), M(HACK4),
119 M(GLINE), M(NETWORK), M(IPMISMATCH), M(THROTTLE), M(OLDREALOP),
120 M(CONNEXIT), M(DEBUG),
125 /* Descriptions of all logging subsystems */
126 static struct LogDesc {
127 enum LogSys subsys; /* number for subsystem */
128 char *name; /* subsystem name */
129 struct LogFile *file; /* file descriptor for subsystem */
130 int def_fac; /* default facility */
131 unsigned int def_sno; /* default snomask */
132 int facility; /* -1 means don't use syslog */
133 unsigned int snomask; /* 0 means no server message */
134 enum LogLevel level; /* logging level */
136 #define S(sys, p, sn) { LS_ ## sys, #sys, 0, (p), (sn), (p), (sn), L_DEFAULT }
138 S(CONFIG, 0, SNO_OLDSNO),
139 S(OPERMODE, -1, SNO_HACK4),
140 S(GLINE, -1, SNO_GLINE),
141 S(JUPE, -1, SNO_NETWORK),
143 S(NETWORK, -1, SNO_NETWORK),
146 S(OPER, -1, SNO_OLDREALOP),
151 S(DEBUG, -1, SNO_DEBUG),
154 { LS_LAST_SYSTEM, 0, 0, -1, 0, -1, 0 }
157 /* describes a log file */
159 struct LogFile *next; /* next log file descriptor */
160 struct LogFile **prev_p; /* what points to us */
161 int fd; /* file's descriptor-- -1 if not open */
162 int ref; /* how many things refer to us? */
163 char *file; /* file name */
166 /* modifiable static information */
168 struct LogFile *filelist; /* list of log files */
169 struct LogFile *freelist; /* list of free'd log files */
170 int facility; /* default facility */
171 const char *procname; /* process's name */
172 struct LogFile *dbfile; /* debug file */
173 } logInfo = { 0, 0, LOG_USER, "ircd", 0 };
175 void ircd_log(int priority, const char* fmt, ...)
180 log_vwrite(LS_OLDLOG, priority, 0, fmt, vl);
184 /* helper routine to open a log file if needed */
186 log_open(struct LogFile *lf)
188 /* only open the file if we haven't already */
189 if (lf && lf->fd < 0) {
190 alarm(3); /* if NFS hangs, we hang only for 3 seconds */
191 lf->fd = open(lf->file, O_WRONLY | O_CREAT | O_APPEND,
199 /* reopen debug log */
201 log_debug_reopen(void)
203 if (!logInfo.dbfile) /* no open debugging file */
206 if (!logInfo.dbfile->file) { /* using terminal output */
207 logInfo.dbfile->fd = 2;
211 /* Ok, it's a real file; close it if necessary and use log_open to open it */
212 if (logInfo.dbfile->fd >= 0)
213 close(logInfo.dbfile->fd);
215 log_open(logInfo.dbfile);
217 if (logInfo.dbfile->fd < 0) { /* try again with /dev/null */
218 if ((logInfo.dbfile->fd = open("/dev/null", O_WRONLY)) < 0)
222 /* massage the file descriptor to be stderr */
223 if (logInfo.dbfile->fd != 2) {
224 dup2(logInfo.dbfile->fd, 2);
225 close(logInfo.dbfile->fd);
226 logInfo.dbfile->fd = 2;
230 /* initialize debugging log */
232 log_debug_init(char *file)
234 logInfo.dbfile = MyMalloc(sizeof(struct LogFile));
236 logInfo.dbfile->next = 0; /* initialize debugging filename */
237 logInfo.dbfile->prev_p = 0;
238 logInfo.dbfile->fd = -1;
239 logInfo.dbfile->ref = 1;
241 if (file) /* store pathname to use */
242 DupString(logInfo.dbfile->file, file);
244 logInfo.dbfile->file = 0;
246 log_debug_reopen(); /* open the debug log */
248 logDesc[LS_DEBUG].file = logInfo.dbfile; /* remember where it went */
251 /* set the debug log file */
253 log_debug_file(char *file)
257 /* If we weren't started with debugging enabled, or if we're using
258 * the terminal, don't do anything at all.
260 if (!logInfo.dbfile || !logInfo.dbfile->file)
263 MyFree(logInfo.dbfile->file); /* free old pathname */
264 DupString(logInfo.dbfile->file, file); /* store new pathname */
266 log_debug_reopen(); /* reopen the debug log */
269 #endif /* DEBUGMODE */
271 /* called in place of open_log(), this stores the process name and prepares
275 log_init(const char *process_name)
277 /* store the process name; probably belongs in ircd.c, but oh well... */
278 if (!EmptyString(process_name))
279 logInfo.procname = process_name;
281 /* ok, open syslog; default facility: LOG_USER */
282 openlog(logInfo.procname, LOG_PID | LOG_NDELAY, logInfo.facility);
285 /* Files are persistently open; this closes and reopens them to allow
291 log_close(); /* close everything...we reopen on demand */
294 log_debug_reopen(); /* reopen debugging log if necessary */
295 #endif /* DEBUGMODE */
297 /* reopen syslog, if needed; default facility: LOG_USER */
298 openlog(logInfo.procname, LOG_PID | LOG_NDELAY, logInfo.facility);
301 /* close the log files */
307 closelog(); /* close syslog */
309 for (ptr = logInfo.filelist; ptr; ptr = ptr->next) {
311 close(ptr->fd); /* close all the files... */
316 if (logInfo.dbfile && logInfo.dbfile->file) {
317 if (logInfo.dbfile->fd >= 0)
318 close(logInfo.dbfile->fd); /* close the debug log file */
320 logInfo.dbfile->fd = -1;
324 /* These write entries to a log file */
326 log_write(enum LogSys subsys, enum LogLevel severity, unsigned int flags,
327 const char *fmt, ...)
332 log_vwrite(subsys, severity, flags, fmt, vl);
337 log_vwrite(enum LogSys subsys, enum LogLevel severity, unsigned int flags,
338 const char *fmt, va_list vl)
341 struct LogDesc *desc;
342 struct LevelData *ldata;
344 struct iovec vector[3];
346 char buf[LOG_BUFSIZE];
347 /* 1234567890123456789012 3 */
348 /* [2000-11-28 16:11:20] \0 */
351 /* check basic assumptions */
352 assert(-1 < (int)subsys);
353 assert((int)subsys < LS_LAST_SYSTEM);
354 assert(-1 < (int)severity);
355 assert((int)severity < L_LAST_LEVEL);
356 assert(0 == (flags & ~LOG_NOMASK));
359 /* find the log data and the severity data */
360 desc = &logDesc[subsys];
361 ldata = &levelData[severity];
363 /* check the set of ordering assumptions */
364 assert(desc->subsys == subsys);
365 assert(ldata->level == severity);
367 /* check severity... */
368 if (severity > desc->level)
371 /* figure out where all we need to log */
372 if (!(flags & LOG_NOFILELOG) && desc->file) {
373 log_open(desc->file);
374 if (desc->file->fd >= 0) /* don't log to file if we can't open the file */
375 flags |= LOG_DOFILELOG;
378 if (!(flags & LOG_NOSYSLOG) && desc->facility >= 0)
379 flags |= LOG_DOSYSLOG; /* will syslog */
381 if (!(flags & LOG_NOSNOTICE) && (desc->snomask != 0 || ldata->snomask != 0))
382 flags |= LOG_DOSNOTICE; /* will send a server notice */
384 /* short-circuit if there's nothing to do... */
385 if (!(flags & LOG_DOMASK))
388 /* Build the basic log string */
392 /* save the length for writev */
393 /* Log format: "SYSTEM [SEVERITY]: log message" */
395 ircd_snprintf(0, buf, sizeof(buf), "%s [%s]: %v", desc->name,
398 /* if we have something to write to... */
399 if (flags & LOG_DOFILELOG) {
401 tstamp = localtime(&curtime); /* build the timestamp */
404 ircd_snprintf(0, timebuf, sizeof(timebuf), "[%d-%d-%d %d:%02d:%02d] ",
405 tstamp->tm_year + 1900, tstamp->tm_mon + 1,
406 tstamp->tm_mday, tstamp->tm_hour, tstamp->tm_min,
409 /* set up the remaining parts of the writev vector... */
410 vector[0].iov_base = timebuf;
411 vector[1].iov_base = buf;
413 vector[2].iov_base = "\n"; /* terminate lines with a \n */
414 vector[2].iov_len = 1;
416 /* write it out to the log file */
417 writev(desc->file->fd, vector, 3);
420 /* oh yeah, syslog it too... */
421 if (flags & LOG_DOSYSLOG)
422 syslog(ldata->syslog | desc->facility, "%s", buf);
424 /* can't forget server notices... */
425 if (flags & LOG_DOSNOTICE)
426 sendto_opmask_butone(0, ldata->snomask ? ldata->snomask : desc->snomask,
430 /* log kills for fun and profit */
432 log_write_kill(const struct Client *victim, const struct Client *killer,
433 const char *inpath, const char *path)
436 log_write(IsServer(killer) ? LS_SERVKILL : LS_OPERKILL, L_TRACE, 0,
437 "A local client %#C KILLED by %#C Path: %s!%s",
438 victim, killer, inpath, path);
440 log_write(IsServer(killer) ? LS_SERVKILL : LS_OPERKILL, L_TRACE, 0,
441 "KILL from %C For %C Path: %s!%s", killer, victim, inpath, path);
444 /* return a struct LogFile for a specific filename--reference counted */
445 static struct LogFile *
446 log_file_create(const char *file)
452 /* if one already exists for that file, return it */
453 for (tmp = logInfo.filelist; tmp; tmp = tmp->next)
454 if (!strcmp(tmp->file, file)) {
459 if (logInfo.freelist) { /* pop one off the free list */
460 tmp = logInfo.freelist;
461 logInfo.freelist = tmp->next;
462 } else /* allocate a new one */
463 tmp = MyMalloc(sizeof(struct LogFile));
465 tmp->fd = -1; /* initialize the structure */
467 DupString(tmp->file, file);
469 tmp->next = logInfo.filelist; /* link it into the list... */
470 tmp->prev_p = &logInfo.filelist;
471 logInfo.filelist->prev_p = &tmp->next;
472 logInfo.filelist = tmp;
477 /* destroy a log file descriptor, under the control of the reference count */
479 log_file_destroy(struct LogFile *lf)
483 if (--lf->ref == 0) {
484 if (lf->next) /* clip it out of the list */
485 lf->next->prev_p = lf->prev_p;
486 *lf->prev_p = lf->next;
488 lf->prev_p = 0; /* we won't use it for the free list */
492 MyFree(lf->file); /* free the file name */
494 lf->next = logInfo.freelist; /* stack it onto the free list */
495 logInfo.freelist = lf;
499 /* finds a subsystem given its name */
500 static struct LogDesc *
501 log_find(char *subsys)
507 /* find the named subsystem */
508 for (i = 0; i < LS_LAST_SYSTEM; i++)
509 if (!ircd_strcmp(subsys, logDesc[i].name))
512 return 0; /* not found */
515 /* find a level given its name */
517 log_lev_find(char *level)
523 /* find the named level */
524 for (i = 0; levelData[i].string; i++)
525 if (!ircd_strcmp(level, levelData[i].string))
526 return levelData[i].level;
528 return L_LAST_LEVEL; /* not found */
531 /* find a facility given its name */
533 log_fac_find(char *facility)
537 assert(0 != facility);
539 /* find the named facility */
540 for (i = 0; facilities[i].name; i++)
541 if (!ircd_strcmp(facility, facilities[i].name))
542 return facilities[i].facility;
544 return LOG_NOTFOUND; /* not found */
547 /* find a snomask value given its name */
549 log_sno_find(char *maskname)
553 assert(0 != maskname);
555 /* find the named snomask */
556 for (i = 0; masks[i].name; i++)
557 if (!ircd_strcmp(maskname, masks[i].name))
558 return masks[i].snomask;
560 return SNO_NOTFOUND; /* not found */
563 /* set the log file for a subsystem */
565 log_set_file(char *subsys, char *filename)
567 struct LogDesc *desc;
570 if (!(desc = log_find(subsys)))
573 /* no change, don't go to the trouble of destroying and recreating */
574 if (filename && !strcmp(desc->file->file, filename))
577 /* debug log is special, since it has to be opened on fd 2 */
578 if (desc->subsys == LS_DEBUG) {
579 log_debug_file(filename);
583 if (desc->file) /* destroy previous entry... */
584 log_file_destroy(desc->file);
586 /* set the file to use */
587 desc->file = filename ? log_file_create(filename) : 0;
590 /* get the log file for a subsystem */
592 log_get_file(char *subsys)
594 struct LogDesc *desc;
597 if (!(desc = log_find(subsys)))
600 return desc->file ? desc->file->file : 0;
603 /* set the facility for a subsystem */
605 log_set_facility(char *subsys, char *facility)
607 struct LogDesc *desc;
611 if (!(desc = log_find(subsys)))
614 /* set syslog facility */
615 if (EmptyString(facility))
616 desc->facility = desc->def_fac;
617 else if ((fac = log_fac_find(facility)) != LOG_NOTFOUND)
618 desc->facility = fac;
621 /* get the facility for a subsystem */
623 log_get_facility(char *subsys)
625 struct LogDesc *desc;
629 if (!(desc = log_find(subsys)))
632 /* find the facility's name */
633 for (i = 0; facilities[i].name; i++)
634 if (desc->facility == facilities[i].facility)
635 return facilities[i].name; /* found it... */
637 return 0; /* shouldn't ever happen */
640 /* set the snomask value for a subsystem */
642 log_set_snomask(char *subsys, char *snomask)
644 struct LogDesc *desc;
648 if (!(desc = log_find(subsys)))
651 /* set snomask value */
652 if (EmptyString(snomask))
653 desc->snomask = desc->def_sno;
654 else if ((sno = log_sno_find(snomask)) != SNO_NOTFOUND)
658 /* get the snomask value for a subsystem */
660 log_get_snomask(char *subsys)
662 struct LogDesc *desc;
666 if (!(desc = log_find(subsys)))
669 /* find the snomask value's name */
670 for (i = 0; masks[i].name; i++)
671 if (desc->snomask == masks[i].snomask)
672 return masks[i].name; /* found it... */
674 return 0; /* shouldn't ever happen */
677 /* set the level for a subsystem */
679 log_set_level(char *subsys, char *level)
681 struct LogDesc *desc;
685 if (!(desc = log_find(subsys)))
688 /* set logging level */
689 if (EmptyString(level))
690 desc->level = L_DEFAULT;
691 else if ((lev = log_lev_find(level)) != L_LAST_LEVEL)
695 /* get the level for a subsystem */
697 log_get_level(char *subsys)
699 struct LogDesc *desc;
703 if (!(desc = log_find(subsys)))
706 /* find the level's name */
707 for (i = 0; levelData[i].string; i++)
708 if (desc->level == levelData[i].level)
709 return levelData[i].string; /* found it... */
711 return 0; /* shouldn't ever happen */
714 /* set the overall default syslog facility */
716 log_set_default(char *facility)
720 oldfac = logInfo.facility;
722 if (EmptyString(facility))
723 logInfo.facility = LOG_USER;
724 else if ((fac = log_fac_find(facility)) != LOG_NOTFOUND &&
725 fac != LOG_NONE && fac != LOG_DEFAULT)
726 logInfo.facility = fac;
728 if (logInfo.facility != oldfac) {
729 closelog(); /* reopen syslog with new facility setting */
730 openlog(logInfo.procname, LOG_PID | LOG_NDELAY, logInfo.facility);
734 /* get the overall default syslog facility */
736 log_get_default(void)
740 /* find the facility's name */
741 for (i = 0; facilities[i].name; i++)
742 if (logInfo.facility == facilities[i].facility)
743 return facilities[i].name; /* found it... */
745 return 0; /* shouldn't ever happen */