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