1 /* log.c - Diagnostic and error logging
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of x3.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
23 #include "helpfile.h" /* send_message, message_register, etc */
26 struct logDestination
;
28 struct logDest_vtable
{
29 const char *type_name
;
30 struct logDestination
* (*open
)(const char *args
);
31 void (*reopen
)(struct logDestination
*self
);
32 void (*close
)(struct logDestination
*self
);
33 void (*log_audit
)(struct logDestination
*self
, struct log_type
*type
, struct logEntry
*entry
);
34 void (*log_replay
)(struct logDestination
*self
, struct log_type
*type
, int is_write
, const char *line
);
35 void (*log_module
)(struct logDestination
*self
, struct log_type
*type
, enum log_severity sev
, const char *message
);
38 struct logDestination
{
39 struct logDest_vtable
*vtbl
;
44 DECLARE_LIST(logList
, struct logDestination
*);
48 struct logList logs
[LOG_NUM_SEVERITIES
];
49 struct logEntry
*log_oldest
;
50 struct logEntry
*log_newest
;
51 unsigned int log_count
;
53 unsigned int max_count
;
54 unsigned int default_set
: 1;
57 static const char *log_severity_names
[] = {
70 static struct dict
*log_dest_types
;
71 static struct dict
*log_dests
;
72 static struct dict
*log_types
;
73 static struct log_type
*log_default
;
74 static int log_inited
, log_debugged
;
76 DEFINE_LIST(logList
, struct logDestination
*);
77 static void log_format_audit(struct logEntry
*entry
);
78 static const struct message_entry msgtab
[] = {
79 { "MSG_INVALID_FACILITY", "$b%s$b is an invalid log facility." },
80 { "MSG_INVALID_SEVERITY", "$b%s$b is an invalid severity level." },
84 static struct logDestination
*
85 log_open(const char *name
)
87 struct logDest_vtable
*vtbl
;
88 struct logDestination
*ld
;
92 if ((ld
= dict_find(log_dests
, name
, NULL
))) {
96 if ((sep
= strchr(name
, ':'))) {
97 memcpy(type_name
, name
, sep
-name
);
98 type_name
[sep
-name
] = 0;
100 strcpy(type_name
, name
);
102 if (!(vtbl
= dict_find(log_dest_types
, type_name
, NULL
))) {
103 log_module(MAIN_LOG
, LOG_ERROR
, "Invalid log type for log '%s'.", name
);
106 if (!(ld
= vtbl
->open(sep
? sep
+1 : 0)))
108 ld
->name
= strdup(name
);
109 dict_insert(log_dests
, ld
->name
, ld
);
115 logList_open(struct logList
*ll
, struct record_data
*rd
)
117 struct logDestination
*ld
;
124 if ((ld
= log_open(rd
->d
.qstring
)))
125 logList_append(ll
, ld
);
127 case RECDB_STRING_LIST
:
128 for (ii
=0; ii
<rd
->d
.slist
->used
; ++ii
) {
129 if ((ld
= log_open(rd
->d
.slist
->list
[ii
])))
130 logList_append(ll
, ld
);
139 logList_join(struct logList
*target
, const struct logList
*source
)
141 unsigned int ii
, jj
, kk
;
146 target
->used
+= source
->used
;
147 target
->size
+= source
->used
;
148 target
->list
= realloc(target
->list
, target
->size
* sizeof(target
->list
[0]));
149 for (ii
= 0; ii
< source
->used
; ++ii
, ++jj
) {
151 for (dup
= 0, kk
= 0; kk
< jj
; kk
++) {
152 if (target
->list
[kk
] == source
->list
[ii
]) {
162 target
->list
[jj
] = source
->list
[ii
];
163 target
->list
[jj
]->refcnt
++;
168 logList_close(struct logList
*ll
)
171 for (ii
=0; ii
<ll
->used
; ++ii
) {
172 if (!--ll
->list
[ii
]->refcnt
) {
173 struct logDestination
*ld
= ll
->list
[ii
];
185 enum log_severity ls
;
187 for (it
= dict_first(log_types
); it
; it
= iter_next(it
)) {
189 for (ls
= 0; ls
< LOG_NUM_SEVERITIES
; ls
++) {
190 logList_close(<
->logs
[ls
]);
192 lt
->logs
[ls
].size
= 0;
193 lt
->logs
[ls
].used
= 0;
194 lt
->logs
[ls
].list
= 0;
200 log_type_free_oldest(struct log_type
*lt
)
202 struct logEntry
*next
;
206 next
= lt
->log_oldest
->next
;
207 free(lt
->log_oldest
->default_desc
);
208 free(lt
->log_oldest
);
209 lt
->log_oldest
= next
;
214 log_type_free(void *ptr
)
216 struct log_type
*lt
= ptr
;
218 while (lt
->log_oldest
)
219 log_type_free_oldest(lt
);
228 dict_delete(log_types
);
229 dict_delete(log_dests
);
230 dict_delete(log_dest_types
);
233 static enum log_severity
234 find_severity(const char *text
)
236 enum log_severity ls
;
237 for (ls
= 0; ls
< LOG_NUM_SEVERITIES
; ++ls
)
238 if (!ircncasecmp(text
, log_severity_names
[ls
], strlen(log_severity_names
[ls
])))
240 return LOG_NUM_SEVERITIES
;
243 /* Log keys are based on syslog.conf syntax:
244 * KEY := LOGSET '.' SEVSET
245 * LOGSET := LOGLIT | LOGLIT ',' LOGSET
246 * LOGLIT := a registered log type
247 * SEVSET := '*' | SEVLIT | '<' SEVLIT | '<=' SEVLIT | '>' SEVLIT | '>=' SEVLIT | SEVLIT ',' SEVSET
248 * SEVLIT := one of log_severity_names
249 * A KEY contains the Cartesian product of the logs in its LOGSET
250 * and the severities in its SEVSET.
254 log_parse_logset(char *buffer
, struct string_list
*slist
)
258 char *cont
= strchr(buffer
, ',');
261 string_list_append(slist
, strdup(buffer
));
267 log_parse_sevset(char *buffer
, char targets
[LOG_NUM_SEVERITIES
])
269 memset(targets
, 0, LOG_NUM_SEVERITIES
);
272 enum log_severity bound
;
275 cont
= strchr(buffer
, ',');
278 if (buffer
[0] == '*' && buffer
[1] == 0) {
279 for (bound
= 0; bound
< LOG_NUM_SEVERITIES
; bound
++) {
280 /* make people explicitly specify replay targets */
281 if (bound
!= LOG_REPLAY
)
284 } else if (buffer
[0] == '<') {
285 if (buffer
[1] == '=')
286 bound
= find_severity(buffer
+2) + 1;
288 bound
= find_severity(buffer
+1);
289 for (first
= 1; bound
> 0; bound
--) {
290 /* make people explicitly specify replay targets */
291 if (bound
!= LOG_REPLAY
|| first
) {
296 } else if (buffer
[0] == '>') {
297 if (buffer
[1] == '=')
298 bound
= find_severity(buffer
+2);
300 bound
= find_severity(buffer
+1) + 1;
301 for (first
= 1; bound
< LOG_NUM_SEVERITIES
; bound
++) {
302 /* make people explicitly specify replay targets */
303 if (bound
!= LOG_REPLAY
|| first
) {
309 if (buffer
[0] == '=')
311 bound
= find_severity(buffer
);
319 log_parse_cross(const char *buffer
, struct string_list
*types
, char sevset
[LOG_NUM_SEVERITIES
])
323 dup
= strdup(buffer
);
324 sep
= strchr(dup
, '.');
326 log_parse_logset(dup
, types
);
327 log_parse_sevset(sep
, sevset
);
332 log_parse_options(struct log_type
*type
, struct dict
*conf
)
335 opt
= database_get_data(conf
, "max_age", RECDB_QSTRING
);
337 type
->max_age
= ParseInterval(opt
);
338 opt
= database_get_data(conf
, "max_count", RECDB_QSTRING
);
340 type
->max_count
= strtoul(opt
, NULL
, 10);
346 struct record_data
*rd
, *rd2
;
349 struct log_type
*type
;
350 enum log_severity sev
;
354 dict_delete(log_dests
);
356 log_dests
= dict_new();
357 dict_set_free_keys(log_dests
, free
);
359 rd
= conf_get_node("logs");
360 if (rd
&& (rd
->type
== RECDB_OBJECT
)) {
361 for (it
= dict_first(rd
->d
.object
); it
; it
= iter_next(it
)) {
362 if ((sep
= strchr(iter_key(it
), '.'))) {
363 struct logList logList
;
364 char sevset
[LOG_NUM_SEVERITIES
];
365 struct string_list
*slist
;
367 /* It looks like a <type>.<severity> record. Try to parse it. */
368 slist
= alloc_string_list(4);
369 log_parse_cross(iter_key(it
), slist
, sevset
);
371 logList_open(&logList
, iter_data(it
));
372 for (ii
= 0; ii
< slist
->used
; ++ii
) {
373 type
= log_register_type(slist
->list
[ii
], NULL
);
374 for (sev
= 0; sev
< LOG_NUM_SEVERITIES
; ++sev
) {
377 logList_join(&type
->logs
[sev
], &logList
);
380 logList_close(&logList
);
381 free_string_list(slist
);
382 } else if ((rd2
= iter_data(it
))
383 && (rd2
->type
== RECDB_OBJECT
)
384 && (type
= log_register_type(iter_key(it
), NULL
))) {
385 log_parse_options(type
, rd2
->d
.object
);
387 log_module(MAIN_LOG
, LOG_ERROR
, "Unknown logs subkey '%s'.", iter_key(it
));
398 enum log_severity sev
;
399 struct logDestination
*log_stdout
;
400 struct logList target
;
402 log_stdout
= log_open("std:out");
403 logList_init(&target
);
404 logList_append(&target
, log_stdout
);
406 for (sev
= 0; sev
< LOG_NUM_SEVERITIES
; ++sev
)
407 logList_join(&log_default
->logs
[sev
], &target
);
409 logList_close(&target
);
417 for (it
= dict_first(log_dests
); it
; it
= iter_next(it
)) {
418 struct logDestination
*ld
= iter_data(it
);
419 ld
->vtbl
->reopen(ld
);
424 log_register_type(const char *name
, const char *default_log
)
426 struct log_type
*type
;
427 struct logDestination
*dest
;
428 enum log_severity sev
;
430 if (!(type
= dict_find(log_types
, name
, NULL
))) {
431 type
= calloc(1, sizeof(*type
));
432 type
->name
= strdup(name
);
434 type
->max_count
= 1024;
435 dict_insert(log_types
, type
->name
, type
);
437 if (default_log
&& !type
->default_set
) {
438 /* If any severity level was unspecified in the config, use the default. */
440 for (sev
= 0; sev
< LOG_NUM_SEVERITIES
; ++sev
) {
441 if (sev
== LOG_REPLAY
)
442 continue; /* never default LOG_REPLAY */
443 if (!type
->logs
[sev
].size
) {
444 logList_init(&type
->logs
[sev
]);
446 if (!(dest
= log_open(default_log
)))
450 logList_append(&type
->logs
[sev
], dest
);
454 type
->default_set
= 1;
459 /* logging functions */
462 log_audit(struct log_type
*type
, enum log_severity sev
, struct userNode
*user
, struct userNode
*bot
, const char *channel_name
, unsigned int flags
, const char *command
)
464 struct logEntry
*entry
;
465 unsigned int size
, ii
;
468 /* First make sure severity is appropriate */
469 if ((sev
!= LOG_COMMAND
) && (sev
!= LOG_OVERRIDE
) && (sev
!= LOG_STAFF
)) {
470 log_module(MAIN_LOG
, LOG_ERROR
, "Illegal audit severity %d", sev
);
473 /* Allocate and fill in the log entry */
474 size
= sizeof(*entry
) + strlen(user
->nick
) + strlen(command
) + 2;
475 if (user
->handle_info
)
476 size
+= strlen(user
->handle_info
->handle
) + 1;
478 size
+= strlen(channel_name
) + 1;
479 if (flags
& AUDIT_HOSTMASK
)
480 size
+= strlen(user
->ident
) + strlen(user
->hostname
) + 2;
481 entry
= calloc(1, size
);
482 str_next
= (char*)(entry
+ 1);
487 size
= strlen(channel_name
) + 1;
488 entry
->channel_name
= memcpy(str_next
, channel_name
, size
);
492 size
= strlen(user
->nick
) + 1;
493 entry
->user_nick
= memcpy(str_next
, user
->nick
, size
);
496 if (user
->handle_info
) {
497 size
= strlen(user
->handle_info
->handle
) + 1;
498 entry
->user_account
= memcpy(str_next
, user
->handle_info
->handle
, size
);
501 if (flags
& AUDIT_HOSTMASK
) {
502 size
= sprintf(str_next
, "%s@%s", user
->ident
, user
->hostname
) + 1;
503 entry
->user_hostmask
= str_next
;
506 entry
->user_hostmask
= 0;
509 size
= strlen(command
) + 1;
510 entry
->command
= memcpy(str_next
, command
, size
);
514 /* fill in the default text for the event */
515 log_format_audit(entry
);
517 /* insert into the linked list */
519 entry
->prev
= type
->log_newest
;
520 if (type
->log_newest
)
521 type
->log_newest
->next
= entry
;
523 type
->log_oldest
= entry
;
524 type
->log_newest
= entry
;
527 /* remove old elements from the linked list */
528 while (type
->log_count
> type
->max_count
)
529 log_type_free_oldest(type
);
530 while (type
->log_oldest
&& (type
->log_oldest
->time
+ type
->max_age
< (unsigned long)now
))
531 log_type_free_oldest(type
);
532 if (type
->log_oldest
)
533 type
->log_oldest
->prev
= 0;
535 type
->log_newest
= 0;
537 /* call the destination logs */
538 for (ii
=0; ii
<type
->logs
[sev
].used
; ++ii
) {
539 struct logDestination
*ld
= type
->logs
[sev
].list
[ii
];
540 ld
->vtbl
->log_audit(ld
, type
, entry
);
542 for (ii
=0; ii
<log_default
->logs
[sev
].used
; ++ii
) {
543 struct logDestination
*ld
= log_default
->logs
[sev
].list
[ii
];
544 ld
->vtbl
->log_audit(ld
, type
, entry
);
549 log_replay(struct log_type
*type
, int is_write
, const char *line
)
553 for (ii
=0; ii
<type
->logs
[LOG_REPLAY
].used
; ++ii
) {
554 struct logDestination
*ld
= type
->logs
[LOG_REPLAY
].list
[ii
];
555 ld
->vtbl
->log_replay(ld
, type
, is_write
, line
);
557 for (ii
=0; ii
<log_default
->logs
[LOG_REPLAY
].used
; ++ii
) {
558 struct logDestination
*ld
= log_default
->logs
[LOG_REPLAY
].list
[ii
];
559 ld
->vtbl
->log_replay(ld
, type
, is_write
, line
);
564 log_module(struct log_type
*type
, enum log_severity sev
, const char *format
, ...)
570 if (sev
> LOG_FATAL
) {
571 log_module(MAIN_LOG
, LOG_ERROR
, "Illegal log_module severity %d", sev
);
574 va_start(args
, format
);
575 vsnprintf(msgbuf
, sizeof(msgbuf
), format
, args
);
578 for (ii
=0; ii
<type
->logs
[sev
].used
; ++ii
) {
579 struct logDestination
*ld
= type
->logs
[sev
].list
[ii
];
580 ld
->vtbl
->log_module(ld
, type
, sev
, msgbuf
);
582 for (ii
=0; ii
<log_default
->logs
[sev
].used
; ++ii
) {
583 struct logDestination
*ld
= log_default
->logs
[sev
].list
[ii
];
584 ld
->vtbl
->log_module(ld
, type
, sev
, msgbuf
);
587 /* Special behavior before we start full operation */
588 fprintf(stderr
, "%s: %s\n", log_severity_names
[sev
], msgbuf
);
590 if (sev
== LOG_FATAL
) {
591 assert(0 && "fatal message logged");
596 /* audit log searching */
599 log_discrim_create(struct userNode
*service
, struct userNode
*user
, unsigned int argc
, char *argv
[])
602 struct logSearch
*discrim
;
604 /* Assume all criteria require arguments. */
607 send_message(user
, service
, "MSG_MISSING_PARAMS", argv
[0]);
611 discrim
= malloc(sizeof(struct logSearch
));
612 memset(discrim
, 0, sizeof(*discrim
));
614 discrim
->max_time
= INT_MAX
;
615 discrim
->severities
= ~0;
617 for (ii
=1; ii
<argc
-1; ii
++) {
618 if (!irccasecmp(argv
[ii
], "bot")) {
619 struct userNode
*bot
= GetUserH(argv
[++ii
]);
621 send_message(user
, service
, "MSG_NICK_UNKNOWN", argv
[ii
]);
623 } else if (!IsLocal(bot
)) {
624 send_message(user
, service
, "MSG_NOT_A_SERVICE", argv
[ii
]);
627 discrim
->masks
.bot
= bot
;
628 } else if (!irccasecmp(argv
[ii
], "channel")) {
629 discrim
->masks
.channel_name
= argv
[++ii
];
630 } else if (!irccasecmp(argv
[ii
], "nick")) {
631 discrim
->masks
.user_nick
= argv
[++ii
];
632 } else if (!irccasecmp(argv
[ii
], "account")) {
633 discrim
->masks
.user_account
= argv
[++ii
];
634 } else if (!irccasecmp(argv
[ii
], "hostmask")) {
635 discrim
->masks
.user_hostmask
= argv
[++ii
];
636 } else if (!irccasecmp(argv
[ii
], "command")) {
637 discrim
->masks
.command
= argv
[++ii
];
638 } else if (!irccasecmp(argv
[ii
], "age")) {
639 const char *cmp
= argv
[++ii
];
642 discrim
->min_time
= now
- ParseInterval(cmp
+2);
644 discrim
->min_time
= now
- (ParseInterval(cmp
+1) - 1);
645 } else if (cmp
[0] == '>') {
647 discrim
->max_time
= now
- ParseInterval(cmp
+2);
649 discrim
->max_time
= now
- (ParseInterval(cmp
+1) - 1);
651 discrim
->min_time
= now
- ParseInterval(cmp
+2);
653 } else if (!irccasecmp(argv
[ii
], "limit")) {
654 discrim
->limit
= strtoul(argv
[++ii
], NULL
, 10);
655 } else if (!irccasecmp(argv
[ii
], "level")) {
656 char *severity
= argv
[++ii
];
657 discrim
->severities
= 0;
659 enum log_severity sev
= find_severity(severity
);
660 if (sev
== LOG_NUM_SEVERITIES
) {
661 send_message(user
, service
, "MSG_INVALID_SEVERITY", severity
);
664 discrim
->severities
|= 1 << sev
;
665 severity
= strchr(severity
, ',');
670 } else if (!irccasecmp(argv
[ii
], "type")) {
671 if (!(discrim
->type
= dict_find(log_types
, argv
[++ii
], NULL
))) {
672 send_message(user
, service
, "MSG_INVALID_FACILITY", argv
[ii
]);
676 send_message(user
, service
, "MSG_INVALID_CRITERIA", argv
[ii
]);
688 entry_match(struct logSearch
*discrim
, struct logEntry
*entry
)
690 if ((entry
->time
< discrim
->min_time
)
691 || (entry
->time
> discrim
->max_time
)
692 || !(discrim
->severities
& (1 << entry
->slvl
))
693 || (discrim
->masks
.bot
&& (discrim
->masks
.bot
!= entry
->bot
))
694 /* don't do glob matching, so that !events #a*b does not match #acb */
695 || (discrim
->masks
.channel_name
696 && (!entry
->channel_name
697 || irccasecmp(entry
->channel_name
, discrim
->masks
.channel_name
)))
698 || (discrim
->masks
.user_nick
699 && !match_ircglob(entry
->user_nick
, discrim
->masks
.user_nick
))
700 || (discrim
->masks
.user_account
701 && (!entry
->user_account
702 || !match_ircglob(entry
->user_account
, discrim
->masks
.user_account
)))
703 || (discrim
->masks
.user_hostmask
704 && entry
->user_hostmask
705 && !match_ircglob(entry
->user_hostmask
, discrim
->masks
.user_hostmask
))
706 || (discrim
->masks
.command
707 && !match_ircglob(entry
->command
, discrim
->masks
.command
))) {
714 log_report_entry(struct logEntry
*match
, void *extra
)
716 struct logReport
*rpt
= extra
;
717 send_message_type(4, rpt
->user
, rpt
->reporter
, "%s", match
->default_desc
);
721 log_entry_search(struct logSearch
*discrim
, entry_search_func esf
, void *data
)
723 unsigned int matched
= 0;
726 static volatile struct logEntry
*last
;
727 struct logEntry
*entry
;
729 for (entry
= discrim
->type
->log_oldest
, last
= NULL
;
731 last
= entry
, entry
= entry
->next
) {
733 if (entry_match(discrim
, entry
)) {
735 if (++matched
>= discrim
->limit
)
742 for (it
= dict_first(log_types
); it
; it
= iter_next(it
)) {
743 discrim
->type
= iter_data(it
);
744 matched
+= log_entry_search(discrim
, esf
, data
);
751 /* generic helper functions */
754 log_format_timestamp(time_t when
, struct string_buffer
*sbuf
)
757 localtime_r(&when
, &local
);
758 if (sbuf
->size
< 24) {
761 sbuf
->list
= calloc(1, 24);
763 sbuf
->used
= sprintf(sbuf
->list
, "[%02d:%02d:%02d %02d/%02d/%04d]", local
.tm_hour
, local
.tm_min
, local
.tm_sec
, local
.tm_mon
+1, local
.tm_mday
, local
.tm_year
+1900);
767 log_format_audit(struct logEntry
*entry
)
769 struct string_buffer sbuf
;
770 memset(&sbuf
, 0, sizeof(sbuf
));
771 log_format_timestamp(entry
->time
, &sbuf
);
772 string_buffer_append_string(&sbuf
, " (");
773 string_buffer_append_string(&sbuf
, entry
->bot
->nick
);
774 if (entry
->channel_name
) {
775 string_buffer_append(&sbuf
, ':');
776 string_buffer_append_string(&sbuf
, entry
->channel_name
);
778 string_buffer_append_string(&sbuf
, ") [");
779 string_buffer_append_string(&sbuf
, entry
->user_nick
);
780 if (entry
->user_hostmask
) {
781 string_buffer_append(&sbuf
, '!');
782 string_buffer_append_string(&sbuf
, entry
->user_hostmask
);
784 if (entry
->user_account
) {
785 string_buffer_append(&sbuf
, ':');
786 string_buffer_append_string(&sbuf
, entry
->user_account
);
788 string_buffer_append_string(&sbuf
, "]: ");
789 string_buffer_append_string(&sbuf
, entry
->command
);
790 entry
->default_desc
= strdup(sbuf
.list
);
794 /* shared stub log operations act as a noop */
797 ldNop_reopen(UNUSED_ARG(struct logDestination
*self_
)) {
798 /* no operation necessary */
802 ldNop_replay(UNUSED_ARG(struct logDestination
*self_
), UNUSED_ARG(struct log_type
*type
), UNUSED_ARG(int is_write
), UNUSED_ARG(const char *line
)) {
803 /* no operation necessary */
808 struct logDest_file
{
809 struct logDestination base
;
813 static struct logDest_vtable ldFile_vtbl
;
815 static struct logDestination
*
816 ldFile_open(const char *args
) {
817 struct logDest_file
*ld
;
818 ld
= calloc(1, sizeof(*ld
));
819 ld
->base
.vtbl
= &ldFile_vtbl
;
820 ld
->fname
= strdup(args
);
821 ld
->output
= fopen(ld
->fname
, "a");
826 ldFile_reopen(struct logDestination
*self_
) {
827 struct logDest_file
*self
= (struct logDest_file
*)self_
;
828 fclose(self
->output
);
829 self
->output
= fopen(self
->fname
, "a");
833 ldFile_close(struct logDestination
*self_
) {
834 struct logDest_file
*self
= (struct logDest_file
*)self_
;
835 fclose(self
->output
);
841 ldFile_audit(struct logDestination
*self_
, UNUSED_ARG(struct log_type
*type
), struct logEntry
*entry
) {
842 struct logDest_file
*self
= (struct logDest_file
*)self_
;
843 fputs(entry
->default_desc
, self
->output
);
844 fputc('\n', self
->output
);
845 fflush(self
->output
);
849 ldFile_replay(struct logDestination
*self_
, UNUSED_ARG(struct log_type
*type
), int is_write
, const char *line
) {
850 struct logDest_file
*self
= (struct logDest_file
*)self_
;
851 struct string_buffer sbuf
;
852 memset(&sbuf
, 0, sizeof(sbuf
));
853 log_format_timestamp(now
, &sbuf
);
854 string_buffer_append_string(&sbuf
, is_write
? "W: " : " ");
855 string_buffer_append_string(&sbuf
, line
);
856 fputs(sbuf
.list
, self
->output
);
857 fputc('\n', self
->output
);
859 fflush(self
->output
);
863 ldFile_module(struct logDestination
*self_
, struct log_type
*type
, enum log_severity sev
, const char *message
) {
864 struct logDest_file
*self
= (struct logDest_file
*)self_
;
865 struct string_buffer sbuf
;
866 memset(&sbuf
, 0, sizeof(sbuf
));
867 log_format_timestamp(now
, &sbuf
);
868 fprintf(self
->output
, "%s (%s:%s) %s\n", sbuf
.list
, type
->name
, log_severity_names
[sev
], message
);
870 fflush(self
->output
);
873 static struct logDest_vtable ldFile_vtbl
= {
885 static struct logDest_vtable ldStd_vtbl
;
887 static struct logDestination
*
888 ldStd_open(const char *args
) {
889 struct logDest_file
*ld
;
890 ld
= calloc(1, sizeof(*ld
));
891 ld
->base
.vtbl
= &ldStd_vtbl
;
892 ld
->fname
= strdup(args
);
894 /* Print to stderr if given "err" and default to stdout otherwise. */
896 ld
->output
= fdopen(atoi(args
), "a");
897 else if (!strcasecmp(args
, "err"))
906 ldStd_close(struct logDestination
*self_
) {
907 struct logDest_file
*self
= (struct logDest_file
*)self_
;
913 ldStd_replay(struct logDestination
*self_
, UNUSED_ARG(struct log_type
*type
), int is_write
, const char *line
) {
914 struct logDest_file
*self
= (struct logDest_file
*)self_
;
915 fprintf(self
->output
, "%s%s\n", is_write
? "W: " : " ", line
);
919 ldStd_module(struct logDestination
*self_
, UNUSED_ARG(struct log_type
*type
), enum log_severity sev
, const char *message
) {
920 struct logDest_file
*self
= (struct logDest_file
*)self_
;
921 fprintf(self
->output
, "%s: %s\n", log_severity_names
[sev
], message
);
924 static struct logDest_vtable ldStd_vtbl
= {
937 struct logDestination base
;
940 static struct logDest_vtable ldIrc_vtbl
;
942 static struct logDestination
*
943 ldIrc_open(const char *args
) {
944 struct logDest_irc
*ld
;
945 ld
= calloc(1, sizeof(*ld
));
946 ld
->base
.vtbl
= &ldIrc_vtbl
;
947 ld
->target
= strdup(args
);
952 ldIrc_close(struct logDestination
*self_
) {
953 struct logDest_irc
*self
= (struct logDest_irc
*)self_
;
959 ldIrc_audit(struct logDestination
*self_
, UNUSED_ARG(struct log_type
*type
), struct logEntry
*entry
) {
960 struct logDest_irc
*self
= (struct logDest_irc
*)self_
;
962 if (entry
->channel_name
) {
963 send_target_message(4, self
->target
, entry
->bot
, "(%s", strchr(strchr(entry
->default_desc
, ' '), ':')+1);
965 send_target_message(4, self
->target
, entry
->bot
, "%s", strchr(entry
->default_desc
, ')')+2);
970 ldIrc_module(struct logDestination
*self_
, struct log_type
*type
, enum log_severity sev
, const char *message
) {
971 struct logDest_irc
*self
= (struct logDest_irc
*)self_
;
972 extern struct userNode
*opserv
;
974 send_target_message(4, self
->target
, opserv
, "%s %s: %s\n", type
->name
, log_severity_names
[sev
], message
);
977 static struct logDest_vtable ldIrc_vtbl
= {
983 ldNop_replay
, /* totally ignore this - it would be a recipe for disaster */
990 log_types
= dict_new();
991 dict_set_free_keys(log_types
, free
);
992 dict_set_free_data(log_types
, log_type_free
);
993 log_dest_types
= dict_new();
994 /* register log types */
995 dict_insert(log_dest_types
, ldFile_vtbl
.type_name
, &ldFile_vtbl
);
996 dict_insert(log_dest_types
, ldStd_vtbl
.type_name
, &ldStd_vtbl
);
997 dict_insert(log_dest_types
, ldIrc_vtbl
.type_name
, &ldIrc_vtbl
);
998 conf_register_reload(log_conf_read
);
999 log_default
= log_register_type("*", NULL
);
1000 reg_exit_func(cleanup_logs
);
1001 message_register_table(msgtab
);
1005 void SyncLog(char *fmt
,...)
1008 char buff
[MAXLEN
*4];
1012 va_start(args
, fmt
);
1013 vsnprintf(buff
, MAXLEN
, fmt
, args
);
1014 buff
[MAXLEN
- 1] = '\0';
1017 for (tmp
= buff
; *tmp
; tmp
++)
1019 if ((*tmp
== '\n') || (*tmp
== '\r'))
1021 else if (*tmp
== '\001')
1025 if((LogFile
= fopen("sync.log", "a")))
1027 fprintf(LogFile
, "%s: %s\n", time2str(time(NULL
)), buff
);