1 /* log.c - Diagnostic and error logging
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
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 3 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 */
28 #define MAXLOGSEARCHLENGTH 10000
30 extern struct userNode
*chanserv
;
32 struct logDestination
;
34 struct logDest_vtable
{
35 const char *type_name
;
36 struct logDestination
* (*open
)(const char *args
);
37 void (*reopen
)(struct logDestination
*self
);
38 void (*close
)(struct logDestination
*self
);
39 void (*log_audit
)(struct logDestination
*self
, struct log_type
*type
, struct logEntry
*entry
);
40 void (*log_replay
)(struct logDestination
*self
, struct log_type
*type
, int is_write
, const char *line
);
41 void (*log_module
)(struct logDestination
*self
, struct log_type
*type
, enum log_severity sev
, const char *message
);
44 struct logDestination
{
45 struct logDest_vtable
*vtbl
;
50 DECLARE_LIST(logList
, struct logDestination
*);
54 struct logList logs
[LOG_NUM_SEVERITIES
];
55 struct logEntry
*log_oldest
;
56 struct logEntry
*log_newest
;
57 unsigned int log_count
;
59 unsigned int max_count
;
61 unsigned int default_set
: 1;
64 static const char *log_severity_names
[] = {
77 static struct dict
*log_dest_types
;
78 static struct dict
*log_dests
;
79 static struct dict
*log_types
;
80 static struct log_type
*log_default
;
81 static int log_inited
, log_debugged
;
83 DEFINE_LIST(logList
, struct logDestination
*)
84 static void log_format_audit(struct logEntry
*entry
);
85 static const struct message_entry msgtab
[] = {
86 { "MSG_INVALID_FACILITY", "$b%s$b is an invalid log facility." },
87 { "MSG_INVALID_SEVERITY", "$b%s$b is an invalid severity level." },
89 { "LAST_RESULTS", "$b%s]$b %s %s $b%s$b %s" },
90 { "LAST_ERROR", "%s:%s" },
91 { "LAST_COMMAND_LOG", "Channel Events for %s" },
92 { "LAST_LINE", "----------------------------------------" },
93 { "LAST_STOPPING_AT", "--------- Stopping at %d lines ---------" },
94 { "LAST_MAX_AGE", "-------- Data age limit reached --------" },
95 { "LAST_END_OF_LOG", "---------- Found %d Matches ------------" },
100 static struct logDestination
*
101 log_open(const char *name
)
103 struct logDest_vtable
*vtbl
;
104 struct logDestination
*ld
;
108 if ((ld
= dict_find(log_dests
, name
, NULL
))) {
112 if ((sep
= strchr(name
, ':'))) {
113 memcpy(type_name
, name
, sep
-name
);
114 type_name
[sep
-name
] = 0;
116 strcpy(type_name
, name
);
118 if (!(vtbl
= dict_find(log_dest_types
, type_name
, NULL
))) {
119 log_module(MAIN_LOG
, LOG_ERROR
, "Invalid log type for log '%s'.", name
);
122 if (!(ld
= vtbl
->open(sep
? sep
+1 : 0)))
124 ld
->name
= strdup(name
);
125 dict_insert(log_dests
, ld
->name
, ld
);
131 logList_open(struct logList
*ll
, struct record_data
*rd
)
133 struct logDestination
*ld
;
140 if ((ld
= log_open(rd
->d
.qstring
)))
141 logList_append(ll
, ld
);
143 case RECDB_STRING_LIST
:
144 for (ii
=0; ii
<rd
->d
.slist
->used
; ++ii
) {
145 if ((ld
= log_open(rd
->d
.slist
->list
[ii
])))
146 logList_append(ll
, ld
);
155 logList_join(struct logList
*target
, const struct logList
*source
)
157 unsigned int ii
, jj
, kk
;
162 target
->used
+= source
->used
;
163 target
->size
+= source
->used
;
164 target
->list
= realloc(target
->list
, target
->size
* sizeof(target
->list
[0]));
165 for (ii
= 0; ii
< source
->used
; ++ii
, ++jj
) {
167 for (is_duplicate
= 0, kk
= 0; kk
< jj
; kk
++) {
168 if (target
->list
[kk
] == source
->list
[ii
]) {
178 target
->list
[jj
] = source
->list
[ii
];
179 target
->list
[jj
]->refcnt
++;
184 logList_close(struct logList
*ll
)
187 for (ii
=0; ii
<ll
->used
; ++ii
) {
188 if (!--ll
->list
[ii
]->refcnt
) {
189 struct logDestination
*ld
= ll
->list
[ii
];
201 enum log_severity ls
;
203 for (it
= dict_first(log_types
); it
; it
= iter_next(it
)) {
205 for (ls
= 0; ls
< LOG_NUM_SEVERITIES
; ls
++) {
206 logList_close(<
->logs
[ls
]);
208 lt
->logs
[ls
].size
= 0;
209 lt
->logs
[ls
].used
= 0;
210 lt
->logs
[ls
].list
= 0;
216 log_type_free_oldest(struct log_type
*lt
)
218 struct logEntry
*next
;
222 next
= lt
->log_oldest
->next
;
223 free(lt
->log_oldest
->default_desc
);
224 free(lt
->log_oldest
);
225 lt
->log_oldest
= next
;
230 log_type_free(void *ptr
)
232 struct log_type
*lt
= ptr
;
234 while (lt
->log_oldest
)
235 log_type_free_oldest(lt
);
240 cleanup_logs(UNUSED_ARG(void *extra
))
244 dict_delete(log_types
);
245 dict_delete(log_dests
);
246 dict_delete(log_dest_types
);
249 static enum log_severity
250 find_severity(const char *text
)
252 enum log_severity ls
;
253 for (ls
= 0; ls
< LOG_NUM_SEVERITIES
; ++ls
)
254 if (!ircncasecmp(text
, log_severity_names
[ls
], strlen(log_severity_names
[ls
])))
256 return LOG_NUM_SEVERITIES
;
259 /* Log keys are based on syslog.conf syntax:
260 * KEY := LOGSET '.' SEVSET
261 * LOGSET := LOGLIT | LOGLIT ',' LOGSET
262 * LOGLIT := a registered log type
263 * SEVSET := '*' | SEVLIT | '<' SEVLIT | '<=' SEVLIT | '>' SEVLIT | '>=' SEVLIT | SEVLIT ',' SEVSET
264 * SEVLIT := one of log_severity_names
265 * A KEY contains the Cartesian product of the logs in its LOGSET
266 * and the severities in its SEVSET.
270 log_parse_logset(char *buffer
, struct string_list
*slist
)
274 char *cont
= strchr(buffer
, ',');
277 string_list_append(slist
, strdup(buffer
));
283 log_parse_sevset(char *buffer
, char targets
[LOG_NUM_SEVERITIES
])
285 memset(targets
, 0, LOG_NUM_SEVERITIES
);
288 enum log_severity bound
;
291 cont
= strchr(buffer
, ',');
294 if (buffer
[0] == '*' && buffer
[1] == 0) {
295 for (bound
= 0; bound
< LOG_NUM_SEVERITIES
; bound
++) {
296 /* make people explicitly specify replay targets */
297 if (bound
!= LOG_REPLAY
)
300 } else if (buffer
[0] == '<') {
301 if (buffer
[1] == '=')
302 bound
= find_severity(buffer
+2) + 1;
304 bound
= find_severity(buffer
+1);
305 for (first
= 1; bound
> 0; bound
--) {
306 /* make people explicitly specify replay targets */
307 if (bound
!= LOG_REPLAY
|| first
) {
312 } else if (buffer
[0] == '>') {
313 if (buffer
[1] == '=')
314 bound
= find_severity(buffer
+2);
316 bound
= find_severity(buffer
+1) + 1;
317 for (first
= 1; bound
< LOG_NUM_SEVERITIES
; bound
++) {
318 /* make people explicitly specify replay targets */
319 if (bound
!= LOG_REPLAY
|| first
) {
325 if (buffer
[0] == '=')
327 bound
= find_severity(buffer
);
335 log_parse_cross(const char *buffer
, struct string_list
*types
, char sevset
[LOG_NUM_SEVERITIES
])
337 char *buffer_copy
, *sep
;
339 buffer_copy
= strdup(buffer
);
340 sep
= strchr(buffer_copy
, '.');
342 log_parse_logset(buffer_copy
, types
);
343 log_parse_sevset(sep
, sevset
);
348 log_parse_options(struct log_type
*type
, struct dict
*conf
)
351 opt
= database_get_data(conf
, "max_age", RECDB_QSTRING
);
353 type
->max_age
= ParseInterval(opt
);
354 opt
= database_get_data(conf
, "max_count", RECDB_QSTRING
);
356 type
->max_count
= strtoul(opt
, NULL
, 10);
362 struct record_data
*rd
, *rd2
;
365 struct log_type
*type
;
366 enum log_severity sev
;
370 dict_delete(log_dests
);
372 log_dests
= dict_new();
373 dict_set_free_keys(log_dests
, free
);
375 rd
= conf_get_node("logs");
376 if (rd
&& (rd
->type
== RECDB_OBJECT
)) {
377 for (it
= dict_first(rd
->d
.object
); it
; it
= iter_next(it
)) {
378 if ((sep
= strchr(iter_key(it
), '.'))) {
379 struct logList logList
;
380 char sevset
[LOG_NUM_SEVERITIES
];
381 struct string_list
*slist
;
383 /* It looks like a <type>.<severity> record. Try to parse it. */
384 slist
= alloc_string_list(4);
385 log_parse_cross(iter_key(it
), slist
, sevset
);
387 logList_open(&logList
, iter_data(it
));
388 for (ii
= 0; ii
< slist
->used
; ++ii
) {
389 type
= log_register_type(slist
->list
[ii
], NULL
);
390 for (sev
= 0; sev
< LOG_NUM_SEVERITIES
; ++sev
) {
393 logList_join(&type
->logs
[sev
], &logList
);
396 logList_close(&logList
);
397 free_string_list(slist
);
398 } else if ((rd2
= iter_data(it
))
399 && (rd2
->type
== RECDB_OBJECT
)
400 && (type
= log_register_type(iter_key(it
), NULL
))) {
401 log_parse_options(type
, rd2
->d
.object
);
403 log_module(MAIN_LOG
, LOG_ERROR
, "Unknown logs subkey '%s'.", iter_key(it
));
414 enum log_severity sev
;
415 struct logDestination
*log_stdout
;
416 struct logList target
;
418 log_stdout
= log_open("std:out");
419 logList_init(&target
);
420 logList_append(&target
, log_stdout
);
422 for (sev
= 0; sev
< LOG_NUM_SEVERITIES
; ++sev
)
423 logList_join(&log_default
->logs
[sev
], &target
);
425 logList_close(&target
);
433 for (it
= dict_first(log_dests
); it
; it
= iter_next(it
)) {
434 struct logDestination
*ld
= iter_data(it
);
435 ld
->vtbl
->reopen(ld
);
440 log_register_type(const char *name
, const char *default_log
)
442 struct log_type
*type
;
443 struct logDestination
*dest
;
444 enum log_severity sev
;
446 if (!(type
= dict_find(log_types
, name
, NULL
))) {
447 type
= calloc(1, sizeof(*type
));
448 type
->name
= strdup(name
);
450 type
->max_count
= 1024;
451 dict_insert(log_types
, type
->name
, type
);
453 if (default_log
&& !type
->default_set
) {
454 /* If any severity level was unspecified in the config, use the default. */
456 for (sev
= 0; sev
< LOG_NUM_SEVERITIES
; ++sev
) {
457 if (sev
== LOG_REPLAY
)
458 continue; /* never default LOG_REPLAY */
459 if (!type
->logs
[sev
].size
) {
460 logList_init(&type
->logs
[sev
]);
462 if (!(dest
= log_open(default_log
)))
466 logList_append(&type
->logs
[sev
], dest
);
470 type
->default_set
= 1;
475 /* logging functions */
478 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
)
480 struct logEntry
*entry
;
481 unsigned int size
, ii
;
484 /* First make sure severity is appropriate */
485 if ((sev
!= LOG_COMMAND
) && (sev
!= LOG_OVERRIDE
) && (sev
!= LOG_STAFF
)) {
486 log_module(MAIN_LOG
, LOG_ERROR
, "Illegal audit severity %d", sev
);
489 /* Allocate and fill in the log entry */
490 size
= sizeof(*entry
) + strlen(user
->nick
) + strlen(command
) + 2;
491 if (user
->handle_info
)
492 size
+= strlen(user
->handle_info
->handle
) + 1;
494 size
+= strlen(channel_name
) + 1;
495 if (flags
& AUDIT_HOSTMASK
)
496 size
+= strlen(user
->ident
) + strlen(user
->hostname
) + 2;
497 entry
= calloc(1, size
);
498 str_next
= (char*)(entry
+ 1);
503 size
= strlen(channel_name
) + 1;
504 entry
->channel_name
= memcpy(str_next
, channel_name
, size
);
508 size
= strlen(user
->nick
) + 1;
509 entry
->user_nick
= memcpy(str_next
, user
->nick
, size
);
512 if (user
->handle_info
) {
513 size
= strlen(user
->handle_info
->handle
) + 1;
514 entry
->user_account
= memcpy(str_next
, user
->handle_info
->handle
, size
);
517 if (flags
& AUDIT_HOSTMASK
) {
518 size
= sprintf(str_next
, "%s@%s", user
->ident
, user
->hostname
) + 1;
519 entry
->user_hostmask
= str_next
;
522 entry
->user_hostmask
= 0;
525 size
= strlen(command
) + 1;
526 entry
->command
= memcpy(str_next
, command
, size
);
530 /* fill in the default text for the event */
531 log_format_audit(entry
);
533 /* insert into the linked list */
535 entry
->prev
= type
->log_newest
;
536 if (type
->log_newest
)
537 type
->log_newest
->next
= entry
;
539 type
->log_oldest
= entry
;
540 type
->log_newest
= entry
;
543 /* remove old elements from the linked list */
544 while (type
->log_count
> type
->max_count
)
545 log_type_free_oldest(type
);
546 while (type
->log_oldest
&& (type
->log_oldest
->time
+ (time_t)type
->max_age
< now
))
547 log_type_free_oldest(type
);
548 if (type
->log_oldest
)
549 type
->log_oldest
->prev
= 0;
551 type
->log_newest
= 0;
553 /* call the destination logs */
554 for (ii
=0; ii
<type
->logs
[sev
].used
; ++ii
) {
555 struct logDestination
*ld
= type
->logs
[sev
].list
[ii
];
556 ld
->vtbl
->log_audit(ld
, type
, entry
);
558 for (ii
=0; ii
<log_default
->logs
[sev
].used
; ++ii
) {
559 struct logDestination
*ld
= log_default
->logs
[sev
].list
[ii
];
560 ld
->vtbl
->log_audit(ld
, type
, entry
);
565 log_replay(struct log_type
*type
, int is_write
, const char *line
)
569 for (ii
=0; ii
<type
->logs
[LOG_REPLAY
].used
; ++ii
) {
570 struct logDestination
*ld
= type
->logs
[LOG_REPLAY
].list
[ii
];
571 ld
->vtbl
->log_replay(ld
, type
, is_write
, line
);
573 for (ii
=0; ii
<log_default
->logs
[LOG_REPLAY
].used
; ++ii
) {
574 struct logDestination
*ld
= log_default
->logs
[LOG_REPLAY
].list
[ii
];
575 ld
->vtbl
->log_replay(ld
, type
, is_write
, line
);
580 log_module(struct log_type
*type
, enum log_severity sev
, const char *format
, ...)
591 if (sev
> LOG_FATAL
) {
592 log_module(MAIN_LOG
, LOG_ERROR
, "Illegal log_module severity %d", sev
);
595 va_start(args
, format
);
596 vsnprintf(msgbuf
, sizeof(msgbuf
), format
, args
);
599 for (ii
=0; ii
<type
->logs
[sev
].used
; ++ii
) {
600 struct logDestination
*ld
= type
->logs
[sev
].list
[ii
];
601 ld
->vtbl
->log_module(ld
, type
, sev
, msgbuf
);
603 for (ii
=0; ii
<log_default
->logs
[sev
].used
; ++ii
) {
604 struct logDestination
*ld
= log_default
->logs
[sev
].list
[ii
];
605 ld
->vtbl
->log_module(ld
, type
, sev
, msgbuf
);
608 /* Special behavior before we start full operation */
609 fprintf(stderr
, "%s: %s\n", log_severity_names
[sev
], msgbuf
);
612 if (sev
== LOG_FATAL
) {
613 assert(0 && "fatal message logged");
618 /* audit log searching */
621 log_discrim_create(struct userNode
*service
, struct userNode
*user
, unsigned int argc
, char *argv
[])
624 struct logSearch
*discrim
;
626 /* Assume all criteria require arguments. */
629 send_message(user
, service
, "MSG_MISSING_PARAMS", argv
[0]);
633 discrim
= malloc(sizeof(struct logSearch
));
634 memset(discrim
, 0, sizeof(*discrim
));
636 discrim
->max_time
= INT_MAX
;
637 discrim
->severities
= ~0;
639 for (ii
=1; ii
<argc
-1; ii
++) {
640 if (!irccasecmp(argv
[ii
], "bot")) {
641 struct userNode
*bot
= GetUserH(argv
[++ii
]);
643 send_message(user
, service
, "MSG_NICK_UNKNOWN", argv
[ii
]);
645 } else if (!IsLocal(bot
)) {
646 send_message(user
, service
, "MSG_NOT_A_SERVICE", argv
[ii
]);
649 discrim
->masks
.bot
= bot
;
650 } else if (!irccasecmp(argv
[ii
], "channel")) {
651 discrim
->masks
.channel_name
= argv
[++ii
];
652 } else if (!irccasecmp(argv
[ii
], "nick")) {
653 discrim
->masks
.user_nick
= argv
[++ii
];
654 } else if (!irccasecmp(argv
[ii
], "account")) {
655 discrim
->masks
.user_account
= argv
[++ii
];
656 } else if (!irccasecmp(argv
[ii
], "hostmask")) {
657 discrim
->masks
.user_hostmask
= argv
[++ii
];
658 } else if (!irccasecmp(argv
[ii
], "command")) {
659 discrim
->masks
.command
= argv
[++ii
];
660 } else if (!irccasecmp(argv
[ii
], "age")) {
661 const char *cmp
= argv
[++ii
];
664 discrim
->min_time
= now
- ParseInterval(cmp
+2);
666 discrim
->min_time
= now
- (ParseInterval(cmp
+1) - 1);
667 } else if (cmp
[0] == '>') {
669 discrim
->max_time
= now
- ParseInterval(cmp
+2);
671 discrim
->max_time
= now
- (ParseInterval(cmp
+1) - 1);
673 discrim
->min_time
= now
- ParseInterval(cmp
);
675 } else if (!irccasecmp(argv
[ii
], "limit")) {
676 discrim
->limit
= strtoul(argv
[++ii
], NULL
, 10);
677 } else if (!irccasecmp(argv
[ii
], "level")) {
678 char *severity
= argv
[++ii
];
679 discrim
->severities
= 0;
681 enum log_severity sev
= find_severity(severity
);
682 if (sev
== LOG_NUM_SEVERITIES
) {
683 send_message(user
, service
, "MSG_INVALID_SEVERITY", severity
);
686 discrim
->severities
|= 1 << sev
;
687 severity
= strchr(severity
, ',');
692 } else if (!irccasecmp(argv
[ii
], "type")) {
693 if (!(discrim
->type
= dict_find(log_types
, argv
[++ii
], NULL
))) {
694 send_message(user
, service
, "MSG_INVALID_FACILITY", argv
[ii
]);
698 send_message(user
, service
, "MSG_INVALID_CRITERIA", argv
[ii
]);
710 entry_match(struct logSearch
*discrim
, struct logEntry
*entry
)
712 if ((entry
->time
< discrim
->min_time
)
713 || (entry
->time
> discrim
->max_time
)
714 || !(discrim
->severities
& (1 << entry
->slvl
))
715 || (discrim
->masks
.bot
&& (discrim
->masks
.bot
!= entry
->bot
))
716 /* don't do glob matching, so that !events #a*b does not match #acb */
717 || (discrim
->masks
.channel_name
718 && (!entry
->channel_name
719 || irccasecmp(entry
->channel_name
, discrim
->masks
.channel_name
)))
720 || (discrim
->masks
.user_nick
721 && !match_ircglob(entry
->user_nick
, discrim
->masks
.user_nick
))
722 || (discrim
->masks
.user_account
723 && (!entry
->user_account
724 || !match_ircglob(entry
->user_account
, discrim
->masks
.user_account
)))
725 || (discrim
->masks
.user_hostmask
726 && entry
->user_hostmask
727 && !match_ircglob(entry
->user_hostmask
, discrim
->masks
.user_hostmask
))
728 || (discrim
->masks
.command
729 && !match_ircglob(entry
->command
, discrim
->masks
.command
))) {
736 log_report_entry(struct logEntry
*match
, void *extra
)
738 struct logReport
*rpt
= extra
;
739 send_message_type(4, rpt
->user
, rpt
->reporter
, "%s", match
->default_desc
);
743 log_entry_search(struct logSearch
*discrim
, entry_search_func esf
, void *data
)
745 unsigned int matched
= 0;
748 struct logEntry
*entry
;
750 for (entry
= discrim
->type
->log_oldest
;
752 entry
= entry
->next
) {
754 if (entry_match(discrim
, entry
)) {
756 if (++matched
>= discrim
->limit
)
763 for (it
= dict_first(log_types
); it
; it
= iter_next(it
)) {
764 discrim
->type
= iter_data(it
);
765 matched
+= log_entry_search(discrim
, esf
, data
);
772 /* generic helper functions */
775 log_format_timestamp(unsigned long when
, struct string_buffer
*sbuf
)
780 localtime_r(&feh
, &local
);
781 if (sbuf
->size
< 24) {
784 sbuf
->list
= calloc(1, 24);
786 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);
790 log_format_audit(struct logEntry
*entry
)
792 struct string_buffer sbuf
;
793 memset(&sbuf
, 0, sizeof(sbuf
));
794 log_format_timestamp(entry
->time
, &sbuf
);
795 string_buffer_append_string(&sbuf
, " (");
796 string_buffer_append_string(&sbuf
, entry
->bot
->nick
);
797 if (entry
->channel_name
) {
798 string_buffer_append(&sbuf
, ':');
799 string_buffer_append_string(&sbuf
, entry
->channel_name
);
801 string_buffer_append_string(&sbuf
, ") [");
802 string_buffer_append_string(&sbuf
, entry
->user_nick
);
803 if (entry
->user_hostmask
) {
804 string_buffer_append(&sbuf
, '!');
805 string_buffer_append_string(&sbuf
, entry
->user_hostmask
);
807 if (entry
->user_account
) {
808 string_buffer_append(&sbuf
, ':');
809 string_buffer_append_string(&sbuf
, entry
->user_account
);
811 string_buffer_append_string(&sbuf
, "]: ");
812 string_buffer_append_string(&sbuf
, entry
->command
);
813 entry
->default_desc
= strdup(sbuf
.list
);
817 /* shared stub log operations act as a noop */
820 ldNop_reopen(UNUSED_ARG(struct logDestination
*self_
)) {
821 /* no operation necessary */
825 ldNop_replay(UNUSED_ARG(struct logDestination
*self_
), UNUSED_ARG(struct log_type
*type
), UNUSED_ARG(int is_write
), UNUSED_ARG(const char *line
)) {
826 /* no operation necessary */
831 struct logDest_file
{
832 struct logDestination base
;
836 static struct logDest_vtable ldFile_vtbl
;
838 static struct logDestination
*
839 ldFile_open(const char *args
) {
840 struct logDest_file
*ld
;
841 ld
= calloc(1, sizeof(*ld
));
842 ld
->base
.vtbl
= &ldFile_vtbl
;
843 ld
->fname
= strdup(args
);
844 ld
->output
= fopen(ld
->fname
, "a");
849 ldFile_reopen(struct logDestination
*dest_
) {
850 struct logDest_file
*dest
= (struct logDest_file
*)dest_
;
851 fclose(dest
->output
);
852 dest
->output
= fopen(dest
->fname
, "a");
856 ldFile_close(struct logDestination
*dest_
) {
857 struct logDest_file
*dest
= (struct logDest_file
*)dest_
;
858 fclose(dest
->output
);
864 ldFile_audit(struct logDestination
*dest_
, UNUSED_ARG(struct log_type
*type
), struct logEntry
*entry
) {
865 struct logDest_file
*dest
= (struct logDest_file
*)dest_
;
866 fputs(entry
->default_desc
, dest
->output
);
867 fputc('\n', dest
->output
);
868 fflush(dest
->output
);
872 ldFile_replay(struct logDestination
*dest_
, UNUSED_ARG(struct log_type
*type
), int is_write
, const char *line
) {
873 struct logDest_file
*dest
= (struct logDest_file
*)dest_
;
874 struct string_buffer sbuf
;
875 memset(&sbuf
, 0, sizeof(sbuf
));
876 log_format_timestamp(now
, &sbuf
);
877 string_buffer_append_string(&sbuf
, is_write
? "W: " : " ");
878 string_buffer_append_string(&sbuf
, line
);
879 fputs(sbuf
.list
, dest
->output
);
880 fputc('\n', dest
->output
);
882 fflush(dest
->output
);
886 ldFile_module(struct logDestination
*dest_
, struct log_type
*type
, enum log_severity sev
, const char *message
) {
887 struct logDest_file
*dest
= (struct logDest_file
*)dest_
;
888 struct string_buffer sbuf
;
889 memset(&sbuf
, 0, sizeof(sbuf
));
890 log_format_timestamp(now
, &sbuf
);
891 fprintf(dest
->output
, "%s (%s:%s) %s\n", sbuf
.list
, type
->name
, log_severity_names
[sev
], message
);
893 fflush(dest
->output
);
896 static struct logDest_vtable ldFile_vtbl
= {
908 static struct logDest_vtable ldStd_vtbl
;
910 static struct logDestination
*
911 ldStd_open(const char *args
) {
912 struct logDest_file
*ld
;
913 ld
= calloc(1, sizeof(*ld
));
914 ld
->base
.vtbl
= &ldStd_vtbl
;
915 ld
->fname
= strdup(args
);
917 /* Print to stderr if given "err" and default to stdout otherwise. */
919 ld
->output
= fdopen(atoi(args
), "a");
920 else if (!strcasecmp(args
, "err"))
929 ldStd_close(struct logDestination
*dest_
) {
930 struct logDest_file
*dest
= (struct logDest_file
*)dest_
;
936 ldStd_replay(struct logDestination
*dest_
, UNUSED_ARG(struct log_type
*type
), int is_write
, const char *line
) {
937 struct logDest_file
*dest
= (struct logDest_file
*)dest_
;
938 fprintf(dest
->output
, "%s%s\n", is_write
? "W: " : " ", line
);
942 ldStd_module(struct logDestination
*dest_
, UNUSED_ARG(struct log_type
*type
), enum log_severity sev
, const char *message
) {
943 struct logDest_file
*dest
= (struct logDest_file
*)dest_
;
944 fprintf(dest
->output
, "%s: %s\n", log_severity_names
[sev
], message
);
947 static struct logDest_vtable ldStd_vtbl
= {
960 struct logDestination base
;
963 static struct logDest_vtable ldIrc_vtbl
;
965 static struct logDestination
*
966 ldIrc_open(const char *args
) {
967 struct logDest_irc
*ld
;
968 ld
= calloc(1, sizeof(*ld
));
969 ld
->base
.vtbl
= &ldIrc_vtbl
;
970 ld
->target
= strdup(args
);
975 ldIrc_close(struct logDestination
*dest_
) {
976 struct logDest_irc
*dest
= (struct logDest_irc
*)dest_
;
982 ldIrc_audit(struct logDestination
*dest_
, UNUSED_ARG(struct log_type
*type
), struct logEntry
*entry
) {
983 struct logDest_irc
*dest
= (struct logDest_irc
*)dest_
;
985 if (entry
->channel_name
) {
986 send_target_message(5, dest
->target
, entry
->bot
, "(%s", strchr(strchr(entry
->default_desc
, ' '), ':')+1);
988 send_target_message(5, dest
->target
, entry
->bot
, "%s", strchr(entry
->default_desc
, ')')+2);
993 ldIrc_module(struct logDestination
*dest_
, struct log_type
*type
, enum log_severity sev
, const char *message
) {
994 struct logDest_irc
*dest
= (struct logDest_irc
*)dest_
;
995 extern struct userNode
*opserv
;
997 send_target_message(5, dest
->target
, opserv
, "%s %s: %s\n", type
->name
, log_severity_names
[sev
], message
);
1000 static struct logDest_vtable ldIrc_vtbl
= {
1006 ldNop_replay
, /* totally ignore this - it would be a recipe for disaster */
1013 log_types
= dict_new();
1014 dict_set_free_keys(log_types
, free
);
1015 dict_set_free_data(log_types
, log_type_free
);
1016 log_dest_types
= dict_new();
1017 /* register log types */
1018 dict_insert(log_dest_types
, ldFile_vtbl
.type_name
, &ldFile_vtbl
);
1019 dict_insert(log_dest_types
, ldStd_vtbl
.type_name
, &ldStd_vtbl
);
1020 dict_insert(log_dest_types
, ldIrc_vtbl
.type_name
, &ldIrc_vtbl
);
1021 conf_register_reload(log_conf_read
);
1022 log_default
= log_register_type("*", NULL
);
1023 reg_exit_func(cleanup_logs
, NULL
);
1024 message_register_table(msgtab
);
1028 void SyncLog(char *fmt
,...)
1031 char buff
[MAXLEN
*4];
1035 va_start(args
, fmt
);
1036 vsnprintf(buff
, MAXLEN
, fmt
, args
);
1037 buff
[MAXLEN
- 1] = '\0';
1040 for (tmp
= buff
; *tmp
; tmp
++)
1042 if ((*tmp
== '\n') || (*tmp
== '\r'))
1044 else if (*tmp
== '\001')
1048 if((LogFile
= fopen("sync.log", "a")))
1050 fprintf(LogFile
, "%s: %s\n", time2str(time(NULL
)), buff
);
1056 int parselog(char *LogLine
, struct userNode
*user
, struct chanNode
*cptr
, char *chan
, char *nuh
, char *command
, char *rest
)
1058 struct svccmd
*svccmd
;
1060 struct service
*service
;
1062 char serv
[NICKLEN
+1];
1064 char myservc
[MAXLEN
];
1065 char mynuhbuf
[MAXLEN
];
1080 datestr
= (char *) mysep(&LogLine
, "]");
1081 mywho
= (char *) mysep(&LogLine
, " ");
1082 if (user
->handle_info
&& ((user
->handle_info
->opserv_level
> 0) || IsOper(user
)))
1083 mynuh
= (char *) mysep(&LogLine
, " ");
1085 mynick
= (char *) mysep(&LogLine
, "!");
1086 mysep(&LogLine
, "@");
1087 mysep(&LogLine
, ":");
1088 myacc
= (char *) mysep(&LogLine
, " ");
1089 sprintf(mynuhbuf
, "%s:%s", mynick
, myacc
);
1092 mycommand
= (char *) mysep(&LogLine
, " ");
1093 myrest
= (char *) mysep(&LogLine
, "\0");
1094 myserva
= (char *) mysep(&mywho
, ":");
1095 mychana
= (char *) mysep(&mywho
, ":");
1096 myserv
= (char *) mysep(&myserva
, "(");
1097 mychan
= (char *) mysep(&mychana
, ")");
1117 if(*chan
&& strcasecmp(mychan
, chan
))
1122 info
= conf_get_data("services/opserv/nick", RECDB_QSTRING
);
1127 strcpy(myservc
, myserv
);
1130 if (!strcmp(myserv
, info
)) {
1134 if ((service
= service_find(myserv
))) {
1135 if (!(cmd
= dict_find(service
->commands
, mycommand
, NULL
)))
1138 if (!(svccmd
= svccmd_resolve_name(cmd
, mycommand
)))
1141 pos
= snprintf(buf
, sizeof(buf
), "%s.%s", svccmd
->command
->parent
->name
, svccmd
->command
->name
);
1143 if (svccmd
->alias
.used
) {
1145 unsplit_string((char**)svccmd
->alias
.list
+1, svccmd
->alias
.used
-1, buf
+pos
);
1150 if (!(strcmp(buf
+0, "OpServ.OP")))
1152 if (!(strcmp(buf
+0, "OpServ.DEOP")))
1154 if (!(strcmp(buf
+0, "OpServ.VOICE")))
1156 if (!(strcmp(buf
+0, "OpServ.DEVOICE")))
1158 if (!(strcmp(buf
+0, "OpServ.KICK")))
1160 if (!(strcmp(buf
+0, "OpServ.KICKBAN")))
1163 if (!(strcmp(buf
+0, "OpServ.OPALL")))
1165 if (!(strcmp(buf
+0, "OpServ.DEOPALL")))
1167 if (!(strcmp(buf
+0, "OpServ.VOICEALL")))
1169 if (!(strcmp(buf
+0, "OpServ.DEVOICEALL")))
1171 if (!(strcmp(buf
+0, "OpServ.KICKALL")))
1173 if (!(strcmp(buf
+0, "OpServ.KICKBANALL")))
1177 if (!(strcmp(buf
+0, "OpServ.INVITE")))
1179 if (!(strcmp(buf
+0, "OpServ.INVITEME")))
1181 if (!(strcmp(buf
+0, "OpServ.CLEARBANS")))
1183 if (!(strcmp(buf
+0, "OpServ.CLEARMODES")))
1187 send_message(user
, chanserv
, "LAST_RESULTS", datestr
, myserv
, mynuh
, mycommand
, myrest
);
1191 sprintf(serv
, "%s", "");
1192 send_message(user
, chanserv
, "LAST_RESULTS", datestr
, serv
, mynuh
, mycommand
, myrest
);
1199 int ShowLog(struct userNode
*user
, struct chanNode
*cptr
, char *chan
, char *nuh
, char *command
, char *rest
, int maxlines
)
1202 int i
, s
, Last
= 0, filelen
, first
;
1203 int line
= 0, searchline
= 0;
1206 char Buff
[Block
+1] = "", PrevBuff
[Block
+1] = "";
1207 char LogLine
[(Block
+1)*2] = ""; /* To hold our exported results. */
1209 if(!(TheFile
= fopen(AccountingLog
, "r")))
1211 send_message(user
, chanserv
, "LAST_ERROR", AccountingLog
, strerror(errno
));
1214 s
= fseek(TheFile
, 0, SEEK_END
); /* Start at the end. */
1215 filelen
= ftell(TheFile
); /* Find out the length. */
1216 FilePosition
= 0; /* (from the bottom) */
1217 send_message(user
, chanserv
, "LAST_COMMAND_LOG", cptr
->name
);
1218 send_message(user
, chanserv
, "LAST_LINE");
1221 FilePosition
+= Block
;
1222 if(FilePosition
> filelen
)
1224 FilePosition
= filelen
;
1227 if((s
= fseek(TheFile
, filelen
-FilePosition
, SEEK_SET
)) < 0)
1229 send_message(user
, chanserv
, "LAST_ERROR", AccountingLog
, strerror(errno
));
1233 s
= fread(Buff
, 1, Block
, TheFile
);
1237 send_message(user
, chanserv
, "LAST_ERROR", AccountingLog
, strerror(errno
));
1242 for(i
= s
-1; i
>= 0; i
--)
1247 strcpy(LogLine
, &Buff
[i
+1]);
1249 strcat(LogLine
, PrevBuff
);
1252 if(parselog(LogLine
, user
, cptr
, chan
, nuh
, command
, rest
))
1254 if(line
>= maxlines
)
1256 send_message(user
, chanserv
, "LAST_STOPPING_AT", maxlines
);
1259 if( searchline
>= MAXLOGSEARCHLENGTH
)
1261 send_message(user
, chanserv
, "LAST_MAX_AGE");
1268 strcpy(PrevBuff
, Buff
); /* Save the remaining bit. */
1270 send_message(user
, chanserv
, "LAST_END_OF_LOG", line
);