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