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