1 
2 #include "config_xor.h"
3 
4 #include <stdio.h>
5 #include <string.h>
6 #include <stdlib.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <fcntl.h>
10 
11 #ifdef HAVE_DIRENT_H
12 #include <dirent.h>
13 #define NAMLEN(dirent) sl_strlen((dirent)->d_name)
14 #else
15 #define dirent direct
16 #define NAMLEN(dirent) (dirent)->d_namlen
17 #ifdef HAVE_SYS_NDIR_H
18 #include <sys/ndir.h>
19 #endif
20 #ifdef HAVE_SYS_DIR_H
21 #include <sys/dir.h>
22 #endif
23 #ifdef HAVE_NDIR_H
24 #include <ndir.h>
25 #endif
26 #endif
27 
28 #if !defined(O_NONBLOCK)
29 #if defined(O_NDELAY)
30 #define O_NONBLOCK  O_NDELAY
31 #else
32 #define O_NONBLOCK  0
33 #endif
34 #endif
35 
36 #ifdef USE_LOGFILE_MONITOR
37 
38 #undef  FIL__
39 #define FIL__  _("sh_log_check.c")
40 
41 /* Debian/Ubuntu: libpcre3-dev */
42 #ifdef HAVE_PCRE_PCRE_H
43 #include <pcre/pcre.h>
44 #else
45 #include <pcre.h>
46 #endif
47 
48 #include "samhain.h"
49 #include "sh_pthread.h"
50 #include "sh_utils.h"
51 #include "sh_unix.h"
52 #include "sh_string.h"
53 #include "sh_log_check.h"
54 #include "sh_log_evalrule.h"
55 #include "sh_log_correlate.h"
56 #include "sh_log_mark.h"
57 #include "sh_log_repeat.h"
58 #include "sh_extern.h"
59 
60 /* List of supported logfile types, format is
61  * {
62  *   "TYPE_CODE",
63  *   Reader_Callback_Function,
64  *   Parser_Callback_function,
65  *   Evaluate_Callback_Function
66  * }
67  * If Reader_Callback_Function is NULL, the default (line-oriented
68  * text file) reader is used.
69  */
70 struct sh_logfile_type sh_logtypes_def[] = {
71     {  "SYSLOG", NULL,            sh_parse_syslog, NULL },
72     {  "SAMBA",  sh_read_samba,   sh_parse_samba,  NULL },
73     {  "APACHE", NULL,            sh_parse_apache, sh_eval_fileinfo_apache },
74 #if defined(HAVE_SYS_ACCT_H)
75     {  "PACCT",  sh_read_pacct,   sh_parse_pacct,  NULL },
76 #endif
77     {  "SHELL",  sh_read_shell,   sh_parse_shell,  NULL },
78 };
79 
80 /* -------------------------- Internal Stuff -------------------------- */
81 
82 struct logfile_record {
83   dev_t  device_id;
84   ino_t  inode;
85   fpos_t offset;
86 };
87 
88 static int    do_checkpoint_cleanup = S_FALSE;
89 
90 static char * save_dir = NULL;
91 
get_save_dir(void)92 static const char * get_save_dir(void)
93 {
94   int retval;
95 
96   if (!save_dir)
97     {
98       save_dir = sh_util_strdup(DEFAULT_DATAROOT);
99 
100       SH_MUTEX_LOCK(mutex_thread_nolog);
101       retval = tf_trust_check (save_dir, SL_YESPRIV);
102       SH_MUTEX_UNLOCK(mutex_thread_nolog);
103 
104       if (retval != 0)
105 	{
106 	  return(NULL);
107 	}
108     }
109   return save_dir;
110 }
111 
clean_dir()112 static void clean_dir()
113 {
114   DIR * dir;
115   struct dirent * entry;
116 
117   const char * dirpath;
118 
119   if (S_FALSE == do_checkpoint_cleanup)
120     return;
121 
122   dirpath = get_save_dir();
123 
124   if (dirpath)
125     {
126       dir = opendir(dirpath);
127       if (dir)
128 	{
129 	  unsigned long a, b;
130 	  int retval;
131 	  char c;
132 	  size_t dlen = strlen(dirpath) + 1;
133 	  time_t now  = time(NULL);
134 
135 	  while (NULL != (entry = readdir(dir)))
136 	    {
137 	      retval = sscanf(entry->d_name, "%lu_%lu%c", &a, &b, &c);
138 
139 	      if (2 == retval)
140 		{
141 		  struct stat buf;
142 		  char * path;
143 		  size_t  plen = strlen(entry->d_name) + 1;
144 
145 		  if (S_TRUE == sl_ok_adds(plen, dlen))
146 		    {
147 		      plen += dlen;
148 		      path = SH_ALLOC(plen);
149 		      (void) sl_snprintf(path, plen, "%s/%s",
150 					 dirpath,entry->d_name);
151 
152 		      if (0 == retry_lstat(FIL__, __LINE__, path, &buf) &&
153 			  S_ISREG(buf.st_mode))
154 			{
155 			  if (buf.st_mtime < now &&
156 			      (now - buf.st_mtime) > 2592000) /* 30 days */
157 			    {
158 			      if (0 == tf_trust_check (path, SL_YESPRIV))
159 				{
160 				  unlink(path);
161 				}
162 			    }
163 			}
164 		    }
165 		}
166 	    }
167 	  closedir(dir);
168 	}
169     }
170 }
171 
build_path(struct sh_logfile * record)172 static char * build_path (struct sh_logfile * record)
173 {
174   size_t plen;
175   char * path = NULL;
176   const char * dir = get_save_dir();
177 
178   if (dir)
179     {
180       plen = strlen(dir);
181 
182       if (S_TRUE == sl_ok_adds(plen, 130))
183 	{
184 	  plen += 130; /* 64 + 64 + 2 */
185 	  path = SH_ALLOC(plen);
186 	  (void) sl_snprintf(path, plen, "%s/%lu_%lu", dir,
187 			     (unsigned long) record->device_id,
188 			     (unsigned long) record->inode);
189 	}
190     }
191 
192   return path;
193 }
194 
save_pos(struct sh_logfile * record)195 static void save_pos (struct sh_logfile * record)
196 {
197   char * path;
198   FILE * fd;
199   mode_t mask;
200   struct logfile_record save_rec;
201 
202   path = build_path(record);
203 
204   if (path)
205     {
206       if (0 != sh_unix_check_piddir (path))
207 	{
208 	  SH_FREE(path);
209 	  return;
210 	}
211 
212       mask = umask(S_IWGRP | S_IWOTH);
213       fd = fopen(path, "wb");
214       (void) umask(mask);
215 
216       if (fd)
217 	{
218 	  save_rec.device_id = record->device_id;
219 	  save_rec.inode     = record->inode;
220 	  memcpy(&(save_rec.offset), &(record->offset), sizeof(fpos_t));
221 	  if (1 != fwrite(&save_rec, sizeof(struct logfile_record), 1, fd))
222 	    {
223 	      (void) sl_fclose(FIL__, __LINE__, fd);
224 	      (void) remove(path);
225 	    }
226 	  else
227 	    {
228 	      (void) sl_fclose(FIL__, __LINE__, fd);
229 	    }
230 	}
231       SH_FREE(path);
232     }
233   return;
234 }
235 
read_pos(struct sh_logfile * record)236 static int read_pos (struct sh_logfile * record)
237 {
238   int    retval = 0;
239   char * path;
240   FILE * fd;
241   struct logfile_record save_rec;
242 
243   path = build_path(record);
244 
245   if (path)
246     {
247       fd = fopen(path, "rb");
248       if (fd)
249 	{
250 	  if (1 == fread(&save_rec, sizeof(struct logfile_record), 1, fd))
251 	    {
252 	      if (save_rec.device_id == record->device_id &&
253 		  save_rec.inode     == record->inode)
254 		{
255 		  memcpy(&(record->offset),&(save_rec.offset),sizeof(fpos_t));
256 		  retval = 1;
257 		}
258 	    }
259 	  (void) sl_fclose(FIL__, __LINE__, fd);
260 	}
261       SH_FREE(path);
262     }
263   return retval;
264 }
265 
266 /*@null@*/ static struct sh_logfile * sh_watched_logs = NULL;
267 
sh_add_watch(const char * str)268 int sh_add_watch (const char * str)
269 {
270   char * filename;
271 
272   unsigned int    i;
273   unsigned int    defsize;
274   struct sh_logfile_type * log_type = NULL;
275   struct sh_logfile * thisfile;
276   struct stat buf;
277 
278   unsigned int nfields = 3; /* logtype:path[:regex] */
279   size_t       lengths[3];
280   char *       new = sh_util_strdup(str);
281   char **      splits = split_array(new, &nfields, ':', lengths);
282 
283   if (nfields < 2 || (lengths[0] == 0 || lengths[0] >= SH_MAX_LCODE_SIZE || lengths[1] == 0))
284     {
285       sh_string * msg =  sh_string_new(0);
286       sh_string_add_from_char(msg, _("Format error: "));
287       sh_string_add_from_char(msg, str);
288 
289       SH_MUTEX_LOCK(mutex_thread_nolog);
290       sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
291 		      sh_string_str(msg),
292 		      _("sh_add_watch"));
293       SH_MUTEX_UNLOCK(mutex_thread_nolog);
294       sh_string_destroy(&msg);
295 
296       SH_FREE(new);
297       return -2;
298     }
299 
300   defsize =
301     (unsigned int) (sizeof(sh_logtypes_def)/sizeof(struct sh_logfile_type));
302 
303   for (i = 0; i < defsize; ++i)
304     {
305       if (0 == strcmp(splits[0], sh_logtypes_def[i].code))
306 	{
307 	  log_type = &(sh_logtypes_def[i]);
308 	  break;
309 	}
310     }
311 
312   if (log_type == NULL)
313     {
314       sh_string * msg =  sh_string_new(0);
315       sh_string_add_from_char(msg, _("Unsupported log type: "));
316       sh_string_add_from_char(msg, splits[0]);
317 
318       SH_MUTEX_LOCK(mutex_thread_nolog);
319       sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
320 		      sh_string_str(msg),
321 		      _("sh_add_watch"));
322       SH_MUTEX_UNLOCK(mutex_thread_nolog);
323       sh_string_destroy(&msg);
324 
325       SH_FREE(new);
326       return -3;
327     }
328 
329   if (splits[1][0] != '/' && 0 != strcmp(splits[0], _("SHELL")))
330     {
331       sh_string * msg =  sh_string_new(0);
332       sh_string_add_from_char(msg, _("Logfile path not absolute: "));
333       sh_string_add_from_char(msg, splits[1]);
334 
335       SH_MUTEX_LOCK(mutex_thread_nolog);
336       sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
337 		      sh_string_str(msg),
338 		      _("sh_add_watch"));
339       SH_MUTEX_UNLOCK(mutex_thread_nolog);
340       sh_string_destroy(&msg);
341 
342       SH_FREE(new);
343       return -4;
344     }
345 
346   filename = /*@i@*/sh_util_strdup(splits[1]);
347   thisfile = SH_ALLOC(sizeof(struct sh_logfile));
348 
349   thisfile->filename     = filename;
350   if (0 == strcmp(splits[0], _("SHELL")))
351     thisfile->flags        = SH_LOGFILE_NOFILE;
352   else
353     thisfile->flags        = SH_LOGFILE_REWIND;
354   thisfile->inode        = 0;
355   thisfile->device_id    = 0;
356   thisfile->fp           = NULL;
357   if (log_type->get_record)
358     thisfile->get_record   = log_type->get_record;
359   else
360     thisfile->get_record   = sh_default_reader;
361   thisfile->parse_record = log_type->parse_record;
362 
363   /* An optional regex for parsing the file. The result
364    * 'fileinfo' should contain info about host/time position.
365    */
366   if (log_type->eval_fileinfo)
367     {
368       if (nfields == 3 && lengths[2] > 0)
369 	{
370 	  thisfile->fileinfo     = log_type->eval_fileinfo(splits[2]);
371 
372 	  if (thisfile->fileinfo == NULL)
373 	    {
374 	      sh_string * msg =  sh_string_new(0);
375 	      sh_string_add_from_char(msg, _("Logfile format description not recognized: "));
376 	      sh_string_add_from_char(msg, splits[2]);
377 
378 	      SH_MUTEX_LOCK(mutex_thread_nolog);
379 	      sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
380 			      sh_string_str(msg),
381 			      _("sh_add_watch"));
382 	      SH_MUTEX_UNLOCK(mutex_thread_nolog);
383 	      sh_string_destroy(&msg);
384 
385 	      SH_FREE(filename);
386 	      SH_FREE(thisfile);
387 	      SH_FREE(new);
388 	      return -1;
389 	    }
390 	}
391       else
392 	{
393 	  sh_string * msg =  sh_string_new(0);
394 	  sh_string_add_from_char(msg, _("Logfile format description missing: "));
395 	  sh_string_add_from_char(msg, splits[1]);
396 
397 	  SH_MUTEX_LOCK(mutex_thread_nolog);
398 	  sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
399 			  sh_string_str(msg),
400 			  _("sh_add_watch"));
401 	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
402 	  sh_string_destroy(&msg);
403 
404 	  SH_FREE(filename);
405 	  SH_FREE(thisfile);
406 	  SH_FREE(new);
407 	  return -1;
408 	}
409     }
410   else
411     {
412       thisfile->fileinfo     = NULL;
413     }
414   thisfile->next         = sh_watched_logs;
415 
416   /* Try reading saved offset. On success clear rewind flag.
417    */
418   if ((thisfile->flags & SH_LOGFILE_NOFILE) == 0)
419     {
420       if (0 == stat(thisfile->filename, &buf))
421 	{
422 	  if (S_ISREG(buf.st_mode)
423 #ifdef S_ISLNK
424 	      || S_ISLNK(buf.st_mode)
425 #endif
426 	      )
427 	    {
428 	      thisfile->inode     = buf.st_ino;
429 	      thisfile->device_id = buf.st_dev;
430 
431 	      if (0 != read_pos(thisfile))
432 		{
433 		  thisfile->flags &= ~SH_LOGFILE_REWIND;
434 		}
435 	    }
436 	  else if (S_ISFIFO(buf.st_mode))
437 	    {
438 	      thisfile->inode      = buf.st_ino;
439 	      thisfile->device_id  = buf.st_dev;
440 	      thisfile->flags     |= SH_LOGFILE_PIPE;
441 	    }
442 	}
443       else
444 	{
445 	  sh_string * msg =  sh_string_new(0);
446 	  sh_string_add_from_char(msg, _("Logfile is not a regular file, link, or named pipe: "));
447 	  sh_string_add_from_char(msg, splits[1]);
448 
449 	  SH_MUTEX_LOCK(mutex_thread_nolog);
450 	  sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
451 			  sh_string_str(msg),
452 			  _("sh_add_watch"));
453 	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
454 	  sh_string_destroy(&msg);
455 
456 	  SH_FREE(filename);
457 	  SH_FREE(thisfile);
458 	  SH_FREE(new);
459 	  return -1;
460 	}
461     }
462 
463   sh_watched_logs        = thisfile;
464 
465   SH_FREE(new);
466   return 0;
467 }
468 
sh_dump_watches()469 void sh_dump_watches()
470 {
471   struct sh_logfile * thisfile;
472 
473   while (sh_watched_logs)
474     {
475       thisfile        = sh_watched_logs;
476       sh_watched_logs = thisfile->next;
477 
478       if ((thisfile->flags & SH_LOGFILE_NOFILE) == 0 &&
479 	  (thisfile->flags & SH_LOGFILE_PIPE) == 0)
480 	{
481 	  save_pos(thisfile);
482 	}
483 
484       if ((thisfile->flags & SH_LOGFILE_NOFILE) == 0)
485 	{
486 	  if (thisfile->fp)
487 	    sl_fclose(FIL__, __LINE__, thisfile->fp);
488 	}
489 
490       if (thisfile->filename)
491 	SH_FREE(thisfile->filename);
492       SH_FREE(thisfile);
493     }
494   return;
495 }
496 
497 /* This variable is not used anywhere. It only exist
498  * to assign &new to them, which keeps gcc from
499  * putting it into a register, and avoids the 'clobbered
500  * by longjmp' warning. And no, 'volatile' proved insufficient.
501  */
502 void * sh_dummy_502_thisfile = NULL;
503 
sh_check_watches()504 void sh_check_watches()
505 {
506   struct sh_logrecord * logrecord;
507   struct sh_logfile * thisfile = sh_watched_logs;
508   sh_string * record = sh_string_new(0);
509   char * tmp;
510 
511   /* Take the address to keep gcc from putting them into registers.
512    * Avoids the 'clobbered by longjmp' warning.
513    */
514   sh_dummy_502_thisfile = (void*) &thisfile;
515 
516   while (thisfile)
517     {
518       volatile size_t count = 0;
519 
520       SH_MUTEX_LOCK(mutex_thread_nolog);
521       tmp = sh_util_safe_name (thisfile->filename);
522       sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_CHKS,
523 		      tmp);
524       SH_FREE(tmp);
525       SH_MUTEX_UNLOCK(mutex_thread_nolog);
526 
527       for (;;) {
528 
529 	record = thisfile->get_record(record, thisfile);
530 
531 	if (record)
532 	  {
533 	    logrecord = thisfile->parse_record(record, thisfile->fileinfo);
534 	    ++count;
535 
536 	    if (logrecord)
537 	      {
538 		logrecord->filename = thisfile->filename;
539 
540 		/* Don't report if 'init', just set file pointer
541 		 */
542 		if (sh.flag.checkSum != SH_CHECK_INIT)
543 		  {
544 		    sh_eval_process_msg(logrecord);
545 		  }
546 
547 		if (logrecord->message)
548 		  sh_string_destroy(&(logrecord->message));
549 		if (logrecord->host)
550 		  sh_string_destroy(&(logrecord->host));
551 		if (logrecord->timestr)
552 		  sh_string_destroy(&(logrecord->timestr));
553 		SH_FREE(logrecord);
554 	      }
555 	  }
556 	else
557 	  {
558 	    record = sh_string_new(0);
559 	    break;
560 	  }
561       }
562 
563       SH_MUTEX_LOCK(mutex_thread_nolog);
564       tmp = sh_util_safe_name (thisfile->filename);
565       sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_CHKE,
566 		      tmp, (unsigned long)count);
567       SH_FREE(tmp);
568       SH_MUTEX_UNLOCK(mutex_thread_nolog);
569 
570       thisfile = thisfile->next;
571     }
572   sh_string_destroy(&record);
573   return;
574 }
575 
576 /********************************************************
577  * Search rotated logfile
578  */
579 extern char * sh_rotated_log_search(const char * path, struct stat * buf);
580 
581 
582 /* Open file, position at stored offset
583  */
sh_open_for_reader(struct sh_logfile * logfile)584 int sh_open_for_reader (struct sh_logfile * logfile)
585 {
586   struct stat buf;
587   sh_string * filename;
588 
589   /* check whether file exists, get inode to check for
590    * logfile rotation
591    */
592   if (0 != retry_stat(FIL__, __LINE__, logfile->filename, &buf))
593     {
594       char * tmp;
595 
596       SH_MUTEX_LOCK(mutex_thread_nolog);
597       tmp = sh_util_safe_name (logfile->filename);
598       sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_MISS,
599 		      tmp);
600       SH_FREE(tmp);
601       SH_MUTEX_UNLOCK(mutex_thread_nolog);
602 
603       memset (&(logfile->offset), 0, sizeof(fpos_t));
604       logfile->flags |= SH_LOGFILE_REWIND;
605       return 0;
606     }
607 
608   filename = sh_string_new(0);
609   (void) sh_string_set_from_char (filename, logfile->filename);
610 
611   /* detect and handle logfile rotation
612    */
613   if (logfile->inode != buf.st_ino &&
614       logfile->inode != 0 &&
615       !S_ISFIFO(buf.st_mode))
616     {
617       /* Case 1) We have dealt with the moved file already.
618        *         Clear the moved flag, set the rewind flag,
619        *         fix logfile->inode.
620        */
621       if ((logfile->flags & SH_LOGFILE_MOVED) != 0)
622 	{
623 	  /* done with rotated file, start with current file
624 	   */
625 	  memset (&(logfile->offset), 0, sizeof(fpos_t));
626 	  logfile->flags    |= SH_LOGFILE_REWIND;
627 	  logfile->flags    &= ~SH_LOGFILE_MOVED;
628 	  logfile->inode     = buf.st_ino;
629 	  logfile->device_id = buf.st_dev;
630 	}
631 
632       /* Case 2) Searching for rotated file.
633        *         If found:     set the moved flag, fix path for fopen.
634        *         If not found: set the rewind flag, fix logfile->inode.
635        */
636       else
637 	{
638 	  char *oldfile = sh_rotated_log_search(logfile->filename, &buf);
639 
640 	  if (NULL != oldfile)
641 	    {
642 	      (void) sh_string_set_from_char (filename, oldfile);
643 	      SH_FREE(oldfile);
644 	      logfile->flags |= SH_LOGFILE_MOVED;
645 	    }
646 	  else
647 	    {
648 	      memset (&(logfile->offset), 0, sizeof(fpos_t));
649 	      logfile->flags    |= SH_LOGFILE_REWIND;
650 	      logfile->inode     = buf.st_ino;
651 	      logfile->device_id = buf.st_dev;
652 	    }
653 	}
654     }
655 
656   /* open file
657    */
658   if (!S_ISFIFO(buf.st_mode))
659     {
660       logfile->fp = fopen(filename->str, "r");
661     }
662   else
663     {
664       int fd_temp = open (filename->str, O_RDONLY|O_NONBLOCK);
665 
666       if (fd_temp >= 0)
667 	{
668 	  logfile->fp = fdopen(fd_temp, "r");
669 	}
670     }
671 
672   if (!logfile->fp)
673     {
674       char * tmp;
675 
676       SH_MUTEX_LOCK(mutex_thread_nolog);
677       tmp = sh_util_safe_name (logfile->filename);
678       sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_EOPEN,
679 		      tmp);
680       SH_FREE(tmp);
681       SH_MUTEX_UNLOCK(mutex_thread_nolog);
682 
683       sh_string_destroy(&filename);
684       return 0;
685     }
686 
687   sh_string_destroy(&filename);
688 
689   if ((logfile->flags & SH_LOGFILE_PIPE) == 0)
690     {
691       if ((logfile->flags & SH_LOGFILE_REWIND) != 0)
692 	{
693 	  rewind(logfile->fp);
694 	  fgetpos(logfile->fp, &(logfile->offset));
695 	  logfile->flags &= ~SH_LOGFILE_REWIND;
696 	}
697       else
698 	{
699 	  /* file too short
700 	   */
701 	  if (0 != fsetpos(logfile->fp, &(logfile->offset)))
702 	    {
703 	      rewind(logfile->fp);
704 	      fgetpos(logfile->fp, &(logfile->offset));
705 	    }
706 	}
707     }
708 
709   return 1;
710 }
711 
712 /******************************************************
713  *  Default reader for ascii text files
714  */
sh_default_reader(sh_string * s,struct sh_logfile * logfile)715 sh_string * sh_default_reader (sh_string * s, struct sh_logfile * logfile)
716 {
717   volatile int         status;
718   char * tmp;
719 
720  start_read:
721 
722   if (logfile->fp)
723     {
724       /* Result cannot be larger than 8192, thus cast is ok
725        */
726       status = (int) sh_string_read(s, logfile->fp, 8192);
727       if (status <= 0)
728 	{
729 	  fgetpos(logfile->fp, &(logfile->offset));
730 	  sl_fclose(FIL__, __LINE__, logfile->fp);
731 	  logfile->fp = NULL;
732 	  sh_string_destroy(&s);
733 	  if (status == 0 || (logfile->flags & SH_LOGFILE_PIPE) != 0)
734 	    {
735 	      return NULL;
736 	    }
737 
738 	  SH_MUTEX_LOCK(mutex_thread_nolog);
739 	  tmp = sh_util_safe_name (logfile->filename);
740 	  sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_EREAD,
741 			  tmp);
742 	  SH_FREE(tmp);
743 	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
744 
745 	  return NULL;
746 	}
747       return s;
748     }
749 
750   if (0 != sh_open_for_reader(logfile))
751     goto start_read;
752 
753   return NULL;
754 }
755 
756 struct task_entry
757 {
758   sh_tas_t task;
759   struct task_entry * next;
760 };
761 
762 static struct task_entry * tasklist = NULL;
763 
task_find(FILE * fp)764 static struct task_entry * task_find(FILE * fp)
765 {
766   struct task_entry * entry = tasklist;
767   while (entry)
768     {
769       if (entry->task.pipe == fp)
770 	return (entry);
771       entry = entry->next;
772     }
773   return NULL;
774 }
775 
task_remove(struct task_entry * task)776 static void task_remove(struct task_entry * task)
777 {
778   struct task_entry * entry = tasklist;
779   struct task_entry * prev  = tasklist;
780 
781   while (entry)
782     {
783       if (entry == task)
784 	{
785 	  if (entry == tasklist)
786 	    {
787 	      tasklist = entry->next;
788 	      SH_FREE(entry);
789 	      return;
790 	    }
791 	  else
792 	    {
793 	      prev->next = entry->next;
794 	      SH_FREE(entry);
795 	      return;
796 	    }
797 	}
798       prev  = entry;
799       entry = entry->next;
800     }
801   return;
802 }
803 
task_add(struct task_entry * entry)804 static void task_add(struct task_entry * entry)
805 {
806   entry->next = tasklist;
807   tasklist    = entry;
808   return;
809 }
810 
sh_command_reader(sh_string * s,struct sh_logfile * logfile)811 sh_string * sh_command_reader (sh_string * s, struct sh_logfile * logfile)
812 {
813   struct task_entry * entry;
814 
815   volatile int        status;
816   char * tmp;
817 
818  start_read:
819 
820   if (logfile->fp)
821     {
822       /* Result cannot be larger than 8192, thus cast is ok
823        */
824       status = (int) sh_string_read(s, logfile->fp, 8192);
825 
826       if (status <= 0)
827 	{
828 	  entry = task_find(logfile->fp);
829 	  sh_ext_pclose (&(entry->task));
830 	  task_remove(entry);
831 
832 	  logfile->fp = NULL;
833 	  sh_string_destroy(&s);
834 
835 	  if (status == 0)
836 	    {
837 	      return NULL;
838 	    }
839 
840 	  SH_MUTEX_LOCK(mutex_thread_nolog);
841 	  tmp = sh_util_safe_name (logfile->filename);
842 	  sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_EREAD,
843 			  tmp);
844 	  SH_FREE(tmp);
845 	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
846 
847 	  return NULL;
848 	}
849       return s;
850     }
851 
852   entry = SH_ALLOC(sizeof(struct task_entry));
853 
854   status = sh_ext_popen_init (&(entry->task), logfile->filename, logfile->filename, NULL);
855   if (0 == status)
856     {
857       task_add(entry);
858       logfile->fp = entry->task.pipe;
859       goto start_read;
860     }
861   else
862     {
863       SH_FREE(entry);
864       SH_MUTEX_LOCK(mutex_thread_nolog);
865       sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, status, MSG_E_SUBGEN,
866                       _("Could not open pipe"), _("sh_command reader"));
867       SH_MUTEX_UNLOCK(mutex_thread_nolog);
868     }
869 
870   return NULL;
871 }
872 
873 /******************************************************
874  *  Reader for continued text files
875  */
sh_cont_reader(sh_string * s,struct sh_logfile * logfile,char * cont)876 sh_string * sh_cont_reader (sh_string * s, struct sh_logfile * logfile, char*cont)
877 {
878   int         status;
879   char      * tmp;
880   sh_string * str;
881   int         remain = 8192;
882   int         count  = 0;
883 
884   if (!sh_string_truncate(s, 0))
885     return NULL;
886 
887  start_read:
888 
889   if (logfile->fp)
890     {
891       str = sh_string_new(0);
892 
893       /* Result cannot be larger than 8192, thus cast is ok
894        */
895       status = (int) sh_string_read(str, logfile->fp, 8192);
896 
897       if (status > 0)
898 	{
899 
900 	  do {
901 	    s       = sh_string_add (s, str);
902 	    count  += status;
903 	    remain -= status;
904 
905 	    if (remain <= 0)
906 	      {
907 		return s;
908 	      }
909 
910 	    status = (int) sh_string_read_cont(str, logfile->fp, count, cont);
911 
912 	    if (status == 0)
913 	      {
914 		return s;
915 	      }
916 	  }
917 	  while (status > 0);
918 	}
919 
920       if (status <= 0)
921 	{
922 	  fgetpos(logfile->fp, &(logfile->offset));
923 	  sl_fclose(FIL__, __LINE__, logfile->fp);
924 	  logfile->fp = NULL;
925 	  sh_string_destroy(&s);
926 	  if (status == 0 || (logfile->flags & SH_LOGFILE_PIPE) != 0)
927 	    {
928 	      return NULL;
929 	    }
930 
931 	  SH_MUTEX_LOCK(mutex_thread_nolog);
932 	  tmp = sh_util_safe_name (logfile->filename);
933 	  sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_EREAD,
934 			  tmp);
935 	  SH_FREE(tmp);
936 	  SH_MUTEX_UNLOCK(mutex_thread_nolog);
937 
938 	  return NULL;
939 	}
940 
941       return s;
942     }
943 
944   if (0 != sh_open_for_reader(logfile))
945     goto start_read;
946 
947   return NULL;
948 }
949 
950 /******************************************************
951  *  Reader for binary files
952  */
sh_binary_reader(void * s,size_t size,struct sh_logfile * logfile)953 sh_string * sh_binary_reader (void * s, size_t size,
954 			      struct sh_logfile * logfile)
955 {
956   size_t         status;
957 
958  start_read:
959 
960   if (logfile->fp)
961     {
962 
963       status = fread(s, size, 1, logfile->fp);
964 
965       if (status != 1)
966 	{
967 	  memset(s, 0, size);
968 	  if (ferror(logfile->fp) && (logfile->flags & SH_LOGFILE_PIPE) == 0)
969 	    {
970 	      char * tmp;
971 	      SH_MUTEX_LOCK(mutex_thread_nolog);
972 	      tmp = sh_util_safe_name (logfile->filename);
973 	      sh_error_handle((-1), FIL__, __LINE__, errno, MSG_LOGMON_EREAD,
974 			      tmp);
975 	      SH_FREE(tmp);
976 	      SH_MUTEX_UNLOCK(mutex_thread_nolog);
977 	    }
978 	  fgetpos(logfile->fp, &(logfile->offset));
979 	  sl_fclose(FIL__, __LINE__, logfile->fp);
980 	  logfile->fp = NULL;
981 	  return NULL;
982 	}
983       return s;
984     }
985 
986   if (0 != sh_open_for_reader(logfile))
987     goto start_read;
988 
989   return NULL;
990 }
991 
992 
993 
994 /**********************************************************
995  *
996  * Utilities
997  *
998  **********************************************************/
999 
1000 /* Return current year, unless that would result
1001  * in a date far in the future. If that happens,
1002  * return last year.
1003  */
year_guess(struct tm * btime)1004 static int year_guess (struct tm * btime)
1005 {
1006   int           year;
1007   struct tm     ts;
1008   time_t        now    = time(NULL);
1009   time_t        check;
1010 
1011   memcpy(&ts, localtime(&now), sizeof(struct tm));
1012   year = ts.tm_year;
1013 
1014   /* Check result to detect year wrap
1015    * (logfile entry from last year).
1016    */
1017   btime->tm_year = year;
1018   check = mktime(btime);
1019   if (check > (now + (86400*30)))
1020     --year;
1021 
1022   return year;
1023 }
1024 
conv_timestamp(struct tm * btime,struct tm * old_tm,time_t * old_time)1025 time_t conv_timestamp (struct tm * btime,
1026 		       struct tm * old_tm, time_t * old_time)
1027 {
1028   time_t timestamp;
1029   long   offtime;
1030 
1031   /* timestamp - mktime is slooow, thus cache result
1032    */
1033   if (btime->tm_isdst == old_tm->tm_isdst &&
1034       btime->tm_year  == old_tm->tm_year  &&
1035       btime->tm_mon   == old_tm->tm_mon   &&
1036       btime->tm_mday  == old_tm->tm_mday)
1037     {
1038       offtime =
1039 	(btime->tm_hour - old_tm->tm_hour) * 3600 +
1040 	(btime->tm_min  - old_tm->tm_min)  * 60   +
1041 	(btime->tm_sec  - old_tm->tm_sec);
1042 
1043       *old_time += offtime;
1044       memcpy(old_tm, btime, sizeof(struct tm));
1045       timestamp = *old_time;
1046     }
1047   else
1048     {
1049       int year_btime = btime->tm_year;
1050 
1051       if (btime->tm_year == 0)
1052 	btime->tm_year = year_guess(btime);
1053       timestamp = mktime(btime);
1054       btime->tm_year = year_btime;
1055       *old_time  = timestamp;
1056       memcpy(old_tm, btime, sizeof(struct tm));
1057     }
1058   return timestamp;
1059 }
1060 
1061 /*********************************************************
1062  *
1063  * MODULE STUFF
1064  *
1065  *********************************************************/
1066 #include "sh_modules.h"
1067 
1068 SH_MUTEX_STATIC(mutex_logmon_check, PTHREAD_MUTEX_INITIALIZER);
1069 
1070 static int ShLogmonActive        = S_FALSE;
1071 #define SH_LOGMON_INTERVAL 10
1072 static time_t sh_logmon_interval = SH_LOGMON_INTERVAL;
1073 
sh_log_check_init(struct mod_type * arg)1074 int sh_log_check_init (struct mod_type * arg)
1075 {
1076 #if !defined(HAVE_PTHREAD)
1077   (void) arg;
1078 #endif
1079 
1080   if (ShLogmonActive == S_FALSE)
1081     return SH_MOD_FAILED;
1082 #ifdef HAVE_PTHREAD
1083   if (arg != NULL && arg->initval < 0 &&
1084       (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE))
1085     {
1086       if (0 == sh_pthread_create(sh_threaded_module_run, (void *)arg))
1087 	return SH_MOD_THREAD;
1088       else
1089 	return SH_MOD_FAILED;
1090     }
1091   else if (arg != NULL && arg->initval == SH_MOD_THREAD &&
1092 	   (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE))
1093     {
1094       return SH_MOD_THREAD;
1095     }
1096 #endif
1097   if (sh_watched_logs != NULL)
1098     return 0;
1099 
1100   return -1;
1101 }
1102 
sh_log_check_timer(time_t tcurrent)1103 int sh_log_check_timer(time_t tcurrent)
1104 {
1105   static time_t lastcheck = 0;
1106 
1107   SL_ENTER(_("sh_log_check_timer"));
1108   if ((time_t) (tcurrent - lastcheck) >= sh_logmon_interval)
1109     {
1110       lastcheck  = tcurrent;
1111       SL_RETURN((-1), _("sh_log_check_timer"));
1112     }
1113   SL_RETURN(0, _("sh_log_check_timer"));
1114 }
1115 
1116 
sh_log_check_check(void)1117 int sh_log_check_check(void)
1118 {
1119   int status = 0;
1120 
1121   SL_ENTER(_("sh_log_check_check"));
1122 
1123   SH_MUTEX_LOCK(mutex_logmon_check);
1124 
1125   status = 0;
1126 
1127   if( ShLogmonActive != S_FALSE )
1128     {
1129       sh_check_watches();
1130       sh_keep_match();
1131       sh_log_mark_check();
1132       clean_dir();
1133     }
1134   SH_MUTEX_UNLOCK(mutex_logmon_check);
1135 
1136   SL_RETURN(status, _("sh_log_check_check"));
1137 }
1138 
sh_log_check_reconf(void)1139 int sh_log_check_reconf(void)
1140 {
1141   int status = 0;
1142 
1143   SL_ENTER(_("sh_log_check_check"));
1144 
1145   SH_MUTEX_LOCK(mutex_logmon_check);
1146 
1147   ShLogmonActive     = S_FALSE;
1148   sh_logmon_interval = SH_LOGMON_INTERVAL;
1149   sh_dump_watches();
1150   sh_eval_cleanup();
1151 
1152   SH_MUTEX_UNLOCK(mutex_logmon_check);
1153 
1154   SL_RETURN(status, _("sh_log_check_check"));
1155 }
1156 
sh_log_check_cleanup(void)1157 int sh_log_check_cleanup(void)
1158 {
1159   sh_log_mark_destroy();
1160   return sh_log_check_reconf();
1161 }
1162 
1163 /*********************  OPTIONS **********************/
1164 
1165 static int sh_logmon_set_active  (const char *str);
1166 static int sh_logmon_set_clean  (const char *str);
1167 static int sh_logmon_set_interval(const char *str);
1168 static int sh_logmon_add_watch (const char * str);
1169 static int sh_logmon_add_group (const char * str);
1170 static int sh_logmon_end_group (const char * str);
1171 static int sh_logmon_add_host  (const char * str);
1172 static int sh_logmon_end_host  (const char * str);
1173 static int sh_logmon_add_queue (const char * str);
1174 static int sh_logmon_add_rule  (const char * str);
1175 extern int sh_set_hidepid(const char *s);
1176 static int sh_logmon_set_save_dir(const char *s);
1177 
1178 sh_rconf sh_log_check_table[] = {
1179     {
1180         N_("logmonactive"),
1181         sh_logmon_set_active,
1182     },
1183     {
1184         N_("logmoninterval"),
1185         sh_logmon_set_interval,
1186     },
1187     {
1188         N_("logmonclean"),
1189         sh_logmon_set_clean,
1190     },
1191     {
1192         N_("logmonwatch"),
1193         sh_logmon_add_watch,
1194     },
1195     {
1196         N_("logmonqueue"),
1197         sh_logmon_add_queue,
1198     },
1199     {
1200         N_("logmongroup"),
1201         sh_logmon_add_group,
1202     },
1203     {
1204         N_("logmonendgroup"),
1205         sh_logmon_end_group,
1206     },
1207     {
1208         N_("logmonhost"),
1209         sh_logmon_add_host,
1210     },
1211     {
1212         N_("logmonendhost"),
1213         sh_logmon_end_host,
1214     },
1215     {
1216         N_("logmonrule"),
1217         sh_logmon_add_rule,
1218     },
1219     {
1220         N_("logmonhidepid"),
1221         sh_set_hidepid,
1222     },
1223     {
1224         N_("logmonsavedir"),
1225         sh_logmon_set_save_dir,
1226     },
1227     {
1228         N_("logmonmarkseverity"),
1229         sh_log_set_mark_severity,
1230     },
1231     {
1232         N_("logmonburstthreshold"),
1233         sh_repeat_set_trigger,
1234     },
1235     {
1236         N_("logmonburstqueue"),
1237         sh_repeat_set_queue,
1238     },
1239     {
1240         N_("logmonburstcron"),
1241         sh_repeat_set_cron,
1242     },
1243     {
1244         N_("logmondeadtime"),
1245         sh_keep_deadtime,
1246     },
1247     {
1248         NULL,
1249         NULL
1250     }
1251 };
1252 
1253 /* Decide if we're active.
1254  */
sh_logmon_set_active(const char * str)1255 static int sh_logmon_set_active(const char *str)
1256 {
1257   int value;
1258 
1259   SL_ENTER(_("sh_logmon_set_active"));
1260 
1261   value = sh_util_flagval(str, &ShLogmonActive);
1262 
1263   SL_RETURN((value), _("sh_logmon_set_active"));
1264 }
1265 
1266 /* Decide if we're active.
1267  */
sh_logmon_set_clean(const char * str)1268 static int sh_logmon_set_clean(const char *str)
1269 {
1270   int value;
1271 
1272   SL_ENTER(_("sh_logmon_set_active"));
1273 
1274   value = sh_util_flagval(str, &do_checkpoint_cleanup);
1275 
1276   SL_RETURN((value), _("sh_logmon_set_active"));
1277 }
1278 
sh_logmon_set_save_dir(const char * str)1279 static int sh_logmon_set_save_dir(const char *str)
1280 {
1281   int retval = -1;
1282 
1283   SL_ENTER(_("sh_logmon_set_save_dir"));
1284 
1285   if (str && str[0] == '/')
1286     {
1287       if (save_dir)
1288 	{
1289 	  SH_FREE(save_dir);
1290 	  save_dir = NULL;
1291 	}
1292       save_dir = sh_util_strdup(str);
1293       retval = 0;
1294     }
1295 
1296   SL_RETURN((retval), _("sh_logmon_set_save_dir"));
1297 }
1298 
sh_logmon_set_interval(const char * c)1299 static int sh_logmon_set_interval (const char * c)
1300 {
1301   int retval = 0;
1302   long val;
1303 
1304   SL_ENTER(_("sh_logmon_set_interval"));
1305   val = strtol (c, (char **)NULL, 10);
1306   if (val <= 0)
1307     {
1308       SH_MUTEX_LOCK(mutex_thread_nolog);
1309       sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
1310 		       _("log monitoring interval"), c);
1311       SH_MUTEX_UNLOCK(mutex_thread_nolog);
1312       retval = -1;
1313     }
1314   else
1315     {
1316       sh_logmon_interval = (time_t) val;
1317     }
1318   SL_RETURN(retval, _("sh_logmon_set_interval"));
1319 }
1320 
1321 /* Add a watch on a logfile.
1322  * Format: TYPE : Filename [: File_Format]
1323  */
sh_logmon_add_watch(const char * str)1324 static int sh_logmon_add_watch (const char * str)
1325 {
1326   return sh_add_watch(str);
1327 }
1328 
1329 /* Add a host.
1330  * Format: Name_Regex
1331  */
sh_logmon_add_host(const char * str)1332 static int sh_logmon_add_host (const char * str)
1333 {
1334   return sh_eval_hadd(str);
1335 }
1336 
1337 /* End a host.
1338  * Format: Name
1339  */
sh_logmon_end_host(const char * str)1340 static int sh_logmon_end_host (const char * str)
1341 {
1342   (void) str;
1343   return sh_eval_hend(NULL);
1344 }
1345 
1346 /* Add a group of rules.
1347  * Groups can be under hosts, but not vice versa.
1348  * Format: Name : Prefix_Regex
1349  */
sh_logmon_add_group(const char * str)1350 static int sh_logmon_add_group (const char * str)
1351 {
1352   return sh_eval_gadd(str);
1353 }
1354 
1355 /* End a group of rules.
1356  * Format: Name
1357  */
sh_logmon_end_group(const char * str)1358 static int sh_logmon_end_group (const char * str)
1359 {
1360   (void) str;
1361   return sh_eval_gend(NULL);
1362 }
1363 
1364 /* Define a reporting queue.
1365  * Format: Label : [Interval] : TYPE : Severity[:alias]
1366  * TYPE must be 'report' or 'sum'
1367  * Interval is ignored for TYPE='report'
1368  */
sh_logmon_add_queue(const char * str)1369 static int sh_logmon_add_queue (const char * str)
1370 {
1371   return sh_eval_qadd(str);
1372 }
1373 
1374 /* Define a check rule.
1375  * Format: [KEEP(seconds,label):]Queue_Label : Regex
1376  * KEEP indicates that we keep the label, to perform
1377  *      correlation matching
1378  */
sh_logmon_add_rule(const char * str)1379 static int sh_logmon_add_rule (const char * str)
1380 {
1381   return sh_eval_radd(str);
1382 }
1383 
1384 
1385 #if 0
1386 
1387 /* >>>>>>>>>>>  MAIN <<<<<<<<<<<<<<<<<<< */
1388 
1389 int main (int argc, char * argv[])
1390 {
1391   int status, i;
1392   FILE * fp;
1393   sh_string * s = NULL;
1394   static char template[] = "/tmp/xtest.XXXXXX";
1395 
1396   /* pacct */
1397   status = sh_add_watch("PACCT:/var/log/account/pacct");
1398   sh_check_watches();
1399   sh_dump_watches();
1400   exit(0);
1401 
1402   /* apache log */
1403   sh_eval_gadd("four_o_four:404");
1404   sh_eval_qadd("test:1:sum:7");
1405   sh_eval_radd("test:^(\\d+.\\d+.\\d+.\\d+).*");
1406   sh_eval_gend(NULL);
1407   sh_eval_radd("trash:.*");
1408   status = sh_add_watch("APACHE:/var/log/apache2/access.log:combined");
1409   sh_check_watches();
1410   sh_dump_watches();
1411   exit(0);
1412 
1413   /* logfile */
1414   sh_set_hidepid(1);
1415   sh_eval_hadd("hslxmsrv1");
1416   sh_eval_gadd("postfix:postfix");
1417   sh_eval_qadd("test::report:7");
1418   sh_eval_radd("test:postfix/smtpd: disconnect from localhost.*");
1419   sh_eval_radd("trash:postfix/smtpd: disconnect.*");
1420   sh_eval_hadd("hspc05");
1421   sh_eval_gadd("cron:CRON");
1422   sh_eval_qadd("test:1:sum:7");
1423   sh_eval_radd("test:CRON: PAM adding faulty module: (/lib/security/.*.so)");
1424   sh_eval_radd("trash:.*");
1425   status = sh_add_watch("SYSLOG:/var/log/messages");
1426   sh_check_watches();
1427 
1428   sh_dump_watches();
1429   exit(0);
1430 
1431   printf("%d types\n",
1432 	 (int) (sizeof(sh_logtypes_def)/sizeof(struct sh_logfile_type)));
1433 
1434   /* test sh_add_watch
1435    */
1436   status = sh_add_watch("");
1437   printf("%2d: zero length, expect -1\n", status);
1438   status = sh_add_watch(NULL);
1439   printf("%2d: NULL, expect -2\n", status);
1440   status = sh_add_watch("0123456789012345:/var/log/messages");
1441   printf("%2d: long, expect -2\n", status);
1442   status = sh_add_watch("012345678901234:/var/log/messages");
1443   printf("%2d: exact length, expect -3\n", status);
1444   status = sh_add_watch("01234567890123:56789");
1445   printf("%2d: short length, expect -3\n", status);
1446   status = sh_add_watch("SYSLOG:var/log/messages");
1447   printf("%2d: short badpath, expect -4\n", status);
1448   status = sh_add_watch("SYSLOG:/var/log/messages");
1449   /* status = sh_add_watch("SYSLOG:/var/log/dpkg.log.1"); */
1450   printf("%2d: short path ok, expect 0\n", status);
1451 
1452   /* test sh_string_read
1453    */
1454   s = sh_string_new();
1455 
1456   status = /*@i@*/mkstemp(template);
1457 
1458   if (status < 0) {
1459     fprintf(stderr, "error in mkstemp!\n"); exit(EXIT_FAILURE); }
1460 
1461   fp = fdopen(status, "r+");
1462   if (!fp) {
1463     fprintf(stderr, "error in fdopen!\n"); exit(EXIT_FAILURE); }
1464 
1465   for (i = 0; i <  80; ++i) { fputc ('a', fp); } fputc ('\n', fp); /* 0 */
1466   for (i = 0; i < 118; ++i) { fputc ('a', fp); } fputc ('\n', fp); /* 1 */
1467   for (i = 0; i < 119; ++i) { fputc ('a', fp); } fputc ('\n', fp); /* 2 */
1468   for (i = 0; i < 120; ++i) { fputc ('a', fp); } fputc ('\n', fp); /* 3 */
1469   for (i = 0; i < 121; ++i) { fputc ('a', fp); } fputc ('\n', fp); /* 4 */
1470   for (i = 0; i < 238; ++i) { fputc ('a', fp); } fputc ('\n', fp);
1471   for (i = 0; i < 239; ++i) { fputc ('a', fp); } fputc ('\n', fp);
1472   for (i = 0; i < 240; ++i) { fputc ('a', fp); } fputc ('\n', fp);
1473   for (i = 0; i < 241; ++i) { fputc ('a', fp); } fputc ('\n', fp);
1474 
1475   rewind(fp);
1476 
1477   for (i = 0; i < 9; ++i)
1478     {
1479       status = (int) sh_string_read(s, fp, 120);
1480       printf("%d: status = %d, len = %d, size = %d\n",
1481 	     i, status, (int)s->len, (int)s->siz);
1482       if (status == -2)
1483 	(void) sh_string_read(s, fp, 240);
1484       else
1485 	printf("%s\n", s->str);
1486     }
1487 
1488   rewind(fp);
1489 
1490   (void) sh_string_truncate(s, 0);
1491 
1492   for (i = 0; i < 9; ++i)
1493     {
1494       status = (int) sh_string_read(s, fp, 240);
1495       printf("%d: status = %d, len = %d, size = %d\n",
1496 	     i, status, (int)s->len, (int)s->siz);
1497       if (status == -2)
1498 	(void) sh_string_read(s, fp, 240);
1499       else
1500 	{
1501 	  for (status = 0; status < (int)s->len; ++status)
1502 	    {
1503 	      if (s->str[status] != 'a')
1504 		{
1505 		  break;
1506 		}
1507 	    }
1508 	  printf("%d %s\n", status, s->str);
1509 	}
1510     }
1511 
1512   sl_fclose(FIL__, __LINE__, fp); remove(template);
1513 
1514 
1515 
1516   return 0;
1517 }
1518 #endif
1519 
1520 /* #ifdef USE_LOGFILE_MONITOR */
1521 #endif
1522 
1523