fixed ssl.c bug when ssl backend returns IO_BLOCKED but IO engine doesn't get informe...
[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: ircd_log.c 1620 2006-02-16 03:49:55Z entrope $
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),      M(AUTH),
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(IAUTH, -1, SNO_NETWORK),
166   S(DEBUG, -1, SNO_DEBUG),
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     lf->fd = open(lf->file, O_WRONLY | O_CREAT | O_APPEND,
199                   S_IRUSR | S_IWUSR);
200   }
201 }
202
203 #ifdef DEBUGMODE
204
205 /** Reopen debug log file. */
206 static void
207 log_debug_reopen(void)
208 {
209   if (!logInfo.dbfile) /* no open debugging file */
210     return;
211
212   if (!logInfo.dbfile->file) { /* using terminal output */
213     logInfo.dbfile->fd = 2;
214     return;
215   }
216
217   /* Ok, it's a real file; close it if necessary and use log_open to open it */
218   if (logInfo.dbfile->fd >= 0) {
219     close(logInfo.dbfile->fd);
220     logInfo.dbfile->fd = -1; /* mark that it's closed for log_open */
221   }
222
223   log_open(logInfo.dbfile);
224
225   if (logInfo.dbfile->fd < 0) { /* try again with /dev/null */
226     if ((logInfo.dbfile->fd = open("/dev/null", O_WRONLY)) < 0)
227       exit(-1);
228   }
229
230   /* massage the file descriptor to be stderr */
231   if (logInfo.dbfile->fd != 2) {
232     int fd;
233     fd = dup2(logInfo.dbfile->fd, 2);
234     close(logInfo.dbfile->fd);
235     logInfo.dbfile->fd = fd;
236   }
237 }
238
239 /** initialize debugging log file.
240  * @param[in] usetty If non-zero, log to terminal instead of file.
241  */
242 void
243 log_debug_init(int usetty)
244 {
245   logInfo.dbfile = (struct LogFile*) MyMalloc(sizeof(struct LogFile));
246
247   logInfo.dbfile->next = 0; /* initialize debugging filename */
248   logInfo.dbfile->prev_p = 0;
249   logInfo.dbfile->fd = -1;
250   logInfo.dbfile->ref = 1;
251
252   if (usetty) /* store pathname to use */
253     logInfo.dbfile->file = 0;
254   else
255     DupString(logInfo.dbfile->file, LOGFILE);
256
257   log_debug_reopen(); /* open the debug log */
258
259   logDesc[LS_DEBUG].file = logInfo.dbfile; /* remember where it went */
260 }
261
262 #endif /* DEBUGMODE */
263
264 /** Set the debug log file name.
265  * @param[in] file File name, or NULL to select the default.
266  * @return Zero if the file was reopened; non-zero if not debugging to file.
267  */
268 static int
269 log_debug_file(const char *file)
270 {
271 #ifdef DEBUGMODE
272   if (!file)
273     file = LOGFILE;
274
275   /* If we weren't started with debugging enabled, or if we're using
276    * the terminal, don't do anything at all.
277    */
278   if (!logInfo.dbfile || !logInfo.dbfile->file)
279     return 1;
280
281   MyFree(logInfo.dbfile->file); /* free old pathname */
282   DupString(logInfo.dbfile->file, file); /* store new pathname */
283
284   log_debug_reopen(); /* reopen the debug log */
285 #endif /* DEBUGMODE */
286   return 0;
287 }
288
289 /** Initialize logging subsystem.
290  * @param[in] process_name Process name to interactions with syslog.
291  */
292 void
293 log_init(const char *process_name)
294 {
295   /* store the process name; probably belongs in ircd.c, but oh well... */
296   if (!EmptyString(process_name))
297     logInfo.procname = process_name;
298
299   /* ok, open syslog; default facility: LOG_USER */
300   openlog(logInfo.procname, LOG_PID | LOG_NDELAY, logInfo.facility);
301 }
302
303 /** Reopen log files (so admins can do things like rotate log files). */
304 void
305 log_reopen(void)
306 {
307   log_close(); /* close everything...we reopen on demand */
308
309 #ifdef DEBUGMODE
310   log_debug_reopen(); /* reopen debugging log if necessary */
311 #endif /* DEBUGMODE */
312
313   /* reopen syslog, if needed; default facility: LOG_USER */
314   openlog(logInfo.procname, LOG_PID | LOG_NDELAY, logInfo.facility);
315 }
316
317 /** Close all log files. */
318 void
319 log_close(void)
320 {
321   struct LogFile *ptr;
322
323   closelog(); /* close syslog */
324
325   for (ptr = logInfo.filelist; ptr; ptr = ptr->next) {
326     if (ptr->fd >= 0)
327       close(ptr->fd); /* close all the files... */
328
329     ptr->fd = -1;
330   }
331
332   if (logInfo.dbfile && logInfo.dbfile->file) {
333     if (logInfo.dbfile->fd >= 0)
334       close(logInfo.dbfile->fd); /* close the debug log file */
335
336     logInfo.dbfile->fd = -1;
337   }
338 }
339
340 /** Write a logging entry.
341  * @param[in] subsys Target subsystem.
342  * @param[in] severity Severity of message.
343  * @param[in] flags Combination of zero or more of LOG_NOSYSLOG, LOG_NOFILELOG, LOG_NOSNOTICE to suppress certain output.
344  * @param[in] fmt Format string for message.
345  */
346 void
347 log_write(enum LogSys subsys, enum LogLevel severity, unsigned int flags,
348           const char *fmt, ...)
349 {
350   va_list vl;
351
352   va_start(vl, fmt);
353   log_vwrite(subsys, severity, flags, fmt, vl);
354   va_end(vl);
355 }
356
357 /** Write a logging entry using a va_list.
358  * @param[in] subsys Target subsystem.
359  * @param[in] severity Severity of message.
360  * @param[in] flags Combination of zero or more of LOG_NOSYSLOG, LOG_NOFILELOG, LOG_NOSNOTICE to suppress certain output.
361  * @param[in] fmt Format string for message.
362  * @param[in] vl Variable-length argument list for message.
363  */
364 void
365 log_vwrite(enum LogSys subsys, enum LogLevel severity, unsigned int flags,
366            const char *fmt, va_list vl)
367 {
368   struct VarData vd;
369   struct LogDesc *desc;
370   struct LevelData *ldata;
371   struct tm *tstamp;
372   struct iovec vector[3];
373   time_t curtime;
374   char buf[LOG_BUFSIZE];
375   /* 1234567890123456789012 3 */
376   /* [2000-11-28 16:11:20] \0 */
377   char timebuf[23];
378
379   /* check basic assumptions */
380   assert(-1 < (int)subsys);
381   assert((int)subsys < LS_LAST_SYSTEM);
382   assert(-1 < (int)severity);
383   assert((int)severity < L_LAST_LEVEL);
384   assert(0 == (flags & ~LOG_NOMASK));
385   assert(0 != fmt);
386
387   /* find the log data and the severity data */
388   desc = &logDesc[subsys];
389   ldata = &levelData[severity];
390
391   /* check the set of ordering assumptions */
392   assert(desc->subsys == subsys);
393   assert(ldata->level == severity);
394
395   /* check severity... */
396   if (severity > desc->level)
397     return;
398
399   /* figure out where all we need to log */
400   if (!(flags & LOG_NOFILELOG) && desc->file) {
401     log_open(desc->file);
402     if (desc->file->fd >= 0) /* don't log to file if we can't open the file */
403       flags |= LOG_DOFILELOG;
404   }
405
406   if (!(flags & LOG_NOSYSLOG) && desc->facility >= 0)
407     flags |= LOG_DOSYSLOG; /* will syslog */
408
409   if (!(flags & LOG_NOSNOTICE) && (desc->snomask != 0 || ldata->snomask != 0))
410     flags |= LOG_DOSNOTICE; /* will send a server notice */
411
412   /* short-circuit if there's nothing to do... */
413   if (!(flags & LOG_DOMASK))
414     return;
415
416   /* Build the basic log string */
417   vd.vd_format = fmt;
418   va_copy(vd.vd_args, vl);
419
420   /* save the length for writev */
421   /* Log format: "SYSTEM [SEVERITY]: log message" */
422   vector[1].iov_len =
423     ircd_snprintf(0, buf, sizeof(buf), "%s [%s]: %v", desc->name,
424                   ldata->string, &vd);
425
426   /* if we have something to write to... */
427   if (flags & LOG_DOFILELOG) {
428     curtime = TStime();
429     tstamp = localtime(&curtime); /* build the timestamp */
430
431     vector[0].iov_len =
432       ircd_snprintf(0, timebuf, sizeof(timebuf), "[%d-%d-%d %d:%02d:%02d] ",
433                     tstamp->tm_year + 1900, tstamp->tm_mon + 1,
434                     tstamp->tm_mday, tstamp->tm_hour, tstamp->tm_min,
435                     tstamp->tm_sec);
436
437     /* set up the remaining parts of the writev vector... */
438     vector[0].iov_base = timebuf;
439     vector[1].iov_base = buf;
440
441     vector[2].iov_base = (void*) "\n"; /* terminate lines with a \n */
442     vector[2].iov_len = 1;
443
444     /* write it out to the log file */
445     writev(desc->file->fd, vector, 3);
446   }
447
448   /* oh yeah, syslog it too... */
449   if (flags & LOG_DOSYSLOG)
450     syslog(ldata->syslog | desc->facility, "%s", buf);
451
452   /* can't forget server notices... */
453   if (flags & LOG_DOSNOTICE)
454     sendto_opmask_butone(0, ldata->snomask ? ldata->snomask : desc->snomask,
455                          "%s", buf);
456 }
457
458 /** Log an appropriate message for kills.
459  * @param[in] victim %Client being killed.
460  * @param[in] killer %User or server doing the killing.
461  * @param[in] inpath Peer that sent us the KILL message.
462  * @param[in] path Kill path that sent to us by \a inpath.
463  * @param[in] msg Kill reason.
464  */
465 void
466 log_write_kill(const struct Client *victim, const struct Client *killer,
467                const char *inpath, const char *path, const char *msg)
468 {
469   if (MyUser(victim))
470     log_write(IsServer(killer) ? LS_SERVKILL : LS_OPERKILL, L_TRACE, 0,
471               "A local client %#C KILLED by %#C Path: %s!%s %s",
472               victim, killer, inpath, path, msg);
473   else
474     log_write(IsServer(killer) ? LS_SERVKILL : LS_OPERKILL, L_TRACE, 0,
475               "KILL from %C For %C Path: %s!%s %s", killer, victim, inpath,
476               path, msg);
477 }
478
479 /** Find a reference-counted LogFile by file name.
480  * @param[in] file Name of file.
481  * @return A log file descriptor with LogFile::ref at least 1.
482  */
483 static struct LogFile *
484 log_file_create(const char *file)
485 {
486   struct LogFile *tmp;
487
488   assert(0 != file);
489
490   /* if one already exists for that file, return it */
491   for (tmp = logInfo.filelist; tmp; tmp = tmp->next)
492     if (!strcmp(tmp->file, file)) {
493       tmp->ref++;
494       return tmp;
495     }
496
497   if (logInfo.freelist) { /* pop one off the free list */
498     tmp = logInfo.freelist;
499     logInfo.freelist = tmp->next;
500   } else /* allocate a new one */
501     tmp = (struct LogFile*) MyMalloc(sizeof(struct LogFile));
502
503   tmp->fd = -1; /* initialize the structure */
504   tmp->ref = 1;
505   DupString(tmp->file, file);
506
507   tmp->next = logInfo.filelist; /* link it into the list... */
508   tmp->prev_p = &logInfo.filelist;
509   if (logInfo.filelist)
510     logInfo.filelist->prev_p = &tmp->next;
511   logInfo.filelist = tmp;
512
513   return tmp;
514 }
515
516 /** Dereference a log file.
517  * If the reference count is exactly one on entry to this function,
518  * the file is closed and its structure is freed.
519  * @param[in] lf Log file to dereference.
520  */
521 static void
522 log_file_destroy(struct LogFile *lf)
523 {
524   assert(0 != lf);
525
526   if (--lf->ref == 0) {
527     if (lf->next) /* clip it out of the list */
528       lf->next->prev_p = lf->prev_p;
529     *lf->prev_p = lf->next;
530
531     lf->prev_p = 0; /* we won't use it for the free list */
532     if (lf->fd >= 0)
533       close(lf->fd);
534     lf->fd = -1;
535     MyFree(lf->file); /* free the file name */
536
537     lf->next = logInfo.freelist; /* stack it onto the free list */
538     logInfo.freelist = lf;
539   }
540 }
541
542 /** Look up a log subsystem by name.
543  * @param[in] subsys Subsystem name.
544  * @return Pointer to the subsystem's LogDesc, or NULL if none exists.
545  */
546 static struct LogDesc *
547 log_find(const char *subsys)
548 {
549   int i;
550
551   assert(0 != subsys);
552
553   /* find the named subsystem */
554   for (i = 0; i < LS_LAST_SYSTEM; i++)
555     if (!ircd_strcmp(subsys, logDesc[i].name))
556       return &logDesc[i];
557
558   return 0; /* not found */
559 }
560
561 /** Return canonical version of log subsystem name.
562  * @param[in] subsys Subsystem name.
563  * @return A constant string containing the canonical name.
564  */
565 char *
566 log_canon(const char *subsys)
567 {
568   struct LogDesc *desc;
569
570   if (!(desc = log_find(subsys)))
571     return 0;
572
573   return desc->name;
574 }
575
576 /** Look up a log level by name.
577  * @param[in] level Log level name.
578  * @return LogLevel enumeration, or L_LAST_LEVEL if none exists.
579  */
580 static enum LogLevel
581 log_lev_find(const char *level)
582 {
583   int i;
584
585   assert(0 != level);
586
587   /* find the named level */
588   for (i = 0; levelData[i].string; i++)
589     if (!ircd_strcmp(level, levelData[i].string))
590       return levelData[i].level;
591
592   return L_LAST_LEVEL; /* not found */
593 }
594
595 /** Look up the canonical name for a log level.
596  * @param[in] lev
597  * @return A constant string containing the level's canonical name.
598  */
599 static char *
600 log_lev_name(enum LogLevel lev)
601 {
602   assert(-1 < (int)lev);
603   assert((int)lev < L_LAST_LEVEL);
604   assert(lev == levelData[lev].level);
605
606   return levelData[lev].string;
607 }
608
609 /** Look up a syslog facility by name.
610  * @param[in] facility Facility name.
611  * @return Syslog facility value, or LOG_NOTFOUND if none exists.
612  */
613 static int
614 log_fac_find(const char *facility)
615 {
616   int i;
617
618   assert(0 != facility);
619
620   /* find the named facility */
621   for (i = 0; facilities[i].name; i++)
622     if (!ircd_strcmp(facility, facilities[i].name))
623       return facilities[i].facility;
624
625   return LOG_NOTFOUND; /* not found */
626 }
627
628 /** Look up the name for a syslog facility.
629  * @param[in] fac Facility value.
630  * @return Canonical name for facility, or NULL if none exists.
631  */
632 static char *
633 log_fac_name(int fac)
634 {
635   int i;
636
637   /* find the facility */
638   for (i = 0; facilities[i].name; i++)
639     if (facilities[i].facility == fac)
640       return facilities[i].name;
641
642   return 0; /* not found; should never happen */
643 }
644
645 /** Look up a server notice mask by name.
646  * @param[in] maskname Name of server notice mask.
647  * @return Bitmask for server notices, or 0 if none exists.
648  */
649 static unsigned int
650 log_sno_find(const char *maskname)
651 {
652   int i;
653
654   assert(0 != maskname);
655
656   /* find the named snomask */
657   for (i = 0; masks[i].name; i++)
658     if (!ircd_strcmp(maskname, masks[i].name))
659       return masks[i].snomask;
660
661   return SNO_NOTFOUND; /* not found */
662 }
663
664 /** Look up the canonical name for a server notice mask.
665  * @param[in] sno Server notice mask.
666  * @return Canonical name for the mask, or NULL if none exists.
667  */
668 static char *
669 log_sno_name(unsigned int sno)
670 {
671   int i;
672
673   /* find the snomask */
674   for (i = 0; masks[i].name; i++)
675     if (masks[i].snomask == sno)
676       return masks[i].name;
677
678   return 0; /* not found; should never happen */
679 }
680
681 /** Set a log file for a particular subsystem.
682  * @param[in] subsys Subsystem name.
683  * @param[in] filename Log file to write to.
684  * @return Zero on success; non-zero on error.
685  */
686 int
687 log_set_file(const char *subsys, const char *filename)
688 {
689   struct LogDesc *desc;
690
691   /* find subsystem */
692   if (!(desc = log_find(subsys)))
693     return 2;
694
695   if (filename)
696     desc->mark |= LOG_MARK_FILE; /* mark that file has been changed */
697   else
698     desc->mark &= ~LOG_MARK_FILE; /* file has been reset to defaults */
699
700   /* no change, don't go to the trouble of destroying and recreating */
701   if (desc->file && desc->file->file && filename &&
702       !strcmp(desc->file->file, filename))
703     return 0;
704
705   /* debug log is special, since it has to be opened on fd 2 */
706   if (desc->subsys == LS_DEBUG)
707     return log_debug_file(filename);
708
709   if (desc->file) /* destroy previous entry... */
710     log_file_destroy(desc->file);
711
712   /* set the file to use */
713   desc->file = filename ? log_file_create(filename) : 0;
714
715   return 0;
716 }
717
718 /** Find the log file name for a subsystem.
719  * @param[in] subsys Subsystem name.
720  * @return Log file for the subsystem, or NULL if not being logged to a file.
721  */
722 char *
723 log_get_file(const char *subsys)
724 {
725   struct LogDesc *desc;
726
727   /* find subsystem */
728   if (!(desc = log_find(subsys)))
729     return 0;
730
731   return desc->file ? desc->file->file : 0;
732 }
733
734 /** Set the syslog facility for a particular subsystem.
735  * @param[in] subsys Subsystem name.
736  * @param[in] facility Facility name to log to.
737  * @return Zero on success; non-zero on error.
738  */
739 int
740 log_set_facility(const char *subsys, const char *facility)
741 {
742   struct LogDesc *desc;
743   int fac;
744
745   /* find subsystem */
746   if (!(desc = log_find(subsys)))
747     return 2;
748
749   /* set syslog facility */
750   if (EmptyString(facility)) {
751     desc->facility = desc->def_fac;
752     desc->mark &= ~LOG_MARK_FACILITY;
753   } else if ((fac = log_fac_find(facility)) != LOG_NOTFOUND) {
754     desc->facility = fac;
755     if (fac == desc->def_fac)
756       desc->mark &= ~LOG_MARK_FACILITY;
757     else
758       desc->mark |= LOG_MARK_FACILITY;
759   } else
760     return 1;
761
762   return 0;
763 }
764
765 /** Find the facility name for a subsystem.
766  * @param[in] subsys Subsystem name.
767  * @return Facility name being used, or NULL if not being logged to syslog.
768  */
769 char *
770 log_get_facility(const char *subsys)
771 {
772   struct LogDesc *desc;
773
774   /* find subsystem */
775   if (!(desc = log_find(subsys)))
776     return 0;
777
778   /* find the facility's name */
779   return log_fac_name(desc->facility);
780 }
781
782 /** Set the server notice mask for a subsystem.
783  * @param[in] subsys Subsystem name.
784  * @param[in] snomask Server notice mask name.
785  * @return Zero on success; non-zero on error.
786  */
787 int
788 log_set_snomask(const char *subsys, const char *snomask)
789 {
790   struct LogDesc *desc;
791   unsigned int sno = SNO_DEFAULT;
792
793   /* find subsystem */
794   if (!(desc = log_find(subsys)))
795     return 2;
796
797   /* set snomask value */
798   if (EmptyString(snomask)) {
799     desc->snomask = desc->def_sno;
800     desc->mark &= ~LOG_MARK_SNOMASK;
801   } else if ((sno = log_sno_find(snomask)) != SNO_NOTFOUND) {
802     desc->snomask = sno;
803     if (sno == desc->def_sno)
804       desc->mark &= ~LOG_MARK_SNOMASK;
805     else
806       desc->mark |= LOG_MARK_SNOMASK;
807   } else
808     return 1;
809
810   return 0;
811 }
812
813 /** Find the server notice mask name for a subsystem.
814  * @param[in] subsys Subsystem name.
815  * @return Name of server notice mask being used, or NULL if none.
816  */
817 char *
818 log_get_snomask(const char *subsys)
819 {
820   struct LogDesc *desc;
821
822   /* find subsystem */
823   if (!(desc = log_find(subsys)))
824     return 0;
825
826   /* find the snomask value's name */
827   return log_sno_name(desc->snomask);
828 }
829
830 /** Set the verbosity level for a subsystem.
831  * @param[in] subsys Subsystem name.
832  * @param[in] level Minimum log level.
833  * @return Zero on success; non-zero on error.
834  */
835 int
836 log_set_level(const char *subsys, const char *level)
837 {
838   struct LogDesc *desc;
839   enum LogLevel lev;
840
841   /* find subsystem */
842   if (!(desc = log_find(subsys)))
843     return 2;
844
845   /* set logging level */
846   if (EmptyString(level)) {
847     desc->level = L_DEFAULT;
848     desc->mark &= ~LOG_MARK_LEVEL;
849   } else if ((lev = log_lev_find(level)) != L_LAST_LEVEL) {
850     desc->level = lev;
851     if (lev == L_DEFAULT)
852       desc->mark &= ~LOG_MARK_LEVEL;
853     else
854       desc->mark |= LOG_MARK_LEVEL;
855   } else
856     return 1;
857
858   return 0;
859 }
860
861 /** Find the verbosity level for a subsystem.
862  * @param[in] subsys Subsystem name.
863  * @return Minimum verbosity level being used, or NULL on error.
864  */
865 char *
866 log_get_level(const char *subsys)
867 {
868   struct LogDesc *desc;
869
870   /* find subsystem */
871   if (!(desc = log_find(subsys)))
872     return 0;
873
874   /* find the level's name */
875   return log_lev_name(desc->level);
876 }
877
878 /** Set the default syslog facility.
879  * @param[in] facility Syslog facility name.
880  * @return Zero on success, non-zero on error.
881  */
882 int
883 log_set_default(const char *facility)
884 {
885   int fac, oldfac;
886
887   oldfac = logInfo.facility;
888
889   if (EmptyString(facility))
890     logInfo.facility = LOG_USER;
891   else if ((fac = log_fac_find(facility)) != LOG_NOTFOUND &&
892            fac != LOG_NONE && fac != LOG_DEFAULT)
893     logInfo.facility = fac;
894   else
895     return 1;
896
897   if (logInfo.facility != oldfac) {
898     closelog(); /* reopen syslog with new facility setting */
899     openlog(logInfo.procname, LOG_PID | LOG_NDELAY, logInfo.facility);
900   }
901
902   return 0;
903 }
904
905 /** Find the default syslog facility name.
906  * @return Canonical name of default syslog facility, or NULL if none.
907  */
908 char *
909 log_get_default(void)
910 {
911   /* find the facility's name */
912   return log_fac_name(logInfo.facility);
913 }
914
915 /** Clear all marks. */
916 void
917 log_feature_unmark(void)
918 {
919   int i;
920
921   for (i = 0; i < LS_LAST_SYSTEM; i++)
922     logDesc[i].mark = 0;
923 }
924
925 /** Reset unmodified fields in all log subsystems to their defaults.
926  * @param[in] flag If non-zero, clear default syslog facility.
927  */
928 int
929 log_feature_mark(int flag)
930 {
931   int i;
932
933   if (flag)
934     log_set_default(0);
935
936   for (i = 0; i < LS_LAST_SYSTEM; i++) {
937     if (!(logDesc[i].mark & LOG_MARK_FILE)) {
938       if (logDesc[i].subsys != LS_DEBUG) { /* debug is special */
939         if (logDesc[i].file) /* destroy previous entry... */
940           log_file_destroy(logDesc[i].file);
941         logDesc[i].file = 0;
942       }
943     }
944
945     if (!(logDesc[i].mark & LOG_MARK_FACILITY)) /* set default facility */
946       logDesc[i].facility = logDesc[i].def_fac;
947
948     if (!(logDesc[i].mark & LOG_MARK_SNOMASK)) /* set default snomask */
949       logDesc[i].snomask = logDesc[i].def_sno;
950
951     if (!(logDesc[i].mark & LOG_MARK_LEVEL)) /* set default level */
952       logDesc[i].level = L_DEFAULT;
953   }
954
955   return 0; /* we don't have a notify handler */
956 }
957
958 /** Feature list callback to report log settings.
959  * @param[in] to Client requesting list.
960  * @param[in] flag If non-zero, report default syslog facility.
961  */
962 void
963 log_feature_report(struct Client *to, int flag)
964 {
965   int i;
966
967   for (i = 0; i < LS_LAST_SYSTEM; i++)
968   {
969     if (logDesc[i].mark & LOG_MARK_FILE) /* report file */
970       send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s FILE %s",
971                  logDesc[i].name, (logDesc[i].file && logDesc[i].file->file ?
972                                    logDesc[i].file->file : "(terminal)"));
973
974     if (logDesc[i].mark & LOG_MARK_FACILITY) /* report facility */
975       send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s FACILITY %s",
976                  logDesc[i].name, log_fac_name(logDesc[i].facility));
977
978     if (logDesc[i].mark & LOG_MARK_SNOMASK) /* report snomask */
979       send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s SNOMASK %s",
980                  logDesc[i].name, log_sno_name(logDesc[i].snomask));
981
982     if (logDesc[i].mark & LOG_MARK_LEVEL) /* report log level */
983       send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s LEVEL %s",
984                  logDesc[i].name, log_lev_name(logDesc[i].level));
985   }
986
987   if (flag) /* report default facility */
988     send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s",
989                log_fac_name(logInfo.facility));
990 }