| 1 | /* SAMHAIN file system integrity testing                                   */ | 
|---|
| 2 | /* Copyright (C) 2011       Rainer Wichmann                                */ | 
|---|
| 3 | /*                                                                         */ | 
|---|
| 4 | /*  This program is free software; you can redistribute it                 */ | 
|---|
| 5 | /*  and/or modify                                                          */ | 
|---|
| 6 | /*  it under the terms of the GNU General Public License as                */ | 
|---|
| 7 | /*  published by                                                           */ | 
|---|
| 8 | /*  the Free Software Foundation; either version 2 of the License, or      */ | 
|---|
| 9 | /*  (at your option) any later version.                                    */ | 
|---|
| 10 | /*                                                                         */ | 
|---|
| 11 | /*  This program is distributed in the hope that it will be useful,        */ | 
|---|
| 12 | /*  but WITHOUT ANY WARRANTY; without even the implied warranty of         */ | 
|---|
| 13 | /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          */ | 
|---|
| 14 | /*  GNU General Public License for more details.                           */ | 
|---|
| 15 | /*                                                                         */ | 
|---|
| 16 | /*  You should have received a copy of the GNU General Public License      */ | 
|---|
| 17 | /*  along with this program; if not, write to the Free Software            */ | 
|---|
| 18 | /*  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.              */ | 
|---|
| 19 |  | 
|---|
| 20 | /*************************************************************************** | 
|---|
| 21 | * | 
|---|
| 22 | * This file provides a module for samhain to use inotify for file checking. | 
|---|
| 23 | * | 
|---|
| 24 | */ | 
|---|
| 25 |  | 
|---|
| 26 | #include "config_xor.h" | 
|---|
| 27 |  | 
|---|
| 28 | #if (defined(SH_WITH_CLIENT) || defined(SH_STANDALONE)) | 
|---|
| 29 |  | 
|---|
| 30 | #include "samhain.h" | 
|---|
| 31 | #include "sh_utils.h" | 
|---|
| 32 | #include "sh_modules.h" | 
|---|
| 33 | #include "sh_pthread.h" | 
|---|
| 34 | #include "sh_inotify.h" | 
|---|
| 35 | #include "sh_unix.h" | 
|---|
| 36 | #include "sh_hash.h" | 
|---|
| 37 | #include "sh_files.h" | 
|---|
| 38 | #include "sh_ignore.h" | 
|---|
| 39 |  | 
|---|
| 40 | #define FIL__  _("sh_fInotify.c") | 
|---|
| 41 |  | 
|---|
| 42 | sh_watches sh_file_watches = SH_INOTIFY_INITIALIZER; | 
|---|
| 43 |  | 
|---|
| 44 | #if defined(HAVE_SYS_INOTIFY_H) | 
|---|
| 45 |  | 
|---|
| 46 | static sh_watches sh_file_missing = SH_INOTIFY_INITIALIZER; | 
|---|
| 47 |  | 
|---|
| 48 | #include <sys/inotify.h> | 
|---|
| 49 |  | 
|---|
| 50 | /* --- Configuration ------- */ | 
|---|
| 51 |  | 
|---|
| 52 | static int ShfInotifyActive = S_FALSE; | 
|---|
| 53 |  | 
|---|
| 54 | static unsigned long ShfInotifyWatches = 0; | 
|---|
| 55 |  | 
|---|
| 56 | static int sh_fInotify_active(const char *s) | 
|---|
| 57 | { | 
|---|
| 58 | int value; | 
|---|
| 59 |  | 
|---|
| 60 | SL_ENTER(_("sh_fInotify_active")); | 
|---|
| 61 | value = sh_util_flagval(s, &ShfInotifyActive); | 
|---|
| 62 | if (value == 0 && ShfInotifyActive != S_FALSE) | 
|---|
| 63 | { | 
|---|
| 64 | sh.flag.inotify |= SH_INOTIFY_USE; | 
|---|
| 65 | sh.flag.inotify |= SH_INOTIFY_DOSCAN; | 
|---|
| 66 | sh.flag.inotify |= SH_INOTIFY_NEEDINIT; | 
|---|
| 67 | } | 
|---|
| 68 | if (value == 0 && ShfInotifyActive == S_FALSE) | 
|---|
| 69 | { | 
|---|
| 70 | sh.flag.inotify = 0; | 
|---|
| 71 | } | 
|---|
| 72 | SL_RETURN((value), _("sh_fInotify_active")); | 
|---|
| 73 | } | 
|---|
| 74 |  | 
|---|
| 75 | static int sh_fInotify_watches(const char *s) | 
|---|
| 76 | { | 
|---|
| 77 | int retval = -1; | 
|---|
| 78 | char * foo; | 
|---|
| 79 | unsigned long value; | 
|---|
| 80 |  | 
|---|
| 81 | SL_ENTER(_("sh_fInotify_watches")); | 
|---|
| 82 |  | 
|---|
| 83 | value = strtoul(s, &foo, 0); | 
|---|
| 84 | if (*foo == '\0') | 
|---|
| 85 | { | 
|---|
| 86 | ShfInotifyWatches = (value > 2147483647) ? 2147483647 /* MAX_INT_32 */: value; | 
|---|
| 87 | retval = 0; | 
|---|
| 88 | } | 
|---|
| 89 | SL_RETURN((retval), _("sh_fInotify_watches")); | 
|---|
| 90 | } | 
|---|
| 91 |  | 
|---|
| 92 |  | 
|---|
| 93 | sh_rconf sh_fInotify_table[] = { | 
|---|
| 94 | { | 
|---|
| 95 | N_("inotifyactive"), | 
|---|
| 96 | sh_fInotify_active, | 
|---|
| 97 | }, | 
|---|
| 98 | { | 
|---|
| 99 | N_("inotifywatches"), | 
|---|
| 100 | sh_fInotify_watches, | 
|---|
| 101 | }, | 
|---|
| 102 | { | 
|---|
| 103 | NULL, | 
|---|
| 104 | NULL | 
|---|
| 105 | } | 
|---|
| 106 | }; | 
|---|
| 107 |  | 
|---|
| 108 | /* --- End Configuration --- */ | 
|---|
| 109 |  | 
|---|
| 110 | static int sh_fInotify_init_internal(void); | 
|---|
| 111 | static int sh_fInotify_process(struct inotify_event * event); | 
|---|
| 112 | static int sh_fInotify_report(struct inotify_event * event, char * filename, | 
|---|
| 113 | int class, unsigned long check_mask, int ftype, int rdepth); | 
|---|
| 114 |  | 
|---|
| 115 | int sh_fInotify_init(struct mod_type * arg) | 
|---|
| 116 | { | 
|---|
| 117 | #ifndef HAVE_PTHREAD | 
|---|
| 118 | (void) arg; | 
|---|
| 119 | return SH_MOD_FAILED; | 
|---|
| 120 | #else | 
|---|
| 121 |  | 
|---|
| 122 | if (ShfInotifyActive == S_FALSE) | 
|---|
| 123 | return SH_MOD_FAILED; | 
|---|
| 124 |  | 
|---|
| 125 | if (sh.flag.checkSum == SH_CHECK_INIT) | 
|---|
| 126 | return SH_MOD_FAILED; | 
|---|
| 127 |  | 
|---|
| 128 | if (arg != NULL && arg->initval < 0 && | 
|---|
| 129 | (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE)) | 
|---|
| 130 | { | 
|---|
| 131 | /* Init from main thread */ | 
|---|
| 132 | sh.flag.inotify |= SH_INOTIFY_DOSCAN; | 
|---|
| 133 | sh.flag.inotify |= SH_INOTIFY_NEEDINIT; | 
|---|
| 134 |  | 
|---|
| 135 | if (0 == sh_pthread_create(sh_threaded_module_run, (void *)arg)) | 
|---|
| 136 | { | 
|---|
| 137 | return SH_MOD_THREAD; | 
|---|
| 138 | } | 
|---|
| 139 | else | 
|---|
| 140 | { | 
|---|
| 141 | sh.flag.inotify = 0; | 
|---|
| 142 | return SH_MOD_FAILED; | 
|---|
| 143 | } | 
|---|
| 144 | } | 
|---|
| 145 | else if (arg != NULL && arg->initval < 0 && | 
|---|
| 146 | (sh.flag.isdaemon != S_TRUE && sh.flag.loop != S_TRUE)) | 
|---|
| 147 | { | 
|---|
| 148 | sh.flag.inotify = 0; | 
|---|
| 149 | return SH_MOD_FAILED; | 
|---|
| 150 | } | 
|---|
| 151 | else if (arg != NULL && arg->initval == SH_MOD_THREAD && | 
|---|
| 152 | (sh.flag.isdaemon == S_TRUE || sh.flag.loop == S_TRUE)) | 
|---|
| 153 | { | 
|---|
| 154 | /* Reconfigure from main thread */ | 
|---|
| 155 | /* sh_fInotify_init_internal(); */ | 
|---|
| 156 | sh.flag.inotify |= SH_INOTIFY_DOSCAN; | 
|---|
| 157 | sh.flag.inotify |= SH_INOTIFY_NEEDINIT; | 
|---|
| 158 | return SH_MOD_THREAD; | 
|---|
| 159 | } | 
|---|
| 160 |  | 
|---|
| 161 | /* Within thread, init */ | 
|---|
| 162 | return sh_fInotify_init_internal(); | 
|---|
| 163 | #endif | 
|---|
| 164 | } | 
|---|
| 165 |  | 
|---|
| 166 | int sh_fInotify_run() | 
|---|
| 167 | { | 
|---|
| 168 | ssize_t len = -1; | 
|---|
| 169 | char *  buffer; | 
|---|
| 170 | static int count  = 0; | 
|---|
| 171 | static int count2 = 0; | 
|---|
| 172 |  | 
|---|
| 173 | if (ShfInotifyActive == S_FALSE) | 
|---|
| 174 | { | 
|---|
| 175 | return SH_MOD_FAILED; | 
|---|
| 176 | } | 
|---|
| 177 |  | 
|---|
| 178 | if ( (sh.flag.inotify & SH_INOTIFY_DOSCAN) || | 
|---|
| 179 | (sh.flag.inotify & SH_INOTIFY_NEEDINIT)) | 
|---|
| 180 | { | 
|---|
| 181 | if (0 != sh_fInotify_init_internal()) | 
|---|
| 182 | { | 
|---|
| 183 | return SH_MOD_FAILED; | 
|---|
| 184 | } | 
|---|
| 185 | } | 
|---|
| 186 |  | 
|---|
| 187 | buffer = SH_ALLOC(16384); | 
|---|
| 188 |  | 
|---|
| 189 | /* Blocking read from inotify file descriptor. | 
|---|
| 190 | */ | 
|---|
| 191 | len = sh_inotify_read_timeout(buffer, 16384, 1); | 
|---|
| 192 |  | 
|---|
| 193 | if (len > 0) | 
|---|
| 194 | { | 
|---|
| 195 | struct inotify_event *event; | 
|---|
| 196 | int i = 0; | 
|---|
| 197 |  | 
|---|
| 198 | while (i < len) | 
|---|
| 199 | { | 
|---|
| 200 | event = (struct inotify_event *) &(buffer[i]); | 
|---|
| 201 |  | 
|---|
| 202 | sh_fInotify_process(event); | 
|---|
| 203 |  | 
|---|
| 204 | i += sizeof (struct inotify_event) + event->len; | 
|---|
| 205 | } | 
|---|
| 206 |  | 
|---|
| 207 | if ( (sh.flag.inotify & SH_INOTIFY_DOSCAN) || | 
|---|
| 208 | (sh.flag.inotify & SH_INOTIFY_NEEDINIT)) | 
|---|
| 209 | { | 
|---|
| 210 | if (0 != sh_fInotify_init_internal()) | 
|---|
| 211 | { | 
|---|
| 212 | SH_FREE(buffer); | 
|---|
| 213 | return SH_MOD_FAILED; | 
|---|
| 214 | } | 
|---|
| 215 | } | 
|---|
| 216 | } | 
|---|
| 217 |  | 
|---|
| 218 | /* Re-scan 'dormant' list of sh_file_missing. | 
|---|
| 219 | */ | 
|---|
| 220 | sh_inotify_recheck_watches (&sh_file_watches, &sh_file_missing); | 
|---|
| 221 |  | 
|---|
| 222 | ++count; | 
|---|
| 223 | ++count2; | 
|---|
| 224 |  | 
|---|
| 225 | if (count >= 10) | 
|---|
| 226 | { | 
|---|
| 227 | count = 0; /* Re-expand glob patterns to discover added files. */ | 
|---|
| 228 | sh.flag.inotify |= SH_INOTIFY_INSCAN; | 
|---|
| 229 | sh_files_check_globFilePatterns(); | 
|---|
| 230 | sh.flag.inotify &= ~SH_INOTIFY_INSCAN; | 
|---|
| 231 | sh.flag.inotify |= SH_INOTIFY_NEEDINIT; | 
|---|
| 232 | } | 
|---|
| 233 |  | 
|---|
| 234 | if (count2 >= 300) | 
|---|
| 235 | { | 
|---|
| 236 | count2 = 0; /* Update baseline database. */ | 
|---|
| 237 | if (sh.flag.checkSum == SH_CHECK_CHECK && sh.flag.update == S_TRUE) | 
|---|
| 238 | sh_hash_writeout (); | 
|---|
| 239 | } | 
|---|
| 240 |  | 
|---|
| 241 | SH_FREE(buffer); | 
|---|
| 242 | return 0; | 
|---|
| 243 | } | 
|---|
| 244 |  | 
|---|
| 245 | /* We block in the read() call on the inotify descriptor, | 
|---|
| 246 | * so we always run. | 
|---|
| 247 | */ | 
|---|
| 248 | int sh_fInotify_timer(time_t tcurrent) | 
|---|
| 249 | { | 
|---|
| 250 | (void) tcurrent; | 
|---|
| 251 | return 1; | 
|---|
| 252 | } | 
|---|
| 253 |  | 
|---|
| 254 | int sh_fInotify_cleanup() | 
|---|
| 255 | { | 
|---|
| 256 | sh_inotify_purge_dormant(&sh_file_watches); | 
|---|
| 257 | sh_inotify_remove(&sh_file_watches); | 
|---|
| 258 | sh_inotify_init(&sh_file_watches); | 
|---|
| 259 | return 0; | 
|---|
| 260 | } | 
|---|
| 261 |  | 
|---|
| 262 | int sh_fInotify_reconf() | 
|---|
| 263 | { | 
|---|
| 264 | sh.flag.inotify   = 0; | 
|---|
| 265 |  | 
|---|
| 266 | ShfInotifyWatches = 0; | 
|---|
| 267 | ShfInotifyActive  = 0; | 
|---|
| 268 |  | 
|---|
| 269 | return sh_fInotify_cleanup(); | 
|---|
| 270 | } | 
|---|
| 271 |  | 
|---|
| 272 | #define PROC_WATCHES_MAX _("/proc/sys/fs/inotify/max_user_watches") | 
|---|
| 273 |  | 
|---|
| 274 | static void sh_fInotify_set_nwatches() | 
|---|
| 275 | { | 
|---|
| 276 | static int fails = 0; | 
|---|
| 277 |  | 
|---|
| 278 | if (ShfInotifyWatches == 0 || fails == 1) | 
|---|
| 279 | return; | 
|---|
| 280 |  | 
|---|
| 281 | if (0 == access(PROC_WATCHES_MAX, R_OK|W_OK)) /* flawfinder: ignore */ | 
|---|
| 282 | { | 
|---|
| 283 | FILE * fd; | 
|---|
| 284 |  | 
|---|
| 285 | if (NULL != (fd = fopen(PROC_WATCHES_MAX, "r+"))) | 
|---|
| 286 | { | 
|---|
| 287 | char   str[128]; | 
|---|
| 288 | char * ret; | 
|---|
| 289 | char * ptr; | 
|---|
| 290 | unsigned long  wn; | 
|---|
| 291 |  | 
|---|
| 292 | str[0] = '\0'; | 
|---|
| 293 | ret = fgets(str, 128, fd); | 
|---|
| 294 | if (ret && *str != '\0') | 
|---|
| 295 | { | 
|---|
| 296 | wn = strtoul(str, &ptr, 0); | 
|---|
| 297 | if (*ptr == '\0' || *ptr == '\n') | 
|---|
| 298 | { | 
|---|
| 299 | if (wn < ShfInotifyWatches) | 
|---|
| 300 | { | 
|---|
| 301 | sl_snprintf(str, sizeof(str), "%lu\n", ShfInotifyWatches); | 
|---|
| 302 | (void) fseek(fd, 0L, SEEK_SET); | 
|---|
| 303 | fputs(str, fd); | 
|---|
| 304 | } | 
|---|
| 305 | sl_fclose(FIL__, __LINE__, fd); | 
|---|
| 306 | return; | 
|---|
| 307 | } | 
|---|
| 308 | } | 
|---|
| 309 | sl_fclose(FIL__, __LINE__, fd); | 
|---|
| 310 | } | 
|---|
| 311 | } | 
|---|
| 312 | SH_MUTEX_LOCK(mutex_thread_nolog); | 
|---|
| 313 | sh_error_handle((-1), FIL__, __LINE__, 0, MSG_E_SUBGEN, | 
|---|
| 314 | _("Cannot set max_user_watches"), | 
|---|
| 315 | _("sh_fInotify_set_nwatches")); | 
|---|
| 316 | SH_MUTEX_UNLOCK(mutex_thread_nolog); | 
|---|
| 317 | fails = 1; | 
|---|
| 318 | return; | 
|---|
| 319 | } | 
|---|
| 320 |  | 
|---|
| 321 | /* The watch fd is thread specific. To have it in the fInotify thread, | 
|---|
| 322 | * the main thread writes a list of files/dirs to watch, and here we | 
|---|
| 323 | * now pop files from the list to add watches for them. | 
|---|
| 324 | */ | 
|---|
| 325 | static int sh_fInotify_init_internal() | 
|---|
| 326 | { | 
|---|
| 327 | char * filename; | 
|---|
| 328 | int    class; | 
|---|
| 329 | int    type; | 
|---|
| 330 | int    rdepth; | 
|---|
| 331 | unsigned long check_mask; | 
|---|
| 332 | int    retval; | 
|---|
| 333 | int    errnum; | 
|---|
| 334 |  | 
|---|
| 335 | if (ShfInotifyActive == S_FALSE) | 
|---|
| 336 | return SH_MOD_FAILED; | 
|---|
| 337 |  | 
|---|
| 338 | /* Wait until file scan is finished. | 
|---|
| 339 | */ | 
|---|
| 340 | while((sh.flag.inotify & SH_INOTIFY_DOSCAN) != 0) | 
|---|
| 341 | { | 
|---|
| 342 | retry_msleep(1,0); | 
|---|
| 343 |  | 
|---|
| 344 | if (ShfInotifyActive == S_FALSE) | 
|---|
| 345 | return SH_MOD_FAILED; | 
|---|
| 346 | } | 
|---|
| 347 |  | 
|---|
| 348 | sh_fInotify_set_nwatches(); | 
|---|
| 349 |  | 
|---|
| 350 | while (NULL != (filename = sh_inotify_pop_dormant(&sh_file_watches, | 
|---|
| 351 | &class, &check_mask, | 
|---|
| 352 | &type, &rdepth))) | 
|---|
| 353 | { | 
|---|
| 354 | retval = sh_inotify_add_watch(filename, &sh_file_watches, &errnum, | 
|---|
| 355 | class, check_mask, type, rdepth); | 
|---|
| 356 |  | 
|---|
| 357 | if (retval < 0) | 
|---|
| 358 | { | 
|---|
| 359 | char errbuf[SH_ERRBUF_SIZE]; | 
|---|
| 360 |  | 
|---|
| 361 | sh_error_message(errnum, errbuf, sizeof(errbuf)); | 
|---|
| 362 |  | 
|---|
| 363 | if ((errnum == ENOENT) || (errnum == EEXIST)) | 
|---|
| 364 | { | 
|---|
| 365 | /* (1) Did it exist at init ? | 
|---|
| 366 | */ | 
|---|
| 367 | if (sh_hash_have_it (filename) >= 0) | 
|---|
| 368 | { | 
|---|
| 369 | /* (2) Do we want to report on it ? | 
|---|
| 370 | */ | 
|---|
| 371 | if (S_FALSE == sh_ignore_chk_del(filename)) | 
|---|
| 372 | { | 
|---|
| 373 | char * epath = sh_util_safe_name (filename); | 
|---|
| 374 |  | 
|---|
| 375 | SH_MUTEX_LOCK(mutex_thread_nolog); | 
|---|
| 376 | sh_error_handle( (class == SH_LEVEL_ALLIGNORE) ? | 
|---|
| 377 | ShDFLevel[class] : | 
|---|
| 378 | ShDFLevel[SH_ERR_T_FILE], | 
|---|
| 379 | FIL__, __LINE__, errnum, MSG_E_SUBGPATH, | 
|---|
| 380 | errbuf, _("sh_fInotify_init_internal"), epath); | 
|---|
| 381 | SH_MUTEX_UNLOCK(mutex_thread_nolog); | 
|---|
| 382 | SH_FREE(epath); | 
|---|
| 383 | } | 
|---|
| 384 | } | 
|---|
| 385 | } | 
|---|
| 386 | else | 
|---|
| 387 | { | 
|---|
| 388 | SH_MUTEX_LOCK(mutex_thread_nolog); | 
|---|
| 389 | sh_error_handle((-1), FIL__, __LINE__, errnum, MSG_E_SUBGEN, | 
|---|
| 390 | errbuf, _("sh_fInotify_init_internal")); | 
|---|
| 391 | SH_MUTEX_UNLOCK(mutex_thread_nolog); | 
|---|
| 392 | } | 
|---|
| 393 | } | 
|---|
| 394 | SH_FREE(filename); | 
|---|
| 395 | } | 
|---|
| 396 |  | 
|---|
| 397 | /* Need this because mod_check() may run after | 
|---|
| 398 | * DOSCAN is finished, hence wouldn't call init(). | 
|---|
| 399 | */ | 
|---|
| 400 | sh.flag.inotify &= ~SH_INOTIFY_NEEDINIT; | 
|---|
| 401 |  | 
|---|
| 402 | return 0; | 
|---|
| 403 | } | 
|---|
| 404 |  | 
|---|
| 405 | static void sh_fInotify_logmask(struct inotify_event * event) | 
|---|
| 406 | { | 
|---|
| 407 | char dbgbuf[256]; | 
|---|
| 408 |  | 
|---|
| 409 | sl_strlcpy (dbgbuf, "inotify mask: ", sizeof(dbgbuf)); | 
|---|
| 410 |  | 
|---|
| 411 | if (event->mask & IN_ACCESS) sl_strlcat(dbgbuf, "IN_ACCESS ", sizeof(dbgbuf)); | 
|---|
| 412 | if (event->mask & IN_ATTRIB) sl_strlcat(dbgbuf, "IN_ATTRIB ", sizeof(dbgbuf)); | 
|---|
| 413 | if (event->mask & IN_CLOSE_WRITE) sl_strlcat(dbgbuf, "IN_CLOSE_WRITE ", sizeof(dbgbuf)); | 
|---|
| 414 | if (event->mask & IN_CLOSE_NOWRITE) sl_strlcat(dbgbuf, "IN_CLOSE_NOWRITE ", sizeof(dbgbuf)); | 
|---|
| 415 | if (event->mask & IN_CREATE) sl_strlcat(dbgbuf, "IN_CREATE ", sizeof(dbgbuf)); | 
|---|
| 416 | if (event->mask & IN_DELETE) sl_strlcat(dbgbuf, "IN_DELETE ", sizeof(dbgbuf)); | 
|---|
| 417 | if (event->mask & IN_DELETE_SELF) sl_strlcat(dbgbuf, "IN_DELETE_SELF ", sizeof(dbgbuf)); | 
|---|
| 418 | if (event->mask & IN_MODIFY) sl_strlcat(dbgbuf, "IN_MODIFY ", sizeof(dbgbuf)); | 
|---|
| 419 | if (event->mask & IN_MOVE_SELF) sl_strlcat(dbgbuf, "IN_MOVE_SELF ", sizeof(dbgbuf)); | 
|---|
| 420 | if (event->mask & IN_MOVED_FROM) sl_strlcat(dbgbuf, "IN_MOVED_FROM ", sizeof(dbgbuf)); | 
|---|
| 421 | if (event->mask & IN_MOVED_TO) sl_strlcat(dbgbuf, "IN_MOVED_TO ", sizeof(dbgbuf)); | 
|---|
| 422 | if (event->mask & IN_OPEN) sl_strlcat(dbgbuf, "IN_OPEN ", sizeof(dbgbuf)); | 
|---|
| 423 | if (event->mask & IN_IGNORED) sl_strlcat(dbgbuf, "IN_IGNORED ", sizeof(dbgbuf)); | 
|---|
| 424 | if (event->mask & IN_ISDIR) sl_strlcat(dbgbuf, "IN_ISDIR ", sizeof(dbgbuf)); | 
|---|
| 425 | if (event->mask & IN_Q_OVERFLOW) sl_strlcat(dbgbuf, "IN_Q_OVERFLOW ", sizeof(dbgbuf)); | 
|---|
| 426 | if (event->mask & IN_UNMOUNT) sl_strlcat(dbgbuf, "IN_UNMOUNT ", sizeof(dbgbuf)); | 
|---|
| 427 |  | 
|---|
| 428 | /* fprintf(stderr, "FIXME: %s\n", dbgbuf); */ | 
|---|
| 429 |  | 
|---|
| 430 | SH_MUTEX_LOCK(mutex_thread_nolog); | 
|---|
| 431 | sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN, | 
|---|
| 432 | dbgbuf, _("sh_fInotify_process")); | 
|---|
| 433 | SH_MUTEX_UNLOCK(mutex_thread_nolog); | 
|---|
| 434 | } | 
|---|
| 435 |  | 
|---|
| 436 | static int sh_fInotify_process(struct inotify_event * event) | 
|---|
| 437 | { | 
|---|
| 438 | int class; | 
|---|
| 439 | int ftype; | 
|---|
| 440 | int rdepth; | 
|---|
| 441 | unsigned long check_mask; | 
|---|
| 442 | char * filename; | 
|---|
| 443 | extern int flag_err_debug; | 
|---|
| 444 |  | 
|---|
| 445 | if (flag_err_debug == SL_TRUE) | 
|---|
| 446 | { | 
|---|
| 447 | sh_fInotify_logmask(event); | 
|---|
| 448 | } | 
|---|
| 449 |  | 
|---|
| 450 | if (event->wd >= 0) | 
|---|
| 451 | { | 
|---|
| 452 | filename = sh_inotify_search_item(&sh_file_watches, event->wd, | 
|---|
| 453 | &class, &check_mask, &ftype, &rdepth); | 
|---|
| 454 |  | 
|---|
| 455 | if (filename) | 
|---|
| 456 | { | 
|---|
| 457 | sh_fInotify_report(event, filename, class, check_mask, ftype, rdepth); | 
|---|
| 458 | SH_FREE(filename); | 
|---|
| 459 | } | 
|---|
| 460 | else if (sh.flag.inotify & SH_INOTIFY_NEEDINIT) | 
|---|
| 461 | { | 
|---|
| 462 | return 1; | 
|---|
| 463 | } | 
|---|
| 464 | else if ((event->mask & IN_UNMOUNT) == 0 && (event->mask & IN_IGNORED) == 0) | 
|---|
| 465 | { | 
|---|
| 466 | /* Remove watch ? Seems reasonable. */ | 
|---|
| 467 | sh_inotify_rm_watch(NULL, NULL, event->wd); | 
|---|
| 468 |  | 
|---|
| 469 | SH_MUTEX_LOCK(mutex_thread_nolog); | 
|---|
| 470 | sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, event->wd, MSG_E_SUBGEN, | 
|---|
| 471 | _("Watch removed: file path unknown"), | 
|---|
| 472 | _("sh_fInotify_process")); | 
|---|
| 473 | SH_MUTEX_UNLOCK(mutex_thread_nolog); | 
|---|
| 474 | } | 
|---|
| 475 | } | 
|---|
| 476 | else if ((event->mask & IN_Q_OVERFLOW) != 0) | 
|---|
| 477 | { | 
|---|
| 478 | sh.flag.inotify |= SH_INOTIFY_DOSCAN; | 
|---|
| 479 | sh.flag.inotify |= SH_INOTIFY_NEEDINIT; | 
|---|
| 480 | SH_MUTEX_LOCK(mutex_thread_nolog); | 
|---|
| 481 | sh_error_handle(SH_ERR_WARN, FIL__, __LINE__, event->wd, MSG_E_SUBGEN, | 
|---|
| 482 | _("Inotify queue overflow"), | 
|---|
| 483 | _("sh_fInotify_process")); | 
|---|
| 484 | SH_MUTEX_UNLOCK(mutex_thread_nolog); | 
|---|
| 485 | return 1; | 
|---|
| 486 | } | 
|---|
| 487 |  | 
|---|
| 488 | return 0; | 
|---|
| 489 | } | 
|---|
| 490 |  | 
|---|
| 491 | void sh_fInotify_report_add(char * path, int class, unsigned long check_mask) | 
|---|
| 492 | { | 
|---|
| 493 | if (S_FALSE == sh_ignore_chk_new(path)) | 
|---|
| 494 | { | 
|---|
| 495 | int reported = 0; | 
|---|
| 496 |  | 
|---|
| 497 | sh_files_clear_file_reported(path); | 
|---|
| 498 |  | 
|---|
| 499 | sh_files_search_file(path, &class, &check_mask, &reported); | 
|---|
| 500 |  | 
|---|
| 501 | sh_files_filecheck (class, check_mask, path, NULL, | 
|---|
| 502 | &reported, 0); | 
|---|
| 503 | if (SH_FFLAG_REPORTED_SET(reported)) | 
|---|
| 504 | sh_files_set_file_reported(path); | 
|---|
| 505 | } | 
|---|
| 506 | return; | 
|---|
| 507 | } | 
|---|
| 508 |  | 
|---|
| 509 |  | 
|---|
| 510 | static void sh_fInotify_report_miss(char * name, int level) | 
|---|
| 511 | { | 
|---|
| 512 | char * tmp = sh_util_safe_name (name); | 
|---|
| 513 |  | 
|---|
| 514 | SH_MUTEX_LOCK(mutex_thread_nolog); | 
|---|
| 515 | sh_error_handle (level, FIL__, __LINE__, 0, MSG_FI_MISS, tmp); | 
|---|
| 516 | SH_MUTEX_UNLOCK(mutex_thread_nolog); | 
|---|
| 517 | ++sh.statistics.files_report; | 
|---|
| 518 | SH_FREE(tmp); | 
|---|
| 519 | return; | 
|---|
| 520 | } | 
|---|
| 521 |  | 
|---|
| 522 | static int sh_fInotify_report_change (struct inotify_event * event, | 
|---|
| 523 | char * path, char * filename, | 
|---|
| 524 | int class, unsigned long check_mask, int ftype) | 
|---|
| 525 | { | 
|---|
| 526 | int    reported; | 
|---|
| 527 | int ret = sh_files_search_file(path, &class, &check_mask, &reported); | 
|---|
| 528 |  | 
|---|
| 529 | if ((ret == 0) && (event->len > 0) && (ftype == SH_INOTIFY_FILE)) | 
|---|
| 530 | { | 
|---|
| 531 | ; /* do nothing, watch was for directory monitored as file only */ | 
|---|
| 532 | } | 
|---|
| 533 | else | 
|---|
| 534 | { | 
|---|
| 535 | sh_files_filecheck (class, check_mask, filename, | 
|---|
| 536 | (event->len > 0) ? event->name : NULL, | 
|---|
| 537 | &reported, 0); | 
|---|
| 538 | } | 
|---|
| 539 | return 0; | 
|---|
| 540 | } | 
|---|
| 541 |  | 
|---|
| 542 |  | 
|---|
| 543 | static int sh_fInotify_report_missing (struct inotify_event * event, | 
|---|
| 544 | char * path, | 
|---|
| 545 | int class, unsigned long check_mask, int ftype) | 
|---|
| 546 | { | 
|---|
| 547 | int    reported; | 
|---|
| 548 | int isdir = (event->mask & IN_ISDIR); | 
|---|
| 549 | int level = (class == SH_LEVEL_ALLIGNORE) ? | 
|---|
| 550 | ShDFLevel[class] : | 
|---|
| 551 | ShDFLevel[(isdir == 0) ? SH_ERR_T_FILE : SH_ERR_T_DIR]; | 
|---|
| 552 |  | 
|---|
| 553 | if (S_FALSE == sh_ignore_chk_del(path)) | 
|---|
| 554 | { | 
|---|
| 555 | if (0 != hashreport_missing(path, level)) | 
|---|
| 556 | { | 
|---|
| 557 | int ret = sh_files_search_file(path, &class, &check_mask, &reported); | 
|---|
| 558 |  | 
|---|
| 559 | if ((ret == 0) && (event->len > 0) && (ftype == SH_INOTIFY_FILE)) | 
|---|
| 560 | { | 
|---|
| 561 | ; /* do nothing, watch was for directory monitored as file only */ | 
|---|
| 562 | } | 
|---|
| 563 | else | 
|---|
| 564 | { | 
|---|
| 565 | /* Removal of a directory triggers: | 
|---|
| 566 | * (1) IN_DELETE IN_ISDIR | 
|---|
| 567 | * (2) IN_DELETE_SELF | 
|---|
| 568 | */ | 
|---|
| 569 | if ((event->mask & IN_DELETE_SELF) == 0) | 
|---|
| 570 | sh_fInotify_report_miss(path, level); | 
|---|
| 571 | } | 
|---|
| 572 | } | 
|---|
| 573 | } | 
|---|
| 574 |  | 
|---|
| 575 | #ifndef REPLACE_OLD | 
|---|
| 576 | sh_hash_set_visited_true(path); | 
|---|
| 577 | #else | 
|---|
| 578 | sh_hash_set_missing(path); | 
|---|
| 579 | #endif | 
|---|
| 580 | if (sh.flag.reportonce == S_TRUE) | 
|---|
| 581 | sh_files_set_file_reported(path); | 
|---|
| 582 |  | 
|---|
| 583 | /* Move to 'dormant' list, if not file within directory. | 
|---|
| 584 | */ | 
|---|
| 585 | if (event->len == 0) | 
|---|
| 586 | sh_inotify_rm_watch(&sh_file_watches, &sh_file_missing, event->wd); | 
|---|
| 587 |  | 
|---|
| 588 | return 0; | 
|---|
| 589 | } | 
|---|
| 590 |  | 
|---|
| 591 | static int sh_fInotify_report_added (struct inotify_event * event, | 
|---|
| 592 | char * path, char * filename, | 
|---|
| 593 | int class, unsigned long check_mask, | 
|---|
| 594 | int ftype, int rdepth) | 
|---|
| 595 | { | 
|---|
| 596 | if (S_FALSE == sh_ignore_chk_new(path)) | 
|---|
| 597 | { | 
|---|
| 598 | int reported; | 
|---|
| 599 | int ret; | 
|---|
| 600 | int retD = 0; | 
|---|
| 601 | int rdepthD = rdepth; | 
|---|
| 602 |  | 
|---|
| 603 | sh_files_clear_file_reported(path); | 
|---|
| 604 |  | 
|---|
| 605 | ret = sh_files_search_file(path, &class, &check_mask, &reported); | 
|---|
| 606 |  | 
|---|
| 607 | if ((ret == 0) && (event->len > 0) && (ftype == SH_INOTIFY_FILE)) | 
|---|
| 608 | { | 
|---|
| 609 | ; /* do nothing, watch was for directory monitored as file only */ | 
|---|
| 610 | } | 
|---|
| 611 | else | 
|---|
| 612 | { | 
|---|
| 613 | int classD = class; | 
|---|
| 614 | int reportedD = reported; | 
|---|
| 615 | unsigned long check_maskD = check_mask; | 
|---|
| 616 |  | 
|---|
| 617 | if (event->mask & IN_ISDIR) | 
|---|
| 618 | { | 
|---|
| 619 | retD = sh_files_search_dir(path, &classD, &check_maskD, | 
|---|
| 620 | &reportedD, &rdepthD); | 
|---|
| 621 | if (retD != 0) | 
|---|
| 622 | { | 
|---|
| 623 | if (ret == 0) | 
|---|
| 624 | { | 
|---|
| 625 | class      = classD; | 
|---|
| 626 | check_mask = check_maskD; | 
|---|
| 627 | } | 
|---|
| 628 | } | 
|---|
| 629 | } | 
|---|
| 630 |  | 
|---|
| 631 | sh_files_filecheck (class, check_mask, filename, | 
|---|
| 632 | (event->len > 0) ? event->name : NULL, | 
|---|
| 633 | &reported, 0); | 
|---|
| 634 |  | 
|---|
| 635 | if (event->mask & IN_ISDIR) | 
|---|
| 636 | { | 
|---|
| 637 | sh.flag.inotify |= SH_INOTIFY_INSCAN; | 
|---|
| 638 | sh_files_checkdir (classD, check_maskD, rdepthD, | 
|---|
| 639 | path, (event->len > 0) ? event->name : NULL); | 
|---|
| 640 | sh.flag.inotify &= ~SH_INOTIFY_INSCAN; | 
|---|
| 641 | sh.flag.inotify |= SH_INOTIFY_NEEDINIT; | 
|---|
| 642 | sh_dirs_reset  (); | 
|---|
| 643 | sh_files_reset (); | 
|---|
| 644 | } | 
|---|
| 645 |  | 
|---|
| 646 | } | 
|---|
| 647 |  | 
|---|
| 648 | if (SH_FFLAG_REPORTED_SET(reported)) | 
|---|
| 649 | sh_files_set_file_reported(path); | 
|---|
| 650 |  | 
|---|
| 651 | if ((ret != 0) || (event->mask & IN_ISDIR)) | 
|---|
| 652 | { | 
|---|
| 653 | sh_inotify_add_watch(path, &sh_file_watches, &ret, | 
|---|
| 654 | class, check_mask, | 
|---|
| 655 | (event->mask & IN_ISDIR)?SH_INOTIFY_DIR:SH_INOTIFY_FILE, | 
|---|
| 656 | rdepthD); | 
|---|
| 657 | } | 
|---|
| 658 | } | 
|---|
| 659 | return 0; | 
|---|
| 660 | } | 
|---|
| 661 |  | 
|---|
| 662 | static int sh_fInotify_report(struct inotify_event * event, char * filename, | 
|---|
| 663 | int class, unsigned long check_mask, int ftype, int rdepth) | 
|---|
| 664 | { | 
|---|
| 665 | char * fullpath = NULL; | 
|---|
| 666 | char * path; | 
|---|
| 667 |  | 
|---|
| 668 | if (event->len > 0) | 
|---|
| 669 | { | 
|---|
| 670 | fullpath = sh_util_strconcat(filename, "/", event->name, NULL); | 
|---|
| 671 | path = fullpath; | 
|---|
| 672 | } | 
|---|
| 673 | else | 
|---|
| 674 | { | 
|---|
| 675 | path = filename; | 
|---|
| 676 | } | 
|---|
| 677 |  | 
|---|
| 678 | if ( (event->mask & (IN_ATTRIB|IN_MODIFY)) != 0) | 
|---|
| 679 | { | 
|---|
| 680 | sh_fInotify_report_change (event, path, filename, | 
|---|
| 681 | class, check_mask, ftype); | 
|---|
| 682 | } | 
|---|
| 683 | else if ((event->mask & (IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF|IN_MOVED_FROM)) != 0) | 
|---|
| 684 | { | 
|---|
| 685 | sh_fInotify_report_missing (event, path, | 
|---|
| 686 | class, check_mask, ftype); | 
|---|
| 687 | } | 
|---|
| 688 | else if((event->mask & (IN_CREATE|IN_MOVED_TO)) != 0) | 
|---|
| 689 | { | 
|---|
| 690 | sh_fInotify_report_added (event, path, filename, | 
|---|
| 691 | class, check_mask, | 
|---|
| 692 | ftype, rdepth); | 
|---|
| 693 | } | 
|---|
| 694 |  | 
|---|
| 695 | if (fullpath) | 
|---|
| 696 | SH_FREE(fullpath); | 
|---|
| 697 |  | 
|---|
| 698 | return 0; | 
|---|
| 699 | } | 
|---|
| 700 |  | 
|---|
| 701 |  | 
|---|
| 702 | #endif | 
|---|
| 703 |  | 
|---|
| 704 | #endif | 
|---|