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