source: trunk/src/sh_log_check.c@ 376

Last change on this file since 376 was 362, checked in by katerina, 13 years ago

Fix for ticket #267 (Multiple compiler warnings with gcc 4.6.1).

File size: 35.0 KB
RevLine 
[183]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>
[271]9#include <fcntl.h>
[183]10
[276]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
[271]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
[183]36#ifdef USE_LOGFILE_MONITOR
37
38#undef FIL__
39#define FIL__ _("sh_log_check.c")
40
41/* Debian/Ubuntu: libpcre3-dev */
[203]42#ifdef HAVE_PCRE_PCRE_H
43#include <pcre/pcre.h>
44#else
[183]45#include <pcre.h>
[203]46#endif
[183]47
48#include "samhain.h"
49#include "sh_pthread.h"
50#include "sh_utils.h"
[265]51#include "sh_unix.h"
[183]52#include "sh_string.h"
53#include "sh_log_check.h"
54#include "sh_log_evalrule.h"
[265]55#include "sh_log_correlate.h"
56#include "sh_log_mark.h"
57#include "sh_log_repeat.h"
[275]58#include "sh_extern.h"
[183]59
60/* List of supported logfile types, format is
[260]61 * {
62 * "TYPE_CODE",
63 * Reader_Callback_Function,
64 * Parser_Callback_function,
65 * Evaluate_Callback_Function
66 * }
[183]67 * If Reader_Callback_Function is NULL, the default (line-oriented
68 * text file) reader is used.
69 */
70struct sh_logfile_type sh_logtypes_def[] = {
[185]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 },
[183]74#if defined(HAVE_SYS_ACCT_H)
[185]75 { "PACCT", sh_read_pacct, sh_parse_pacct, NULL },
[183]76#endif
[276]77 { "SHELL", sh_read_shell, sh_parse_shell, NULL },
[183]78};
79
80/* -------------------------- Internal Stuff -------------------------- */
81
82struct logfile_record {
83 dev_t device_id;
84 ino_t inode;
85 fpos_t offset;
86};
87
[276]88static int do_checkpoint_cleanup = S_FALSE;
89
[265]90static char * save_dir = NULL;
[183]91
[276]92static const char * get_save_dir(void)
[183]93{
[276]94 int retval;
[183]95
96 if (!save_dir)
97 {
[276]98 save_dir = sh_util_strdup(DEFAULT_DATAROOT);
[183]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 }
[276]109 return save_dir;
110}
[183]111
[276]112static void clean_dir()
113{
114 DIR * dir;
115 struct dirent * entry;
[183]116
[276]117 const char * dirpath;
118
119 if (S_FALSE == do_checkpoint_cleanup)
120 return;
121
122 dirpath = get_save_dir();
123
124 if (dirpath)
[183]125 {
[276]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 (SL_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 }
[183]169 }
[276]170}
[183]171
[276]172static 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 (SL_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
[183]192 return path;
193}
194
195static void save_pos (struct sh_logfile * record)
196{
197 char * path;
198 FILE * fd;
[276]199 mode_t mask;
[183]200 struct logfile_record save_rec;
201
202 path = build_path(record);
203
204 if (path)
205 {
[265]206 if (0 != sh_unix_check_piddir (path))
207 {
208 SH_FREE(path);
209 return;
210 }
211
[276]212 mask = umask(S_IWGRP | S_IWOTH);
[183]213 fd = fopen(path, "wb");
[276]214 (void) umask(mask);
215
[183]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 {
[252]223 (void) sl_fclose(FIL__, __LINE__, fd);
[183]224 (void) remove(path);
225 }
226 else
227 {
[252]228 (void) sl_fclose(FIL__, __LINE__, fd);
[183]229 }
230 }
231 SH_FREE(path);
232 }
233 return;
234}
235
236static 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 }
[252]259 (void) sl_fclose(FIL__, __LINE__, fd);
[183]260 }
261 SH_FREE(path);
262 }
263 return retval;
264}
265
266/*@null@*/ static struct sh_logfile * sh_watched_logs = NULL;
267
268int 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
[275]329 if (splits[1][0] != '/' && 0 != strcmp(splits[0], _("SHELL")))
[183]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;
[276]350 if (0 == strcmp(splits[0], _("SHELL")))
[275]351 thisfile->flags = SH_LOGFILE_NOFILE;
352 else
353 thisfile->flags = SH_LOGFILE_REWIND;
[183]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 */
[275]418 if ((thisfile->flags & SH_LOGFILE_NOFILE) == 0)
[183]419 {
[275]420 if (0 == stat(thisfile->filename, &buf))
421 {
422 if (S_ISREG(buf.st_mode)
[271]423#ifdef S_ISLNK
[275]424 || S_ISLNK(buf.st_mode)
[271]425#endif
[275]426 )
[271]427 {
[275]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 }
[271]435 }
[275]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 }
[183]442 }
[275]443 else
[271]444 {
[275]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;
[271]460 }
[183]461 }
462
463 sh_watched_logs = thisfile;
464
465 SH_FREE(new);
466 return 0;
467}
468
469void 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
[275]478 if ((thisfile->flags & SH_LOGFILE_NOFILE) == 0 &&
479 (thisfile->flags & SH_LOGFILE_PIPE) == 0)
[271]480 {
481 save_pos(thisfile);
482 }
[183]483
[275]484 if ((thisfile->flags & SH_LOGFILE_NOFILE) == 0)
485 {
486 if (thisfile->fp)
487 sl_fclose(FIL__, __LINE__, thisfile->fp);
488 }
489
[183]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 */
502static void * sh_dummy_thisfile = NULL;
503
504void 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_thisfile = (void*) &thisfile;
515
516 while (thisfile)
517 {
[271]518 volatile size_t count = 0;
519
[183]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;
[185]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 }
[183]546
547 if (logrecord->message)
548 sh_string_destroy(&(logrecord->message));
549 if (logrecord->host)
550 sh_string_destroy(&(logrecord->host));
[185]551 if (logrecord->timestr)
552 sh_string_destroy(&(logrecord->timestr));
[183]553 SH_FREE(logrecord);
554 }
555 }
556 else
557 {
[185]558 record = sh_string_new(0);
[183]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#include <unistd.h>
580#include <libgen.h>
581#include <dirent.h>
582
583char * sh_rotated_log_search(const char * path, struct stat * buf)
584{
585
586 size_t size;
587 int i;
588 char * searchpath;
589 struct stat sbuf;
590 DIR * dp;
591 char * dname;
592 char * bname;
593
594 dname = sh_util_dirname(path);
595 bname = sh_util_basename(path);
596
597 size = strlen(dname) + strlen(bname) + 4;
598 searchpath = SH_ALLOC(size);
599
600 for (i = 0; i < 2; ++i)
601 {
602 snprintf(searchpath, size, "%s/%s.%1d", dname, bname, i);
603 if (0 == stat(searchpath, &sbuf) && sbuf.st_ino == buf->st_ino)
604 {
605 SH_FREE(dname);
606 SH_FREE(bname);
607 return searchpath;
608 }
609 }
610
611 SH_FREE(searchpath);
612
613 if (NULL != (dp = opendir(dname)))
614 {
615 struct dirent * de;
616
617 while (NULL != (de = readdir(dp)))
618 {
619 if (0 == strcmp(de->d_name, ".") || 0 == strcmp(de->d_name, ".."))
620 continue;
621
622 size = strlen(dname) + strlen(de->d_name) + 2;
623 searchpath = SH_ALLOC(size);
624 snprintf(searchpath, size, "%s/%s", dname, de->d_name);
625
626 if (0 == stat(searchpath, &sbuf) && sbuf.st_ino == buf->st_ino)
627 {
628 SH_FREE(dname);
629 SH_FREE(bname);
630 closedir(dp);
631 return searchpath;
632 }
633
634 SH_FREE(searchpath);
635 }
636 closedir(dp);
637 }
638
639 SH_FREE(dname);
640 SH_FREE(bname);
641
642 return NULL;
643}
644
645/* Open file, position at stored offset
646 */
647int sh_open_for_reader (struct sh_logfile * logfile)
648{
649 struct stat buf;
650 sh_string * filename;
651
652 /* check whether file exists, get inode to check for
653 * logfile rotation
654 */
655 if (0 != retry_stat(FIL__, __LINE__, logfile->filename, &buf))
656 {
657 char * tmp;
658
659 SH_MUTEX_LOCK(mutex_thread_nolog);
660 tmp = sh_util_safe_name (logfile->filename);
661 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_MISS,
662 tmp);
663 SH_FREE(tmp);
664 SH_MUTEX_UNLOCK(mutex_thread_nolog);
665
666 memset (&(logfile->offset), '\0', sizeof(fpos_t));
667 logfile->flags |= SH_LOGFILE_REWIND;
668 return 0;
669 }
670
671 filename = sh_string_new(0);
672 (void) sh_string_set_from_char (filename, logfile->filename);
673
674 /* detect and handle logfile rotation
675 */
[271]676 if (logfile->inode != buf.st_ino &&
677 logfile->inode != 0 &&
678 !S_ISFIFO(buf.st_mode))
[183]679 {
680 /* Case 1) We have dealt with the moved file already.
681 * Clear the moved flag, set the rewind flag,
682 * fix logfile->inode.
683 */
684 if ((logfile->flags & SH_LOGFILE_MOVED) != 0)
685 {
686 /* done with rotated file, start with current file
687 */
688 memset (&(logfile->offset), '\0', sizeof(fpos_t));
689 logfile->flags |= SH_LOGFILE_REWIND;
690 logfile->flags &= ~SH_LOGFILE_MOVED;
691 logfile->inode = buf.st_ino;
692 logfile->device_id = buf.st_dev;
693 }
694
695 /* Case 2) Searching for rotated file.
696 * If found: set the moved flag, fix path for fopen.
697 * If not found: set the rewind flag, fix logfile->inode.
698 */
699 else
700 {
701 char *oldfile = sh_rotated_log_search(logfile->filename, &buf);
702
703 if (NULL != oldfile)
704 {
705 (void) sh_string_set_from_char (filename, oldfile);
706 SH_FREE(oldfile);
707 logfile->flags |= SH_LOGFILE_MOVED;
708 }
709 else
710 {
711 memset (&(logfile->offset), '\0', sizeof(fpos_t));
712 logfile->flags |= SH_LOGFILE_REWIND;
713 logfile->inode = buf.st_ino;
714 logfile->device_id = buf.st_dev;
715 }
716 }
717 }
718
719 /* open file
720 */
[271]721 if (!S_ISFIFO(buf.st_mode))
722 {
723 logfile->fp = fopen(filename->str, "r");
724 }
725 else
726 {
727 int fd_temp = open (filename->str, O_RDONLY|O_NONBLOCK);
728
729 if (fd_temp >= 0)
730 {
731 logfile->fp = fdopen(fd_temp, "r");
732 }
733 }
734
[183]735 if (!logfile->fp)
736 {
737 char * tmp;
738
739 SH_MUTEX_LOCK(mutex_thread_nolog);
740 tmp = sh_util_safe_name (logfile->filename);
741 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_EOPEN,
742 tmp);
743 SH_FREE(tmp);
744 SH_MUTEX_UNLOCK(mutex_thread_nolog);
745
746 sh_string_destroy(&filename);
747 return 0;
748 }
749
750 sh_string_destroy(&filename);
751
[271]752 if ((logfile->flags & SH_LOGFILE_PIPE) == 0)
[183]753 {
[271]754 if ((logfile->flags & SH_LOGFILE_REWIND) != 0)
[183]755 {
756 rewind(logfile->fp);
757 fgetpos(logfile->fp, &(logfile->offset));
[271]758 logfile->flags &= ~SH_LOGFILE_REWIND;
[183]759 }
[271]760 else
761 {
762 /* file too short
763 */
764 if (0 != fsetpos(logfile->fp, &(logfile->offset)))
765 {
766 rewind(logfile->fp);
767 fgetpos(logfile->fp, &(logfile->offset));
768 }
769 }
[183]770 }
771
772 return 1;
773}
774
775/******************************************************
776 * Default reader for ascii text files
777 */
778sh_string * sh_default_reader (sh_string * s, struct sh_logfile * logfile)
779{
[275]780 volatile int status;
[183]781 char * tmp;
782
783 start_read:
784
785 if (logfile->fp)
786 {
787 /* Result cannot be larger than 8192, thus cast is ok
788 */
789 status = (int) sh_string_read(s, logfile->fp, 8192);
790 if (status <= 0)
791 {
792 fgetpos(logfile->fp, &(logfile->offset));
[252]793 sl_fclose(FIL__, __LINE__, logfile->fp);
[183]794 logfile->fp = NULL;
795 sh_string_destroy(&s);
[271]796 if (status == 0 || (logfile->flags & SH_LOGFILE_PIPE) != 0)
[183]797 {
798 return NULL;
799 }
800
801 SH_MUTEX_LOCK(mutex_thread_nolog);
802 tmp = sh_util_safe_name (logfile->filename);
803 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_EREAD,
804 tmp);
805 SH_FREE(tmp);
806 SH_MUTEX_UNLOCK(mutex_thread_nolog);
807
808 return NULL;
809 }
810 return s;
811 }
812
813 if (0 != sh_open_for_reader(logfile))
814 goto start_read;
815
816 return NULL;
817}
818
[276]819struct task_entry
[275]820{
821 sh_tas_t task;
[276]822 struct task_entry * next;
823};
[275]824
[276]825static struct task_entry * tasklist = NULL;
826
827static struct task_entry * task_find(FILE * fp)
828{
829 struct task_entry * entry = tasklist;
830 while (entry)
831 {
832 if (entry->task.pipe == fp)
833 return (entry);
834 entry = entry->next;
835 }
836 return NULL;
837}
838
839static void task_remove(struct task_entry * task)
840{
841 struct task_entry * entry = tasklist;
842 struct task_entry * prev = tasklist;
843
844 while (entry)
845 {
846 if (entry == task)
847 {
848 if (entry == tasklist)
849 {
850 tasklist = entry->next;
851 SH_FREE(entry);
852 return;
853 }
854 else
855 {
856 prev->next = entry->next;
857 SH_FREE(entry);
858 return;
859 }
860 }
861 prev = entry;
862 entry = entry->next;
863 }
864 return;
865}
866
867static void task_add(struct task_entry * entry)
868{
869 entry->next = tasklist;
870 tasklist = entry;
871 return;
872}
873
874sh_string * sh_command_reader (sh_string * s, struct sh_logfile * logfile)
875{
876 struct task_entry * entry;
877
878 struct sigaction new_act;
879 struct sigaction old_act;
880
881 volatile int status;
[275]882 char * tmp;
883
884 start_read:
885
886 if (logfile->fp)
887 {
888 /* ignore SIGPIPE (instead get EPIPE if connection is closed)
889 */
[276]890 memset(&new_act, 0, sizeof(struct sigaction));
[275]891 new_act.sa_handler = SIG_IGN;
892 (void) retry_sigaction (FIL__, __LINE__, SIGPIPE, &new_act, &old_act);
893
894 /* Result cannot be larger than 8192, thus cast is ok
895 */
896 status = (int) sh_string_read(s, logfile->fp, 8192);
897
898 /* restore old signal handler
899 */
900 (void) retry_sigaction (FIL__, __LINE__, SIGPIPE, &old_act, NULL);
901
902 if (status <= 0)
903 {
[276]904 entry = task_find(logfile->fp);
905 sh_ext_pclose (&(entry->task));
906 task_remove(entry);
907
[275]908 logfile->fp = NULL;
909 sh_string_destroy(&s);
910
911 if (status == 0)
912 {
913 return NULL;
914 }
915
916 SH_MUTEX_LOCK(mutex_thread_nolog);
917 tmp = sh_util_safe_name (logfile->filename);
918 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_EREAD,
919 tmp);
920 SH_FREE(tmp);
921 SH_MUTEX_UNLOCK(mutex_thread_nolog);
922
923 return NULL;
924 }
925 return s;
926 }
927
[276]928 entry = SH_ALLOC(sizeof(struct task_entry));
929
930 status = sh_ext_popen_init (&(entry->task), logfile->filename);
[275]931 if (0 == status)
932 {
[276]933 task_add(entry);
934 logfile->fp = entry->task.pipe;
[275]935 goto start_read;
936 }
937 else
938 {
[276]939 SH_FREE(entry);
[275]940 SH_MUTEX_LOCK(mutex_thread_nolog);
941 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, status, MSG_E_SUBGEN,
942 _("Could not open pipe"), _("sh_command reader"));
943 SH_MUTEX_UNLOCK(mutex_thread_nolog);
944 }
945
946 return NULL;
947}
948
[183]949/******************************************************
[185]950 * Reader for continued text files
951 */
952sh_string * sh_cont_reader (sh_string * s, struct sh_logfile * logfile, char*cont)
953{
954 int status;
955 char * tmp;
956 sh_string * str;
957 int remain = 8192;
958 int count = 0;
959
960 if (!sh_string_truncate(s, 0))
961 return NULL;
962
963 start_read:
964
965 if (logfile->fp)
966 {
967 str = sh_string_new(0);
968
969 /* Result cannot be larger than 8192, thus cast is ok
970 */
971 status = (int) sh_string_read(str, logfile->fp, 8192);
972
973 if (status > 0)
974 {
975
976 do {
977 s = sh_string_add (s, str);
978 count += status;
979 remain -= status;
980
981 if (remain <= 0)
982 {
983 return s;
984 }
985
986 status = (int) sh_string_read_cont(str, logfile->fp, count, cont);
987
988 if (status == 0)
989 {
990 return s;
991 }
992 }
993 while (status > 0);
994 }
995
996 if (status <= 0)
997 {
998 fgetpos(logfile->fp, &(logfile->offset));
[252]999 sl_fclose(FIL__, __LINE__, logfile->fp);
[185]1000 logfile->fp = NULL;
1001 sh_string_destroy(&s);
[271]1002 if (status == 0 || (logfile->flags & SH_LOGFILE_PIPE) != 0)
[185]1003 {
1004 return NULL;
1005 }
1006
1007 SH_MUTEX_LOCK(mutex_thread_nolog);
1008 tmp = sh_util_safe_name (logfile->filename);
1009 sh_error_handle((-1), FIL__, __LINE__, 0, MSG_LOGMON_EREAD,
1010 tmp);
1011 SH_FREE(tmp);
1012 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1013
1014 return NULL;
1015 }
1016
1017 return s;
1018 }
1019
1020 if (0 != sh_open_for_reader(logfile))
1021 goto start_read;
1022
1023 return NULL;
1024}
1025
1026/******************************************************
[183]1027 * Reader for binary files
1028 */
[362]1029sh_string * sh_binary_reader (void * s, size_t size,
1030 struct sh_logfile * logfile)
[183]1031{
1032 size_t status;
1033
1034 start_read:
1035
1036 if (logfile->fp)
1037 {
1038
1039 status = fread(s, size, 1, logfile->fp);
1040
1041 if (status != 1)
1042 {
[362]1043 memset(s, '\0', size);
[271]1044 if (ferror(logfile->fp) && (logfile->flags & SH_LOGFILE_PIPE) == 0)
[183]1045 {
1046 char * tmp;
1047 SH_MUTEX_LOCK(mutex_thread_nolog);
1048 tmp = sh_util_safe_name (logfile->filename);
1049 sh_error_handle((-1), FIL__, __LINE__, errno, MSG_LOGMON_EREAD,
1050 tmp);
1051 SH_FREE(tmp);
1052 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1053 }
1054 fgetpos(logfile->fp, &(logfile->offset));
[252]1055 sl_fclose(FIL__, __LINE__, logfile->fp);
[183]1056 logfile->fp = NULL;
1057 return NULL;
1058 }
1059 return s;
1060 }
1061
1062 if (0 != sh_open_for_reader(logfile))
1063 goto start_read;
1064
1065 return NULL;
1066}
1067
1068
1069
1070/**********************************************************
1071 *
1072 * Utilities
1073 *
1074 **********************************************************/
1075
[265]1076/* Return current year, unless that would result
1077 * in a date far in the future. If that happens,
1078 * return last year.
1079 */
1080static int year_guess (struct tm * btime)
1081{
1082 int year;
1083 struct tm ts;
1084 time_t now = time(NULL);
1085 time_t check;
1086
1087 memcpy(&ts, localtime(&now), sizeof(struct tm));
1088 year = ts.tm_year;
1089
1090 /* Check result to detect year wrap
1091 * (logfile entry from last year).
1092 */
1093 btime->tm_year = year;
1094 check = mktime(btime);
1095 if (check > (now + (86400*30)))
1096 --year;
1097
1098 return year;
1099}
1100
[183]1101time_t conv_timestamp (struct tm * btime,
1102 struct tm * old_tm, time_t * old_time)
1103{
1104 time_t timestamp;
1105 long offtime;
1106
1107 /* timestamp - mktime is slooow, thus cache result
1108 */
1109 if (btime->tm_isdst == old_tm->tm_isdst &&
1110 btime->tm_year == old_tm->tm_year &&
1111 btime->tm_mon == old_tm->tm_mon &&
1112 btime->tm_mday == old_tm->tm_mday)
1113 {
1114 offtime =
1115 (btime->tm_hour - old_tm->tm_hour) * 3600 +
1116 (btime->tm_min - old_tm->tm_min) * 60 +
1117 (btime->tm_sec - old_tm->tm_sec);
[265]1118
[183]1119 *old_time += offtime;
1120 memcpy(old_tm, btime, sizeof(struct tm));
1121 timestamp = *old_time;
1122 }
1123 else
1124 {
[265]1125 int year_btime = btime->tm_year;
1126
1127 if (btime->tm_year == 0)
1128 btime->tm_year = year_guess(btime);
[183]1129 timestamp = mktime(btime);
[265]1130 btime->tm_year = year_btime;
[183]1131 *old_time = timestamp;
1132 memcpy(old_tm, btime, sizeof(struct tm));
1133 }
1134 return timestamp;
1135}
1136
1137/*********************************************************
1138 *
1139 * MODULE STUFF
1140 *
1141 *********************************************************/
1142#include "sh_modules.h"
1143
1144SH_MUTEX_STATIC(mutex_logmon_check, PTHREAD_MUTEX_INITIALIZER);
1145
1146static int ShLogmonActive = S_FALSE;
1147#define SH_LOGMON_INTERVAL 10
1148static time_t sh_logmon_interval = SH_LOGMON_INTERVAL;
1149
1150int sh_log_check_init (struct mod_type * arg)
1151{
[257]1152#if !defined(HAVE_PTHREAD)
1153 (void) arg;
1154#endif
1155
[183]1156 if (ShLogmonActive == S_FALSE)
1157 return SH_MOD_FAILED;
1158#ifdef HAVE_PTHREAD
1159 if (arg != NULL && arg->initval < 0 &&
1160 (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE))
1161 {
1162 if (0 == sh_pthread_create(sh_threaded_module_run, (void *)arg))
1163 return SH_MOD_THREAD;
1164 else
1165 return SH_MOD_FAILED;
1166 }
[335]1167 else if (arg != NULL && arg->initval == SH_MOD_THREAD &&
1168 (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE))
1169 {
1170 return SH_MOD_THREAD;
1171 }
[183]1172#endif
1173 if (sh_watched_logs != NULL)
1174 return 0;
1175
1176 return -1;
1177}
1178
1179int sh_log_check_timer(time_t tcurrent)
1180{
1181 static time_t lastcheck = 0;
1182
1183 SL_ENTER(_("sh_log_check_timer"));
1184 if ((time_t) (tcurrent - lastcheck) >= sh_logmon_interval)
1185 {
1186 lastcheck = tcurrent;
1187 SL_RETURN((-1), _("sh_log_check_timer"));
1188 }
1189 SL_RETURN(0, _("sh_log_check_timer"));
1190}
1191
1192
1193int sh_log_check_check(void)
1194{
1195 int status = 0;
1196
1197 SL_ENTER(_("sh_log_check_check"));
1198
1199 SH_MUTEX_LOCK(mutex_logmon_check);
1200
1201 status = 0;
1202
1203 if( ShLogmonActive != S_FALSE )
1204 {
1205 sh_check_watches();
[260]1206 sh_keep_match();
[265]1207 sh_log_mark_check();
[276]1208 clean_dir();
[183]1209 }
1210 SH_MUTEX_UNLOCK(mutex_logmon_check);
1211
1212 SL_RETURN(status, _("sh_log_check_check"));
1213}
1214
1215int sh_log_check_reconf(void)
1216{
1217 int status = 0;
1218
1219 SL_ENTER(_("sh_log_check_check"));
1220
1221 SH_MUTEX_LOCK(mutex_logmon_check);
1222
1223 ShLogmonActive = S_FALSE;
1224 sh_logmon_interval = SH_LOGMON_INTERVAL;
1225 sh_dump_watches();
1226 sh_eval_cleanup();
1227
1228 SH_MUTEX_UNLOCK(mutex_logmon_check);
1229
1230 SL_RETURN(status, _("sh_log_check_check"));
1231}
1232
1233int sh_log_check_cleanup(void)
1234{
[265]1235 sh_log_mark_destroy();
[183]1236 return sh_log_check_reconf();
1237}
1238
1239/********************* OPTIONS **********************/
1240
1241static int sh_logmon_set_active (const char *str);
[276]1242static int sh_logmon_set_clean (const char *str);
[183]1243static int sh_logmon_set_interval(const char *str);
1244static int sh_logmon_add_watch (const char * str);
1245static int sh_logmon_add_group (const char * str);
1246static int sh_logmon_end_group (const char * str);
1247static int sh_logmon_add_host (const char * str);
1248static int sh_logmon_end_host (const char * str);
1249static int sh_logmon_add_queue (const char * str);
1250static int sh_logmon_add_rule (const char * str);
1251extern int sh_set_hidepid(const char *s);
[265]1252static int sh_logmon_set_save_dir(const char *s);
[183]1253
1254sh_rconf sh_log_check_table[] = {
1255 {
1256 N_("logmonactive"),
1257 sh_logmon_set_active,
1258 },
1259 {
1260 N_("logmoninterval"),
1261 sh_logmon_set_interval,
1262 },
1263 {
[276]1264 N_("logmonclean"),
1265 sh_logmon_set_clean,
1266 },
1267 {
[183]1268 N_("logmonwatch"),
1269 sh_logmon_add_watch,
1270 },
1271 {
1272 N_("logmonqueue"),
1273 sh_logmon_add_queue,
1274 },
1275 {
1276 N_("logmongroup"),
1277 sh_logmon_add_group,
1278 },
1279 {
1280 N_("logmonendgroup"),
1281 sh_logmon_end_group,
1282 },
1283 {
1284 N_("logmonhost"),
1285 sh_logmon_add_host,
1286 },
1287 {
1288 N_("logmonendhost"),
1289 sh_logmon_end_host,
1290 },
1291 {
1292 N_("logmonrule"),
1293 sh_logmon_add_rule,
1294 },
1295 {
1296 N_("logmonhidepid"),
1297 sh_set_hidepid,
1298 },
1299 {
[265]1300 N_("logmonsavedir"),
1301 sh_logmon_set_save_dir,
1302 },
1303 {
1304 N_("logmonmarkseverity"),
[349]1305 sh_log_set_mark_severity,
[265]1306 },
1307 {
1308 N_("logmonburstthreshold"),
1309 sh_repeat_set_trigger,
1310 },
1311 {
1312 N_("logmonburstqueue"),
1313 sh_repeat_set_queue,
1314 },
1315 {
1316 N_("logmonburstcron"),
1317 sh_repeat_set_cron,
1318 },
1319 {
[358]1320 N_("logmondeadtime"),
1321 sh_keep_deadtime,
1322 },
1323 {
[183]1324 NULL,
1325 NULL
1326 }
1327};
1328
1329/* Decide if we're active.
1330 */
1331static int sh_logmon_set_active(const char *str)
1332{
1333 int value;
1334
1335 SL_ENTER(_("sh_logmon_set_active"));
1336
1337 value = sh_util_flagval(str, &ShLogmonActive);
1338
1339 SL_RETURN((value), _("sh_logmon_set_active"));
1340}
1341
[276]1342/* Decide if we're active.
1343 */
1344static int sh_logmon_set_clean(const char *str)
1345{
1346 int value;
1347
1348 SL_ENTER(_("sh_logmon_set_active"));
1349
1350 value = sh_util_flagval(str, &do_checkpoint_cleanup);
1351
1352 SL_RETURN((value), _("sh_logmon_set_active"));
1353}
1354
[265]1355static int sh_logmon_set_save_dir(const char *str)
1356{
1357 int retval = -1;
1358
1359 SL_ENTER(_("sh_logmon_set_save_dir"));
1360
1361 if (str && str[0] == '/')
1362 {
1363 if (save_dir)
1364 {
1365 SH_FREE(save_dir);
1366 save_dir = NULL;
1367 }
1368 save_dir = sh_util_strdup(str);
1369 retval = 0;
1370 }
1371
1372 SL_RETURN((retval), _("sh_logmon_set_save_dir"));
1373}
1374
[183]1375static int sh_logmon_set_interval (const char * c)
1376{
1377 int retval = 0;
1378 long val;
1379
1380 SL_ENTER(_("sh_logmon_set_interval"));
1381 val = strtol (c, (char **)NULL, 10);
1382 if (val <= 0)
1383 {
1384 SH_MUTEX_LOCK(mutex_thread_nolog);
1385 sh_error_handle ((-1), FIL__, __LINE__, EINVAL, MSG_EINVALS,
1386 _("log monitoring interval"), c);
1387 SH_MUTEX_UNLOCK(mutex_thread_nolog);
1388 retval = -1;
1389 }
[362]1390 else
1391 {
1392 sh_logmon_interval = (time_t) val;
1393 }
1394 SL_RETURN(retval, _("sh_logmon_set_interval"));
[183]1395}
1396
1397/* Add a watch on a logfile.
1398 * Format: TYPE : Filename [: File_Format]
1399 */
1400static int sh_logmon_add_watch (const char * str)
1401{
1402 return sh_add_watch(str);
1403}
1404
1405/* Add a host.
1406 * Format: Name_Regex
1407 */
1408static int sh_logmon_add_host (const char * str)
1409{
1410 return sh_eval_hadd(str);
1411}
1412
1413/* End a host.
1414 * Format: Name
1415 */
1416static int sh_logmon_end_host (const char * str)
1417{
1418 (void) str;
1419 return sh_eval_hend(NULL);
1420}
1421
1422/* Add a group of rules.
1423 * Groups can be under hosts, but not vice versa.
1424 * Format: Name : Prefix_Regex
1425 */
1426static int sh_logmon_add_group (const char * str)
1427{
1428 return sh_eval_gadd(str);
1429}
1430
1431/* End a group of rules.
1432 * Format: Name
1433 */
1434static int sh_logmon_end_group (const char * str)
1435{
1436 (void) str;
1437 return sh_eval_gend(NULL);
1438}
1439
1440/* Define a reporting queue.
[272]1441 * Format: Label : [Interval] : TYPE : Severity[:alias]
[183]1442 * TYPE must be 'report' or 'sum'
1443 * Interval is ignored for TYPE='report'
1444 */
1445static int sh_logmon_add_queue (const char * str)
1446{
1447 return sh_eval_qadd(str);
1448}
1449
1450/* Define a check rule.
[260]1451 * Format: [KEEP(seconds,label):]Queue_Label : Regex
1452 * KEEP indicates that we keep the label, to perform
1453 * correlation matching
[183]1454 */
1455static int sh_logmon_add_rule (const char * str)
1456{
1457 return sh_eval_radd(str);
1458}
1459
1460
1461#if 0
1462
1463/* >>>>>>>>>>> MAIN <<<<<<<<<<<<<<<<<<< */
1464
1465int main (int argc, char * argv[])
1466{
1467 int status, i;
1468 FILE * fp;
1469 sh_string * s = NULL;
1470 static char template[] = "/tmp/xtest.XXXXXX";
1471
1472 /* pacct */
1473 status = sh_add_watch("PACCT:/var/log/account/pacct");
1474 sh_check_watches();
1475 sh_dump_watches();
1476 exit(0);
1477
1478 /* apache log */
1479 sh_eval_gadd("four_o_four:404");
1480 sh_eval_qadd("test:1:sum:7");
1481 sh_eval_radd("test:^(\\d+.\\d+.\\d+.\\d+).*");
1482 sh_eval_gend(NULL);
1483 sh_eval_radd("trash:.*");
1484 status = sh_add_watch("APACHE:/var/log/apache2/access.log:combined");
1485 sh_check_watches();
1486 sh_dump_watches();
1487 exit(0);
1488
1489 /* logfile */
1490 sh_set_hidepid(1);
1491 sh_eval_hadd("hslxmsrv1");
1492 sh_eval_gadd("postfix:postfix");
1493 sh_eval_qadd("test::report:7");
1494 sh_eval_radd("test:postfix/smtpd: disconnect from localhost.*");
1495 sh_eval_radd("trash:postfix/smtpd: disconnect.*");
1496 sh_eval_hadd("hspc05");
1497 sh_eval_gadd("cron:CRON");
1498 sh_eval_qadd("test:1:sum:7");
1499 sh_eval_radd("test:CRON: PAM adding faulty module: (/lib/security/.*.so)");
1500 sh_eval_radd("trash:.*");
1501 status = sh_add_watch("SYSLOG:/var/log/messages");
1502 sh_check_watches();
1503
1504 sh_dump_watches();
1505 exit(0);
1506
1507 printf("%d types\n",
1508 (int) (sizeof(sh_logtypes_def)/sizeof(struct sh_logfile_type)));
1509
1510 /* test sh_add_watch
1511 */
1512 status = sh_add_watch("");
1513 printf("%2d: zero length, expect -1\n", status);
1514 status = sh_add_watch(NULL);
1515 printf("%2d: NULL, expect -2\n", status);
1516 status = sh_add_watch("0123456789012345:/var/log/messages");
1517 printf("%2d: long, expect -2\n", status);
1518 status = sh_add_watch("012345678901234:/var/log/messages");
1519 printf("%2d: exact length, expect -3\n", status);
1520 status = sh_add_watch("01234567890123:56789");
1521 printf("%2d: short length, expect -3\n", status);
1522 status = sh_add_watch("SYSLOG:var/log/messages");
1523 printf("%2d: short badpath, expect -4\n", status);
1524 status = sh_add_watch("SYSLOG:/var/log/messages");
[237]1525 /* status = sh_add_watch("SYSLOG:/var/log/dpkg.log.1"); */
[183]1526 printf("%2d: short path ok, expect 0\n", status);
1527
1528 /* test sh_string_read
1529 */
1530 s = sh_string_new();
1531
1532 status = /*@i@*/mkstemp(template);
1533
1534 if (status < 0) {
1535 fprintf(stderr, "error in mkstemp!\n"); exit(EXIT_FAILURE); }
1536
1537 fp = fdopen(status, "r+");
1538 if (!fp) {
1539 fprintf(stderr, "error in fdopen!\n"); exit(EXIT_FAILURE); }
1540
[237]1541 for (i = 0; i < 80; ++i) { fputc ('a', fp); } fputc ('\n', fp); /* 0 */
1542 for (i = 0; i < 118; ++i) { fputc ('a', fp); } fputc ('\n', fp); /* 1 */
1543 for (i = 0; i < 119; ++i) { fputc ('a', fp); } fputc ('\n', fp); /* 2 */
1544 for (i = 0; i < 120; ++i) { fputc ('a', fp); } fputc ('\n', fp); /* 3 */
1545 for (i = 0; i < 121; ++i) { fputc ('a', fp); } fputc ('\n', fp); /* 4 */
[183]1546 for (i = 0; i < 238; ++i) { fputc ('a', fp); } fputc ('\n', fp);
1547 for (i = 0; i < 239; ++i) { fputc ('a', fp); } fputc ('\n', fp);
1548 for (i = 0; i < 240; ++i) { fputc ('a', fp); } fputc ('\n', fp);
1549 for (i = 0; i < 241; ++i) { fputc ('a', fp); } fputc ('\n', fp);
1550
1551 rewind(fp);
1552
1553 for (i = 0; i < 9; ++i)
1554 {
1555 status = (int) sh_string_read(s, fp, 120);
1556 printf("%d: status = %d, len = %d, size = %d\n",
1557 i, status, (int)s->len, (int)s->siz);
1558 if (status == -2)
1559 (void) sh_string_read(s, fp, 240);
1560 else
1561 printf("%s\n", s->str);
1562 }
1563
1564 rewind(fp);
1565
1566 (void) sh_string_truncate(s, 0);
1567
1568 for (i = 0; i < 9; ++i)
1569 {
1570 status = (int) sh_string_read(s, fp, 240);
1571 printf("%d: status = %d, len = %d, size = %d\n",
1572 i, status, (int)s->len, (int)s->siz);
1573 if (status == -2)
1574 (void) sh_string_read(s, fp, 240);
1575 else
1576 {
1577 for (status = 0; status < (int)s->len; ++status)
1578 {
1579 if (s->str[status] != 'a')
1580 {
1581 break;
1582 }
1583 }
1584 printf("%d %s\n", status, s->str);
1585 }
1586 }
1587
[252]1588 sl_fclose(FIL__, __LINE__, fp); remove(template);
[183]1589
1590
1591
1592 return 0;
1593}
1594#endif
1595
1596/* #ifdef USE_LOGFILE_MONITOR */
1597#endif
1598
Note: See TracBrowser for help on using the repository browser.