Created
December 3, 2020 06:42
-
-
Save lundman/e775e1c59c938631a0826813951aab42 to your computer and use it in GitHub Desktop.
Small tool using libfswatcher to read a list of files to monitor - requires patched libfswatcher for watch_access method.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Quick & Dirty libfswatch wrapper | |
* | |
* gcc -o lfswatch lfswatch.c -I/export/home/lundman/fswatch-1.15.0/libfswatch/src/libfswatch/c -L/export/home/lundman/fswatch-1.15.0/libfswatch/src/libfswatch/.libs -R/export/home/lundman/fswatch-1.15.0/libfswatch/src/libfswatch/.libs -lfswatch | |
* | |
* - lundman | |
*/ | |
#include <stdio.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <stdlib.h> | |
#include <pthread.h> | |
#include <string.h> | |
#include <strings.h> | |
#include <errno.h> | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <syslog.h> | |
#include "libfswatch.h" | |
void my_callback(fsw_cevent const * const events, | |
const unsigned int event_num, | |
void * data); | |
static int verbose = 0; | |
struct entry { | |
char *name; | |
int strlen; | |
int isdir; | |
int access; | |
struct entry *next; | |
}; | |
typedef struct entry entry_t; | |
static entry_t *entry_head = NULL; | |
static void * | |
start_monitor_impl(void *arg) | |
{ | |
FSW_HANDLE handle = (FSW_HANDLE) arg; | |
int rc; | |
rc = fsw_start_monitor(handle); | |
if (rc != FSW_OK) | |
fsw_last_error(); | |
} | |
static int | |
read_file(char *file) | |
{ | |
FILE *fd; | |
char buffer[256], *r; | |
uint_t line = 0; | |
struct stat stbf; | |
fd = fopen(file, "r"); | |
if (fd == NULL) { | |
fprintf(stderr, "Unable to read file '%s': %s\r\n", | |
file, strerror(errno)); | |
return -1; | |
} | |
while(fgets(buffer, sizeof(buffer), fd) != NULL) { | |
line++; | |
if (buffer[0] != '/') { | |
printf("Path does not start with '/' line %u -- skipping\n", line); | |
continue; | |
} | |
// This code is weak, ca not handle spaces | |
r = strrchr(buffer, ' '); | |
if (r == NULL) { | |
printf("Unable to parse line %u -- skipping\n", line); | |
continue; | |
} | |
*r = 0; | |
if (stat(buffer, &stbf) != 0) { | |
printf("Unable to stat file '%s' line %u -- skipping\n", | |
buffer, line); | |
continue; | |
} | |
entry_t *e; | |
e = malloc(sizeof (entry_t)); | |
if (e == NULL) { | |
fprintf(stderr, "Out of memory\r\n"); | |
exit(-1); | |
} | |
e->name = strdup(buffer); | |
e->strlen = strlen(buffer); | |
e->access = strtol(&r[1], NULL, 2); | |
if (S_ISDIR(stbf.st_mode)) { | |
e->isdir = 1; | |
if (verbose) | |
printf("'%s' adding directory: 0x%x\n", buffer, e->access); | |
} else { | |
e->isdir = 0; | |
if (verbose) | |
printf("'%s' adding file: 0x%x\n", buffer, e->access); | |
} | |
e->next = entry_head; | |
entry_head = e; | |
} | |
return 0; | |
} | |
int | |
main(int argc, char **argv) | |
{ | |
int rc; | |
pthread_t thread1, thread2; | |
entry_t *e; | |
if (argc == 3) { | |
if (strcmp("-d", argv[1]) == 0) | |
verbose = 1; | |
read_file(argv[2]); | |
} else if (argc == 2) { | |
read_file(argv[1]); | |
} else { | |
printf("%s filename\n" | |
"\n" | |
"Read <filename> of entries to watch, and what to watch for.\n" | |
"/var/tmp 00000000000010\n" | |
"/etc/passwd 00000000000001\n\n" | |
"\n" | |
"Check any creations in /var/tmp, and any access to /etc/passwd.\n" | |
"\n" | |
" .- AttributeChange\n" | |
" |.- OwnerChanged\n" | |
" ||.- Renamed\n" | |
" |||.- Removed\n" | |
" ||||.- Updated / Modified\n" | |
" |||||.- Created\n" | |
" ||||||.- PlatformSpecific (FileAccess)\n" | |
" |||||||\n" | |
"000000001111111\n" | |
"\n", | |
argv[0]); | |
exit(0); | |
} | |
rc = fsw_init_library(); | |
if (rc != FSW_OK) { | |
fprintf(stderr, "Error calling fsw_init_library: %d\r\n", | |
rc); | |
fsw_last_error(); | |
exit(-1); | |
} | |
if (verbose) | |
printf("libfswatch init OK.\n"); | |
// We have to create two monitor handles here, as | |
// files with watch_access() enabled, does not mix with | |
// directories. | |
FSW_HANDLE handle1, handle2; | |
handle1 = fsw_init_session(fen_monitor_type); | |
if (handle1 == (FSW_HANDLE)FSW_INVALID_HANDLE) { | |
fprintf(stderr, "Error calling fsw_init_session: %d\r\n", | |
rc); | |
fsw_last_error(); | |
exit(-1); | |
} | |
for (e = entry_head; e; e = e->next) { | |
if (e->isdir) | |
rc = fsw_add_path(handle1, e->name); | |
} | |
// rc = fsw_add_path(handle1, "/var/tmp"); | |
if (rc != FSW_OK) | |
fsw_last_error(); | |
rc = fsw_set_callback(handle1, my_callback, NULL); | |
if (rc != FSW_OK) | |
fsw_last_error(); | |
rc = fsw_set_recursive(handle1, true); | |
if (rc != FSW_OK) | |
fsw_last_error(); | |
handle2 = fsw_init_session(fen_monitor_type); | |
if (handle2 == (FSW_HANDLE)FSW_INVALID_HANDLE) { | |
fprintf(stderr, "Error calling fsw_init_session: %d\r\n", | |
rc); | |
fsw_last_error(); | |
exit(-1); | |
} | |
for (e = entry_head; e; e = e->next) { | |
if (!e->isdir) | |
rc = fsw_add_path(handle2, e->name); | |
} | |
if (rc != FSW_OK) | |
fsw_last_error(); | |
rc = fsw_set_callback(handle2, my_callback, NULL); | |
if (rc != FSW_OK) | |
fsw_last_error(); | |
rc = fsw_set_watch_access(handle2, true); | |
if (rc != FSW_OK) | |
fsw_last_error(); | |
pthread_create(&thread1, NULL, start_monitor_impl, (void *) handle1); | |
pthread_create(&thread2, NULL, start_monitor_impl, (void *) handle2); | |
if (verbose) | |
printf("Monitoring...\n"); | |
// Wait for threads to quit | |
pthread_join(thread1, NULL); | |
pthread_join(thread2, NULL); | |
if (verbose) | |
printf("Shutting down libfswatch\n"); | |
if (fsw_is_running(handle1)) | |
fsw_stop_monitor(handle1); | |
if (fsw_is_running(handle2)) | |
fsw_stop_monitor(handle2); | |
fsw_destroy_session(handle1); | |
fsw_destroy_session(handle2); | |
return (0); | |
} | |
// Internal wrapper to make PlatformSpecific nicer | |
static char * | |
event_flag_name(enum fsw_event_flag flag) | |
{ | |
if (flag & PlatformSpecific) | |
return "FileAccess|FileExecute"; | |
if (flag & 0x40) | |
return "OwnerModified"; | |
return fsw_get_event_flag_name(flag); | |
} | |
void | |
my_callback(fsw_cevent const * const events, | |
const unsigned int event_num, | |
void *data) | |
{ | |
char buffer[1024]; | |
if (verbose) | |
printf("Got %d event(s) for '%s'\n", | |
events->flags_num, events->path); | |
entry_t *e; | |
buffer[0] = 0; | |
// Find entry. | |
for (e = entry_head; e; e = e->next) { | |
if (strncmp(events->path, e->name, e->strlen) == 0) { | |
if (verbose) | |
printf("Found match. Access: 0x%x ", e->access); | |
for (int i=0; i < events->flags_num; i++) { | |
// bit AND the flags and access to act | |
if (events->flags[i] & e->access) { | |
snprintf(buffer, sizeof(buffer), | |
"| 0x%x: %s ", | |
events->flags[0], | |
event_flag_name(events->flags[i])); | |
} | |
} | |
break; // Match found, bail out. | |
} | |
} | |
if (verbose) | |
printf("\n"); | |
// If we built up a string, as in, access AND flags matched, then | |
// we should produce syslog entry. | |
if (strlen(buffer) > 0) { | |
static char lastevent[1024] = ""; | |
static time_t tlast = 0; | |
time_t tnow; | |
int delta; | |
if (tlast == 0) | |
time(&tlast); | |
time(&tnow); | |
delta = tnow - tlast; | |
// Check against the last event, so we can eat repetitions | |
if (delta < 60 && | |
strcmp(buffer, lastevent) == 0) { | |
if (verbose) | |
printf("Ignoring repeated event\n"); | |
return; | |
} | |
strcpy(lastevent, buffer); | |
time(&tlast); | |
if (verbose) | |
printf("syslog(audit, event): '%s' : %s\n", | |
events->path, | |
buffer); | |
syslog(LOG_NOTICE|LOG_AUDIT, | |
"fswatcher: '%s' : %s", | |
events->path, buffer); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment