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