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