| #define _GNU_SOURCE |
| #include <sys/inotify.h> |
| #include <errno.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <ctype.h> |
| #include <sys/types.h> |
| #include <syslog.h> |
| #include "../setfiles/restore.h" |
| #include <glob.h> |
| #include <libgen.h> |
| #include <sys/stat.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include <fcntl.h> |
| #include <selinux/selinux.h> |
| #include "restorecond.h" |
| #include "stringslist.h" |
| #include "utmpwatcher.h" |
| |
| /* size of the event structure, not counting name */ |
| #define EVENT_SIZE (sizeof (struct inotify_event)) |
| /* reasonable guess as to size of 1024 events */ |
| #define BUF_LEN (1024 * (EVENT_SIZE + 16)) |
| |
| |
| struct watchList { |
| struct watchList *next; |
| int wd; |
| char *dir; |
| struct stringsList *files; |
| }; |
| struct watchList *firstDir = NULL; |
| |
| int watch_list_isempty(void) { |
| return firstDir == NULL; |
| } |
| |
| void watch_list_add(int fd, const char *path) |
| { |
| struct watchList *ptr = NULL; |
| size_t i = 0; |
| struct watchList *prev = NULL; |
| glob_t globbuf; |
| char *x = strdup(path); |
| if (!x) exitApp("Out of Memory"); |
| char *file = basename(x); |
| char *dir = dirname(x); |
| ptr = firstDir; |
| |
| if (exclude(path)) goto end; |
| |
| globbuf.gl_offs = 1; |
| if (glob(path, |
| GLOB_TILDE | GLOB_PERIOD, |
| NULL, |
| &globbuf) >= 0) { |
| for (i=0; i < globbuf.gl_pathc; i++) { |
| int len = strlen(globbuf.gl_pathv[i]) -2; |
| if (len > 0 && strcmp(&globbuf.gl_pathv[i][len--], "/.") == 0) continue; |
| if (len > 0 && strcmp(&globbuf.gl_pathv[i][len], "/..") == 0) continue; |
| if (process_one_realpath(globbuf.gl_pathv[i], 0) > 0) |
| process_one_realpath(globbuf.gl_pathv[i], 1); |
| } |
| globfree(&globbuf); |
| } |
| |
| while (ptr != NULL) { |
| if (strcmp(dir, ptr->dir) == 0) { |
| strings_list_add(&ptr->files, file); |
| goto end; |
| } |
| prev = ptr; |
| ptr = ptr->next; |
| } |
| ptr = calloc(1, sizeof(struct watchList)); |
| |
| if (!ptr) exitApp("Out of Memory"); |
| |
| ptr->wd = inotify_add_watch(fd, dir, IN_CREATE | IN_MOVED_TO); |
| if (ptr->wd == -1) { |
| free(ptr); |
| if (! run_as_user) |
| syslog(LOG_ERR, "Unable to watch (%s) %s\n", |
| path, strerror(errno)); |
| goto end; |
| } |
| |
| ptr->dir = strdup(dir); |
| if (!ptr->dir) |
| exitApp("Out of Memory"); |
| |
| strings_list_add(&ptr->files, file); |
| if (prev) |
| prev->next = ptr; |
| else |
| firstDir = ptr; |
| |
| if (debug_mode) |
| printf("%d: Dir=%s, File=%s\n", ptr->wd, ptr->dir, file); |
| |
| end: |
| free(x); |
| return; |
| } |
| |
| /* |
| A file was in a direcroty has been created. This function checks to |
| see if it is one that we are watching. |
| */ |
| |
| int watch_list_find(int wd, const char *file) |
| { |
| struct watchList *ptr = NULL; |
| ptr = firstDir; |
| if (debug_mode) |
| printf("%d: File=%s\n", wd, file); |
| while (ptr != NULL) { |
| if (ptr->wd == wd) { |
| int exact=0; |
| if (strings_list_find(ptr->files, file, &exact) == 0) { |
| char *path = NULL; |
| if (asprintf(&path, "%s/%s", ptr->dir, file) < |
| 0) |
| exitApp("Error allocating memory."); |
| |
| process_one_realpath(path, 0); |
| free(path); |
| return 0; |
| } |
| if (debug_mode) |
| strings_list_print(ptr->files); |
| |
| /* Not found in this directory */ |
| return -1; |
| } |
| ptr = ptr->next; |
| } |
| /* Did not find a directory */ |
| return -1; |
| } |
| |
| void watch_list_free(int fd) |
| { |
| struct watchList *ptr = NULL; |
| struct watchList *prev = NULL; |
| ptr = firstDir; |
| |
| while (ptr != NULL) { |
| inotify_rm_watch(fd, ptr->wd); |
| strings_list_free(ptr->files); |
| free(ptr->dir); |
| prev = ptr; |
| ptr = ptr->next; |
| free(prev); |
| } |
| firstDir = NULL; |
| } |
| |
| /* |
| Inotify watch loop |
| */ |
| int watch(int fd, const char *watch_file) |
| { |
| char buf[BUF_LEN]; |
| int len, i = 0; |
| if (firstDir == NULL) return 0; |
| |
| len = read(fd, buf, BUF_LEN); |
| if (len < 0) { |
| if (terminate == 0) { |
| syslog(LOG_ERR, "Read error (%s)", strerror(errno)); |
| return 0; |
| } |
| syslog(LOG_ERR, "terminated"); |
| return -1; |
| } else if (!len) |
| /* BUF_LEN too small? */ |
| return -1; |
| while (i < len) { |
| struct inotify_event *event; |
| event = (struct inotify_event *)&buf[i]; |
| if (debug_mode) |
| printf("wd=%d mask=%u cookie=%u len=%u\n", |
| event->wd, event->mask, |
| event->cookie, event->len); |
| if (event->mask & ~IN_IGNORED) { |
| if (event->wd == master_wd) |
| read_config(fd, watch_file); |
| else { |
| switch (utmpwatcher_handle(fd, event->wd)) { |
| case -1: /* Message was not for utmpwatcher */ |
| if (event->len) |
| watch_list_find(event->wd, event->name); |
| break; |
| case 1: /* utmp has changed need to reload */ |
| read_config(fd, watch_file); |
| break; |
| |
| default: /* No users logged in or out */ |
| break; |
| } |
| } |
| } |
| |
| i += EVENT_SIZE + event->len; |
| } |
| return 0; |
| } |
| |
| static void process_config(int fd, FILE * cfg) |
| { |
| char *line_buf = NULL; |
| size_t len = 0; |
| |
| while (getline(&line_buf, &len, cfg) > 0) { |
| char *buffer = line_buf; |
| while (isspace(*buffer)) |
| buffer++; |
| if (buffer[0] == '#') |
| continue; |
| int l = strlen(buffer) - 1; |
| if (l <= 0) |
| continue; |
| buffer[l] = 0; |
| if (buffer[0] == '~') { |
| if (run_as_user) { |
| char *ptr=NULL; |
| if (asprintf(&ptr, "%s%s", homedir, &buffer[1]) < 0) |
| exitApp("Error allocating memory."); |
| |
| watch_list_add(fd, ptr); |
| free(ptr); |
| } else { |
| utmpwatcher_add(fd, &buffer[1]); |
| } |
| } else { |
| watch_list_add(fd, buffer); |
| } |
| } |
| free(line_buf); |
| } |
| |
| /* |
| Read config file ignoring Comment lines |
| Files specified one per line. Files with "~" will be expanded to the logged in users |
| homedirs. |
| */ |
| |
| void read_config(int fd, const char *watch_file_path) |
| { |
| |
| FILE *cfg = NULL; |
| if (debug_mode) |
| printf("Read Config\n"); |
| |
| watch_list_free(fd); |
| |
| cfg = fopen(watch_file_path, "r"); |
| if (!cfg){ |
| perror(watch_file_path); |
| exitApp("Error reading config file"); |
| } |
| process_config(fd, cfg); |
| fclose(cfg); |
| |
| inotify_rm_watch(fd, master_wd); |
| master_wd = |
| inotify_add_watch(fd, watch_file_path, IN_MOVED_FROM | IN_MODIFY); |
| if (master_wd == -1) |
| exitApp("Error watching config file."); |
| } |