Cleanup code so it builds with C++ again
[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  *   $Id$
24  */
25 #include "config.h"
26
27 #include "ircd_log.h"
28 #include "client.h"
29 #include "ircd_alloc.h"
30 #include "ircd_reply.h"
31 #include "ircd_snprintf.h"
32 #include "ircd_string.h"
33 #include "ircd.h"
34 #include "numeric.h"
35 #include "s_debug.h"
36 #include "send.h"
37 #include "struct.h"
38
39 #include <assert.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <stdarg.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <sys/stat.h>
47 #include <sys/types.h>
48 #include <sys/uio.h>
49 #include <syslog.h>
50 #include <time.h>
51 #include <unistd.h>
52
53 #define LOG_BUFSIZE 2048 
54
55 /* select default log level cutoff */
56 #ifdef DEBUGMODE
57 # define L_DEFAULT      L_DEBUG
58 #else
59 # define L_DEFAULT      L_INFO
60 #endif
61
62 #define LOG_DOSYSLOG    0x10
63 #define LOG_DOFILELOG   0x20
64 #define LOG_DOSNOTICE   0x40
65
66 #define LOG_DOMASK      (LOG_DOSYSLOG | LOG_DOFILELOG | LOG_DOSNOTICE)
67
68 /* Map severity levels to strings and syslog levels */
69 static struct LevelData {
70   enum LogLevel level;
71   char         *string;
72   int           syslog;
73   unsigned int  snomask; /* 0 means use default in LogDesc */
74 } levelData[] = {
75 #define L(level, syslog, mask) { L_ ## level, #level, (syslog), (mask) }
76   L(CRIT, LOG_CRIT, SNO_OLDSNO),
77   L(ERROR, LOG_ERR, 0),
78   L(WARNING, LOG_WARNING, 0),
79   L(NOTICE, LOG_NOTICE, 0),
80   L(TRACE, LOG_INFO, 0),
81   L(INFO, LOG_INFO, 0),
82   L(DEBUG, LOG_INFO, SNO_DEBUG),
83 #undef L
84   { L_LAST_LEVEL, 0, 0, 0 }
85 };
86
87 /* Just in case some implementation of syslog has them... */
88 #undef LOG_NONE
89 #undef LOG_DEFAULT
90 #undef LOG_NOTFOUND
91
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 */
95
96 /* Map names to syslog facilities--allows syslog configuration from .conf */
97 static struct {
98   char *name;
99   int facility;
100 } facilities[] = {
101 #define F(fac) { #fac, LOG_ ## fac }
102   F(NONE),    F(DEFAULT), F(AUTH),
103 #ifdef LOG_AUTHPRIV
104   F(AUTHPRIV),
105 #endif
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),
109 #undef F
110   { 0, 0 }
111 };
112
113 #define SNO_NONE     0x00000000 /* don't send server notices */
114 #define SNO_NOTFOUND 0xffffffff /* didn't find a SNO_MASK value for name */
115
116 /* Map names to snomask values--allows configuration from .conf */
117 static struct {
118   char *name;
119   unsigned int snomask;
120 } masks[] = {
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),
126 #undef M
127   { 0, 0 }
128 };
129
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 */
134
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 */
146 } logDesc[] = {
147 #define S(sys, p, sn) { LS_##sys, #sys, 0, 0, (p), (sn), (p), (sn), L_DEFAULT }
148   S(SYSTEM, -1, 0),
149   S(CONFIG, 0, SNO_OLDSNO),
150   S(OPERMODE, -1, SNO_HACK4),
151   S(GLINE, -1, SNO_GLINE),
152   S(JUPE, -1, SNO_NETWORK),
153   S(WHO, -1, 0),
154   S(NETWORK, -1, SNO_NETWORK),
155   S(OPERKILL, -1, 0),
156   S(SERVKILL, -1, 0),
157   S(USER, -1, 0),
158   S(OPER, -1, SNO_OLDREALOP),
159   S(RESOLVER, -1, 0),
160   S(SOCKET, -1, 0),
161   S(DEBUG, -1, SNO_DEBUG),
162   S(OLDLOG, -1, 0),
163 #undef S
164   { LS_LAST_SYSTEM, 0, 0, -1, 0, -1, 0 }
165 };
166
167 /* describes a log file */
168 struct LogFile {
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 */
174 };
175
176 /* modifiable static information */
177 static struct {
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 };
184
185 /* helper routine to open a log file if needed */
186 static void
187 log_open(struct LogFile *lf)
188 {
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,
193                   S_IREAD | S_IWRITE);
194     alarm(0);
195   }
196 }
197
198 #ifdef DEBUGMODE
199
200 /* reopen debug log */
201 static void
202 log_debug_reopen(void)
203 {
204   if (!logInfo.dbfile) /* no open debugging file */
205     return;
206
207   if (!logInfo.dbfile->file) { /* using terminal output */
208     logInfo.dbfile->fd = 2;
209     return;
210   }
211
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 */
216   }
217
218   log_open(logInfo.dbfile);
219
220   if (logInfo.dbfile->fd < 0) { /* try again with /dev/null */
221     if ((logInfo.dbfile->fd = open("/dev/null", O_WRONLY)) < 0)
222       exit(-1);
223   }
224
225   /* massage the file descriptor to be stderr */
226   if (logInfo.dbfile->fd != 2) {
227     int fd;
228     fd = dup2(logInfo.dbfile->fd, 2);
229     close(logInfo.dbfile->fd);
230     logInfo.dbfile->fd = fd;
231   }
232 }
233
234 /* initialize debugging log */
235 void
236 log_debug_init(int usetty)
237 {
238   logInfo.dbfile = (struct LogFile*) MyMalloc(sizeof(struct LogFile));
239
240   logInfo.dbfile->next = 0; /* initialize debugging filename */
241   logInfo.dbfile->prev_p = 0;
242   logInfo.dbfile->fd = -1;
243   logInfo.dbfile->ref = 1;
244
245   if (usetty) /* store pathname to use */
246     logInfo.dbfile->file = 0;
247   else
248     DupString(logInfo.dbfile->file, LOGFILE);
249
250   log_debug_reopen(); /* open the debug log */
251
252   logDesc[LS_DEBUG].file = logInfo.dbfile; /* remember where it went */
253 }
254
255 #endif /* DEBUGMODE */
256
257 /* set the debug log file */
258 static int
259 log_debug_file(const char *file)
260 {
261 #ifdef DEBUGMODE
262   if (!file)
263     file = LOGFILE;
264
265   /* If we weren't started with debugging enabled, or if we're using
266    * the terminal, don't do anything at all.
267    */
268   if (!logInfo.dbfile || !logInfo.dbfile->file)
269     return 1;
270
271   MyFree(logInfo.dbfile->file); /* free old pathname */
272   DupString(logInfo.dbfile->file, file); /* store new pathname */
273
274   log_debug_reopen(); /* reopen the debug log */
275 #endif /* DEBUGMODE */
276   return 0;
277 }
278
279 /* called in place of open_log(), this stores the process name and prepares
280  * for syslogging
281  */
282 void
283 log_init(const char *process_name)
284 {
285   /* store the process name; probably belongs in ircd.c, but oh well... */
286   if (!EmptyString(process_name))
287     logInfo.procname = process_name;
288
289   /* ok, open syslog; default facility: LOG_USER */
290   openlog(logInfo.procname, LOG_PID | LOG_NDELAY, logInfo.facility);
291 }
292
293 /* Files are persistently open; this closes and reopens them to allow
294  * log file rotation
295  */
296 void
297 log_reopen(void)
298 {
299   log_close(); /* close everything...we reopen on demand */
300
301 #ifdef DEBUGMODE
302   log_debug_reopen(); /* reopen debugging log if necessary */
303 #endif /* DEBUGMODE */
304
305   /* reopen syslog, if needed; default facility: LOG_USER */
306   openlog(logInfo.procname, LOG_PID | LOG_NDELAY, logInfo.facility);
307 }
308
309 /* close the log files */
310 void
311 log_close(void)
312 {
313   struct LogFile *ptr;
314
315   closelog(); /* close syslog */
316
317   for (ptr = logInfo.filelist; ptr; ptr = ptr->next) {
318     if (ptr->fd >= 0)
319       close(ptr->fd); /* close all the files... */
320
321     ptr->fd = -1;
322   }
323
324   if (logInfo.dbfile && logInfo.dbfile->file) {
325     if (logInfo.dbfile->fd >= 0)
326       close(logInfo.dbfile->fd); /* close the debug log file */
327
328     logInfo.dbfile->fd = -1;
329   }
330 }
331
332 /* These write entries to a log file */
333 void
334 log_write(enum LogSys subsys, enum LogLevel severity, unsigned int flags,
335           const char *fmt, ...)
336 {
337   va_list vl;
338
339   va_start(vl, fmt);
340   log_vwrite(subsys, severity, flags, fmt, vl);
341   va_end(vl);
342 }
343
344 void
345 log_vwrite(enum LogSys subsys, enum LogLevel severity, unsigned int flags,
346            const char *fmt, va_list vl)
347 {
348   struct VarData vd;
349   struct LogDesc *desc;
350   struct LevelData *ldata;
351   struct tm *tstamp;
352   struct iovec vector[3];
353   time_t curtime;
354   char buf[LOG_BUFSIZE];
355   /* 1234567890123456789012 3 */
356   /* [2000-11-28 16:11:20] \0 */
357   char timebuf[23];
358
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));
365   assert(0 != fmt);
366
367   /* find the log data and the severity data */
368   desc = &logDesc[subsys];
369   ldata = &levelData[severity];
370
371   /* check the set of ordering assumptions */
372   assert(desc->subsys == subsys);
373   assert(ldata->level == severity);
374
375   /* check severity... */
376   if (severity > desc->level)
377     return;
378
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;
384   }
385
386   if (!(flags & LOG_NOSYSLOG) && desc->facility >= 0)
387     flags |= LOG_DOSYSLOG; /* will syslog */
388
389   if (!(flags & LOG_NOSNOTICE) && (desc->snomask != 0 || ldata->snomask != 0))
390     flags |= LOG_DOSNOTICE; /* will send a server notice */
391
392   /* short-circuit if there's nothing to do... */
393   if (!(flags & LOG_DOMASK))
394     return;
395
396   /* Build the basic log string */
397   vd.vd_format = fmt;
398   vd.vd_args = vl;
399
400   /* save the length for writev */
401   /* Log format: "SYSTEM [SEVERITY]: log message" */
402   vector[1].iov_len =
403     ircd_snprintf(0, buf, sizeof(buf), "%s [%s]: %v", desc->name,
404                   ldata->string, &vd);
405
406   /* if we have something to write to... */
407   if (flags & LOG_DOFILELOG) {
408     curtime = TStime();
409     tstamp = localtime(&curtime); /* build the timestamp */
410
411     vector[0].iov_len =
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,
415                     tstamp->tm_sec);
416
417     /* set up the remaining parts of the writev vector... */
418     vector[0].iov_base = timebuf;
419     vector[1].iov_base = buf;
420
421     vector[2].iov_base = (void*) "\n"; /* terminate lines with a \n */
422     vector[2].iov_len = 1;
423
424     /* write it out to the log file */
425     writev(desc->file->fd, vector, 3);
426   }
427
428   /* oh yeah, syslog it too... */
429   if (flags & LOG_DOSYSLOG)
430     syslog(ldata->syslog | desc->facility, "%s", buf);
431
432   /* can't forget server notices... */
433   if (flags & LOG_DOSNOTICE)
434     sendto_opmask_butone(0, ldata->snomask ? ldata->snomask : desc->snomask,
435                          "%s", buf);
436 }
437
438 /* log kills for fun and profit */
439 void
440 log_write_kill(const struct Client *victim, const struct Client *killer,
441                const char *inpath, const char *path, const char *msg)
442 {
443   if (MyUser(victim))
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);
447   else
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,
450               path, msg);
451 }
452
453 /* return a struct LogFile for a specific filename--reference counted */
454 static struct LogFile *
455 log_file_create(const char *file)
456 {
457   struct LogFile *tmp;
458
459   assert(0 != file);
460
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)) {
464       tmp->ref++;
465       return tmp;
466     }
467
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));
473
474   tmp->fd = -1; /* initialize the structure */
475   tmp->ref = 1;
476   DupString(tmp->file, file);
477
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;
483
484   return tmp;
485 }
486
487 /* destroy a log file descriptor, under the control of the reference count */
488 static void
489 log_file_destroy(struct LogFile *lf)
490 {
491   assert(0 != lf);
492
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;
497
498     lf->prev_p = 0; /* we won't use it for the free list */
499     if (lf->fd >= 0)
500       close(lf->fd);
501     lf->fd = -1;
502     MyFree(lf->file); /* free the file name */
503
504     lf->next = logInfo.freelist; /* stack it onto the free list */
505     logInfo.freelist = lf;
506   }
507 }
508
509 /* finds a subsystem given its name */
510 static struct LogDesc *
511 log_find(const char *subsys)
512 {
513   int i;
514
515   assert(0 != subsys);
516
517   /* find the named subsystem */
518   for (i = 0; i < LS_LAST_SYSTEM; i++)
519     if (!ircd_strcmp(subsys, logDesc[i].name))
520       return &logDesc[i];
521
522   return 0; /* not found */
523 }
524
525 /* canonicalize subsystem names */
526 char *
527 log_canon(const char *subsys)
528 {
529   struct LogDesc *desc;
530
531   if (!(desc = log_find(subsys)))
532     return 0;
533
534   return desc->name;
535 }
536
537 /* find a level given its name */
538 static enum LogLevel
539 log_lev_find(const char *level)
540 {
541   int i;
542
543   assert(0 != level);
544
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;
549
550   return L_LAST_LEVEL; /* not found */
551 }
552
553 /* return a name for a level */
554 static char *
555 log_lev_name(enum LogLevel lev)
556 {
557   assert(-1 < (int)lev);
558   assert((int)lev < L_LAST_LEVEL);
559   assert(lev == levelData[lev].level);
560
561   return levelData[lev].string;
562 }
563
564 /* find a facility given its name */
565 static int
566 log_fac_find(const char *facility)
567 {
568   int i;
569
570   assert(0 != facility);
571
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;
576
577   return LOG_NOTFOUND; /* not found */
578 }
579
580 /* return a name for a facility */
581 static char *
582 log_fac_name(int fac)
583 {
584   int i;
585
586   /* find the facility */
587   for (i = 0; facilities[i].name; i++)
588     if (facilities[i].facility == fac)
589       return facilities[i].name;
590
591   return 0; /* not found; should never happen */
592 }
593
594 /* find a snomask value given its name */
595 static unsigned int
596 log_sno_find(const char *maskname)
597 {
598   int i;
599
600   assert(0 != maskname);
601
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;
606
607   return SNO_NOTFOUND; /* not found */
608 }
609
610 /* return a name for a snomask value */
611 static char *
612 log_sno_name(unsigned int sno)
613 {
614   int i;
615
616   /* find the snomask */
617   for (i = 0; masks[i].name; i++)
618     if (masks[i].snomask == sno)
619       return masks[i].name;
620
621   return 0; /* not found; should never happen */
622 }
623
624 /* set the log file for a subsystem */
625 int
626 log_set_file(const char *subsys, const char *filename)
627 {
628   struct LogDesc *desc;
629
630   /* find subsystem */
631   if (!(desc = log_find(subsys)))
632     return 2;
633
634   if (filename)
635     desc->mark |= LOG_MARK_FILE; /* mark that file has been changed */
636   else
637     desc->mark &= ~LOG_MARK_FILE; /* file has been reset to defaults */
638
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))
642     return 0;
643
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);
647
648   if (desc->file) /* destroy previous entry... */
649     log_file_destroy(desc->file);
650
651   /* set the file to use */
652   desc->file = filename ? log_file_create(filename) : 0;
653
654   return 0;
655 }
656
657 /* get the log file for a subsystem */
658 char *
659 log_get_file(const char *subsys)
660 {
661   struct LogDesc *desc;
662
663   /* find subsystem */
664   if (!(desc = log_find(subsys)))
665     return 0;
666
667   return desc->file ? desc->file->file : 0;
668 }
669
670 /* set the facility for a subsystem */
671 int
672 log_set_facility(const char *subsys, const char *facility)
673 {
674   struct LogDesc *desc;
675   int fac;
676
677   /* find subsystem */
678   if (!(desc = log_find(subsys)))
679     return 2;
680
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;
689     else
690       desc->mark |= LOG_MARK_FACILITY;
691   } else
692     return 1;
693
694   return 0;
695 }
696
697 /* get the facility for a subsystem */
698 char *
699 log_get_facility(const char *subsys)
700 {
701   struct LogDesc *desc;
702
703   /* find subsystem */
704   if (!(desc = log_find(subsys)))
705     return 0;
706
707   /* find the facility's name */
708   return log_fac_name(desc->facility);
709 }
710
711 /* set the snomask value for a subsystem */
712 int
713 log_set_snomask(const char *subsys, const char *snomask)
714 {
715   struct LogDesc *desc;
716   unsigned int sno = SNO_DEFAULT;
717
718   /* find subsystem */
719   if (!(desc = log_find(subsys)))
720     return 2;
721
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) {
727     desc->snomask = sno;
728     if (sno == desc->def_sno)
729       desc->mark &= ~LOG_MARK_SNOMASK;
730     else
731       desc->mark |= LOG_MARK_SNOMASK;
732   } else
733     return 1;
734
735   return 0;
736 }
737
738 /* get the snomask value for a subsystem */
739 char *
740 log_get_snomask(const char *subsys)
741 {
742   struct LogDesc *desc;
743
744   /* find subsystem */
745   if (!(desc = log_find(subsys)))
746     return 0;
747
748   /* find the snomask value's name */
749   return log_sno_name(desc->snomask);
750 }
751
752 /* set the level for a subsystem */
753 int
754 log_set_level(const char *subsys, const char *level)
755 {
756   struct LogDesc *desc;
757   enum LogLevel lev;
758
759   /* find subsystem */
760   if (!(desc = log_find(subsys)))
761     return 2;
762
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) {
768     desc->level = lev;
769     if (lev == L_DEFAULT)
770       desc->mark &= ~LOG_MARK_LEVEL;
771     else
772       desc->mark |= LOG_MARK_LEVEL;
773   } else
774     return 1;
775
776   return 0;
777 }
778
779 /* get the level for a subsystem */
780 char *
781 log_get_level(const char *subsys)
782 {
783   struct LogDesc *desc;
784
785   /* find subsystem */
786   if (!(desc = log_find(subsys)))
787     return 0;
788
789   /* find the level's name */
790   return log_lev_name(desc->level);
791 }
792
793 /* set the overall default syslog facility */
794 int
795 log_set_default(const char *facility)
796 {
797   int fac, oldfac;
798
799   oldfac = logInfo.facility;
800
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;
806   else
807     return 1;
808
809   if (logInfo.facility != oldfac) {
810     closelog(); /* reopen syslog with new facility setting */
811     openlog(logInfo.procname, LOG_PID | LOG_NDELAY, logInfo.facility);
812   }
813
814   return 0;
815 }
816
817 /* get the overall default syslog facility */
818 char *
819 log_get_default(void)
820 {
821   /* find the facility's name */
822   return log_fac_name(logInfo.facility);
823 }
824
825 /* Clear the marks... */
826 void
827 log_feature_unmark(void)
828 {
829   int i;
830
831   for (i = 0; i < LS_LAST_SYSTEM; i++)
832     logDesc[i].mark = 0;
833 }
834
835 /* Reset unmarked fields to defaults... */
836 int
837 log_feature_mark(int flag)
838 {
839   int i;
840
841   if (flag)
842     log_set_default(0);
843
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);
849         logDesc[i].file = 0;
850       }
851     }
852
853     if (!(logDesc[i].mark & LOG_MARK_FACILITY)) /* set default facility */
854       logDesc[i].facility = logDesc[i].def_fac;
855
856     if (!(logDesc[i].mark & LOG_MARK_SNOMASK)) /* set default snomask */
857       logDesc[i].snomask = logDesc[i].def_sno;
858
859     if (!(logDesc[i].mark & LOG_MARK_LEVEL)) /* set default level */
860       logDesc[i].level = L_DEFAULT;
861   }
862
863   return 0; /* we don't have a notify handler */
864 }
865
866 /* Report feature settings */
867 void
868 log_feature_report(struct Client *to, int flag)
869 {
870   int i;
871
872   for (i = 0; i < LS_LAST_SYSTEM; i++)
873   {
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)"));
878
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));
882
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));
886
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));
890   }
891
892   if (flag) /* report default facility */
893     send_reply(to, SND_EXPLICIT | RPL_STATSFLINE, "F LOG %s",
894                log_fac_name(logInfo.facility));
895 }