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