From 84f3db7f13dfbc5edbfe59527795664c924711bf Mon Sep 17 00:00:00 2001 From: olemorud Date: Tue, 13 Dec 2022 18:33:43 +0100 Subject: [PATCH] Add exec_tracker.c --- exec_tracker.c | 394 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 394 insertions(+) create mode 100644 exec_tracker.c diff --git a/exec_tracker.c b/exec_tracker.c new file mode 100644 index 0000000..32613f0 --- /dev/null +++ b/exec_tracker.c @@ -0,0 +1,394 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFFER_LEN 4096 + +static void handle_event(int inotify_instance, int *watched, char **filenames, int *counter, size_t watched_len); +static char** filenames_in_dir(char *path, size_t *return_len); +static char** filenames_in_path_envvar(size_t *return_len); +static void free_null_terminated_pointer_array(char **ptr); +static void print_event(uint32_t event); +static void save_result(char **filenames, int* counter, size_t arr_len); + + +/* This code is a bit over-commented in general */ +int +main(int argc, char * const argv[]) +{ + /* Get null terminated list of files to watch, create counter of same size */ + + char **file_names; + int *use_counter, *watched; + size_t files_len; + + switch (argc) { + case 1: + file_names = filenames_in_path_envvar(&files_len); + break; + case 2: + file_names = filenames_in_dir(argv[1], &files_len); + break; + default: + errx(EXIT_FAILURE, "Usage: `%s `. If dir is empty, directories in PATH will be monitored", argv[0]); + } + + if (file_names == NULL) + err(EXIT_FAILURE, "Error when finding files"); + + use_counter = calloc(files_len, sizeof(int)); + + if( use_counter == NULL ) + err(EXIT_FAILURE, "Failed to allocate %zu bytes for use counter", files_len*sizeof(int)); + + + /* Create file descriptor for accessing the inotify API */ + + int inotify_instance = inotify_init(); + + if ( inotify_instance == -1 ) + err(EXIT_FAILURE, "Failed to initialize inotify instance"); + + + /* Mark files for events */ + + watched = calloc(files_len, sizeof(int)); + + if( watched == NULL ) + err(EXIT_FAILURE, "Failed to allocate %zu bytes for watch descriptors", files_len*sizeof(int)); + + for(size_t i=0; file_names[i] != NULL; i++) { + watched[i] = inotify_add_watch(inotify_instance, file_names[i], IN_ACCESS); + + if(watched[i] == -1) + err(EXIT_FAILURE, "Cannot watch %s", file_names[i]); + } + + + /* Prepare for polling */ + + struct pollfd poll_fds[2]; + nfds_t fd_count = 2; + + poll_fds[0].fd = STDIN_FILENO; + poll_fds[0].events = POLLIN; + + poll_fds[1].fd = inotify_instance; + poll_fds[1].events = POLLIN; + + + /* Wait for events and/or terminal input */ + + int poll_num; + + fprintf(stderr, "Listening for events... (Press enter to end)\n"); + + while (1) { + poll_num = poll(poll_fds, fd_count, -1); + + if( poll_num == -1 && errno != EINTR) + err(EXIT_FAILURE, "Failed to poll"); + + if (poll_num == 0) + continue; + + if (poll_fds[0].revents & POLLIN) { + char c; + while (read(STDIN_FILENO, &c, 1) > 0 && c != '\n') + /* noop */; + save_result(file_names, use_counter, files_len); + break; + } + + if( poll_fds[1].revents & POLLIN ) { + handle_event(inotify_instance, watched, file_names, use_counter, files_len); + } + } + + + close(inotify_instance); + + free_null_terminated_pointer_array(file_names); + free(watched); + free(use_counter); + + return EXIT_SUCCESS; +} + + +/* Free allocated memory pointed to by pointers + in 'ptr', then free 'ptr' itself */ +static void +free_null_terminated_pointer_array(char **ptr) +{ + for(char **p = ptr; *p != NULL; p++ ){ + free(*p); + } + + free(ptr); +} + + +/* Returns a null terminated array of strings + for every file in PATH */ +static char** +filenames_in_path_envvar(size_t *return_len) { + size_t n_files, i, output_size; + char **filenames, **output, *saveptr, *path, *token; + void *tmp; + + path = strdup(getenv("PATH")); + + saveptr = NULL; + output_size = 1024; + output = malloc(output_size * sizeof(char*)); + + if (output == NULL) { + warn("Failed to allocate %zu bytes", output_size); + return NULL; + } + + token = strtok_r(path, ":", &saveptr); + + i = 0; + while (token != NULL) { + n_files = 0; + filenames = filenames_in_dir(token, &n_files); + n_files -= 1; + + if ( filenames == NULL ) { + fprintf(stderr, "failed to get filenames in %s\n", token); + token = strtok_r(NULL, ":", &saveptr); + continue; + } + + if (i + n_files > output_size) { + while(i + n_files > output_size) + output_size *= 2; + + tmp = realloc(output, output_size * sizeof(char*)); + + if (tmp == NULL) { + warn("failed to reallocate %zu bytes", output_size * sizeof(char*)); + return NULL; + } + + output = (char**) tmp; + } + + memcpy(output+i, filenames, n_files * sizeof(char*)); + i += n_files; + + free(filenames); + + token = strtok_r(NULL, ":", &saveptr); + } + output_size = i + 1; + tmp = realloc(output, output_size * sizeof(char*)); + + if (tmp != NULL){ + output = (char**)tmp; + } + + output[i] = NULL; + + free(path); + + *return_len = output_size; + + return output; +} + + +/* Returns null terminated array + of filepaths in directory */ +static char** +filenames_in_dir(char *path, size_t *return_len) +{ + size_t result_size, n_items=0; + char **result; + void *tmp; + struct dirent *dir_entity; + DIR *directory_stream; + + result_size = 64; + result = malloc(result_size * sizeof(char*)); + + if( result == NULL ){ + warn("Failed to malloc %zu bytes", result_size); + return NULL; + } + + directory_stream = opendir(path); + + if (directory_stream == NULL) { + warn("Failed to open directory %s", path); + goto fail; + } + + n_items = 0; + errno = 0; // To distinguish end of stream from an error, + // set errno to zero before calling readdir() + while ((dir_entity = readdir(directory_stream)) != NULL) { + if(n_items >= result_size){ + result_size *= 2; + result = realloc(result, result_size * sizeof(char*)); + } + + if (dir_entity->d_type == DT_REG ) { + result[n_items] = calloc(strlen(path) + strlen(dir_entity->d_name) + 2, sizeof(char)); + result[n_items] = strcat(result[n_items], path); + result[n_items] = strcat(result[n_items], "/"); + result[n_items] = strcat(result[n_items], dir_entity->d_name); + n_items += 1; + } + + // dirent *dir_entity is statically allocated, dont free()! + } + result_size = n_items+1; + tmp = realloc(result, result_size * sizeof(char*)); + + if ( tmp == NULL ) { + warn("Failed to realloc %zu bytes", result_size); + return NULL; + } + + result = (char**)tmp; + + result[n_items] = NULL; + + if ( errno != 0 ){ + warn("Error when reading directory %s", path); + goto fail; + } + + if ( closedir(directory_stream) == -1 ) + warn("Failed to close directory"); + + *return_len = result_size; + + return result; + +fail: + for(size_t i=0; ilen) { + event = (struct inotify_event *) p; + + for (int i=0; i < watched_len; i++) { + if( watched[i] == event->wd ) { + fprintf(stderr, "%s ", filenames[i]); + counter[i] += 1; + break; + } + } + } +} + + +/* Prints event mask values in a human-readable format */ +static void +print_event(uint32_t event) +{ + if(event & IN_ACCESS){ + fprintf(stderr,"IN_ACCESS "); + } + if(event & IN_ATTRIB){ + fprintf(stderr,"IN_ATTRIB "); + } + if(event & IN_CLOSE_WRITE){ + fprintf(stderr,"IN_CLOSE_WRITE "); + } + if(event & IN_CLOSE_NOWRITE){ + fprintf(stderr,"IN_CLOSE_NOWRITE "); + } + if(event & IN_CREATE){ + fprintf(stderr,"IN_CREATE "); + } + if(event & IN_DELETE){ + fprintf(stderr,"IN_DELETE "); + } + if(event & IN_DELETE_SELF){ + fprintf(stderr,"IN_DELETE_SELF "); + } + if(event & IN_MODIFY){ + fprintf(stderr,"IN_MODIFY "); + } + if(event & IN_MOVE_SELF){ + fprintf(stderr,"IN_MOVE_SELF "); + } + if(event & IN_MOVED_FROM){ + fprintf(stderr,"IN_MOVED_FROM "); + } + if(event & IN_OPEN){ + fprintf(stderr,"IN_OPEN "); + } +} + + +/* Saves number of recorded events for each + executable t 'uses.log' */ +static void +save_result(char **filenames, int* counter, size_t arr_len) +{ + FILE *savefile; + + savefile = fopen("uses.log", "w"); + + if (savefile == NULL) { + warn("Failed to save to file"); + return; + } + + for (size_t i=0; i