1#define _GNU_SOURCE 2#include <sys/inotify.h> 3#include <errno.h> 4#include <stdio.h> 5#include <stdlib.h> 6#include <string.h> 7#include <unistd.h> 8#include <ctype.h> 9#include <sys/types.h> 10#include <syslog.h> 11#include "restore.h" 12#include <glob.h> 13#include <libgen.h> 14#include <sys/stat.h> 15#include <string.h> 16#include <stdio.h> 17#include <fcntl.h> 18#include <selinux/selinux.h> 19#include "restorecond.h" 20#include "stringslist.h" 21#include "utmpwatcher.h" 22 23/* size of the event structure, not counting name */ 24#define EVENT_SIZE (sizeof (struct inotify_event)) 25/* reasonable guess as to size of 1024 events */ 26#define BUF_LEN (1024 * (EVENT_SIZE + 16)) 27 28struct watchList { 29 struct watchList *next; 30 int wd; 31 char *dir; 32 struct stringsList *files; 33}; 34struct watchList *firstDir = NULL; 35 36int watch_list_isempty(void) { 37 return firstDir == NULL; 38} 39 40void watch_list_add(int fd, const char *path) 41{ 42 struct watchList *ptr = NULL; 43 size_t i = 0; 44 struct watchList *prev = NULL; 45 glob_t globbuf; 46 char *x = strdup(path); 47 if (!x) exitApp("Out of Memory"); 48 char *file = basename(x); 49 char *dir = dirname(x); 50 ptr = firstDir; 51 int len; 52 53 globbuf.gl_offs = 1; 54 if (glob(path, 55 GLOB_TILDE | GLOB_PERIOD, 56 NULL, 57 &globbuf) >= 0) { 58 for (i = 0; i < globbuf.gl_pathc; i++) { 59 len = strlen(globbuf.gl_pathv[i]) - 2; 60 if (len > 0 && 61 strcmp(&globbuf.gl_pathv[i][len--], "/.") == 0) 62 continue; 63 if (len > 0 && 64 strcmp(&globbuf.gl_pathv[i][len], "/..") == 0) 65 continue; 66 selinux_restorecon(globbuf.gl_pathv[i], 67 r_opts.restorecon_flags); 68 } 69 globfree(&globbuf); 70 } 71 72 while (ptr != NULL) { 73 if (strcmp(dir, ptr->dir) == 0) { 74 strings_list_add(&ptr->files, file); 75 goto end; 76 } 77 prev = ptr; 78 ptr = ptr->next; 79 } 80 ptr = calloc(1, sizeof(struct watchList)); 81 82 if (!ptr) exitApp("Out of Memory"); 83 84 ptr->wd = inotify_add_watch(fd, dir, IN_CREATE | IN_MOVED_TO); 85 if (ptr->wd == -1) { 86 free(ptr); 87 if (! run_as_user) 88 syslog(LOG_ERR, "Unable to watch (%s) %s\n", 89 path, strerror(errno)); 90 goto end; 91 } 92 93 ptr->dir = strdup(dir); 94 if (!ptr->dir) 95 exitApp("Out of Memory"); 96 97 strings_list_add(&ptr->files, file); 98 if (prev) 99 prev->next = ptr; 100 else 101 firstDir = ptr; 102 103 if (debug_mode) 104 printf("%d: Dir=%s, File=%s\n", ptr->wd, ptr->dir, file); 105 106end: 107 free(x); 108 return; 109} 110 111/* 112 A file was in a direcroty has been created. This function checks to 113 see if it is one that we are watching. 114*/ 115 116int watch_list_find(int wd, const char *file) 117{ 118 struct watchList *ptr = NULL; 119 120 ptr = firstDir; 121 122 if (debug_mode) 123 printf("%d: File=%s\n", wd, file); 124 while (ptr != NULL) { 125 if (ptr->wd == wd) { 126 int exact=0; 127 if (strings_list_find(ptr->files, file, &exact) == 0) { 128 char *path = NULL; 129 if (asprintf(&path, "%s/%s", ptr->dir, file) < 130 0) 131 exitApp("Error allocating memory."); 132 133 selinux_restorecon(path, 134 r_opts.restorecon_flags); 135 free(path); 136 return 0; 137 } 138 if (debug_mode) 139 strings_list_print(ptr->files); 140 141 /* Not found in this directory */ 142 return -1; 143 } 144 ptr = ptr->next; 145 } 146 /* Did not find a directory */ 147 return -1; 148} 149 150void watch_list_free(int fd) 151{ 152 struct watchList *ptr = NULL; 153 struct watchList *prev = NULL; 154 ptr = firstDir; 155 156 while (ptr != NULL) { 157 inotify_rm_watch(fd, ptr->wd); 158 strings_list_free(ptr->files); 159 free(ptr->dir); 160 prev = ptr; 161 ptr = ptr->next; 162 free(prev); 163 } 164 firstDir = NULL; 165} 166 167/* 168 Inotify watch loop 169*/ 170int watch(int fd, const char *watch_file) 171{ 172 char buf[BUF_LEN]; 173 int len, i = 0; 174 if (firstDir == NULL) return 0; 175 176 len = read(fd, buf, BUF_LEN); 177 if (len < 0) { 178 if (terminate == 0) { 179 syslog(LOG_ERR, "Read error (%s)", strerror(errno)); 180 return 0; 181 } 182 syslog(LOG_INFO, "terminated"); 183 return -1; 184 } else if (!len) 185 /* BUF_LEN too small? */ 186 return -1; 187 while (i < len) { 188 struct inotify_event *event; 189 event = (struct inotify_event *)&buf[i]; 190 if (debug_mode) 191 printf("wd=%d mask=%u cookie=%u len=%u\n", 192 event->wd, event->mask, 193 event->cookie, event->len); 194 if (event->mask & ~IN_IGNORED) { 195 if (event->wd == master_wd) 196 read_config(fd, watch_file); 197 else { 198 switch (utmpwatcher_handle(fd, event->wd)) { 199 case -1: /* Message was not for utmpwatcher */ 200 if (event->len) 201 watch_list_find(event->wd, event->name); 202 break; 203 case 1: /* utmp has changed need to reload */ 204 read_config(fd, watch_file); 205 break; 206 207 default: /* No users logged in or out */ 208 break; 209 } 210 } 211 } 212 213 i += EVENT_SIZE + event->len; 214 } 215 return 0; 216} 217 218static void process_config(int fd, FILE * cfg) 219{ 220 char *line_buf = NULL; 221 size_t len = 0; 222 223 while (getline(&line_buf, &len, cfg) > 0) { 224 char *buffer = line_buf; 225 while (isspace(*buffer)) 226 buffer++; 227 if (buffer[0] == '#') 228 continue; 229 int l = strlen(buffer) - 1; 230 if (l <= 0) 231 continue; 232 buffer[l] = 0; 233 if (buffer[0] == '~') { 234 if (run_as_user) { 235 char *ptr=NULL; 236 if (asprintf(&ptr, "%s%s", homedir, &buffer[1]) < 0) 237 exitApp("Error allocating memory."); 238 239 watch_list_add(fd, ptr); 240 free(ptr); 241 } else { 242 utmpwatcher_add(fd, &buffer[1]); 243 } 244 } else { 245 watch_list_add(fd, buffer); 246 } 247 } 248 free(line_buf); 249} 250 251/* 252 Read config file ignoring Comment lines 253 Files specified one per line. Files with "~" will be expanded to the logged in users 254 homedirs. 255*/ 256 257void read_config(int fd, const char *watch_file_path) 258{ 259 260 FILE *cfg = NULL; 261 if (debug_mode) 262 printf("Read Config\n"); 263 264 watch_list_free(fd); 265 266 cfg = fopen(watch_file_path, "r"); 267 if (!cfg){ 268 perror(watch_file_path); 269 exitApp("Error reading config file"); 270 } 271 process_config(fd, cfg); 272 fclose(cfg); 273 274 inotify_rm_watch(fd, master_wd); 275 master_wd = 276 inotify_add_watch(fd, watch_file_path, IN_MOVED_FROM | IN_MODIFY); 277 if (master_wd == -1) 278 exitApp("Error watching config file."); 279} 280