8f5ac69d9223ea67f6fec7ad33771323bab395dc
[ircu2.10.12-pk.git] / ircd / ircd_log.c
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>
5  *
6  *   See file AUTHORS in IRC package for additional names of
7  *   the programmers.
8  *
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)
12  *   any later version.
13  *
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.
18  *
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.
22  */
23 /** @file
24  * @brief IRC logging implementation.
25  * @version $Id$
26  */
27 #include "config.h"
28
29 #include "ircd_log.h"
30 #include "client.h"
31 #include "ircd_alloc.h"
32 #include "ircd_reply.h"
33 #include "ircd_snprintf.h"
34 #include "ircd_string.h"
35 #include "ircd.h"
36 #include "numeric.h"
37 #include "s_debug.h"
38 #include "send.h"
39 #include "struct.h"
40
41 /* #include <assert.h> -- Now using assert in ircd_log.h */
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <stdarg.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <sys/stat.h>
49 #include <sys/types.h>
50 #include <sys/uio.h>
51 #include <syslog.h>
52 #include <time.h>
53 #include <unistd.h>
54
55 int log_inassert = 0;
56
57 #define LOG_BUFSIZE 2048 /**< Maximum length for a log message. */
58
59 /** Select default log level cutoff. */
60 #ifdef DEBUGMODE
61 # define L_DEFAULT      L_DEBUG
62 #else
63 # define L_DEFAULT      L_INFO
64 #endif
65
66 #define LOG_DOSYSLOG    0x10 /**< Try to use syslog. */
67 #define LOG_DOFILELOG   0x20 /**< Try to log to a file. */
68 #define LOG_DOSNOTICE   0x40 /**< Try to notify operators via notice. */
69 /** Bitmask of valid delivery mechanisms. */
70 #define LOG_DOMASK      (LOG_DOSYSLOG | LOG_DOFILELOG | LOG_DOSNOTICE)
71
72 /** Map severity levels to strings and syslog levels */
73 static struct LevelData {
74   enum LogLevel level;   /**< Log level being described. */
75   char         *string;  /**< Textual name of level. */
76   int           syslog;  /**< Syslog priority for log level. */
77   unsigned int  snomask; /**< Server notice mask; 0 means use default in LogDesc. */
78 } levelData[] = {
79 #define L(level, syslog, mask) { L_ ## level, #level, (syslog), (mask) }
80   L(CRIT, LOG_CRIT, SNO_OLDSNO),
81   L(ERROR, LOG_ERR, 0),
82   L(WARNING, LOG_WARNING, 0),
83   L(NOTICE, LOG_NOTICE, 0),
84   L(TRACE, LOG_INFO, 0),
85   L(INFO, LOG_INFO, 0),
86   L(DEBUG, LOG_INFO, SNO_DEBUG),
87 #undef L
88   { L_LAST_LEVEL, 0, 0, 0 }
89 };
90
91 /* Just in case some implementation of syslog has them... */
92 #undef LOG_NONE
93 #undef LOG_DEFAULT
94 #undef LOG_NOTFOUND
95
96 #define LOG_NONE     -1 /**< don't syslog */
97 #define LOG_DEFAULT   0 /**< syslog to logInfo.facility */
98 #define LOG_NOTFOUND -2 /**< didn't find a facility corresponding to name */
99
100 /** Map names to syslog facilities. */
101 static struct {
102   char *name;   /**< Textual name of facility. */
103   int facility; /**< Facility value for syslog(). */
104 } facilities[] = {
105 #define F(fac) { #fac, LOG_ ## fac }
106   F(NONE),    F(DEFAULT), F(AUTH),
107 #ifdef LOG_AUTHPRIV
108   F(AUTHPRIV),
109 #endif
110   F(CRON),    F(DAEMON),  F(LOCAL0),  F(LOCAL1),  F(LOCAL2),  F(LOCAL3),
111   F(LOCAL4),  F(LOCAL5),  F(LOCAL6),  F(LOCAL7),  F(LPR),     F(MAIL),
112   F(NEWS),    F(USER),    F(UUCP),
113 #undef F
114   { 0, 0 }
115 };
116
117 #define SNO_NONE     0x00000000 /**< don't send server notices */
118 #define SNO_NOTFOUND 0xffffffff /**< didn't find a SNO_MASK value for name */
119
120 /** Map names to snomask values. */
121 static struct {
122   char *name;           /**< Name of server notice bit. */
123   unsigned int snomask; /**< Bitmask corresponding to name. */
124 } masks[] = {
125 #define M(mask) { #mask, SNO_ ## mask }
126   M(NONE),       M(OLDSNO),     M(SERVKILL),   M(OPERKILL),   M(HACK2),
127   M(HACK3),      M(UNAUTH),     M(TCPCOMMON),  M(TOOMANY),    M(HACK4),
128   M(GLINE),      M(NETWORK),    M(IPMISMATCH), M(THROTTLE),   M(OLDREALOP),
129   M(CONNEXIT),   M(DEBUG),
130 #undef M
131   { 0, 0 }
132 };
133
134 #define LOG_MARK_FILE           0x0001  /**< file has been changed */
135 #define LOG_MARK_FACILITY       0x0002  /**< facility has been changed */
136 #define LOG_MARK_SNOMASK        0x0004  /**< snomask has been changed */
137 #define LOG_MARK_LEVEL          0x0008  /**< level has been changed */
138
139 /** Descriptions of all logging subsystems. */
140 static struct LogDesc {
141   enum LogSys     subsys;   /**< number for subsystem */
142   char           *name;     /**< subsystem name */
143   struct LogFile *file;     /**< file descriptor for subsystem */
144   unsigned int    mark;     /**< subsystem has been changed */
145   int             def_fac;  /**< default facility */
146   unsigned int    def_sno;  /**< default snomask */
147   int             facility; /**< -1 means don't use syslog */
148   unsigned int    snomask;  /**< 0 means no server message */
149   enum LogLevel   level;    /**< logging level */
150 } logDesc[] = {
151 #define S(sys, p, sn) { LS_##sys, #sys, 0, 0, (p), (sn), (p), (sn), L_DEFAULT }
152   S(SYSTEM, -1, 0),
153   S(CONFIG, 0, SNO_OLDSNO),
154   S(OPERMODE, -1, SNO_HACK4),
155   S(GLINE, -1, SNO_GLINE),
156   S(JUPE, -1, SNO_NETWORK),
157   S(WHO, -1, 0),
158   S(NETWORK, -1, SNO_NETWORK),
159   S(OPERKILL, -1, 0),
160   S(SERVKILL, -1, 0),
161   S(USER, -1, 0),
162   S(OPER, -1, SNO_OLDREALOP),
163   S(RESOLVER, -1, 0),
164   S(SOCKET, -1, 0),
165   S(DEBUG, -1, SNO_DEBUG),
166   S(OLDLOG, -1, 0),
167 #undef S
168   { LS_LAST_SYSTEM, 0, 0, -1, 0, -1, 0 }
169 };
170
171 /** Describes a log file. */
172 struct LogFile {
173   struct LogFile  *next;   /**< next log file descriptor */
174   struct LogFile **prev_p; /**< what points to us */
175   int              fd;     /**< file's descriptor-- -1 if not open */
176   int              ref;    /**< how many things refer to us? */
177   char            *file;   /**< file name */
178 };
179
180 /** Modifiable static information. */
181 static struct {
182   struct LogFile *filelist; /**< list of log files */
183   struct LogFile *freelist; /**< list of free'd log files */
184   int             facility; /**< default facility */
185   const char     *procname; /**< process's name */
186   struct LogFile *dbfile;   /**< debug file */
187 } logInfo = { 0, 0, LOG_USER, "ircd", 0 };
188
189 /** Helper routine to open a log file if needed.
190  * If the log file is already open, do nothing.
191  * @param[in,out] lf Log file to open.
192  */
193 static void
194 log_open(struct LogFile *lf)
195 {
196   /* only open the file if we haven't already */
197   if (lf && lf->fd < 0) {
198     alarm(3); /* if NFS hangs, we hang only for 3 seconds */
199     lf->fd = open(lf->file, O_WRONLY | O_CREAT | O_APPEND,
200                   S_IRUSR | S_IWUSR);
201     alarm(0);
202   }
203 }
204
205 #ifdef DEBUGMODE
206
207 /** Reopen debug log file. */
208 static void
209 log_debug_reopen(void)
210 {
211   if (!logInfo.dbfile) /* no open debugging file */
212     return;
213
214   if (!logInfo.dbfile->file) { /* using terminal output */
215     logInfo.dbfile->fd = 2;
216     return;
217   }
218
219   /* Ok, it's a real file; close it if necessary and use log_open to open it */
220   if (logInfo.dbfile->fd >= 0) {
221     close(logInfo.dbfile->fd);
222     logInfo.dbfile->fd = -1; /* mark that it's closed for log_open */
223   }
224
225   log_open(logInfo.dbfile);
226
227   if (logInfo.dbfile->fd < 0) { /* try again with /dev/null */
228     if ((logInfo.dbfile->fd = open("/dev/null", O_WRONLY)) < 0)
229       exit(-1);
230   }
231
232   /* massage the file descriptor to be stderr */
233   if (logInfo.dbfile->fd != 2) {
234     int fd;
235     fd = dup2(logInfo.dbfile->fd, 2);
236     close(logInfo.dbfile->fd);
237     logInfo.dbfile->fd = fd;
238   }
239 }
240
241 /** initialize debugging log file.
242  * @param[in] usetty If non-zero, log to terminal instead of file.
243  */
244 void
245 log_debug_init(int usetty)
246 {
247   logInfo.dbfile = (struct LogFile*) MyMalloc(sizeof(struct LogFile));
248
249   logInfo.dbfile->next = 0; /* initialize debugging filename */
250   logInfo.dbfile->prev_p = 0;
251   logInfo.dbfile->fd = -1;
252   logInfo.dbfile->ref = 1;
253
254   if (usetty) /* store pathname to use */
255     logInfo.dbfile->file = 0;
256   else
257     DupString(logInfo.dbfile->file, LOGFILE);
258
259   log_debug_reopen(); /* open the debug log */
260
261   logDesc[LS_DEBUG].file = logInfo.dbfile; /* remember where it went */
262 }
263
264 #endif /* DEBUGMODE */
265
266 /** Set the debug log file name.
267  * @param[in] file File name, or NULL to select the default.
268  * @return Zero if the file was reopened; non-zero if not debugging to file.
269  */
270 static int
271 log_debug_file(const char *file)
272 {
273 #ifdef DEBUGMODE
274   if (!file)
275     file = LOGFILE;
276
277   /* If we weren't started with debugging enabled, or if we're using
278    * the terminal, don't do anything at all.
279    */
280   if (!logInfo.dbfile || !logInfo.dbfile->file)
281     return 1;
282
283   MyFree(logInfo.dbfile->file); /* free old pathname */
284   DupString(logInfo.dbfile->file, file); /* store new pathname */
285
286   log_debug_reopen(); /* reopen the debug log */
287 #endif /* DEBUGMODE */
288   return 0;
289 }
290
291 /** Initialize logging subsystem.
292  * @param[in] process_name Process name to interactions with syslog.
293  */
294 void
295 log_init(const char *process_name)
296 {
297   /* store the process name; probably belongs in ircd.c, but oh well... */
298   if (!EmptyString(process_name))
299     logInfo.procname = process_name;
300
301   /* ok, open syslog; default facility: LOG_USER */
302   openlog(logInfo.procname, LOG_PID | LOG_NDELAY, logInfo.facility);
303 }
304
305 /** Reopen log files (so admins can do things like rotate log files). */
306 void
307 log_reopen(void)
308 {
309   log_close(); /* close everything...we reopen on demand */
310
311 #ifdef DEBUGMODE
312   log_debug_reopen(); /* reopen debugging log if necessary */
313 #endif /* DEBUGMODE */
314
315   /* reopen syslog, if needed; default facility: LOG_USER */
316   openlog(logInfo.procname, LOG_PID | LOG_NDELAY, logInfo.facility);
317 }
318
319 /** Close all log files. */
320 void
321 log_close(void)
322 {
323   struct LogFile *ptr;
324
325   closelog(); /* close syslog */
326
327   for (ptr = logInfo.filelist; ptr; ptr = ptr->next) {
328     if (ptr->fd >= 0)
329       close(ptr->fd); /* close all the files... */
330
331     ptr->fd = -1;
332   }
333
334   if (logInfo.dbfile && logInfo.dbfile->file) {
335     if (logInfo.dbfile->fd >= 0)
336       close(logInfo.dbfile->fd); /* close the debug log file */
337
338     logInfo.dbfile->fd = -1;
339   }
340 }
341
342 /** Write a logging entry.
343  * @param[in] subsys Target subsystem.
344  * @param[in] severity Severity of message.
345  * @param[in] flags Combination of zero or more of LOG_NOSYSLOG, LOG_NOFILELOG, LOG_NOSNOTICE to suppress certain output.
346  * @param[in] fmt Format string for message.
347  */
348 void
349 log_write(enum LogSys subsys, enum LogLevel severity, unsigned int flags,
350           const char *fmt, ...)
351 {
352   va_list vl;
353
354   va_start(vl, fmt);
355   log_vwrite(subsys, severity, flags, fmt, vl);
356   va_end(vl);
357 }
358
359 /** Write a logging entry using a va_list.
360  * @param[in] subsys Target subsystem.
361  * @param[in] severity Severity of message.
362  * @param[in] flags Combination of zero or more of LOG_NOSYSLOG, LOG_NOFILELOG, LOG_NOSNOTICE to suppress certain output.
363  * @param[in] fmt Format string for message.
364  * @param[in] vl Variable-length argument list for message.
365  */
366 void
367 log_vwrite(enum LogSys subsys, enum LogLevel severity, unsigned int flags,
368            const char *fmt, va_list vl)
369 {
370   struct VarData vd;
371   struct LogDesc *desc;
372   struct LevelData *ldata;
373   struct tm *tstamp;
374   struct iovec vector[3];
375   time_t curtime;
376   char buf[LOG_BUFSIZE];
377   /* 1234567890123456789012 3 */
378   /* [2000-11-28 16:11:20] \0 */
379   char timebuf[23];
380
381   /* check basic assumptions */
382   assert(-1 < (int)subsys);
383   assert((int)subsys < LS_LAST_SYSTEM);
384   assert(-1 < (int)severity);
385   assert((int)severity < L_LAST_LEVEL);
386   assert(0 == (flags & ~LOG_NOMASK));
387   assert(0 != fmt);
388
389   /* find the log data and the severity data */
390   desc = &logDesc[subsys];
391   ldata = &levelData[severity];
392
393   /* check the set of ordering assumptions */
394   assert(desc->subsys == subsys);
395   assert(ldata->level == severity);
396
397   /* check severity... */
398   if (severity > desc->level)
399     return;
400
401   /* figure out where all we need to log */
402   if (!(flags & LOG_NOFILELOG) && desc->file) {
403     log_open(desc->file);
404     if (desc->file->fd >= 0) /* don't log to file if we can't open the file */
405       flags |= LOG_DOFILELOG;
406   }
407
408   if (!(flags & LOG_NOSYSLOG) && desc->facility >= 0)
409     flags |= LOG_DOSYSLOG; /* will syslog */
410
411   if (!(flags & LOG_NOSNOTICE) && (desc->snomask != 0 || ldata->snomask != 0))
412     flags |= LOG_DOSNOTICE; /* will send a server notice */
413
414   /* short-circuit if there's nothing to do... */
415   if (!(flags & LOG_DOMASK))
416     return;
417
418   /* Build the basic log string */
419   vd.vd_format = fmt;
420   va_copy(vd.vd_args, vl);
421
422   /* save the length for writev */
423   /* Log format: "SYSTEM [SEVERITY]: log message" */
424   vector[1].iov_len =
425     ircd_snprintf(0, buf, sizeof(buf), "%s [%s]: %v", desc->name,
426                   ldata->string, &vd);
427
428   /* if we have something to write to... */
429   if (flags & LOG_DOFILELOG) {
430     curtime = TStime();
431     tstamp = localtime(&curtime); /* build the timestamp */
432
433     vector[0].iov_len =
434       ircd_snprintf(0, timebuf, sizeof(timebuf), "[%d-%d-%d %d:%02d:%02d] ",
435                     tstamp->tm_year + 1900, tstamp->tm_mon + 1,
436                     tstamp->tm_mday, tstamp->tm_hour, tstamp->tm_min,
437                     tstamp->tm_sec);
438
439     /* set up the remaining parts of the writev vector... */
440     vector[0].iov_base = timebuf;
441     vector[1].iov_base = buf;
442
443     vector[2].iov_base = (void*) "\n"; /* terminate lines with a \n */
444     vector[2].iov_len = 1;
445
446     /* write it out to the log file */
447     writev(desc->file->fd, vector, 3);
448   }
449
450   /* oh yeah, syslog it too... */
451   if (flags & LOG_DOSYSLOG)
452     syslog(ldata->syslog | desc->facility, "%s", buf);
453
454   /* can't forget server notices... */
455   if (flags & LOG_DOSNOTICE)
456     sendto_opmask_butone(0, ldata->snomask ? ldata->snomask : desc->snomask,
457                          "%s", buf);
458 }
459
460 /** Log an appropriate message for kills.
461  * @param[in] victim %Client being killed.
462  * @param[in] killer %User or server doing the killing.
463  * @param[in] inpath Peer that sent us the KILL message.
464  * @param[in] path Kill path that sent to us by \a inpath.
465  * @param[in] msg Kill reason.
466  */
467 void
468 log_write_kill(const struct Client *victim, const struct Client *killer,
469                const char *inpath, const char *path, const char *msg)
470 {
471   if (MyUser(victim))
472     log_write(IsServer(killer) ? LS_SERVKILL : LS_OPERKILL, L_TRACE, 0,
473               "A local client %#C KILLED by %#C Path: %s!%s %s",
474               victim, killer, inpath, path, msg);
475   else
476     log_write(IsServer(killer) ? LS_SERVKILL : LS_OPERKILL, L_TRACE, 0,
477               "KILL from %C For %C Path: %s!%s %s", killer, victim, inpath,
478               path, msg);
479 }
480
481 /** Find a reference-counted LogFile by file name.
482  * @param[in] file Name of file.
483  * @return A log file descriptor with LogFile::ref at least 1.
484  */
485 static struct LogFile *
486 log_file_create(const char *file)
487 {
488   struct LogFile *tmp;
489
490   assert(0 != file);
491
492   /* if one already exists for that file, return it */
493   for (tmp = logInfo.filelist; tmp; tmp = tmp->next)
494     if (!strcmp(tmp->file, file)) {
495       tmp->ref++;
496       return tmp;
497     }
498
499   if (logInfo.freelist) { /* pop one off the free list */
500     tmp = logInfo.freelist;
501     logInfo.freelist = tmp->next;
502   } else /* allocate a new one */
503     tmp = (struct LogFile*) MyMalloc(sizeof(struct LogFile));
504
505   tmp->fd = -1; /* initialize the structure */
506   tmp->ref = 1;
507   DupString(tmp->file, file);
508
509   tmp->next = logInfo.filelist; /* link it into the list... */
510   tmp->prev_p = &logInfo.filelist;
511   if (logInfo.filelist)
512     logInfo.filelist->prev_p = &tmp->next;
513   logInfo.filelist = tmp;
514
515   return tmp;
516 }
517
518 /** Dereference a log file.
519  * If the reference count is exactly one on entry to this function,
520  * the file is closed and its structure is freed.
521  * @param[in] lf Log file to dereference.
522  */
523 static void
524 log_file_destroy(struct LogFile *lf)
525 {
526   assert(0 != lf);
527
528   if (--lf->ref == 0) {
529     if (lf->next) /* clip it out of the list */
530       lf->next->prev_p = lf->prev_p;
531     *lf->prev_p = lf->next;
532
533     lf->prev_p = 0; /* we won't use it for the free list */
534     if (lf->fd >= 0)
535       close(lf->fd);
536     lf->fd = -1;
537     MyFree(lf->file); /* free the file name */
538
539     lf->next = logInfo.freelist; /* stack it onto the free list */
540     logInfo.freelist = lf;
541   }
542 }
543
544 /** Look up a log subsystem by name.
545  * @param[in] subsys Subsystem name.
546  * @return Pointer to the subsystem's LogDesc, or NULL if none exists.
547  */
548 static struct LogDesc *
549 log_find(const char *subsys)
550 {
551   int i;
552
553   assert(0 != subsys);
554
555   /* find the named subsystem */
556   for (i = 0; i < LS_LAST_SYSTEM; i++)
557     if (!ircd_strcmp(subsys, logDesc[i].name))
558       return &logDesc[i];
559
560   return 0; /* not found */
561 }
562
563 /** Return canonical version of log subsystem name.
564  * @param[in] subsys Subsystem name.
565  * @return A constant string containing the canonical name.
566  */
567 char *
568 log_canon(const char *subsys)
569 {
570   struct LogDesc *desc;
571
572   if (!(desc = log_find(subsys)))
573     return 0;
574
575   return desc->name;
576 }
577
578 /** Look up a log level by name.
579  * @param[in] level Log level name.
580  * @return LogLevel enumeration, or L_LAST_LEVEL if none exists.
581  */
582 static enum LogLevel
583 log_lev_find(const char *level)
584 {
585   int i;
586
587   assert(0 != level);
588
589   /* find the named level */
590   for (i = 0; levelData[i].string; i++)
591     if (!ircd_strcmp(level, levelData[i].string))
592       return levelData[i].level;
593
594   return L_LAST_LEVEL; /* not found */
595 }
596
597 /** Look up the canonical name for a log level.
598  * @param[in] lev
599  * @return A constant string containing the level's canonical name.
600  */
601 static char *
602 log_lev_name(enum LogLevel lev)
603 {
604   assert(-1 < (int)lev);
605   assert((int)lev < L_LAST_LEVEL);
606   assert(lev == levelData[lev].level);
607
608   return levelData[lev].string;
609 }
610
611 /** Look up a syslog facility by name.
612  * @param[in] facility Facility name.
613  * @return Syslog facility value, or LOG_NOTFOUND if none exists.
614  */
615 static int
616 log_fac_find(const char *facility)
617 {
618   int i;
619
620   assert(0 != facility);
621
622   /* find the named facility */
623   for (i = 0; facilities[i].name; i++)
624     if (!ircd_strcmp(facility, facilities[i].name))
625       return facilities[i].facility;
626
627   return LOG_NOTFOUND; /* not found */
628 }
629
630 /** Look up the name for a syslog facility.
631  * @param[in] fac Facility value.
632  * @return Canonical name for facility, or NULL if none exists.
633  */
634 static char *
635 log_fac_name(int fac)
636 {
637   int i;
638
639   /* find the facility */
640   for (i = 0; facilities[i].name; i++)
641     if (facilities[i].facility == fac)
642       return facilities[i].name;
643
644   return 0; /* not found; should never happen */
645 }
646
647 /** Look up a server notice mask by name.
648  * @param[in] maskname Name of server notice mask.
649  * @return Bitmask for server notices, or 0 if none exists.
650  */
651 static unsigned int
652 log_sno_find(const char *maskname)
653 {
654   int i;
655
656   assert(0 != maskname);
657
658   /* find the named snomask */
659   for (i = 0; masks[i].name; i++)
660     if (!ircd_strcmp(maskname, masks[i].name))
661       return masks[i].snomask;
662
663   return SNO_NOTFOUND; /* not found */
664 }
665
666 /** Look up the canonical name for a server notice mask.
667  * @param[in] sno Server notice mask.
668  * @return Canonical name for the mask, or NULL if none exists.
669  */
670 static char *
671 log_sno_name(unsigned int sno)
672 {
673   int i;
674
675   /* find the snomask */
676   for (i = 0; masks[i].name; i++)
677     if (masks[i].snomask == sno)
678       return masks[i].name;
679
680   return 0; /* not found; should never happen */
681 }
682
683 /** Set a log file for a particular subsystem.
684  * @param[in] subsys Subsystem name.
685  * @param[in] filename Log file to write to.
686  * @return Zero on success; non-zero on error.
687  */
688 int
689 log_set_file(const char *subsys, const char *filename)
690 {
691   struct LogDesc *desc;
692
693   /* find subsystem */
694   if (!(desc = log_find(subsys)))
695     return 2;
696
697   if (filename)
698     desc->mark |= LOG_MARK_FILE; /* mark that file has been changed */
699   else
700     desc->mark &= ~LOG_MARK_FILE; /* file has been reset to defaults */
701
702   /* no change, don't go to the trouble of destroying and recreating */
703   if (desc->file && desc->file->file && filename &&
704       !strcmp(desc->file->file, filename))
705     return 0;
706
707   /* debug log is special, since it has to be opened on fd 2 */
708   if (desc->subsys == LS_DEBUG)
709     return log_debug_file(filename);
710
711   if (desc->file) /* destroy previous entry... */
712     log_file_destroy(desc->file);
713
714   /* set the file to use */
715   desc->file = filename ? log_file_create(filename) : 0;
716
717   return 0;
718 }
719
720 /** Find the log file name for a subsystem.
721  * @param[in] subsys Subsystem name.
722  * @return Log file for the subsystem, or NULL if not being logged to a file.
723  */
724 char *
725 log_get_file(const char *subsys)
726 {
727   struct LogDesc *desc;
728
729   /* find subsystem */
730   if (!(desc = log_find(subsys)))
731     return 0;
732
733   return desc->file ? desc->file->file : 0;
734 }
735
736 /** Set the syslog facility for a particular subsystem.
737  * @param[in] subsys Subsystem name.
738  * @param[in] facility Facility name to log to.
739  * @return Zero on success; non-zero on error.
740  */
741 int
742 log_set_facility(const char *subsys, const char *facility)
743 {
744   struct LogDesc *desc;
745   int fac;
746
747   /* find subsystem */
748   if (!(desc = log_find(subsys)))
749     return 2;
750
751   /* set syslog facility */
752   if (EmptyString(facility)) {
753     desc->facility = desc->def_fac;
754     desc->mark &= ~LOG_MARK_FACILITY;
755   } else if ((fac = log_fac_find(facility)) != LOG_NOTFOUND) {
756     desc->facility = fac;
757     if (fac == desc->def_fac)
758       desc->mark &= ~LOG_MARK_FACILITY;
759     else
760       desc->mark |= LOG_MARK_FACILITY;
761   } else
762     return 1;
763
764   return 0;
765 }
766
767 /** Find the facility name for a subsystem.
768  * @param[in] subsys Subsystem name.
769  * @return Facility name being used, or NULL if not being logged to syslog.
770  */
771 char *
772 log_get_facility(const char *subsys)
773 {
774   struct LogDesc *desc;
775
776   /* find subsystem */
777   if (!(desc = log_find(subsys)))
778     return 0;
779
780   /* find the facility's name */
781   return log_fac_name(desc->facility);
782 }
783
784 /** Set the server notice mask for a subsystem.
785  * @param[in] subsys Subsystem name.
786  * @param[in] snomask Server notice mask name.
787  * @return Zero on success; non-zero on error.
788  */
789 int
790 log_set_snomask(const char *subsys, const char *snomask)
791 {
792   struct LogDesc *desc;
793   unsigned int sno = SNO_DEFAULT;
794
795   /* find subsystem */
796   if (!(desc = log_find(subsys)))
797     return 2;
798
799   /* set snomask value */
800   if (EmptyString(snomask)) {
801     desc->snomask = desc->def_sno;
802     desc->mark &= ~LOG_MARK_SNOMASK;
803   } else if ((sno = log_sno_find(snomask)) != SNO_NOTFOUND) {
804     desc->snomask = sno;
805     if (sno == desc->def_sno)
806       desc->mark &= ~LOG_MARK_SNOMASK;
807     else
808       desc->mark |= LOG_MARK_SNOMASK;
809   } else
810     return 1;
811
812   return 0;
813 }
814
815 /** Find the server notice mask name for a subsystem.
816  * @param[in] subsys Subsystem name.
817  * @return Name of server notice mask being used, or NULL if none.
818  */
819 char *
820 log_get_snomask(const char *subsys)
821 {
822   struct LogDesc *desc;
823
824   /* find subsystem */
825   if (!(desc = log_find(subsys)))
826     return 0;
827
828   /* find the snomask value's name */
829   return log_sno_name(desc->snomask);
830 }
831
832 /** Set the verbosity level for a subsystem.
833  * @param[in] subsys Subsystem name.
834  * @param[in] level Minimum log level.
835  * @return Zero on success; non-zero on error.
836  */
837 int
838 log_set_level(const char *subsys, const char *level)
839 {
840   struct LogDesc *desc;
841   enum LogLevel lev;
842
843   /* find subsystem */
844   if (!(desc = log_find(subsys)))
845     return 2;
846
847   /* set logging level */
848   if (EmptyString(level)) {
849     desc->level = L_DEFAULT;
850     desc->mark &= ~LOG_MARK_LEVEL;
851   } else if ((lev = log_lev_find(level)) != L_LAST_LEVEL) {
852     desc->level = lev;
853     if (lev == L_DEFAULT)
854       desc->mark &= ~LOG_MARK_LEVEL;
855     else
856       desc->mark |= LOG_MARK_LEVEL;
857   } else
858     return 1;
859
860   return 0;
861 }
862
863 /** Find the verbosity level for a subsystem.
864  * @param[in] subsys Subsystem name.
865  * @return Minimum verbosity level being used, or NULL on error.
866  */
867 char *
868 log_get_level(const char *subsys)
869 {
870   struct LogDesc *desc;
871
872   /* find subsystem */
873   if (!(desc = log_find(subsys)))
874     return 0;
875
876   /* find the level's name */
877   return log_lev_name(desc->level);
878 }
879
880 /** Set the default syslog facility.
881  * @param[in] facility Syslog facility name.
882  * @return Zero on success, non-zero on error.
883  */
884 int
885 log_set_default(const char *facility)
886 {
887   int fac, oldfac;
888
889   oldfac = logInfo.facility;
890
891   if (EmptyString(facility))
892     logInfo.facility = LOG_USER;
893   else if ((fac = log_fac_find(facility)) != LOG_NOTFOUND &&
894            fac != LOG_NONE && fac != LOG_DEFAULT)
895     logInfo.facility = fac;
896   else
897     return 1;
898
899   if (logInfo.facility != oldfac) {
900     closelog(); /* reopen syslog with new facility setting */
901     openlog(logInfo.procname, LOG_PID | LOG_NDELAY, logInfo.facility);
902   }
903
904   return 0;
905 }
906
907 /** Find the default syslog facility name.
908  * @return Canonical name of default syslog facility, or NULL if none.
909  */
910 char *
911 log_get_default(void)
912 {
913   /* find the facility's name */
914   return log_fac_name(logInfo.facility);
915 }
916
917 /** Clear all marks. */
918 void
919 log_feature_unmark(void)
920 {
921   int i;
922
923   for (i = 0; i < LS_LAST_SYSTEM; i++)
924     logDesc[i].mark = 0;
925 }
926
927 /** Reset unmodified fields in all log subsystems to their defaults.
928  * @param[in] flag If non-zero, clear default syslog facility.
929  */
930 int
931 log_feature_mark(int flag)
932 {
933   int i;
934
935   if (flag)
936     log_set_default(0);
937
938   for (i = 0; i < LS_LAST_SYSTEM; i++) {
939     if (!(logDesc[i].mark & LOG_MARK_FILE)) {
940       if (logDesc[i].subsys != LS_DEBUG) { /* debug is special */
941         if (logDesc[i].file) /* destroy previous entry... */
942           log_file_destroy(logDesc[i].file);
943         logDesc[i].file = 0;
944       }
945     }
946
947     if (!(logDesc[i].mark & LOG_MARK_FACILITY)) /* set default facility */
948       logDesc[i].facility = logDesc[i].def_fac;
949
950     if (!(logDesc[i].mark & LOG_MARK_SNOMASK)) /* set default snomask */
951       logDesc[i].snomask = logDesc[i].def_sno;
952
953     if (!(logDesc[i].mark & LOG_MARK_LEVEL)) /* set default level */
954       logDesc[i].level = L_DEFAULT;
955   }
956
957   return 0; /* we don't have a notify handler */
958 }
959
960 /** Feature list callback to report log settings.
961  * @param[in] to Client requesting list.
962  * @param[in] flag If non-zero, report default syslog facility.
963  */
964 void
965 log_feature_report(struct Client *to, int flag)
966 {
967   int i;
968
969   for (i = 0; i < LS_LAST_SYSTEM; i++)
970   {
971     if (logDesc[i].mark & LOG_MARK_FILE) /* report file */
972       send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s FILE %s",
973                  logDesc[i].name, (logDesc[i].file && logDesc[i].file->file ?
974                                    logDesc[i].file->file : "(terminal)"));
975
976     if (logDesc[i].mark & LOG_MARK_FACILITY) /* report facility */
977       send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s FACILITY %s",
978                  logDesc[i].name, log_fac_name(logDesc[i].facility));
979
980     if (logDesc[i].mark & LOG_MARK_SNOMASK) /* report snomask */
981       send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s SNOMASK %s",
982                  logDesc[i].name, log_sno_name(logDesc[i].snomask));
983
984     if (logDesc[i].mark & LOG_MARK_LEVEL) /* report log level */
985       send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s LEVEL %s",
986                  logDesc[i].name, log_lev_name(logDesc[i].level));
987   }
988
989   if (flag) /* report default facility */
990     send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s",
991                log_fac_name(logInfo.facility));
992 }