Skip to content

Instantly share code, notes, and snippets.

@lundman
Created December 3, 2020 06:42
Show Gist options
  • Save lundman/e775e1c59c938631a0826813951aab42 to your computer and use it in GitHub Desktop.
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.
/*
* 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