1 /*****************************************************************************
2 *
3 * fetchlog.c - logfile fetcher: pick up last new messages of a logfile
4 *
5 * Copyright (c) 2002 .. 2010 Alexander Haderer (alexander.haderer@loescap.de)
6 *
7 * Last Update: $Author: afrika $
8 * Update Date: $Date: 2010/07/01 16:39:25 $
9 * Source File: $Source: /home/cvsroot/tools/fetchlog/fetchlog.c,v $
10 * CVS/RCS Revision: $Revision: 1.9 $
11 * Status: $State: Exp $
12 *
13 * CVS/RCS Log at end of file
14 *
15 * License:
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 *
31 *****************************************************************************/
32
33
34 #include <stdio.h> /* sprintf */
35 #include <stdlib.h> /* atoi */
36 #include <ctype.h> /* isalpha */
37 #include <string.h> /* strcat, strcpy */
38 #include <fcntl.h> /* open close */
39 #include <sys/types.h> /* stat */
40 #include <sys/stat.h> /* stat */
41 #include <sys/mman.h> /* mmap, madvise */
42 #include <unistd.h> /* access */
43 #include <errno.h> /* errno */
44 #ifdef HAS_REGEX
45 #include <regex.h> /* posix regex stuff */
46 #endif
47
48 /*************************************************
49 * constants
50 *************************************************/
51
52 #define MIN_FIRSTCOL 1 /* Min first col for fetching */
53 #define MAX_LASTCOL 300 /* Max last col for fetching */
54 #define MIN_COLS 20 /* Min no of cols to fetch */
55 #define MIN_FETCHLEN 50 /* Min length of fetched data */
56 #define MAX_FETCHLEN 20000 /* Max length of fetched data */
57
58 #define OK_MESSAGE "OK: no messages"
59 #define ERR_MESSAGE "ERROR: fetchlog: "
60
61 /* suffix for temp bookmarkfile: mkstemp() template */
62 #define FETCH_FILE_SUFFIX "XXXXXX"
63
64 /* conversion flags */
65 #define CONV_NONE 0
66 #define CONV_BRACKET 1
67 #define CONV_PERCENT 2
68 #define CONV_NEWLINE 4
69 #define CONV_OKMSG 8
70 #define CONV_SHELL 16
71 #define CONV_NAGIOS3 32
72
73 /* return/exit codes */
74 #define RET_OK 0 /* ok, no messages ok */
75 #define RET_ERROR 1 /* internal error warn */
76 #define RET_NEWMSG 2 /* logfile has new messages critical */
77 #define RET_PARAM 3 /* wrong parameter, print help */
78
79 /* version info */
80 #define FL_VERSION FETCHLOG_VERSION_NO
81
82 /*************************************************
83 * typedefs
84 *************************************************/
85 typedef struct {
86 long last; /* pos lastchar fetched plus 1 (=first new char) */
87 time_t mtime; /* mtime of logfile when this bookmark was valid */
88 ino_t inode; /* inode number of logfile */
89 } bookmark;
90
91 /*************************************************
92 * prototypes
93 *************************************************/
94 void usage( void );
95 int fetch_logfile( char *logfilename, char *bookmarkfile, int updbm_flag );
96 int read_bookmark( char *bmfile, bookmark *bm );
97 int write_bookmark( char *bmfile, bookmark *bm );
98 int copyline( int opos, char *obuf, char *ipt, int illen );
99 int check_farg( char *farg, int *conv );
100 void perr( char *msg1, char *msg2, int err );
101
102 /*************************************************
103 * globals
104 *************************************************/
105
106 /* fetching */
107 int firstcol_G = MIN_FIRSTCOL;
108 int lastcol_G = MAX_LASTCOL;
109 int fetchlen_G = MAX_FETCHLEN;
110 int conv_G = CONV_NONE;
111 #ifdef HAS_REGEX
112 regex_t *rx_G = NULL;
113 int numrx_G = 0;
114
115 #define RXERRBUFLEN 1000
116 char rxerrbuf_G[RXERRBUFLEN];
117
118 #endif
119
120 /*************************************************
121 * code...
122 *************************************************/
123
124 /************************************************
125 * perr( .. )
126 * print error message to stdout with respect to fetchlen_G
127 ************************************************
128 */
perr(char * msg1,char * msg2,int err)129 void perr( char *msg1, char *msg2, int err ) {
130 char *msg = NULL;
131 int len = 0;
132 int r;
133
134 if( !msg1 ) return;
135
136 len = sizeof( ERR_MESSAGE ) + strlen( msg1 ) + 1; /* 1 == '\n' */
137 if( msg2 ) len += strlen( msg2 ) + 2; /* 2 == ': ' */
138 if( err ) len += strlen( strerror( err ) ) + 2; /* 2 == ': ' */
139
140 if( (msg=malloc( len ) ) == NULL ) { exit( RET_ERROR ); }
141
142 strcpy( msg, ERR_MESSAGE );
143 strcat( msg, msg1 );
144 if( msg2 ) {
145 strcat( msg, ": " );
146 strcat( msg, msg2 );
147 }
148 if( err ) {
149 strcat( msg, ": " );
150 strcat( msg, strerror( err ) );
151 }
152 if( len-1 <= fetchlen_G ) {
153 strcat( msg, "\n" );
154 }else{
155 msg[fetchlen_G-1] = '\n';
156 msg[fetchlen_G-2] = '~';
157 len = fetchlen_G + 1;
158 }
159 r = write( STDOUT_FILENO, msg, len-1 );
160 free( msg );
161 }
162
163 /************************************************
164 * read_bookmark( char *bmfile, bookmark *bm );
165 * return: 0: ok 1: error
166 ************************************************
167 */
read_bookmark(char * bmfile,bookmark * bm)168 int read_bookmark( char *bmfile, bookmark *bm ) {
169 int fd = -1;
170 struct stat sb;
171
172 fd = open( bmfile, O_RDONLY );
173 if( fd == -1 ) {
174 if( errno == ENOENT ) {
175 /* no bookmarkfile --> acts like infinite old bookmarkfile */
176 bm->last = -1;
177 bm->mtime = 0;
178 bm->inode = 0;
179 return 0;
180 }else{
181 perr( "open", bmfile, errno );
182 return 1;
183 }
184 }
185 if( fstat( fd, &sb ) == -1 ) {
186 perr( "stat", bmfile, errno );
187 close( fd );
188 return 1;
189 }
190 if( (int)sb.st_size != sizeof(bookmark) ||
191 ((sb.st_mode & S_IFMT) != S_IFREG) )
192 {
193 perr( "no file/wrong size", bmfile, 0);
194 close( fd );
195 return 1;
196 }
197 if( read( fd, bm, sizeof(bookmark)) != sizeof( bookmark ) ) {
198 perr( "file to short", bmfile, 0 );
199 close( fd );
200 return 1;
201 }
202 close( fd );
203
204 return 0; /* ok */
205
206 } /* read_bookmark() */
207
208
209 /************************************************
210 * write_bookmark( char *bmfile, bookmark *bm );
211 * return: 0: ok 1: error
212 ************************************************
213 */
write_bookmark(char * bmfile,bookmark * bm)214 int write_bookmark( char *bmfile, bookmark *bm ) {
215
216 char *nbmfile = NULL; /* new bmfile (a tmp file to be renamed to bmfile) */
217 int nbmfd = -1;
218
219 nbmfile = (char*)malloc( strlen(bmfile) + sizeof(FETCH_FILE_SUFFIX) );
220 if( nbmfile == NULL ) {
221 perr("malloc", NULL, errno );
222 return 1;
223 }
224 strcpy( nbmfile, bmfile );
225 strcat( nbmfile, FETCH_FILE_SUFFIX );
226 nbmfd = mkstemp( nbmfile );
227 if( nbmfd == -1 ) {
228 perr( "mkstemp", nbmfile, errno );
229 free( nbmfile );
230 return 1;
231 }
232 if( write( nbmfd, bm, sizeof( bookmark ) ) != sizeof( bookmark ) ) {
233 perr( "write", nbmfile, errno );
234 close( nbmfd );
235 unlink( nbmfile );
236 free( nbmfile );
237 return 1;
238 }
239 close( nbmfd );
240 if( rename( nbmfile, bmfile ) == -1 ) {
241 perr( "rename tmpfile to", bmfile, errno );
242 unlink( nbmfile );
243 free( nbmfile );
244 return 1;
245 }
246 free( nbmfile );
247 return 0; /* ok */
248
249 } /* write_bookmark() */
250
251
252
253 /************************************************
254 * fetch_logfile( char *logfilename, char *bookmarkfile, int updbm_flag )
255 ************************************************
256 */
fetch_logfile(char * logfile,char * bmfile,int updbm_flag)257 int fetch_logfile( char *logfile, char *bmfile, int updbm_flag ) {
258
259 bookmark obm, nbm; /* old, new bookmark */
260
261 char *ibuf = NULL; /* input buf (--> the logfile) */
262 size_t ilen = 0;
263 char *ipt = NULL;
264 char *bmpt = NULL; /* points to first new char if bm exists */
265
266 char *obuf = NULL; /* output buf (filled backwards) */
267 int opos; /* first used char pos in obuf */
268
269 /* for CONV_NAGIOS3 */
270 int lastlinepos = 0; /* pos of beginning of lastline in obuf */
271 int lastlinelen = 0; /* len of lastline, excl. trailing '\' 'n' */
272
273 int r; /* write's returncode */
274
275 int i;
276 int fd = -1;
277 struct stat sb;
278 char *lgfile = NULL;
279 int llen = 0;
280 int done = 0;
281
282 if( read_bookmark( bmfile, &obm ) ) return RET_ERROR;
283
284 if( (fd=open( logfile, O_RDONLY )) == -1 ) {
285 perr( "open", logfile, errno );
286 return RET_ERROR;
287 }
288 if( fstat( fd, &sb ) == -1 ) {
289 perr( "stat", logfile, errno );
290 close( fd );
291 return RET_ERROR;
292 }
293
294 nbm.last = (size_t) sb.st_size;
295 nbm.mtime = sb.st_mtime;
296 nbm.inode = sb.st_ino;
297
298 /* something changed meanwhile ? */
299 if( obm.mtime==nbm.mtime && obm.inode==nbm.inode && obm.last==nbm.last ) {
300 if( conv_G & CONV_OKMSG )
301 r = write( STDOUT_FILENO, OK_MESSAGE "\n", sizeof( OK_MESSAGE ) );
302 close( fd );
303 return RET_OK;
304 }
305
306 /*****************/
307
308 obuf = malloc( fetchlen_G+1 );
309 if( obuf == NULL ) {
310 perr( "malloc", NULL, errno );
311 close( fd );
312 return RET_ERROR;
313 }
314 opos = fetchlen_G;
315 *(obuf + opos) = '\0'; /* dummy: opos -> first used char in obuf */
316 if( conv_G & CONV_NEWLINE ) {
317 /* when using CONV_NEWLINE the obuf is filled up like this:
318 1. init: write '\\' 'n' '\n' to the end (3 chars)
319 2. fill (copyline() ) by first prepend line contents and
320 then prepend '\\' 'n'
321 result: An additional '\\' 'n' at the beginning
322 else
323 1. fill (copyline() ) by first prepend newline and then prepend
324 line contents
325 */
326 *(obuf + --opos) = '\n';
327 *(obuf + --opos) = 'n';
328 *(obuf + --opos) = '\\';
329 }
330
331 lgfile = (char*)malloc( strlen(logfile) + sizeof(".X") );
332 if( lgfile == NULL ) {
333 free( obuf );
334 perr( "malloc", NULL, errno );
335 close( fd );
336 return RET_ERROR;
337 }
338
339 /* read in all logfiles and backward fill obuf upto fetchlen_G chars */
340
341 for( i=-1; i<10; i++ ) {
342 /* i==-1: logfile without suffix, else i==logfile suffix */
343 if( i==-1 ) {
344 /* lgfile is already open and sb contains the stat */
345 strcpy( lgfile, logfile );
346 }else{
347 sprintf( lgfile, "%s.%1d", logfile, i );
348
349 if( (fd=open( lgfile, O_RDONLY )) == -1 ) {
350 if( errno==ENOENT && i==0 ) {
351 continue; /* some logrotator start with .1 */
352 }else if( errno==ENOENT && i>0 ) {
353 break;
354 }else{
355 perr( "open", lgfile, errno );
356 free( obuf ); free( lgfile );
357 return RET_ERROR;
358 }
359 }
360 if( fstat( fd, &sb ) == -1 ) {
361 perr( "stat", lgfile, errno );
362 free( obuf ); free( lgfile ); close( fd );
363 return RET_ERROR;
364 }
365 }
366
367 ilen = (size_t) sb.st_size;
368
369 if( ilen == 0 ) {
370 close( fd );
371 if( obm.inode == sb.st_ino ) break;
372 continue;
373 }
374
375 ibuf = mmap( NULL, ilen, PROT_READ, MAP_SHARED, fd, (off_t)0 );
376 if( ibuf == MAP_FAILED ) {
377 perr( "mmap", lgfile, errno );
378 free( obuf ); free( lgfile ); close( fd );
379 return RET_ERROR;
380 }
381
382 #ifdef HAS_MADVISE
383 if( madvise( ibuf, ilen, MADV_RANDOM ) ) {
384 perr( "madvise", NULL, errno );
385 free( obuf ); free( lgfile ); close( fd );
386 munmap( ibuf, ilen );
387 return RET_ERROR;
388 }
389 #endif
390
391 /* check for old bookmark */
392 bmpt = NULL;
393 if( obm.inode == sb.st_ino ) {
394 bmpt = ibuf+obm.last;
395 }
396
397 /* scan backwards for lines but the first */
398 done = 0;
399 for( llen=1,ipt=ibuf+ilen-2; ipt>=ibuf; llen++,ipt-- ) {
400 if( *ipt=='\n' ) {
401 if( ipt+1<bmpt ) { done=1; break; }
402 opos = copyline( opos, obuf, ipt+1, llen );
403 if( opos==0 ) { done=1; break; }
404 llen = 0;
405 }
406 }
407 /* copy first line ? */
408 if( ipt+1==ibuf && done==0 ) {
409 if( ipt+1<bmpt ) { done=1; }
410 else{ opos = copyline( opos, obuf, ipt+1, llen ); }
411 if( opos==0 ) { done=1; }
412 }
413
414 munmap( ibuf, ilen );
415 close( fd );
416 if( done ) break;
417 if( bmpt ) break; /* processed a bookmarked file? --> finito */
418 }
419
420 if( updbm_flag ) {
421 if( write_bookmark( bmfile, &nbm ) ) return RET_ERROR;
422 }
423
424 /* if in Nagios3 mode: prepend short message (the last line fetched) */
425 if( conv_G & CONV_NAGIOS3 ) {
426 /* Nagios2 --> Nagios3 changed accepted format for plugin output.
427 * Nagios2 accepted a line containing one or more '\'+'n' as a single
428 * line. Nagios3 now supports multiline output, and as a result, lines
429 * containing '\'+'n' are now handled as multiline messages (as well as
430 * messages having '\n'). The new multiline format is:
431 * SHORT_MESSAGE | OPTIONAL_PERFORMANCE_DATA
432 * LONG_MESSAGE_LINE_1
433 * LONG_MESSAGE_LINE_2
434 * ...
435 * LONG_MESSAGE_LINE_N
436 *
437 * In Nagios3 mode fetchlog copies LONG_MESSAGE_LINE_N as SHORT_MESSAGE
438 * and leaves OPTIONAL_PERFORMANCE_DATA empty
439 */
440
441 int oidx = fetchlen_G - 4; /* in obuf: last char, last line */
442 lastlinepos = oidx + 1; /* fallback value: empty line at end */
443 lastlinelen = 0; /* fallback value: empty line at end */
444
445 /* determine lastlinepos and lastlinelen */
446 while( oidx > 0 ) {
447 if( *(obuf + oidx) == 'n' && *(obuf + oidx -1) == '\\' ) {
448 lastlinepos = oidx + 1;
449 lastlinelen = fetchlen_G - lastlinepos - 3; /* 3: \ n \n */
450 break;
451 }
452 oidx--;
453 }
454
455 /* case: obuf has enough room for SHORT_MESSAGE */
456 if( lastlinelen + 1 <= opos ) { /* +1 = '|' */
457 *(obuf + --opos) = '|';
458 memmove( obuf+opos-lastlinelen, obuf+lastlinepos, lastlinelen );
459 opos -= lastlinelen;
460
461 /* case: obuf too small: SHORT_MESSAGE and fetched messages overlap,
462 * but not LONG_MESSAGE_LINE_N */
463 }else if( lastlinelen + 6 <= lastlinepos ) { /* +6 = '|\n...' */
464 memmove( obuf, obuf+lastlinepos, lastlinelen );
465 *(obuf + lastlinelen + 0 ) = '|';
466 *(obuf + lastlinelen + 1 ) = '\\';
467 *(obuf + lastlinelen + 2 ) = 'n';
468 *(obuf + lastlinelen + 3 ) = '.';
469 *(obuf + lastlinelen + 4 ) = '.';
470 if( *(obuf + lastlinelen + 5 ) != '\\' ) {
471 *(obuf + lastlinelen + 5 ) = '.';
472 }
473 opos = 0;
474
475 /* case: obuf too small: SHORT_MESSAGE and fetched messages overlap,
476 * including LONG_MESSAGE_LINE_N */
477 }else{
478 memmove( obuf+lastlinepos+2, obuf+lastlinepos, lastlinelen );
479 opos = lastlinepos + 2;
480 }
481 }
482
483 /* only return a message if there is something to print */
484 if( ((conv_G & CONV_NEWLINE)==0 && fetchlen_G-opos==0 ) ||
485 ((conv_G & CONV_NEWLINE)!=0 &&
486 ( ((conv_G & CONV_NAGIOS3)== 0 && fetchlen_G-opos==3 ) ||
487 ((conv_G & CONV_NAGIOS3)!= 0 && fetchlen_G-opos==4 ) ) ) ) {
488
489 if( conv_G & CONV_OKMSG ) {
490 r = write( STDOUT_FILENO, OK_MESSAGE "\n", sizeof( OK_MESSAGE ) );
491 }
492 i = RET_OK;
493 }else{
494 r = write( STDOUT_FILENO, obuf+opos,fetchlen_G-opos);
495 i = RET_NEWMSG;
496 }
497
498 free( obuf ); free( lgfile );
499 return i;
500
501 } /* fetch_logfile() */
502
503
504 /************************************************
505 * copyline( int opos, char *obuf, char *ipt, int illen );
506 * trim new line (ipt, illen), copy it to obuf, convert it, return new opos
507 ************************************************
508 */
copyline(int opos,char * obuf,char * ipt,int illen)509 int copyline( int opos, char *obuf, char *ipt, int illen ) {
510
511 char *p = NULL;
512 int len = 0;
513 int i;
514 #ifdef HAS_REGEX
515 int rxerr = 0;
516 char rxbuf[MAX_LASTCOL+1]; /* +1 for extra '\0' */
517
518 if( numrx_G > 0 ) {
519 /* we have to copy the line to another buffer because regexec()
520 wants a c-string, and we only have a \n terminated line in ipt.
521 Some platforms (e.g. FreeBSD) have a non-portable extension in
522 regcomp() which allows to regex non-null terminated string slices,
523 but to be portable...
524 */
525 if( illen <= firstcol_G ) {
526 rxbuf[0] = '\0';
527 }else{
528 if( illen > lastcol_G ) len = lastcol_G;
529 else len = illen - 1;
530 len -= firstcol_G - 1;
531 memcpy( rxbuf, ipt+firstcol_G-1, len );
532 if( illen-1 > lastcol_G ) rxbuf[len-1] = '~';
533 rxbuf[len] = '\0';
534 }
535
536 for( i=0; i<numrx_G; i++ ) {
537 rxerr = regexec( &(rx_G[i]), rxbuf, (size_t)0, NULL, 0);
538 if( rxerr == 0 ) break; /* match */
539 /* else: no match (rxerr==REG_NOMATCH) or an error (ignored) */
540 }
541 if( rxerr != 0 ) return opos;
542 }
543 #endif
544
545 if( conv_G & CONV_NEWLINE ) {
546 /* fill obuf:
547 prepend concat( '\\' + 'n' + iline ) (newline sequence first) */
548 if( opos > 2 ) {
549 if( illen <= firstcol_G ) {
550 *(obuf+opos-1) = 'n'; *(obuf+opos-2) = '\\';
551 opos -= 2;
552 }else{
553 if( illen > lastcol_G ) len = lastcol_G;
554 else len = illen - 1;
555 len -= firstcol_G - 1;
556 if( len+2 > opos ) {
557 memcpy( obuf+2, ipt+firstcol_G-1+len+2-opos, opos-2 );
558 len = opos - 2;
559 }else{
560 memcpy( obuf+opos-len, ipt+firstcol_G-1, len );
561 }
562 if( illen-1 > lastcol_G ) *(obuf+opos-1) = '~';
563 opos -= len+2;
564 *(obuf+opos+1) = 'n';
565 *(obuf+opos+0) = '\\';
566 }
567 }else{
568 opos = 0;
569 }
570 if( opos==0 ) {
571 p = obuf;
572 *p++='\\'; *p++='n'; *p++='.'; *p++='.'; *p++='.';
573 if( obuf[5]=='n' ) { *p++='.'; }
574 }
575 }else{
576 /* without newline conversion */
577
578 /* fill obuf:
579 prepend concat( iline + '\n' ) (newline char last) */
580 if( opos > 1 ) {
581 if( illen <= firstcol_G ) {
582 *(obuf+opos-1) = '\n';
583 opos -= 1;
584 }else{
585 if( illen > lastcol_G ) len = lastcol_G;
586 else len = illen - 1;
587 len -= firstcol_G - 1;
588 if( len+1 > opos ) {
589 memcpy( obuf, ipt+firstcol_G-1+len+1-opos, opos-1 );
590 len = opos - 1;
591 }else{
592 memcpy( obuf+opos-len-1, ipt+firstcol_G-1, len );
593 }
594 *(obuf+opos-1) = '\n';
595 if( illen-1 > lastcol_G ) *(obuf+opos-2) = '~';
596 opos -= len+1;
597 }
598 }else{
599 opos = 0;
600 }
601 if( opos==0 ) {
602 p = obuf;
603 *p++='.'; *p++='.'; *p++='.';
604 }
605 }
606
607 p = obuf+opos;
608 if( conv_G & CONV_NEWLINE ) p += 2; /* +2: skip '\\' 'n' */
609
610 if( conv_G & CONV_PERCENT ) {
611 for( i=0; i<len; i++ ) {
612 if( *(p+i) == '%' ) *(p+i) = 'p';
613 }
614 }
615
616 if( conv_G & CONV_BRACKET ) {
617 for( i=0; i<len; i++ ) {
618 if( *(p+i) == '<' ) *(p+i) = '(';
619 else if( *(p+i) == '>' ) *(p+i) = ')';
620 }
621 }
622
623 if( conv_G & CONV_SHELL ) {
624 for( i=0; i<len; i++ ) {
625 if( *(p+i) == '$' ) *(p+i) = '_';
626 else if( *(p+i) == '\'' ) *(p+i) = '_';
627 else if( *(p+i) == '\"' ) *(p+i) = '_';
628 else if( *(p+i) == '`' ) *(p+i) = '_';
629 else if( *(p+i) == '^' ) *(p+i) = '_';
630 else if( *(p+i) == '\\' ) *(p+i) = '/';
631 else if( *(p+i) == '|' ) *(p+i) = '_';
632 }
633 }
634
635 return opos;
636
637 } /* copyline() */
638
639
640 /************************************************
641 * check_farg( char *farg )
642 * check if -f arg is in proper format for sccanf(): nnn:nnn:nnnn:XXXXX
643 ************************************************
644 */
check_farg(char * farg,int * conv)645 int check_farg( char *farg, int *conv ) {
646 char *pt = farg;
647 int numdig = 0;
648 int numc = 0;
649
650 *conv = 0;
651
652 for( numdig=0 ; *pt; pt++ ) {
653 if( isdigit( (int) *pt ) ) numdig++;
654 else if( *pt==':' ) break;
655 else return 1;
656 }
657 if( numdig <1 || numdig > 3 ) return 1;
658
659 for( pt++,numdig=0 ; *pt; pt++ ) {
660 if( isdigit( (int) *pt ) ) numdig++;
661 else if( *pt==':' ) break;
662 else return 1;
663 }
664 if( numdig <1 || numdig > 3 ) return 1;
665
666 for( pt++,numdig=0 ; *pt; pt++ ) {
667 if( isdigit( (int) *pt ) ) numdig++;
668 else if( *pt==':' ) break;
669 else return 1;
670 }
671 if( numdig <1 || numdig > 5 ) return 1;
672
673 for( pt++,numc=0 ; *pt; pt++ ) {
674 if ( *pt=='b' ) { *conv |= CONV_BRACKET; numc++; }
675 else if( *pt=='p' ) { *conv |= CONV_PERCENT; numc++; }
676 else if( *pt=='n' ) { *conv |= CONV_NEWLINE; numc++; }
677 else if( *pt=='o' ) { *conv |= CONV_OKMSG; numc++; }
678 else if( *pt=='s' ) { *conv |= CONV_SHELL; numc++; }
679 else if( *pt=='3' ) { *conv |= CONV_NAGIOS3; numc++; }
680 else return 1;
681 }
682 if( numc > 6 ) return 1;
683 return 0; /* ok */
684
685 } /* check_farg() */
686
687 /*************************************************/
usage(void)688 void usage( void ) {
689
690 printf(
691 "fetchlog - fetch the last new log messages - version %s\n\n"
692 " usage 1: fetchlog -f firstcol:lastcol:len:conv logfile bmfile [pattern ...]\n"
693 " usage 2: fetchlog -F firstcol:lastcol:len:conv logfile bmfile [pattern ...]\n"
694 " usage 3: fetchlog [ -V | -h ] \n\n"
695
696 "1: Read all messages of <logfile> [matching any of regex <pattern>] which are\n"
697 "newer than the bookmark in <bmfile>. Print at most <len> bytes from column\n"
698 "<firstcol> upto column <lastcol> to stdout. Adds '...' characters when skip-\n"
699 "ping old lines or '~' when cutting long lines. <conv> sets output conversion:\n"
700 " 'b': convert < and > to ( and ) for safe HTML output\n"
701 " 'p': convert %% to p for safe printf output\n"
702 " 's': convert $'\"`^\\| to _____/_ for safe shell parameter input\n"
703 " 'n': convert newline to \\n for single line output\n"
704 " 'o': show '%s' message if no new messages\n"
705 " '3': output in format compatible with Nagios3 plugins (enables 'no')\n"
706 " 0 < <firstcol> < <lastcol> < %d\n"
707 " <lastcol> - <firstcol> > %d \n"
708 " <len> valid range %d..%d\n"
709 " <conv> is zero or more of 'bpsno3' \n"
710 " <logfile> absolute path to logfile\n"
711 " <bmfile> absolute path to bookmarkfile\n"
712 "2: like 1 and update <bmfile> to remember fetched messages as 'read'\n"
713 "3: print version (-V) or print this help message (-h) \n"
714 , FL_VERSION,
715 OK_MESSAGE,MAX_LASTCOL+1,MIN_COLS-1,MIN_FETCHLEN, MAX_FETCHLEN
716 );
717
718 } /* usage() */
719
720 /*************************************************/
721
main(int argc,char ** argv)722 int main(int argc, char **argv)
723 {
724 int ret=0;
725 int i;
726 int rxerr;
727
728 /* check args */
729 if (argc == 2) {
730 if( argv[1][0] == '-' && argv[1][1] == 'V' ) {
731 printf( "fetchlog version %s \n", FL_VERSION );
732 exit( RET_PARAM );
733 }
734 }else if (argc >= 5) {
735 if( argv[1][0] == '-' &&
736 argv[3][0] == '/' && isalpha((int)argv[3][1]) &&
737 argv[4][0] == '/' && isalpha((int)argv[4][1]) ) {
738 if( argv[1][1] == 'f' || argv[1][1] == 'F' ) {
739 if( check_farg( argv[2], &conv_G ) ) {
740 perr( "invalid parameter", "firstcol, lastcol, len or conv", 0 );
741 exit( RET_PARAM );
742 }
743 ret = sscanf( argv[2], "%3d:%3d:%5d",
744 &firstcol_G, &lastcol_G, &fetchlen_G );
745 if( ret != 3 ) {
746 perr( "invalid parameter", NULL, 0 );
747 exit( RET_PARAM );
748 }
749 if( conv_G & CONV_NAGIOS3 ) {
750 /* nagios3: auto enable conversion 'okmsg' and 'newline' */
751 conv_G |= (CONV_NEWLINE | CONV_OKMSG);
752 }
753 if( firstcol_G >= MIN_FIRSTCOL &&
754 lastcol_G >= firstcol_G + MIN_COLS - 1 &&
755 lastcol_G <= MAX_LASTCOL &&
756 fetchlen_G >= MIN_FETCHLEN &&
757 fetchlen_G <= MAX_FETCHLEN ) {
758
759 /* prepare regex, if any */
760 if( argc > 5 ) {
761 #ifdef HAS_REGEX
762 numrx_G = argc - 5;
763 rx_G = (regex_t*)malloc(numrx_G*sizeof(regex_t));
764 if( rx_G == NULL ) {
765 perr("malloc", NULL, errno );
766 exit( RET_ERROR );
767 }
768 for( i=5; i<argc; i++ ) {
769 rxerr = regcomp( &(rx_G[i-5]), argv[i],
770 REG_EXTENDED | REG_NOSUB );
771 if( rxerr ) {
772 regerror( rxerr, &(rx_G[i-5]),
773 rxerrbuf_G, RXERRBUFLEN );
774 perr("regex", rxerrbuf_G, 0 );
775 exit( RET_ERROR );
776 }
777 }
778 #else
779 i = 0; rxerr = 0;
780 perr("regex", "not supported on this platform", 0 );
781 exit( RET_ERROR );
782 #endif
783 }
784 if( argv[1][1] == 'f' ) {
785 exit( fetch_logfile( argv[3], argv[4], 0 ) );
786 }else{
787 exit( fetch_logfile( argv[3], argv[4], 1 ) );
788 }
789 }else{
790 perr( "out of range: firstcol, lastcol or len", NULL, 0 );
791 exit( RET_PARAM );
792 }
793 }
794 }
795 }
796 usage();
797 return RET_PARAM;
798
799 } /* main() */
800
801 /*
802 * CVS/RCS Log:
803 * $Log: fetchlog.c,v $
804 * Revision 1.9 2010/07/01 16:39:25 afrika
805 * - bugfix: wrong exitcode when fetching with regex and logfile has changed
806 * - new: test-all and make file target for this
807 *
808 * Revision 1.8 2010/06/18 18:17:47 afrika
809 * - new option '3' for nagios3 compatible (multiline) output
810 * - Added sample configs for Nagios 2 + 3
811 * - Updated README.Nagios to Nagios 2 + 3
812 * - Scanning for rotated logfiles now silently skips over to .1 if
813 * .0 is missing because some rotation tools start indexing with .1
814 * - fixed: compiler warnings on Debian Linux
815 *
816 * Revision 1.7 2008/11/21 19:58:51 afrika
817 * - update docs
818 * - now '|' is shell critical character also
819 * - added Greg Baker's files for building a .depot on hpux
820 *
821 * Revision 1.6 2004/03/26 19:46:03 afrika
822 * added regex pattern matching
823 *
824 * Revision 1.5 2003/11/19 15:24:19 afrika
825 * only return "new message" if there is at least one char to put out
826 *
827 * Revision 1.4 2003/11/18 18:44:23 afrika
828 * - removed compile option pre 0.93 exit codes
829 * - no longer use copy of last line fetched to find bookmark location
830 * - use inode to identify a (rotated) logfile instead
831 * - handling of empty (and rotated) logfiles now correct
832 *
833 * Revision 1.3 2002/12/17 18:40:05 afrika
834 * exit code now nagios compatible
835 *
836 * Revision 1.2 2002/12/17 18:04:48 afrika
837 * - inserted CVS tags
838 * - change docs: Netsaint --> Nagios(TM)
839 *
840 *
841 */
842
843