]>
Commit | Line | Data |
---|---|---|
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 | ||
55 | int 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 */ | |
73 | static 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. */ | |
101 | static 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. */ | |
121 | static 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. */ | |
140 | static 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 | S(SETHOST, -1, SNO_OLDSNO), | |
168 | #undef S | |
169 | { LS_LAST_SYSTEM, 0, 0, -1, 0, -1, 0 } | |
170 | }; | |
171 | ||
172 | /** Describes a log file. */ | |
173 | struct 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. */ | |
182 | static 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 | */ | |
194 | static void | |
195 | log_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. */ | |
207 | static void | |
208 | log_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 | */ | |
243 | void | |
244 | log_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 | */ | |
269 | static int | |
270 | log_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 | */ | |
293 | void | |
294 | log_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). */ | |
305 | void | |
306 | log_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. */ | |
319 | void | |
320 | log_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 | */ | |
347 | void | |
348 | log_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 | */ | |
365 | void | |
366 | log_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 | */ | |
466 | void | |
467 | log_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 | */ | |
484 | static struct LogFile * | |
485 | log_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 | */ | |
522 | static void | |
523 | log_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 | */ | |
547 | static struct LogDesc * | |
548 | log_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 | */ | |
566 | char * | |
567 | log_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 | */ | |
581 | static enum LogLevel | |
582 | log_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 | */ | |
600 | static char * | |
601 | log_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 | */ | |
614 | static int | |
615 | log_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 | */ | |
633 | static char * | |
634 | log_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 | */ | |
650 | static unsigned int | |
651 | log_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 | */ | |
669 | static char * | |
670 | log_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 | */ | |
687 | int | |
688 | log_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 | */ | |
723 | char * | |
724 | log_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 | */ | |
740 | int | |
741 | log_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 | */ | |
770 | char * | |
771 | log_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 | */ | |
788 | int | |
789 | log_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 | */ | |
818 | char * | |
819 | log_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 | */ | |
836 | int | |
837 | log_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 | */ | |
866 | char * | |
867 | log_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 | */ | |
883 | int | |
884 | log_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 | */ | |
909 | char * | |
910 | log_get_default(void) | |
911 | { | |
912 | /* find the facility's name */ | |
913 | return log_fac_name(logInfo.facility); | |
914 | } | |
915 | ||
916 | /** Clear all marks. */ | |
917 | void | |
918 | log_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 | */ | |
929 | int | |
930 | log_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 | */ | |
963 | void | |
964 | log_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 | } |