--- ./logsurfer-1.5b/ChangeLog 2002-08-29 21:52:46.000000000 +1200 +++ ./logsurfer+-1.7/ChangeLog 2006-08-25 16:20:49.000000000 +1200 @@ -1,3 +1,41 @@ +Version 1.7 (logsurfer+) + + added -e option to begin processing from the current end + of the input log file ( normally used with -f ) + + put double-quotes around regex expressions in dump file + + if the context argument to a pipe or report action is "-" + then the current context contents are piped into the command + this should shorten most context definitions + + added new action "echo" which simply echo's the output on + stdout, or to a file with optional >file & >>file 1st arg + + added a macro construct in context action fields, if "$lines" + exists in a context action (such as a command line) it will + be substituted by the number of lines in the context + + added syslog action to send a message into syslog + the first argument must be facility:level, the second arg + is a string to send to syslog + +Version 1.6b (logsurfer+) + + fixed -t option in getopts() + +Version 1.6a (logsurfer+) + Kerry Thompson kerry@crypt.gen.nz: + + corrected off-by-one in min_lines checking + + added min_lines context arg to logsurfer.conf man page + +Version 1.6 (logsurfer+) + Kerry Thompson kerry@crypt.gen.nz: + + added an optional parameter at the end of context definition + (just before action) specifying the minimum number of lines + collected which needs to be satisfied before performing action + + added -t option to explicity timeout contexts when exiting + default=off so contexts don't all trigger when we shut down + + only add lines to a context if the context has an action of + 'pipe' or 'report'. In other words, don't store lines in + memory which won't ever be used. The number of matching + lines in the context is still incremented. This allows contexts + to be created which can notify if we _don't_ see an event, such + as regular "syslog pings" from hosts. + Version 1.5b + Corrected off-by-one heap overflow and uninitialized pointer --- ./logsurfer-1.5b/src/exec.h 1999-08-03 22:10:29.000000000 +1200 +++ ./logsurfer+-1.7/src/exec.h 2006-08-29 14:00:23.000000000 +1200 @@ -10,6 +10,9 @@ int prepare_exec(char *); void do_exec(struct action_tokens *); void do_pipe_line(struct action_tokens *); +void do_echo(struct action_tokens *); +void do_syslog(struct action_tokens *); +int parse_syslog_faclevel(char *, int *, int *); void free_tokens(struct action_tokens *); struct action_tokens *collect_tokens(char *); @@ -23,6 +26,9 @@ int prepare_exec(); void do_exec(); void do_pipe_line(); +void do_echo(); +void do_syslog(); +int parse_syslog_faclevel(); void free_tokens(); struct action_tokens *collect_tokens(); --- ./logsurfer-1.5b/src/exec.c 1999-08-03 22:10:29.000000000 +1200 +++ ./logsurfer+-1.7/src/exec.c 2006-08-30 10:48:41.000000000 +1200 @@ -49,6 +49,8 @@ #include #endif +#include + /* local includes */ @@ -292,3 +294,176 @@ #endif /* SENDMAIL_FLUSH */ +/* + * echo the tokens to stdout + */ +void +do_echo(echo_tokens) + struct action_tokens *echo_tokens; +{ + char *echostring; + char *outfile_name; + FILE *outfile; + char *write_mode; + + if ((echostring=echo_tokens->this_word) == NULL ) { + (void) fprintf(stderr, "echo definition without argument\n"); + return; + } + if( echostring[0] == '>' ){ + /* we've got a filename : grab it, open it, and hop to the next string */ + outfile_name = echostring+1; + if( outfile_name[0] == '>' ){ + outfile_name++; + write_mode = "a"; /* append */ + } else { + write_mode = "w"; + } + if (echo_tokens->next == NULL || (echostring=(echo_tokens->next)->this_word) == NULL ) { + (void) fprintf(stderr, "echo definition without argument\n"); + return; + } + outfile = fopen( outfile_name, write_mode ); + if( outfile == NULL ){ + (void) fprintf(stderr, "failed to open echo output file %s\n", outfile_name ); + return; + } + fprintf(outfile,"%s\n",echostring); + fclose(outfile); + } else { + /* normal stdout output */ + fprintf(stdout,"%s\n",echostring); + fflush(stdout); + } + + return; +} + +void +do_syslog(syslog_tokens) +struct action_tokens *syslog_tokens; +{ + char *fac_level_str; + char *log_str; + int syslog_facility, syslog_level; + + fac_level_str = syslog_tokens->this_word; + if(syslog_tokens->next == NULL || (log_str=(syslog_tokens->next)->this_word) == NULL ){ + (void) fprintf(stderr, "syslog command without enough arguments"); + return; + } + + /* parse facility & level */ + if( !parse_syslog_faclevel( fac_level_str, &syslog_facility, &syslog_level ) ){ + (void) fprintf(stderr, "unknown syslog facility/level argument"); + return; + } + + /* open syslog */ + openlog( "logsurfer", LOG_NDELAY|LOG_PID, syslog_facility ); + syslog( syslog_level, "%s", log_str=(syslog_tokens->next)->this_word ); + closelog(); +} + +/* + Parse syslog facility:level parameter string + returns 0-failed, 1-success +*/ +int parse_syslog_faclevel( faclevel, facility, level ) +char *faclevel; +int *facility, *level; +{ + char *levelstr; + + if( (levelstr = strchr(faclevel,':')) == NULL){ + (void) fprintf(stderr, "no colon in facility:level argument" ); /*debug*/ + return 0; + } + levelstr++; + + if( !strncmp(faclevel,"kern",4) ) + *facility = LOG_KERN; +#ifdef LOG_AUTHPRIV + else if( !strncmp(faclevel,"authpriv",8) ) + *facility = LOG_AUTHPRIV; +#endif +#ifdef LOG_CRON + else if( !strncmp(faclevel,"cron",4) ) + *facility = LOG_CRON; +#endif + else if( !strncmp(faclevel,"daemon",6) ) + *facility = LOG_DAEMON; +#ifdef LOG_FTP + else if( !strncmp(faclevel,"ftp",3) ) + *facility = LOG_FTP; +#endif +#ifdef LOG_AUTH + else if( !strncmp(faclevel,"auth",4) ) + *facility = LOG_AUTH; +#endif + else if( !strncmp(faclevel,"local0",6) ) + *facility = LOG_LOCAL0; + else if( !strncmp(faclevel,"local1",6) ) + *facility = LOG_LOCAL1; + else if( !strncmp(faclevel,"local2",6) ) + *facility = LOG_LOCAL2; + else if( !strncmp(faclevel,"local3",6) ) + *facility = LOG_LOCAL3; + else if( !strncmp(faclevel,"local4",6) ) + *facility = LOG_LOCAL4; + else if( !strncmp(faclevel,"local5",6) ) + *facility = LOG_LOCAL5; + else if( !strncmp(faclevel,"local6",6) ) + *facility = LOG_LOCAL6; + else if( !strncmp(faclevel,"local7",6) ) + *facility = LOG_LOCAL7; + else if( !strncmp(faclevel,"lpr",3) ) + *facility = LOG_LPR; + else if( !strncmp(faclevel,"mail",4) ) + *facility = LOG_MAIL; +#ifdef LOG_NEWS + else if( !strncmp(faclevel,"news",4) ) + *facility = LOG_NEWS; +#endif + else if( !strncmp(faclevel,"syslog",6) ) + *facility = LOG_SYSLOG; + else if( !strncmp(faclevel,"user",4) ) + *facility = LOG_USER; +#ifdef LOG_UUCP + else if( !strncmp(faclevel,"uucp",4) ) + *facility = LOG_UUCP; +#endif + else { + /* facility unknown */ + (void) fprintf(stderr, "unknown facility" ); /*debug*/ + return 0; + } + + if( !strncmp(levelstr,"emerg",5) ) + *level = LOG_EMERG; + else if( !strncmp(levelstr,"alert",5) ) + *level = LOG_ALERT; + else if( !strncmp(levelstr,"crit",4) ) + *level = LOG_CRIT; + else if( !strncmp(levelstr,"err",3) ) + *level = LOG_ERR; + else if( !strncmp(levelstr,"warn",4) ) + *level = LOG_WARNING; + else if( !strncmp(levelstr,"notice",6) ) + *level = LOG_NOTICE; + else if( !strncmp(levelstr,"info",4) ) + *level = LOG_INFO; + else if( !strncmp(levelstr,"debug",5) ) + *level = LOG_DEBUG; + else { + /* level unknown */ + (void) fprintf(stderr, "unknown level" ); /*debug*/ + return 0; + } + + return 1; +} + + + + --- ./logsurfer-1.5b/src/globals.h 1999-08-03 22:10:29.000000000 +1200 +++ ./logsurfer+-1.7/src/globals.h 2003-10-21 20:56:09.000000000 +1300 @@ -48,6 +48,9 @@ extern int exit_silent; /* exit silently? */ +extern int timeout_contexts_at_exit; /* timeout contexts when exit? */ + + /* * definitions for the regular expression matching routines * --- ./logsurfer-1.5b/src/logsurfer.h 1999-08-03 22:10:29.000000000 +1200 +++ ./logsurfer+-1.7/src/logsurfer.h 2006-08-24 16:39:31.000000000 +1200 @@ -46,6 +46,8 @@ #define ACTION_DELETE 5 #define ACTION_REPORT 6 #define ACTION_RULE 7 +#define ACTION_ECHO 8 +#define ACTION_SYSLOG 9 struct context_line { @@ -71,6 +73,7 @@ struct re_pattern_buffer *match_not_regex; char *match_not_regex_str; long max_lines; /* maximum number of bodylines */ + long min_lines; /* minimum number of bodylines */ long timeout_abs; /* absolut timeout */ long timeout_rel; /* relativ timeout */ long timeout_rel_offset; /* from the config line */ --- ./logsurfer-1.5b/src/context.h 1999-09-10 21:38:57.000000000 +1200 +++ ./logsurfer+-1.7/src/context.h 2006-08-24 14:12:58.000000000 +1200 @@ -23,6 +23,8 @@ void check_context_timeout(); +void expand_context_action_macros(struct context *); + #else /* __STDC__ */ struct context *create_context(); @@ -42,4 +44,6 @@ void check_context_timeout(); void check_context_linelimit(); +void expand_context_action_macros(); + #endif /* __STDC__ */ --- ./logsurfer-1.5b/src/context.c 2002-08-29 23:12:41.000000000 +1200 +++ ./logsurfer+-1.7/src/context.c 2006-08-30 10:21:35.000000000 +1200 @@ -72,6 +72,7 @@ new_context->match_not_regex_str=NULL; new_context->match_not_regex=NULL; new_context->max_lines=LONG_MAX; + new_context->min_lines=0; new_context->timeout_abs=LONG_MAX; new_context->timeout_rel=LONG_MAX; new_context->timeout_rel_offset=0; @@ -213,6 +214,14 @@ next_context_timeout=new_context->timeout_rel; } + /* if the next field is numeric, then its an optional min_lines */ + src=skip_spaces(src); + if( isdigit(*src) ){ + new_context->min_lines=atol(src); + while ( (*src != '\0') && (!isspace(*src)) ) + src++; + } + /* the rest is the default action */ src=skip_spaces(src); if ( !strncasecmp(src, "ignore", 6) ) { @@ -248,6 +257,26 @@ return; } } + else if ( !strncasecmp(src, "echo", 4) ) { + new_context->action_type=ACTION_ECHO; + src=skip_spaces(src+4); + if ( (new_context->action_tokens=collect_tokens(src)) == NULL ) { + (void) fprintf(stderr, "out of memory creating context: %s\n", + context_def); + destroy_context(new_context); + return; + } + } + else if ( !strncasecmp(src, "syslog", 6) ) { + new_context->action_type=ACTION_SYSLOG; + src=skip_spaces(src+6); + if ( (new_context->action_tokens=collect_tokens(src)) == NULL ) { + (void) fprintf(stderr, "out of memory creating context: %s\n", + context_def); + destroy_context(new_context); + return; + } + } else { (void) fprintf(stderr, "unknown default action: %s\n", src); destroy_context(new_context); @@ -414,37 +443,41 @@ { struct context_body *new_context_body; - /* - * init the new context body - */ - if ( (new_context_body=(struct context_body *) + if( this_context->action_type == ACTION_PIPE || this_context->action_type == ACTION_REPORT ){ + + /* + * init the new context body + */ + if ( (new_context_body=(struct context_body *) malloc(sizeof(struct context_body))) == NULL ) { (void) fprintf(stderr, "out of memory updating context\n"); (void) fprintf(stderr, "dropping line: %s\n", new_context_line->content); return; - } - new_context_body->this_line=new_context_line; - new_context_body->next=NULL; - - /* - * link it into the context - */ - if ( this_context->last == NULL ) { + } + new_context_body->this_line=new_context_line; + new_context_body->next=NULL; + + /* + * link it into the context + */ + if ( this_context->last == NULL ) { this_context->body=new_context_body; this_context->last=new_context_body; - } - else { + } + else { this_context->last->next=new_context_body; this_context->last=new_context_body; + } + + /* + * increment the linkt counter + */ + new_context_line->link_counter++; + } this_context->lines++; /* - * increment the linkt counter - */ - new_context_line->link_counter++; - - /* * adjust a relative timeout (if used) */ if ( this_context->timeout_rel_offset != 0 ) { @@ -470,6 +503,7 @@ case ACTION_IGNORE: break; case ACTION_EXEC: + expand_context_action_macros( this_context ); do_exec(this_context->action_tokens); break; case ACTION_PIPE: @@ -494,13 +528,23 @@ (void) strcpy(dummy_action->this_word, this_context->match_regex_str); dummy_action->next=NULL; this_context->action_tokens->next=dummy_action; - make_report(this_context->action_tokens); + expand_context_action_macros( this_context ); + make_report(this_context->action_tokens, this_context->body); this_context->action_tokens->next=NULL; (void) free(dummy_action->this_word); (void) free(dummy_action); break; case ACTION_REPORT: - make_report(this_context->action_tokens); + expand_context_action_macros( this_context ); + make_report(this_context->action_tokens, this_context->body); + break; + case ACTION_ECHO: + expand_context_action_macros( this_context ); + do_echo(this_context->action_tokens); + break; + case ACTION_SYSLOG: + expand_context_action_macros( this_context ); + do_syslog(this_context->action_tokens); break; default: (void) fprintf(stderr, @@ -527,7 +571,9 @@ next_context=this_context->next; if ( (this_context->timeout_abs < (long) current_time) || (this_context->timeout_rel < (long) current_time) ) { - do_context_action(this_context); + + if (!this_context->min_lines || this_context->lines >= this_context->min_lines ) + do_context_action(this_context); unlink_context(this_context); } else { @@ -555,7 +601,8 @@ while ( this_context != NULL ) { next_context=this_context->next; if ( this_context->lines > this_context->max_lines ) { - do_context_action(this_context); + if (!this_context->min_lines || this_context->lines >= this_context->min_lines ) + do_context_action(this_context); unlink_context(this_context); } this_context=next_context; @@ -564,3 +611,60 @@ } +/* + * expand any macro functions in a string: + * $lines -> replaced with count of lines in a context + */ +#define TEMPSTR_SZ 2048 +void expand_context_action_macros( this_context ) +struct context *this_context;{ + char *pos; + int cpos; + char tempstr[TEMPSTR_SZ], *tp; + char *new_string; + char **cmd_string; + struct action_tokens *act_token; + int replacements = 0; + + /* + * Loop through all action tokens in the context + */ + for( act_token=this_context->action_tokens ; act_token != NULL ; act_token=act_token->next ){ + cmd_string = &act_token->this_word; + for( pos=*cmd_string, tp=tempstr ; *pos ; pos++ ){ + if( *pos == '$' && ( pos == *cmd_string || *(pos-1) != '\\' ) ){ + if( !strncmp(pos+1,"lines",5) ){ + tp += sprintf(tp,"%d",this_context->lines); + pos += 5; + replacements++; + } else { + *tp++ = *pos; + } + } else { + *tp++ = *pos; + } + /* Avoid stack overflow in temp_str */ + if( tp > (tempstr + TEMPSTR_SZ - 16) ){ + fprintf(stderr,"action string too long in expand_context_action_macros"); + return; + } + } + *tp = '\0'; + + if( replacements ){ + /* + * malloc space for the new string, and replace the old on in the action list + */ + if( (new_string = (char *)malloc(strlen(tempstr)+1)) == NULL){ + (void) fprintf(stderr,"malloc failed in expand_context_action_macros\n"); + return; + } + strcpy(new_string,tempstr); + free( *cmd_string ); + *cmd_string = new_string; + } + } + +} + + --- ./logsurfer-1.5b/src/logsurfer.c 2002-08-29 23:36:12.000000000 +1200 +++ ./logsurfer+-1.7/src/logsurfer.c 2004-12-24 11:22:28.000000000 +1300 @@ -1,5 +1,5 @@ /* - * logsurfer V 1.5b + * logsurfer V 1.7 * * (C) DFN-CERT, Germany * Authors: Wolfgang Ley, Uwe Ellermann @@ -116,6 +116,8 @@ int exit_silent=0; /* exit silently? */ +int timeout_contexts_at_exit=0; /* timeout contexts when exit? */ + /* * definitions for the regular expression matching routines * @@ -286,9 +288,9 @@ char *progname; { (void) fprintf(stderr, - "usage: %s [-l startline | -r startregex] [-c configfile] [-d dumpfile] [-p pidfile] [-f] [logfile]\n", + "usage: %s [-l startline | -r startregex] [-c configfile] [-d dumpfile] [-p pidfile] [-f] [-t] [-e] [logfile]\n", progname); - (void) fprintf(stderr, "This is logsurfer version 1.5b\n"); + (void) fprintf(stderr, "This is logsurfer+ version 1.7\n"); exit(1); } @@ -308,6 +310,7 @@ char cf_filename[MAXPATHLEN]; /* configuration filename */ long start_line=0; /* startline within logfile */ + int start_at_end=0; /* start at end of file */ struct re_pattern_buffer *start_regex=NULL; /* regex for startline */ int do_follow=0; /* follow the file? */ FILE *pidfile; /* write pid info to file */ @@ -389,8 +392,12 @@ exit(99); } - while ( (opt = getopt(argc, argv, "fc:d:l:p:r:s")) != EOF ) + while ( (opt = getopt(argc, argv, "efc:d:l:p:r:st")) != EOF ) switch(opt) { + case 'e': + /* start processing at the end of the log file */ + start_at_end=1; + break; case 'f': /* set follow mode on */ do_follow=1; @@ -447,6 +454,9 @@ case 's': exit_silent=1; break; + case 't': + timeout_contexts_at_exit=1; + break; default: usage(progname); break; @@ -471,6 +481,9 @@ logfile_name); exit(6); } + if (start_at_end) { + fseek(logfile,0,SEEK_END); + } } if ( start_line != 0 ) { --- ./logsurfer-1.5b/src/exit.c 1999-08-03 22:10:29.000000000 +1200 +++ ./logsurfer+-1.7/src/exit.c 2006-08-24 16:42:16.000000000 +1200 @@ -113,19 +113,19 @@ (void) fprintf(dumpfile, "*******************************************************\n\n"); for (rule_ptr=all_rules; rule_ptr != NULL; rule_ptr=rule_ptr->next) { (void) fprintf(dumpfile, "-----\n"); - (void) fprintf(dumpfile, "%s ", rule_ptr->match_regex_str); + (void) fprintf(dumpfile, "\"%s\" ", rule_ptr->match_regex_str); if ( rule_ptr->match_not_regex_str == NULL ) (void) fprintf(dumpfile, "-\n"); else - (void) fprintf(dumpfile, "%s\n", rule_ptr->match_not_regex_str); + (void) fprintf(dumpfile, "\"%s\"\n", rule_ptr->match_not_regex_str); if ( rule_ptr->stop_regex_str == NULL ) (void) fprintf(dumpfile, "\t- "); else - (void) fprintf(dumpfile, "\t%s ", rule_ptr->stop_regex_str); + (void) fprintf(dumpfile, "\t\"%s\" ", rule_ptr->stop_regex_str); if ( rule_ptr->stop_not_regex_str == NULL ) (void) fprintf(dumpfile, "- "); else - (void) fprintf(dumpfile, "%s ", rule_ptr->stop_not_regex_str); + (void) fprintf(dumpfile, "\"%s\" ", rule_ptr->stop_not_regex_str); if ( rule_ptr->timeout == LONG_MAX ) (void) fprintf(dumpfile, "0"); else @@ -155,6 +155,12 @@ case ACTION_RULE: (void) fprintf(dumpfile, "RULE %s\n", rule_ptr->action_body); break; + case ACTION_ECHO: + (void) fprintf(dumpfile, "ECHO %s\n", rule_ptr->action_body); + break; + case ACTION_SYSLOG: + (void) fprintf(dumpfile, "SYSLOG %s\n", rule_ptr->action_body); + break; default: (void) fprintf(dumpfile, "(UNKNOWN) - shouldn't happen\n"); } @@ -165,16 +171,16 @@ (void) fprintf(dumpfile, "* CONTEXT Information\n"); (void) fprintf(dumpfile, "*\n"); (void) fprintf(dumpfile, "* match_regex match_not_regex\n"); - (void) fprintf(dumpfile, "* max_lines timeout_abs timeout_rel action\n"); + (void) fprintf(dumpfile, "* max_lines timeout_abs timeout_rel min_lines action\n"); (void) fprintf(dumpfile, "* contents\n"); (void) fprintf(dumpfile, "*******************************************************\n\n"); for ( context_ptr=all_contexts; context_ptr!=NULL; context_ptr=context_ptr->next ) { (void) fprintf(dumpfile, "-----\n"); - (void) fprintf(dumpfile, "%s ", context_ptr->match_regex_str); + (void) fprintf(dumpfile, "\"%s\" ", context_ptr->match_regex_str); if ( context_ptr->match_not_regex_str == NULL ) (void) fprintf(dumpfile, "-\n"); else - (void) fprintf(dumpfile, "%s\n", context_ptr->match_not_regex_str); + (void) fprintf(dumpfile, "\"%s\"\n", context_ptr->match_not_regex_str); if ( context_ptr->max_lines == LONG_MAX ) (void) fprintf(dumpfile, "\t0 "); else @@ -187,6 +193,7 @@ (void) fprintf(dumpfile, "0 "); else (void) fprintf(dumpfile, "%ld ", context_ptr->timeout_rel_offset); + (void) fprintf(dumpfile, "%ld ", context_ptr->min_lines); switch (context_ptr->action_type) { case ACTION_IGNORE: (void) fprintf(dumpfile, "IGNORE"); @@ -200,6 +207,12 @@ case ACTION_REPORT: (void) fprintf(dumpfile, "REPORT"); break; + case ACTION_ECHO: + (void) fprintf(dumpfile, "ECHO"); + break; + case ACTION_SYSLOG: + (void) fprintf(dumpfile, "SYSLOG"); + break; default: (void) fprintf(dumpfile, "(UNKNOWN) - shouldn't happen"); } @@ -319,7 +332,8 @@ next_context=this_context->next; if ( (this_context->timeout_abs != LONG_MAX) || (this_context->timeout_rel != LONG_MAX) ) { - do_context_action(this_context); + if( timeout_contexts_at_exit ) + do_context_action(this_context); unlink_context(this_context); } this_context=next_context; --- ./logsurfer-1.5b/src/rule.c 1999-08-03 22:10:29.000000000 +1200 +++ ./logsurfer+-1.7/src/rule.c 2006-08-24 16:45:11.000000000 +1200 @@ -426,6 +426,38 @@ } (void) strcpy(new_rule->action_body, help_ptr); } + else if (!strncasecmp(help_ptr, "echo", 4)) { + new_rule->action_type=ACTION_ECHO; + help_ptr=skip_spaces(help_ptr+4); + if ( *help_ptr == '\0' ) { + (void) fprintf(stderr, "echo without string in rule: %s\n", + input_line); + destroy_rule(new_rule); + return(NULL); + } + if ( (new_rule->action_body=(char *)malloc(strlen(help_ptr)+1)) == NULL ) { + (void) fprintf(stderr, "out of memory parsing rule\n"); + destroy_rule(new_rule); + return(NULL); + } + (void) strcpy(new_rule->action_body, help_ptr); + } + else if (!strncasecmp(help_ptr, "syslog", 6)) { + new_rule->action_type=ACTION_SYSLOG; + help_ptr=skip_spaces(help_ptr+6); + if ( *help_ptr == '\0' ) { + (void) fprintf(stderr, "syslog without arguments in rule: %s\n", + input_line); + destroy_rule(new_rule); + return(NULL); + } + if ( (new_rule->action_body=(char *)malloc(strlen(help_ptr)+1)) == NULL ) { + (void) fprintf(stderr, "out of memory parsing rule\n"); + destroy_rule(new_rule); + return(NULL); + } + (void) strcpy(new_rule->action_body, help_ptr); + } else { (void) fprintf(stderr, "unknown action in rule: %s\n", input_line); destroy_rule(new_rule); @@ -567,12 +599,30 @@ this_rule->action_body); return; } - make_report(help_ptr); + make_report(help_ptr,NULL); free_tokens(help_ptr); break; case ACTION_RULE: dynamic_add_rule(this_rule); break; + case ACTION_ECHO: + if ((help_ptr=collect_tokens(this_rule->action_body)) == NULL) { + (void) fprintf(stderr, "out of memory generating echo: %s\n", + this_rule->action_body); + return; + } + do_echo(help_ptr); + free_tokens(help_ptr); + break; + case ACTION_SYSLOG: + if ((help_ptr=collect_tokens(this_rule->action_body)) == NULL) { + (void) fprintf(stderr, "out of memory generating syslog: %s\n", + this_rule->action_body); + return; + } + do_syslog(help_ptr); + free_tokens(help_ptr); + break; } return; } --- ./logsurfer-1.5b/man/logsurfer.1.in 1999-11-23 01:04:49.000000000 +1300 +++ ./logsurfer+-1.7/man/logsurfer.1.in 2003-10-21 21:01:28.000000000 +1300 @@ -28,6 +28,8 @@ ] [ .B \-s ] [ +.B \-t +] [ .B filename ] .SH DESCRIPTION @@ -152,6 +154,12 @@ Normally the logsurfer prints some information to stderr if the program exists. If you specify the "-s" (silent) option, then those messages are not printed. +.TP +.B \-t +When logsurfer exits, the -t option will instruct it to explicitly expire +all contexts which have either a relative or absolute timeout configured. +Without the -t option, contexts will not be timed-out and will be removed +silently without triggering their actions. .SH "FILES" .PD 0 .TP 32 --- ./logsurfer-1.5b/man/logsurfer.conf.4.in 1999-11-23 01:05:03.000000000 +1300 +++ ./logsurfer+-1.7/man/logsurfer.conf.4.in 2006-08-25 16:07:35.000000000 +1200 @@ -137,6 +137,23 @@ the current rule), "top" (insert at the top of all rules) or "bottom" (append at the end of all existing rules). Any following keywords should again be in the format of the rule definition. +.TP +echo +The echo action simply echos the next argument string to stdout. This +is useful if you want to output a simple string without invoking a +separate process to do so. If the first argument is ">file", or +">>file" then the second argument is written to the specified file, +either overwriting or appending the file respectively. +.TP +syslog +This allows you to send messages into syslog. The first keyword +following the syslog action is the syslog facility and level in the +format (facility):(level) where facility must be one of auth, +authpriv, cron, daemon, ftp, kern, local0, local1, local2, local3, +local4, local5, local6, local7, lpr, mail, news, syslog, user, uucp +and level must be one of emerg, alert, crit, err, warn, notice, info, +debug. The second argument to the syslog action is a string to send +to syslog, which should be surrounded by quotes. .LP Once a logline has matched a rule this rule perform certain actions. Rules form the "active" part of the logsurfer. @@ -163,7 +180,7 @@ .LP \fIContexts\fP have the following format: .LP -match_regex match_not_regex line_limit timeout_abs timeout_rel default_action +match_regex match_not_regex line_limit timeout_abs timeout_rel [min_lines] default_action .LP .TP 20 match_regex @@ -198,6 +215,14 @@ default action if there are no new messages stored in this context for a certain amount of time. .TP +min_lines +An optional parameter specifying the minimum number of lines matched by a +context before the action will be performed. The action will be performed +if and only if it has collected min_lines or more log lines, otherwise the +context will simply be deleted without any action. Note that min_lines does +not in itself trigger the action - only a timeout or max_lines will do so. +Default is 0 (no check for min_lines) +.TP default_action This is the action that is processed if the linenumber limit or one of the timeouts are reached. The possible actions are the same as described in the @@ -212,7 +237,9 @@ .LP If you need to specify a context (for example in the "open", "delete" and "report" actions) you have to do this by giving the exact regular -expression that this context uses for matching (match_regex). +expression that this context uses for matching (match_regex), alternatively +if you specify "-" as the context, then logsurfer will apply the contents of +the context under which the action is being performed. .LP The timeout functions require timestamps for each message that is processed. To be independent of the message format the logsurfer uses the time when