1
+#include "git-compat-util.h"
2
+#include "dir.h"
3
+#include "fsmonitor-ll.h"
4
+#include "fsm-listen.h"
5
+#include "fsmonitor--daemon.h"
6
+#include "fsmonitor-path-utils.h"
7
+#include "gettext.h"
8
+#include "simple-ipc.h"
9
+#include "string-list.h"
10
+#include "trace.h"
11
+
12
+#include <sys/inotify.h>
13
+
14
+/*
15
+ * Safe value to bitwise OR with rest of mask for
16
+ * kernels that do not support IN_MASK_CREATE
17
+ */
18
+#ifndef IN_MASK_CREATE
19
+#define IN_MASK_CREATE 0x00000000
20
+#endif
21
+
22
+enum shutdown_reason {
23
+ SHUTDOWN_CONTINUE = 0,
24
+ SHUTDOWN_STOP,
25
+ SHUTDOWN_ERROR,
26
+ SHUTDOWN_FORCE
27
+};
28
+
29
+struct watch_entry {
30
+ struct hashmap_entry ent;
31
+ int wd;
32
+ uint32_t cookie;
33
+ const char *dir;
34
+};
35
+
36
+struct rename_entry {
37
+ struct hashmap_entry ent;
38
+ time_t whence;
39
+ uint32_t cookie;
40
+ const char *dir;
41
+};
42
+
43
+struct fsm_listen_data {
44
+ int fd_inotify;
45
+ enum shutdown_reason shutdown;
46
+ struct hashmap watches;
47
+ struct hashmap renames;
48
+ struct hashmap revwatches;
49
+};
50
+
51
+static int watch_entry_cmp(const void *cmp_data UNUSED,
52
+ const struct hashmap_entry *eptr,
53
+ const struct hashmap_entry *entry_or_key,
54
+ const void *keydata UNUSED)
55
+{
56
+ const struct watch_entry *e1, *e2;
57
+
58
+ e1 = container_of(eptr, const struct watch_entry, ent);
59
+ e2 = container_of(entry_or_key, const struct watch_entry, ent);
60
+ return e1->wd != e2->wd;
61
+}
62
+
63
+static int revwatches_entry_cmp(const void *cmp_data UNUSED,
64
+ const struct hashmap_entry *eptr,
65
+ const struct hashmap_entry *entry_or_key,
66
+ const void *keydata UNUSED)
67
+{
68
+ const struct watch_entry *e1, *e2;
69
+
70
+ e1 = container_of(eptr, const struct watch_entry, ent);
71
+ e2 = container_of(entry_or_key, const struct watch_entry, ent);
72
+ return strcmp(e1->dir, e2->dir);
73
+}
74
+
75
+static int rename_entry_cmp(const void *cmp_data UNUSED,
76
+ const struct hashmap_entry *eptr,
77
+ const struct hashmap_entry *entry_or_key,
78
+ const void *keydata UNUSED)
79
+{
80
+ const struct rename_entry *e1, *e2;
81
+
82
+ e1 = container_of(eptr, const struct rename_entry, ent);
83
+ e2 = container_of(entry_or_key, const struct rename_entry, ent);
84
+ return e1->cookie != e2->cookie;
85
+}
86
+
87
+/*
88
+ * Register an inotify watch, add watch descriptor to path mapping
89
+ * and the reverse mapping.
90
+ */
91
+static int add_watch(const char *path, struct fsm_listen_data *data)
92
+{
93
+ const char *interned = strintern(path);
94
+ struct watch_entry *w1, *w2;
95
+
96
+ /* add the inotify watch, don't allow watches to be modified */
97
+ int wd = inotify_add_watch(data->fd_inotify, interned,
98
+ (IN_ALL_EVENTS | IN_ONLYDIR | IN_MASK_CREATE)
99
+ ^ IN_ACCESS ^ IN_CLOSE ^ IN_OPEN);
100
+ if (wd < 0) {
101
+ if (errno == ENOENT || errno == ENOTDIR)
102
+ return 0; /* directory was deleted or is not a directory */
103
+ if (errno == EEXIST)
104
+ return 0; /* watch already exists, no action needed */
105
+ if (errno == ENOSPC)
106
+ return error(_("inotify watch limit reached; "
107
+ "increase fs.inotify.max_user_watches"));
108
+ return error_errno(_("inotify_add_watch('%s') failed"), interned);
109
+ }
110
+
111
+ /* add watch descriptor -> directory mapping */
112
+ CALLOC_ARRAY(w1, 1);
113
+ w1->wd = wd;
114
+ w1->dir = interned;
115
+ hashmap_entry_init(&w1->ent, memhash(&w1->wd, sizeof(int)));
116
+ hashmap_add(&data->watches, &w1->ent);
117
+
118
+ /* add directory -> watch descriptor mapping */
119
+ CALLOC_ARRAY(w2, 1);
120
+ w2->wd = wd;
121
+ w2->dir = interned;
122
+ hashmap_entry_init(&w2->ent, strhash(w2->dir));
123
+ hashmap_add(&data->revwatches, &w2->ent);
124
+
125
+ return 0;
126
+}
127
+
128
+/*
129
+ * Remove the inotify watch, the watch descriptor to path mapping
130
+ * and the reverse mapping.
131
+ */
132
+static void remove_watch(struct watch_entry *w, struct fsm_listen_data *data)
133
+{
134
+ struct watch_entry k1, k2, *w1, *w2;
135
+
136
+ /* remove watch, ignore error if kernel already did it */
137
+ if (inotify_rm_watch(data->fd_inotify, w->wd) && errno != EINVAL)
138
+ error_errno(_("inotify_rm_watch() failed"));
139
+
140
+ k1.wd = w->wd;
141
+ hashmap_entry_init(&k1.ent, memhash(&k1.wd, sizeof(int)));
142
+ w1 = hashmap_remove_entry(&data->watches, &k1, ent, NULL);
143
+ if (!w1)
144
+ BUG("double remove of watch for '%s'", w->dir);
145
+
146
+ if (w1->cookie)
147
+ BUG("removing watch for '%s' which has a pending rename", w1->dir);
148
+
149
+ k2.dir = w->dir;
150
+ hashmap_entry_init(&k2.ent, strhash(k2.dir));
151
+ w2 = hashmap_remove_entry(&data->revwatches, &k2, ent, NULL);
152
+ if (!w2)
153
+ BUG("double remove of reverse watch for '%s'", w->dir);
154
+
155
+ /* w1->dir and w2->dir are interned strings, we don't own them */
156
+ free(w1);
157
+ free(w2);
158
+}
159
+
160
+/*
161
+ * Check for stale directory renames.
162
+ *
163
+ * https://man7.org/linux/man-pages/man7/inotify.7.html
164
+ *
165
+ * Allow for some small timeout to account for the fact that insertion of the
166
+ * IN_MOVED_FROM+IN_MOVED_TO event pair is not atomic, and the possibility that
167
+ * there may not be any IN_MOVED_TO event.
168
+ *
169
+ * If the IN_MOVED_TO event is not received within the timeout then events have
170
+ * been missed and the monitor is in an inconsistent state with respect to the
171
+ * filesystem.
172
+ */
173
+static int check_stale_dir_renames(struct hashmap *renames, time_t max_age)
174
+{
175
+ struct rename_entry *re;
176
+ struct hashmap_iter iter;
177
+
178
+ hashmap_for_each_entry(renames, &iter, re, ent) {
179
+ if (re->whence <= max_age)
180
+ return -1;
181
+ }
182
+ return 0;
183
+}
184
+
185
+/*
186
+ * Track pending renames.
187
+ *
188
+ * Tracking is done via an event cookie to watch descriptor mapping.
189
+ *
190
+ * A rename is not complete until matching an IN_MOVED_TO event is received
191
+ * for a corresponding IN_MOVED_FROM event.
192
+ */
193
+static void add_dir_rename(uint32_t cookie, const char *path,
194
+ struct fsm_listen_data *data)
195
+{
196
+ struct watch_entry k, *w;
197
+ struct rename_entry *re;
198
+
199
+ /* lookup the watch descriptor for the given path */
200
+ k.dir = path;
201
+ hashmap_entry_init(&k.ent, strhash(path));
202
+ w = hashmap_get_entry(&data->revwatches, &k, ent, NULL);
203
+ if (!w) {
204
+ /*
205
+ * This can happen in rare cases where the directory was
206
+ * moved before we had a chance to add a watch on it.
207
+ * Just ignore this rename.
208
+ */
209
+ trace_printf_key(&trace_fsmonitor,
210
+ "no watch found for rename from '%s'", path);
211
+ return;
212
+ }
213
+ w->cookie = cookie;
214
+
215
+ /* add the pending rename to match against later */
216
+ CALLOC_ARRAY(re, 1);
217
+ re->dir = w->dir;
218
+ re->cookie = w->cookie;
219
+ re->whence = time(NULL);
220
+ hashmap_entry_init(&re->ent, memhash(&re->cookie, sizeof(uint32_t)));
221
+ hashmap_add(&data->renames, &re->ent);
222
+}
223
+
224
+/*
225
+ * Handle directory renames
226
+ *
227
+ * Once an IN_MOVED_TO event is received, lookup the rename tracking information
228
+ * via the event cookie and use this information to update the watch.
229
+ */
230
+static void rename_dir(uint32_t cookie, const char *path,
231
+ struct fsm_listen_data *data)
232
+{
233
+ struct rename_entry rek, *re;
234
+ struct watch_entry k, *w;
235
+
236
+ /* lookup a pending rename to match */
237
+ rek.cookie = cookie;
238
+ hashmap_entry_init(&rek.ent, memhash(&rek.cookie, sizeof(uint32_t)));
239
+ re = hashmap_get_entry(&data->renames, &rek, ent, NULL);
240
+ if (re) {
241
+ k.dir = re->dir;
242
+ hashmap_entry_init(&k.ent, strhash(k.dir));
243
+ w = hashmap_get_entry(&data->revwatches, &k, ent, NULL);
244
+ if (w) {
245
+ w->cookie = 0; /* rename handled */
246
+ remove_watch(w, data);
247
+ if (add_watch(path, data))
248
+ trace_printf_key(&trace_fsmonitor,
249
+ "failed to add watch for renamed dir '%s'",
250
+ path);
251
+ } else {
252
+ /* Directory was moved out of watch tree */
253
+ trace_printf_key(&trace_fsmonitor,
254
+ "no matching watch for rename to '%s'", path);
255
+ }
256
+ hashmap_remove_entry(&data->renames, &rek, ent, NULL);
257
+ free(re);
258
+ } else {
259
+ /* Directory was moved from outside the watch tree */
260
+ trace_printf_key(&trace_fsmonitor,
261
+ "no matching cookie for rename to '%s'", path);
262
+ }
263
+}
264
+
265
+/*
266
+ * Recursively add watches to every directory under path
267
+ */
268
+static int register_inotify(const char *path,
269
+ struct fsmonitor_daemon_state *state,
270
+ struct fsmonitor_batch *batch)
271
+{
272
+ DIR *dir;
273
+ const char *rel;
274
+ struct strbuf current = STRBUF_INIT;
275
+ struct dirent *de;
276
+ struct stat fs;
277
+ int ret = -1;
278
+
279
+ dir = opendir(path);
280
+ if (!dir) {
281
+ if (errno == ENOENT || errno == ENOTDIR)
282
+ return 0; /* directory was deleted */
283
+ return error_errno(_("opendir('%s') failed"), path);
284
+ }
285
+
286
+ while ((de = readdir_skip_dot_and_dotdot(dir)) != NULL) {
287
+ strbuf_reset(¤t);
288
+ strbuf_addf(¤t, "%s/%s", path, de->d_name);
289
+ if (lstat(current.buf, &fs)) {
290
+ if (errno == ENOENT)
291
+ continue; /* file was deleted */
292
+ error_errno(_("lstat('%s') failed"), current.buf);
293
+ goto failed;
294
+ }
295
+
296
+ /* recurse into directory */
297
+ if (S_ISDIR(fs.st_mode)) {
298
+ if (add_watch(current.buf, state->listen_data))
299
+ goto failed;
300
+ if (register_inotify(current.buf, state, batch))
301
+ goto failed;
302
+ } else if (batch) {
303
+ rel = current.buf + state->path_worktree_watch.len + 1;
304
+ trace_printf_key(&trace_fsmonitor, "explicitly adding '%s'", rel);
305
+ fsmonitor_batch__add_path(batch, rel);
306
+ }
307
+ }
308
+ ret = 0;
309
+
310
+failed:
311
+ strbuf_release(¤t);
312
+ if (closedir(dir) < 0)
313
+ return error_errno(_("closedir('%s') failed"), path);
314
+ return ret;
315
+}
316
+
317
+static int em_rename_dir_from(uint32_t mask)
318
+{
319
+ return ((mask & IN_ISDIR) && (mask & IN_MOVED_FROM));
320
+}
321
+
322
+static int em_rename_dir_to(uint32_t mask)
323
+{
324
+ return ((mask & IN_ISDIR) && (mask & IN_MOVED_TO));
325
+}
326
+
327
+static int em_remove_watch(uint32_t mask)
328
+{
329
+ return (mask & IN_DELETE_SELF);
330
+}
331
+
332
+static int em_dir_renamed(uint32_t mask)
333
+{
334
+ return ((mask & IN_ISDIR) && (mask & IN_MOVE));
335
+}
336
+
337
+static int em_dir_created(uint32_t mask)
338
+{
339
+ return ((mask & IN_ISDIR) && (mask & IN_CREATE));
340
+}
341
+
342
+static int em_dir_deleted(uint32_t mask)
343
+{
344
+ return ((mask & IN_ISDIR) && (mask & IN_DELETE));
345
+}
346
+
347
+static int em_force_shutdown(uint32_t mask)
348
+{
349
+ return (mask & IN_UNMOUNT) || (mask & IN_Q_OVERFLOW);
350
+}
351
+
352
+static int em_ignore(uint32_t mask)
353
+{
354
+ return (mask & IN_IGNORED) || (mask & IN_MOVE_SELF);
355
+}
356
+
357
+static void log_mask_set(const char *path, uint32_t mask)
358
+{
359
+ struct strbuf msg = STRBUF_INIT;
360
+
361
+ if (mask & IN_ACCESS)
362
+ strbuf_addstr(&msg, "IN_ACCESS|");
363
+ if (mask & IN_MODIFY)
364
+ strbuf_addstr(&msg, "IN_MODIFY|");
365
+ if (mask & IN_ATTRIB)
366
+ strbuf_addstr(&msg, "IN_ATTRIB|");
367
+ if (mask & IN_CLOSE_WRITE)
368
+ strbuf_addstr(&msg, "IN_CLOSE_WRITE|");
369
+ if (mask & IN_CLOSE_NOWRITE)
370
+ strbuf_addstr(&msg, "IN_CLOSE_NOWRITE|");
371
+ if (mask & IN_OPEN)
372
+ strbuf_addstr(&msg, "IN_OPEN|");
373
+ if (mask & IN_MOVED_FROM)
374
+ strbuf_addstr(&msg, "IN_MOVED_FROM|");
375
+ if (mask & IN_MOVED_TO)
376
+ strbuf_addstr(&msg, "IN_MOVED_TO|");
377
+ if (mask & IN_CREATE)
378
+ strbuf_addstr(&msg, "IN_CREATE|");
379
+ if (mask & IN_DELETE)
380
+ strbuf_addstr(&msg, "IN_DELETE|");
381
+ if (mask & IN_DELETE_SELF)
382
+ strbuf_addstr(&msg, "IN_DELETE_SELF|");
383
+ if (mask & IN_MOVE_SELF)
384
+ strbuf_addstr(&msg, "IN_MOVE_SELF|");
385
+ if (mask & IN_UNMOUNT)
386
+ strbuf_addstr(&msg, "IN_UNMOUNT|");
387
+ if (mask & IN_Q_OVERFLOW)
388
+ strbuf_addstr(&msg, "IN_Q_OVERFLOW|");
389
+ if (mask & IN_IGNORED)
390
+ strbuf_addstr(&msg, "IN_IGNORED|");
391
+ if (mask & IN_ISDIR)
392
+ strbuf_addstr(&msg, "IN_ISDIR|");
393
+
394
+ strbuf_strip_suffix(&msg, "|");
395
+
396
+ trace_printf_key(&trace_fsmonitor, "inotify_event: '%s', mask=%#8.8x %s",
397
+ path, mask, msg.buf);
398
+
399
+ strbuf_release(&msg);
400
+}
401
+
402
+int fsm_listen__ctor(struct fsmonitor_daemon_state *state)
403
+{
404
+ int fd;
405
+ int ret = 0;
406
+ struct fsm_listen_data *data;
407
+
408
+ CALLOC_ARRAY(data, 1);
409
+ state->listen_data = data;
410
+ state->listen_error_code = -1;
411
+ data->fd_inotify = -1;
412
+ data->shutdown = SHUTDOWN_ERROR;
413
+
414
+ fd = inotify_init1(O_NONBLOCK);
415
+ if (fd < 0) {
416
+ FREE_AND_NULL(state->listen_data);
417
+ return error_errno(_("inotify_init1() failed"));
418
+ }
419
+
420
+ data->fd_inotify = fd;
421
+
422
+ hashmap_init(&data->watches, watch_entry_cmp, NULL, 0);
423
+ hashmap_init(&data->renames, rename_entry_cmp, NULL, 0);
424
+ hashmap_init(&data->revwatches, revwatches_entry_cmp, NULL, 0);
425
+
426
+ if (add_watch(state->path_worktree_watch.buf, data))
427
+ ret = -1;
428
+ else if (register_inotify(state->path_worktree_watch.buf, state, NULL))
429
+ ret = -1;
430
+ else if (state->nr_paths_watching > 1) {
431
+ if (add_watch(state->path_gitdir_watch.buf, data))
432
+ ret = -1;
433
+ else if (register_inotify(state->path_gitdir_watch.buf, state, NULL))
434
+ ret = -1;
435
+ }
436
+
437
+ if (!ret) {
438
+ state->listen_error_code = 0;
439
+ data->shutdown = SHUTDOWN_CONTINUE;
440
+ }
441
+
442
+ return ret;
443
+}
444
+
445
+void fsm_listen__dtor(struct fsmonitor_daemon_state *state)
446
+{
447
+ struct fsm_listen_data *data;
448
+ struct hashmap_iter iter;
449
+ struct watch_entry *w;
450
+ struct watch_entry **to_remove;
451
+ size_t nr_to_remove = 0, alloc_to_remove = 0;
452
+ size_t i;
453
+ int fd;
454
+
455
+ if (!state || !state->listen_data)
456
+ return;
457
+
458
+ data = state->listen_data;
459
+ fd = data->fd_inotify;
460
+
461
+ /*
462
+ * Collect all entries first, then remove them.
463
+ * We can't modify the hashmap while iterating over it.
464
+ */
465
+ to_remove = NULL;
466
+ hashmap_for_each_entry(&data->watches, &iter, w, ent) {
467
+ ALLOC_GROW(to_remove, nr_to_remove + 1, alloc_to_remove);
468
+ to_remove[nr_to_remove++] = w;
469
+ }
470
+
471
+ for (i = 0; i < nr_to_remove; i++) {
472
+ to_remove[i]->cookie = 0; /* ignore any pending renames */
473
+ remove_watch(to_remove[i], data);
474
+ }
475
+ free(to_remove);
476
+
477
+ hashmap_clear(&data->watches);
478
+
479
+ hashmap_clear(&data->revwatches); /* remove_watch freed the entries */
480
+
481
+ hashmap_clear_and_free(&data->renames, struct rename_entry, ent);
482
+
483
+ FREE_AND_NULL(state->listen_data);
484
+
485
+ if (fd >= 0 && (close(fd) < 0))
486
+ error_errno(_("closing inotify file descriptor failed"));
487
+}
488
+
489
+void fsm_listen__stop_async(struct fsmonitor_daemon_state *state)
490
+{
491
+ if (state && state->listen_data &&
492
+ state->listen_data->shutdown == SHUTDOWN_CONTINUE)
493
+ state->listen_data->shutdown = SHUTDOWN_STOP;
494
+}
495
+
496
+/*
497
+ * Process a single inotify event and queue for publication.
498
+ */
499
+static int process_event(const char *path,
500
+ const struct inotify_event *event,
501
+ struct fsmonitor_batch **batch,
502
+ struct string_list *cookie_list,
503
+ struct fsmonitor_daemon_state *state)
504
+{
505
+ const char *rel;
506
+ const char *last_sep;
507
+
508
+ switch (fsmonitor_classify_path_absolute(state, path)) {
509
+ case IS_INSIDE_DOT_GIT_WITH_COOKIE_PREFIX:
510
+ case IS_INSIDE_GITDIR_WITH_COOKIE_PREFIX:
511
+ /* Use just the filename of the cookie file. */
512
+ last_sep = find_last_dir_sep(path);
513
+ string_list_append(cookie_list,
514
+ last_sep ? last_sep + 1 : path);
515
+ break;
516
+ case IS_INSIDE_DOT_GIT:
517
+ case IS_INSIDE_GITDIR:
518
+ break;
519
+ case IS_DOT_GIT:
520
+ case IS_GITDIR:
521
+ /*
522
+ * If .git directory is deleted or renamed away,
523
+ * we have to quit.
524
+ */
525
+ if (em_dir_deleted(event->mask)) {
526
+ trace_printf_key(&trace_fsmonitor,
527
+ "event: gitdir removed");
528
+ state->listen_data->shutdown = SHUTDOWN_FORCE;
529
+ goto done;
530
+ }
531
+
532
+ if (em_dir_renamed(event->mask)) {
533
+ trace_printf_key(&trace_fsmonitor,
534
+ "event: gitdir renamed");
535
+ state->listen_data->shutdown = SHUTDOWN_FORCE;
536
+ goto done;
537
+ }
538
+ break;
539
+ case IS_WORKDIR_PATH:
540
+ /* normal events in the working directory */
541
+ if (trace_pass_fl(&trace_fsmonitor))
542
+ log_mask_set(path, event->mask);
543
+
544
+ if (!*batch)
545
+ *batch = fsmonitor_batch__new();
546
+
547
+ rel = path + state->path_worktree_watch.len + 1;
548
+ fsmonitor_batch__add_path(*batch, rel);
549
+
550
+ if (em_dir_deleted(event->mask))
551
+ break;
552
+
553
+ /* received IN_MOVE_FROM, add tracking for expected IN_MOVE_TO */
554
+ if (em_rename_dir_from(event->mask))
555
+ add_dir_rename(event->cookie, path, state->listen_data);
556
+
557
+ /* received IN_MOVE_TO, update watch to reflect new path */
558
+ if (em_rename_dir_to(event->mask)) {
559
+ rename_dir(event->cookie, path, state->listen_data);
560
+ if (register_inotify(path, state, *batch)) {
561
+ state->listen_data->shutdown = SHUTDOWN_ERROR;
562
+ goto done;
563
+ }
564
+ }
565
+
566
+ if (em_dir_created(event->mask)) {
567
+ if (add_watch(path, state->listen_data)) {
568
+ state->listen_data->shutdown = SHUTDOWN_ERROR;
569
+ goto done;
570
+ }
571
+ if (register_inotify(path, state, *batch)) {
572
+ state->listen_data->shutdown = SHUTDOWN_ERROR;
573
+ goto done;
574
+ }
575
+ }
576
+ break;
577
+ case IS_OUTSIDE_CONE:
578
+ default:
579
+ trace_printf_key(&trace_fsmonitor,
580
+ "ignoring '%s'", path);
581
+ break;
582
+ }
583
+ return 0;
584
+done:
585
+ return -1;
586
+}
587
+
588
+/*
589
+ * Read the inotify event stream and pre-process events before further
590
+ * processing and eventual publishing.
591
+ */
592
+static void handle_events(struct fsmonitor_daemon_state *state)
593
+{
594
+ /* See https://man7.org/linux/man-pages/man7/inotify.7.html */
595
+ char buf[4096]
596
+ __attribute__ ((aligned(__alignof__(struct inotify_event))));
597
+
598
+ struct hashmap *watches = &state->listen_data->watches;
599
+ struct fsmonitor_batch *batch = NULL;
600
+ struct string_list cookie_list = STRING_LIST_INIT_DUP;
601
+ struct watch_entry k, *w;
602
+ struct strbuf path = STRBUF_INIT;
603
+ const struct inotify_event *event;
604
+ int fd = state->listen_data->fd_inotify;
605
+ ssize_t len;
606
+ char *ptr, *p;
607
+
608
+ for (;;) {
609
+ len = read(fd, buf, sizeof(buf));
610
+ if (len == -1) {
611
+ if (errno == EAGAIN || errno == EINTR)
612
+ goto done;
613
+ error_errno(_("reading inotify message stream failed"));
614
+ state->listen_data->shutdown = SHUTDOWN_ERROR;
615
+ goto done;
616
+ }
617
+
618
+ /* nothing to read */
619
+ if (len == 0)
620
+ goto done;
621
+
622
+ /* Loop over all events in the buffer. */
623
+ for (ptr = buf; ptr < buf + len;
624
+ ptr += sizeof(struct inotify_event) + event->len) {
625
+
626
+ event = (const struct inotify_event *)ptr;
627
+
628
+ if (em_ignore(event->mask))
629
+ continue;
630
+
631
+ /* File system was unmounted or event queue overflowed */
632
+ if (em_force_shutdown(event->mask)) {
633
+ if (trace_pass_fl(&trace_fsmonitor))
634
+ log_mask_set("forcing shutdown", event->mask);
635
+ state->listen_data->shutdown = SHUTDOWN_FORCE;
636
+ goto done;
637
+ }
638
+
639
+ k.wd = event->wd;
640
+ hashmap_entry_init(&k.ent, memhash(&k.wd, sizeof(int)));
641
+
642
+ w = hashmap_get_entry(watches, &k, ent, NULL);
643
+ if (!w) {
644
+ /* Watch was removed, skip event */
645
+ continue;
646
+ }
647
+
648
+ /* directory watch was removed */
649
+ if (em_remove_watch(event->mask)) {
650
+ remove_watch(w, state->listen_data);
651
+ continue;
652
+ }
653
+
654
+ strbuf_reset(&path);
655
+ strbuf_addf(&path, "%s/%s", w->dir, event->name);
656
+
657
+ p = fsmonitor__resolve_alias(path.buf, &state->alias);
658
+ if (!p)
659
+ p = strbuf_detach(&path, NULL);
660
+
661
+ if (process_event(p, event, &batch, &cookie_list, state)) {
662
+ free(p);
663
+ goto done;
664
+ }
665
+ free(p);
666
+ }
667
+ strbuf_reset(&path);
668
+ fsmonitor_publish(state, batch, &cookie_list);
669
+ string_list_clear(&cookie_list, 0);
670
+ batch = NULL;
671
+ }
672
+done:
673
+ strbuf_release(&path);
674
+ fsmonitor_batch__free_list(batch);
675
+ string_list_clear(&cookie_list, 0);
676
+}
677
+
678
+/*
679
+ * Non-blocking read of the inotify events stream. The inotify fd is polled
680
+ * frequently to help minimize the number of queue overflows.
681
+ */
682
+void fsm_listen__loop(struct fsmonitor_daemon_state *state)
683
+{
684
+ int poll_num;
685
+ /*
686
+ * Interval in seconds between checks for stale directory renames.
687
+ * A directory rename that is not completed within this window
688
+ * (i.e. no matching IN_MOVED_TO for an IN_MOVED_FROM) indicates
689
+ * missed events, forcing a shutdown.
690
+ */
691
+ const int interval = 1;
692
+ time_t checked = time(NULL);
693
+ struct pollfd fds[1];
694
+
695
+ fds[0].fd = state->listen_data->fd_inotify;
696
+ fds[0].events = POLLIN;
697
+
698
+ /*
699
+ * Our fs event listener is now running, so it's safe to start
700
+ * serving client requests.
701
+ */
702
+ ipc_server_start_async(state->ipc_server_data);
703
+
704
+ for (;;) {
705
+ switch (state->listen_data->shutdown) {
706
+ case SHUTDOWN_CONTINUE:
707
+ poll_num = poll(fds, 1, 50);
708
+ if (poll_num == -1) {
709
+ if (errno == EINTR)
710
+ continue;
711
+ error_errno(_("polling inotify message stream failed"));
712
+ state->listen_data->shutdown = SHUTDOWN_ERROR;
713
+ continue;
714
+ }
715
+
716
+ if ((time(NULL) - checked) >= interval) {
717
+ checked = time(NULL);
718
+ if (check_stale_dir_renames(&state->listen_data->renames,
719
+ checked - interval)) {
720
+ trace_printf_key(&trace_fsmonitor,
721
+ "missed IN_MOVED_TO events, forcing shutdown");
722
+ state->listen_data->shutdown = SHUTDOWN_FORCE;
723
+ continue;
724
+ }
725
+ }
726
+
727
+ if (poll_num > 0 && (fds[0].revents & POLLIN))
728
+ handle_events(state);
729
+
730
+ continue;
731
+ case SHUTDOWN_ERROR:
732
+ state->listen_error_code = -1;
733
+ ipc_server_stop_async(state->ipc_server_data);
734
+ break;
735
+ case SHUTDOWN_FORCE:
736
+ state->listen_error_code = 0;
737
+ ipc_server_stop_async(state->ipc_server_data);
738
+ break;
739
+ case SHUTDOWN_STOP:
740
+ default:
741
+ state->listen_error_code = 0;
742
+ break;
743
+ }
744
+ return;
745
+ }
746
+}