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.
29 #include "ircd_alloc.h"
30 #include "ircd_reply.h"
31 #include "ircd_snprintf.h"
32 #include "ircd_string.h"
47 #include <sys/types.h>
53 #define LOG_BUFSIZE 2048
55 /* select default log level cutoff */
57 # define L_DEFAULT L_DEBUG
59 # define L_DEFAULT L_INFO
62 #define LOG_DOSYSLOG 0x10
63 #define LOG_DOFILELOG 0x20
64 #define LOG_DOSNOTICE 0x40
66 #define LOG_DOMASK (LOG_DOSYSLOG | LOG_DOFILELOG | LOG_DOSNOTICE)
68 /* Map severity levels to strings and syslog levels */
69 static struct LevelData {
73 unsigned int snomask; /* 0 means use default in LogDesc */
75 #define L(level, syslog, mask) { L_ ## level, #level, (syslog), (mask) }
76 L(CRIT, LOG_CRIT, SNO_OLDSNO),
78 L(WARNING, LOG_WARNING, 0),
79 L(NOTICE, LOG_NOTICE, 0),
80 L(TRACE, LOG_INFO, 0),
82 L(DEBUG, LOG_INFO, SNO_DEBUG),
84 { L_LAST_LEVEL, 0, 0, 0 }
87 /* Just in case some implementation of syslog has them... */
92 #define LOG_NONE -1 /* don't syslog */
93 #define LOG_DEFAULT 0 /* syslog to logInfo.facility */
94 #define LOG_NOTFOUND -2 /* didn't find a facility corresponding to name */
96 /* Map names to syslog facilities--allows syslog configuration from .conf */
101 #define F(fac) { #fac, LOG_ ## fac }
102 F(NONE), F(DEFAULT), F(AUTH),
106 F(CRON), F(DAEMON), F(LOCAL0), F(LOCAL1), F(LOCAL2), F(LOCAL3),
107 F(LOCAL4), F(LOCAL5), F(LOCAL6), F(LOCAL7), F(LPR), F(MAIL),
108 F(NEWS), F(USER), F(UUCP),
113 #define SNO_NONE 0x00000000 /* don't send server notices */
114 #define SNO_NOTFOUND 0xffffffff /* didn't find a SNO_MASK value for name */
116 /* Map names to snomask values--allows configuration from .conf */
119 unsigned int snomask;
121 #define M(mask) { #mask, SNO_ ## mask }
122 M(NONE), M(OLDSNO), M(SERVKILL), M(OPERKILL), M(HACK2),
123 M(HACK3), M(UNAUTH), M(TCPCOMMON), M(TOOMANY), M(HACK4),
124 M(GLINE), M(NETWORK), M(IPMISMATCH), M(THROTTLE), M(OLDREALOP),
125 M(CONNEXIT), M(DEBUG),
130 #define LOG_MARK_FILE 0x0001 /* file has been changed */
131 #define LOG_MARK_FACILITY 0x0002 /* facility has been changed */
132 #define LOG_MARK_SNOMASK 0x0004 /* snomask has been changed */
133 #define LOG_MARK_LEVEL 0x0008 /* level has been changed */
135 /* Descriptions of all logging subsystems */
136 static struct LogDesc {
137 enum LogSys subsys; /* number for subsystem */
138 char *name; /* subsystem name */
139 struct LogFile *file; /* file descriptor for subsystem */
140 unsigned int mark; /* subsystem has been changed */
141 int def_fac; /* default facility */
142 unsigned int def_sno; /* default snomask */
143 int facility; /* -1 means don't use syslog */
144 unsigned int snomask; /* 0 means no server message */
145 enum LogLevel level; /* logging level */
147 #define S(sys, p, sn) { LS_##sys, #sys, 0, 0, (p), (sn), (p), (sn), L_DEFAULT }
149 S(CONFIG, 0, SNO_OLDSNO),
150 S(OPERMODE, -1, SNO_HACK4),
151 S(GLINE, -1, SNO_GLINE),
152 S(JUPE, -1, SNO_NETWORK),
154 S(NETWORK, -1, SNO_NETWORK),
158 S(OPER, -1, SNO_OLDREALOP),
161 S(DEBUG, -1, SNO_DEBUG),
164 { LS_LAST_SYSTEM, 0, 0, -1, 0, -1, 0 }
167 /* describes a log file */
169 struct LogFile *next; /* next log file descriptor */
170 struct LogFile **prev_p; /* what points to us */
171 int fd; /* file's descriptor-- -1 if not open */
172 int ref; /* how many things refer to us? */
173 char *file; /* file name */
176 /* modifiable static information */
178 struct LogFile *filelist; /* list of log files */
179 struct LogFile *freelist; /* list of free'd log files */
180 int facility; /* default facility */
181 const char *procname; /* process's name */
182 struct LogFile *dbfile; /* debug file */
183 } logInfo = { 0, 0, LOG_USER, "ircd", 0 };
185 /* helper routine to open a log file if needed */
187 log_open(struct LogFile *lf)
189 /* only open the file if we haven't already */
190 if (lf && lf->fd < 0) {
191 alarm(3); /* if NFS hangs, we hang only for 3 seconds */
192 lf->fd = open(lf->file, O_WRONLY | O_CREAT | O_APPEND,
200 /* reopen debug log */
202 log_debug_reopen(void)
204 if (!logInfo.dbfile) /* no open debugging file */
207 if (!logInfo.dbfile->file) { /* using terminal output */
208 logInfo.dbfile->fd = 2;
212 /* Ok, it's a real file; close it if necessary and use log_open to open it */
213 if (logInfo.dbfile->fd >= 0) {
214 close(logInfo.dbfile->fd);
215 logInfo.dbfile->fd = -1; /* mark that it's closed for log_open */
218 log_open(logInfo.dbfile);
220 if (logInfo.dbfile->fd < 0) { /* try again with /dev/null */
221 if ((logInfo.dbfile->fd = open("/dev/null", O_WRONLY)) < 0)
225 /* massage the file descriptor to be stderr */
226 if (logInfo.dbfile->fd != 2) {
228 fd = dup2(logInfo.dbfile->fd, 2);
229 close(logInfo.dbfile->fd);
230 logInfo.dbfile->fd = fd;
234 /* initialize debugging log */
236 log_debug_init(int usetty)
238 logInfo.dbfile = (struct LogFile*) MyMalloc(sizeof(struct LogFile));
240 logInfo.dbfile->next = 0; /* initialize debugging filename */
241 logInfo.dbfile->prev_p = 0;
242 logInfo.dbfile->fd = -1;
243 logInfo.dbfile->ref = 1;
245 if (usetty) /* store pathname to use */
246 logInfo.dbfile->file = 0;
248 DupString(logInfo.dbfile->file, LOGFILE);
250 log_debug_reopen(); /* open the debug log */
252 logDesc[LS_DEBUG].file = logInfo.dbfile; /* remember where it went */
255 #endif /* DEBUGMODE */
257 /* set the debug log file */
259 log_debug_file(const char *file)
265 /* If we weren't started with debugging enabled, or if we're using
266 * the terminal, don't do anything at all.
268 if (!logInfo.dbfile || !logInfo.dbfile->file)
271 MyFree(logInfo.dbfile->file); /* free old pathname */
272 DupString(logInfo.dbfile->file, file); /* store new pathname */
274 log_debug_reopen(); /* reopen the debug log */
275 #endif /* DEBUGMODE */
279 /* called in place of open_log(), this stores the process name and prepares
283 log_init(const char *process_name)
285 /* store the process name; probably belongs in ircd.c, but oh well... */
286 if (!EmptyString(process_name))
287 logInfo.procname = process_name;
289 /* ok, open syslog; default facility: LOG_USER */
290 openlog(logInfo.procname, LOG_PID | LOG_NDELAY, logInfo.facility);
293 /* Files are persistently open; this closes and reopens them to allow
299 log_close(); /* close everything...we reopen on demand */
302 log_debug_reopen(); /* reopen debugging log if necessary */
303 #endif /* DEBUGMODE */
305 /* reopen syslog, if needed; default facility: LOG_USER */
306 openlog(logInfo.procname, LOG_PID | LOG_NDELAY, logInfo.facility);
309 /* close the log files */
315 closelog(); /* close syslog */
317 for (ptr = logInfo.filelist; ptr; ptr = ptr->next) {
319 close(ptr->fd); /* close all the files... */
324 if (logInfo.dbfile && logInfo.dbfile->file) {
325 if (logInfo.dbfile->fd >= 0)
326 close(logInfo.dbfile->fd); /* close the debug log file */
328 logInfo.dbfile->fd = -1;
332 /* These write entries to a log file */
334 log_write(enum LogSys subsys, enum LogLevel severity, unsigned int flags,
335 const char *fmt, ...)
340 log_vwrite(subsys, severity, flags, fmt, vl);
345 log_vwrite(enum LogSys subsys, enum LogLevel severity, unsigned int flags,
346 const char *fmt, va_list vl)
349 struct LogDesc *desc;
350 struct LevelData *ldata;
352 struct iovec vector[3];
354 char buf[LOG_BUFSIZE];
355 /* 1234567890123456789012 3 */
356 /* [2000-11-28 16:11:20] \0 */
359 /* check basic assumptions */
360 assert(-1 < (int)subsys);
361 assert((int)subsys < LS_LAST_SYSTEM);
362 assert(-1 < (int)severity);
363 assert((int)severity < L_LAST_LEVEL);
364 assert(0 == (flags & ~LOG_NOMASK));
367 /* find the log data and the severity data */
368 desc = &logDesc[subsys];
369 ldata = &levelData[severity];
371 /* check the set of ordering assumptions */
372 assert(desc->subsys == subsys);
373 assert(ldata->level == severity);
375 /* check severity... */
376 if (severity > desc->level)
379 /* figure out where all we need to log */
380 if (!(flags & LOG_NOFILELOG) && desc->file) {
381 log_open(desc->file);
382 if (desc->file->fd >= 0) /* don't log to file if we can't open the file */
383 flags |= LOG_DOFILELOG;
386 if (!(flags & LOG_NOSYSLOG) && desc->facility >= 0)
387 flags |= LOG_DOSYSLOG; /* will syslog */
389 if (!(flags & LOG_NOSNOTICE) && (desc->snomask != 0 || ldata->snomask != 0))
390 flags |= LOG_DOSNOTICE; /* will send a server notice */
392 /* short-circuit if there's nothing to do... */
393 if (!(flags & LOG_DOMASK))
396 /* Build the basic log string */
400 /* save the length for writev */
401 /* Log format: "SYSTEM [SEVERITY]: log message" */
403 ircd_snprintf(0, buf, sizeof(buf), "%s [%s]: %v", desc->name,
406 /* if we have something to write to... */
407 if (flags & LOG_DOFILELOG) {
409 tstamp = localtime(&curtime); /* build the timestamp */
412 ircd_snprintf(0, timebuf, sizeof(timebuf), "[%d-%d-%d %d:%02d:%02d] ",
413 tstamp->tm_year + 1900, tstamp->tm_mon + 1,
414 tstamp->tm_mday, tstamp->tm_hour, tstamp->tm_min,
417 /* set up the remaining parts of the writev vector... */
418 vector[0].iov_base = timebuf;
419 vector[1].iov_base = buf;
421 vector[2].iov_base = (void*) "\n"; /* terminate lines with a \n */
422 vector[2].iov_len = 1;
424 /* write it out to the log file */
425 writev(desc->file->fd, vector, 3);
428 /* oh yeah, syslog it too... */
429 if (flags & LOG_DOSYSLOG)
430 syslog(ldata->syslog | desc->facility, "%s", buf);
432 /* can't forget server notices... */
433 if (flags & LOG_DOSNOTICE)
434 sendto_opmask_butone(0, ldata->snomask ? ldata->snomask : desc->snomask,
438 /* log kills for fun and profit */
440 log_write_kill(const struct Client *victim, const struct Client *killer,
441 const char *inpath, const char *path, const char *msg)
444 log_write(IsServer(killer) ? LS_SERVKILL : LS_OPERKILL, L_TRACE, 0,
445 "A local client %#C KILLED by %#C Path: %s!%s %s",
446 victim, killer, inpath, path, msg);
448 log_write(IsServer(killer) ? LS_SERVKILL : LS_OPERKILL, L_TRACE, 0,
449 "KILL from %C For %C Path: %s!%s %s", killer, victim, inpath,
453 /* return a struct LogFile for a specific filename--reference counted */
454 static struct LogFile *
455 log_file_create(const char *file)
461 /* if one already exists for that file, return it */
462 for (tmp = logInfo.filelist; tmp; tmp = tmp->next)
463 if (!strcmp(tmp->file, file)) {
468 if (logInfo.freelist) { /* pop one off the free list */
469 tmp = logInfo.freelist;
470 logInfo.freelist = tmp->next;
471 } else /* allocate a new one */
472 tmp = (struct LogFile*) MyMalloc(sizeof(struct LogFile));
474 tmp->fd = -1; /* initialize the structure */
476 DupString(tmp->file, file);
478 tmp->next = logInfo.filelist; /* link it into the list... */
479 tmp->prev_p = &logInfo.filelist;
480 if (logInfo.filelist)
481 logInfo.filelist->prev_p = &tmp->next;
482 logInfo.filelist = tmp;
487 /* destroy a log file descriptor, under the control of the reference count */
489 log_file_destroy(struct LogFile *lf)
493 if (--lf->ref == 0) {
494 if (lf->next) /* clip it out of the list */
495 lf->next->prev_p = lf->prev_p;
496 *lf->prev_p = lf->next;
498 lf->prev_p = 0; /* we won't use it for the free list */
502 MyFree(lf->file); /* free the file name */
504 lf->next = logInfo.freelist; /* stack it onto the free list */
505 logInfo.freelist = lf;
509 /* finds a subsystem given its name */
510 static struct LogDesc *
511 log_find(const char *subsys)
517 /* find the named subsystem */
518 for (i = 0; i < LS_LAST_SYSTEM; i++)
519 if (!ircd_strcmp(subsys, logDesc[i].name))
522 return 0; /* not found */
525 /* canonicalize subsystem names */
527 log_canon(const char *subsys)
529 struct LogDesc *desc;
531 if (!(desc = log_find(subsys)))
537 /* find a level given its name */
539 log_lev_find(const char *level)
545 /* find the named level */
546 for (i = 0; levelData[i].string; i++)
547 if (!ircd_strcmp(level, levelData[i].string))
548 return levelData[i].level;
550 return L_LAST_LEVEL; /* not found */
553 /* return a name for a level */
555 log_lev_name(enum LogLevel lev)
557 assert(-1 < (int)lev);
558 assert((int)lev < L_LAST_LEVEL);
559 assert(lev == levelData[lev].level);
561 return levelData[lev].string;
564 /* find a facility given its name */
566 log_fac_find(const char *facility)
570 assert(0 != facility);
572 /* find the named facility */
573 for (i = 0; facilities[i].name; i++)
574 if (!ircd_strcmp(facility, facilities[i].name))
575 return facilities[i].facility;
577 return LOG_NOTFOUND; /* not found */
580 /* return a name for a facility */
582 log_fac_name(int fac)
586 /* find the facility */
587 for (i = 0; facilities[i].name; i++)
588 if (facilities[i].facility == fac)
589 return facilities[i].name;
591 return 0; /* not found; should never happen */
594 /* find a snomask value given its name */
596 log_sno_find(const char *maskname)
600 assert(0 != maskname);
602 /* find the named snomask */
603 for (i = 0; masks[i].name; i++)
604 if (!ircd_strcmp(maskname, masks[i].name))
605 return masks[i].snomask;
607 return SNO_NOTFOUND; /* not found */
610 /* return a name for a snomask value */
612 log_sno_name(unsigned int sno)
616 /* find the snomask */
617 for (i = 0; masks[i].name; i++)
618 if (masks[i].snomask == sno)
619 return masks[i].name;
621 return 0; /* not found; should never happen */
624 /* set the log file for a subsystem */
626 log_set_file(const char *subsys, const char *filename)
628 struct LogDesc *desc;
631 if (!(desc = log_find(subsys)))
635 desc->mark |= LOG_MARK_FILE; /* mark that file has been changed */
637 desc->mark &= ~LOG_MARK_FILE; /* file has been reset to defaults */
639 /* no change, don't go to the trouble of destroying and recreating */
640 if (desc->file && desc->file->file && filename &&
641 !strcmp(desc->file->file, filename))
644 /* debug log is special, since it has to be opened on fd 2 */
645 if (desc->subsys == LS_DEBUG)
646 return log_debug_file(filename);
648 if (desc->file) /* destroy previous entry... */
649 log_file_destroy(desc->file);
651 /* set the file to use */
652 desc->file = filename ? log_file_create(filename) : 0;
657 /* get the log file for a subsystem */
659 log_get_file(const char *subsys)
661 struct LogDesc *desc;
664 if (!(desc = log_find(subsys)))
667 return desc->file ? desc->file->file : 0;
670 /* set the facility for a subsystem */
672 log_set_facility(const char *subsys, const char *facility)
674 struct LogDesc *desc;
678 if (!(desc = log_find(subsys)))
681 /* set syslog facility */
682 if (EmptyString(facility)) {
683 desc->facility = desc->def_fac;
684 desc->mark &= ~LOG_MARK_FACILITY;
685 } else if ((fac = log_fac_find(facility)) != LOG_NOTFOUND) {
686 desc->facility = fac;
687 if (fac == desc->def_fac)
688 desc->mark &= ~LOG_MARK_FACILITY;
690 desc->mark |= LOG_MARK_FACILITY;
697 /* get the facility for a subsystem */
699 log_get_facility(const char *subsys)
701 struct LogDesc *desc;
704 if (!(desc = log_find(subsys)))
707 /* find the facility's name */
708 return log_fac_name(desc->facility);
711 /* set the snomask value for a subsystem */
713 log_set_snomask(const char *subsys, const char *snomask)
715 struct LogDesc *desc;
716 unsigned int sno = SNO_DEFAULT;
719 if (!(desc = log_find(subsys)))
722 /* set snomask value */
723 if (EmptyString(snomask)) {
724 desc->snomask = desc->def_sno;
725 desc->mark &= ~LOG_MARK_SNOMASK;
726 } else if ((sno = log_sno_find(snomask)) != SNO_NOTFOUND) {
728 if (sno == desc->def_sno)
729 desc->mark &= ~LOG_MARK_SNOMASK;
731 desc->mark |= LOG_MARK_SNOMASK;
738 /* get the snomask value for a subsystem */
740 log_get_snomask(const char *subsys)
742 struct LogDesc *desc;
745 if (!(desc = log_find(subsys)))
748 /* find the snomask value's name */
749 return log_sno_name(desc->snomask);
752 /* set the level for a subsystem */
754 log_set_level(const char *subsys, const char *level)
756 struct LogDesc *desc;
760 if (!(desc = log_find(subsys)))
763 /* set logging level */
764 if (EmptyString(level)) {
765 desc->level = L_DEFAULT;
766 desc->mark &= ~LOG_MARK_LEVEL;
767 } else if ((lev = log_lev_find(level)) != L_LAST_LEVEL) {
769 if (lev == L_DEFAULT)
770 desc->mark &= ~LOG_MARK_LEVEL;
772 desc->mark |= LOG_MARK_LEVEL;
779 /* get the level for a subsystem */
781 log_get_level(const char *subsys)
783 struct LogDesc *desc;
786 if (!(desc = log_find(subsys)))
789 /* find the level's name */
790 return log_lev_name(desc->level);
793 /* set the overall default syslog facility */
795 log_set_default(const char *facility)
799 oldfac = logInfo.facility;
801 if (EmptyString(facility))
802 logInfo.facility = LOG_USER;
803 else if ((fac = log_fac_find(facility)) != LOG_NOTFOUND &&
804 fac != LOG_NONE && fac != LOG_DEFAULT)
805 logInfo.facility = fac;
809 if (logInfo.facility != oldfac) {
810 closelog(); /* reopen syslog with new facility setting */
811 openlog(logInfo.procname, LOG_PID | LOG_NDELAY, logInfo.facility);
817 /* get the overall default syslog facility */
819 log_get_default(void)
821 /* find the facility's name */
822 return log_fac_name(logInfo.facility);
825 /* Clear the marks... */
827 log_feature_unmark(void)
831 for (i = 0; i < LS_LAST_SYSTEM; i++)
835 /* Reset unmarked fields to defaults... */
837 log_feature_mark(int flag)
844 for (i = 0; i < LS_LAST_SYSTEM; i++) {
845 if (!(logDesc[i].mark & LOG_MARK_FILE)) {
846 if (logDesc[i].subsys != LS_DEBUG) { /* debug is special */
847 if (logDesc[i].file) /* destroy previous entry... */
848 log_file_destroy(logDesc[i].file);
853 if (!(logDesc[i].mark & LOG_MARK_FACILITY)) /* set default facility */
854 logDesc[i].facility = logDesc[i].def_fac;
856 if (!(logDesc[i].mark & LOG_MARK_SNOMASK)) /* set default snomask */
857 logDesc[i].snomask = logDesc[i].def_sno;
859 if (!(logDesc[i].mark & LOG_MARK_LEVEL)) /* set default level */
860 logDesc[i].level = L_DEFAULT;
863 return 0; /* we don't have a notify handler */
866 /* Report feature settings */
868 log_feature_report(struct Client *to, int flag)
872 for (i = 0; i < LS_LAST_SYSTEM; i++)
874 if (logDesc[i].mark & LOG_MARK_FILE) /* report file */
875 send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s FILE %s",
876 logDesc[i].name, (logDesc[i].file && logDesc[i].file->file ?
877 logDesc[i].file->file : "(terminal)"));
879 if (logDesc[i].mark & LOG_MARK_FACILITY) /* report facility */
880 send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s FACILITY %s",
881 logDesc[i].name, log_fac_name(logDesc[i].facility));
883 if (logDesc[i].mark & LOG_MARK_SNOMASK) /* report snomask */
884 send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s SNOMASK %s",
885 logDesc[i].name, log_sno_name(logDesc[i].snomask));
887 if (logDesc[i].mark & LOG_MARK_LEVEL) /* report log level */
888 send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s LEVEL %s",
889 logDesc[i].name, log_lev_name(logDesc[i].level));
892 if (flag) /* report default facility */
893 send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s",
894 log_fac_name(logInfo.facility));