behavior. Only respected when `core.fsmonitor` is set to `true`.
fsmonitor.socketDir::
- This Mac OS and Linux-specific option, if set, specifies the directory in
+ This Mac OS-specific option, if set, specifies the directory in
which to create the Unix domain socket used for communication
between the fsmonitor daemon and various Git commands. The directory must
- reside on a native filesystem. Only respected when `core.fsmonitor`
+ reside on a native Mac OS filesystem. Only respected when `core.fsmonitor`
is set to `true`.
correctly with all network-mounted repositories, so such use is considered
experimental.
-On Mac OS and Linux, the inter-process communication (IPC) between various Git
+On Mac OS, the inter-process communication (IPC) between various Git
commands and the fsmonitor daemon is done via a Unix domain socket (UDS) -- a
-special type of file -- which is supported by native Mac OS and Linux filesystems,
+special type of file -- which is supported by native Mac OS filesystems,
but not on network-mounted filesystems, NTFS, or FAT32. Other filesystems
may or may not have the needed support; the fsmonitor daemon is not guaranteed
to work with these filesystems and such use is considered experimental.
`.git` directory is on a network-mounted filesystem, it will instead be
created at `$HOME/.git-fsmonitor-*` unless `$HOME` itself is on a
network-mounted filesystem, in which case you must set the configuration
-variable `fsmonitor.socketDir` to the path of a directory on a native
+variable `fsmonitor.socketDir` to the path of a directory on a Mac OS native
filesystem in which to create the socket file.
If none of the above directories (`.git`, `$HOME`, or `fsmonitor.socketDir`)
-is on a native filesystem the fsmonitor daemon will report an
+is on a native Mac OS file filesystem the fsmonitor daemon will report an
error that will cause the daemon and the currently running command to exit.
-LINUX CAVEATS
-~~~~~~~~~~~~~
-
-On Linux, the fsmonitor daemon uses inotify to monitor filesystem events.
-The inotify system has per-user limits on the number of watches that can
-be created. The default limit is typically 8192 watches per user.
-
-For large repositories with many directories, you may need to increase
-this limit. Check the current limit with:
-
- cat /proc/sys/fs/inotify/max_user_watches
-
-To temporarily increase the limit:
-
- sudo sysctl fs.inotify.max_user_watches=65536
-
-To make the change permanent, add to `/etc/sysctl.conf`:
-
- fs.inotify.max_user_watches=65536
-
CONFIGURATION
-------------
# If your platform has OS-specific ways to tell if a repo is incompatible with
# fsmonitor (whether the hook or IPC daemon version), set FSMONITOR_OS_SETTINGS
# to the "<name>" of the corresponding `compat/fsmonitor/fsm-settings-<name>.c`
-# and `compat/fsmonitor/fsm-ipc-<name>.c` files.
+# that implements the `fsm_os_settings__*()` routines.
#
# Define LINK_FUZZ_PROGRAMS if you want `make all` to also build the fuzz test
# programs in oss-fuzz/.
COMPAT_CFLAGS += -DHAVE_FSMONITOR_DAEMON_BACKEND
COMPAT_OBJS += compat/fsmonitor/fsm-listen-$(FSMONITOR_DAEMON_BACKEND).o
COMPAT_OBJS += compat/fsmonitor/fsm-health-$(FSMONITOR_DAEMON_BACKEND).o
+ COMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_DAEMON_BACKEND).o
endif
ifdef FSMONITOR_OS_SETTINGS
COMPAT_CFLAGS += -DHAVE_FSMONITOR_OS_SETTINGS
- COMPAT_OBJS += compat/fsmonitor/fsm-ipc-$(FSMONITOR_OS_SETTINGS).o
COMPAT_OBJS += compat/fsmonitor/fsm-settings-$(FSMONITOR_OS_SETTINGS).o
- COMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_DAEMON_BACKEND).o
+ COMPAT_OBJS += compat/fsmonitor/fsm-path-utils-$(FSMONITOR_OS_SETTINGS).o
endif
ifdef WITH_BREAKING_CHANGES
#include "fsmonitor--daemon.h"
#include "simple-ipc.h"
-#include "strmap.h"
+#include "khash.h"
#include "run-command.h"
#include "trace.h"
#include "trace2.h"
{
struct strbuf answer = STRBUF_INIT;
int ret;
- int max_wait_ms = 30000;
- int elapsed_ms = 0;
ret = fsmonitor_ipc__send_command("quit", &answer);
return ret;
trace2_region_enter("fsm_client", "polling-for-daemon-exit", NULL);
- while (fsmonitor_ipc__get_state() == IPC_STATE__LISTENING) {
- if (elapsed_ms >= max_wait_ms) {
- trace2_region_leave("fsm_client",
- "polling-for-daemon-exit", NULL);
- return error(_("daemon did not stop within %d seconds"),
- max_wait_ms / 1000);
- }
+ while (fsmonitor_ipc__get_state() == IPC_STATE__LISTENING)
sleep_millisec(50);
- elapsed_ms += 50;
- }
trace2_region_leave("fsm_client", "polling-for-daemon-exit", NULL);
return 0;
unlink(cookie_pathname.buf);
/*
- * Wait for the listener thread to observe the cookie file.
- * Time out after a short interval so that the client
- * does not hang forever if the filesystem does not deliver
- * events (e.g., on certain container/overlay filesystems
- * where inotify watches succeed but events never arrive).
+ * Technically, this is an infinite wait (well, unless another
+ * thread sends us an abort). I'd like to change this to
+ * use `pthread_cond_timedwait()` and return an error/timeout
+ * and let the caller do the trivial response thing, but we
+ * don't have that routine in our thread-utils.
+ *
+ * After extensive beta testing I'm not really worried about
+ * this. Also note that the above open() and unlink() calls
+ * will cause at least two FS events on that path, so the odds
+ * of getting stuck are pretty slim.
*/
- {
- struct timeval now;
- struct timespec ts;
- int err = 0;
-
- gettimeofday(&now, NULL);
- ts.tv_sec = now.tv_sec + 1;
- ts.tv_nsec = now.tv_usec * 1000;
-
- while (cookie->result == FCIR_INIT && !err)
- err = pthread_cond_timedwait(&state->cookies_cond,
- &state->main_lock,
- &ts);
- if (err == ETIMEDOUT && cookie->result == FCIR_INIT) {
- trace_printf_key(&trace_fsmonitor,
- "cookie_wait timed out");
- cookie->result = FCIR_ERROR;
- }
- }
+ while (cookie->result == FCIR_INIT)
+ pthread_cond_wait(&state->cookies_cond,
+ &state->main_lock);
done:
hashmap_remove(&state->cookies, &cookie->entry, NULL);
return 0;
}
+KHASH_INIT(str, const char *, int, 0, kh_str_hash_func, kh_str_hash_equal)
+
static int do_handle_client(struct fsmonitor_daemon_state *state,
const char *command,
ipc_server_reply_cb *reply,
const struct fsmonitor_batch *batch;
struct fsmonitor_batch *remainder = NULL;
intmax_t count = 0, duplicates = 0;
- struct strset shown = STRSET_INIT;
+ kh_str_t *shown;
+ int hash_ret;
int do_trivial = 0;
int do_flush = 0;
int do_cookie = 0;
* so walk the batch list backwards from the current head back
* to the batch (sequence number) they named.
*
- * We use a strset to de-dup the list of pathnames.
+ * We use khash to de-dup the list of pathnames.
*
* NEEDSWORK: each batch contains a list of interned strings,
* so we only need to do pointer comparisons here to build the
* hash table. Currently, we're still comparing the string
* values.
*/
- strset_init_with_options(&shown, NULL, 0);
+ shown = kh_init_str();
for (batch = batch_head;
batch && batch->batch_seq_nr > requested_oldest_seq_nr;
batch = batch->next) {
const char *s = batch->interned_paths[k];
size_t s_len;
- if (!strset_add(&shown, s))
+ if (kh_get_str(shown, s) != kh_end(shown))
duplicates++;
else {
+ kh_put_str(shown, s, &hash_ret);
+
trace_printf_key(&trace_fsmonitor,
"send[%"PRIuMAX"]: %s",
count, s);
total_response_len += payload.len;
}
+ kh_release_str(shown);
+
pthread_mutex_lock(&state->main_lock);
if (token_data->client_ref_count > 0)
trace2_data_intmax("fsmonitor", the_repository, "response/count/duplicates", duplicates);
cleanup:
- strset_clear(&shown);
strbuf_release(&response_token);
strbuf_release(&requested_token_id);
strbuf_release(&payload);
done:
pthread_cond_destroy(&state.cookies_cond);
pthread_mutex_destroy(&state.main_lock);
- {
- struct hashmap_iter iter;
- struct fsmonitor_cookie_item *cookie;
-
- hashmap_for_each_entry(&state.cookies, &iter, cookie, entry)
- free(cookie->name);
- hashmap_clear_and_free(&state.cookies,
- struct fsmonitor_cookie_item, entry);
- }
fsm_listen__dtor(&state);
fsm_health__dtor(&state);
return err;
}
-static int try_to_run_foreground_daemon(int detach_console)
+static int try_to_run_foreground_daemon(int detach_console MAYBE_UNUSED)
{
/*
* Technically, we don't need to probe for an existing daemon
fflush(stderr);
}
- if (detach_console) {
#ifdef GIT_WINDOWS_NATIVE
+ if (detach_console)
FreeConsole();
-#else
- /*
- * Create a new session so that the daemon is detached
- * from the parent's process group. This prevents
- * shells with job control (e.g. bash with "set -m")
- * from waiting on the daemon when they wait for a
- * foreground command that implicitly spawned it.
- */
- if (setsid() == -1)
- warning_errno(_("setsid failed"));
#endif
- }
return !!fsmonitor_run_daemon();
}
cp.no_stdin = 1;
cp.no_stdout = 1;
cp.no_stderr = 1;
- cp.close_fd_above_stderr = 1;
sbgr = start_bg_command(&cp, bg_wait_cb, NULL,
fsmonitor__start_timeout_sec);
+++ /dev/null
-#include "git-compat-util.h"
-#include "config.h"
-#include "fsmonitor-ll.h"
-#include "fsm-health.h"
-#include "fsmonitor--daemon.h"
-
-/*
- * The Linux fsmonitor implementation uses inotify which has its own
- * mechanisms for detecting filesystem unmount and other events that
- * would require the daemon to shutdown. Therefore, we don't need
- * a separate health thread like Windows does.
- *
- * These stub functions satisfy the interface requirements.
- */
-
-int fsm_health__ctor(struct fsmonitor_daemon_state *state UNUSED)
-{
- return 0;
-}
-
-void fsm_health__dtor(struct fsmonitor_daemon_state *state UNUSED)
-{
- return;
-}
-
-void fsm_health__loop(struct fsmonitor_daemon_state *state UNUSED)
-{
- return;
-}
-
-void fsm_health__stop_async(struct fsmonitor_daemon_state *state UNUSED)
-{
-}
+++ /dev/null
-#include "git-compat-util.h"
-#include "dir.h"
-#include "fsmonitor-ll.h"
-#include "fsm-listen.h"
-#include "fsmonitor--daemon.h"
-#include "fsmonitor-path-utils.h"
-#include "gettext.h"
-#include "simple-ipc.h"
-#include "string-list.h"
-#include "trace.h"
-
-#include <sys/inotify.h>
-
-/*
- * Safe value to bitwise OR with rest of mask for
- * kernels that do not support IN_MASK_CREATE
- */
-#ifndef IN_MASK_CREATE
-#define IN_MASK_CREATE 0x00000000
-#endif
-
-enum shutdown_reason {
- SHUTDOWN_CONTINUE = 0,
- SHUTDOWN_STOP,
- SHUTDOWN_ERROR,
- SHUTDOWN_FORCE
-};
-
-struct watch_entry {
- struct hashmap_entry ent;
- int wd;
- uint32_t cookie;
- const char *dir;
-};
-
-struct rename_entry {
- struct hashmap_entry ent;
- time_t whence;
- uint32_t cookie;
- const char *dir;
-};
-
-struct fsm_listen_data {
- int fd_inotify;
- enum shutdown_reason shutdown;
- struct hashmap watches;
- struct hashmap renames;
- struct hashmap revwatches;
-};
-
-static int watch_entry_cmp(const void *cmp_data UNUSED,
- const struct hashmap_entry *eptr,
- const struct hashmap_entry *entry_or_key,
- const void *keydata UNUSED)
-{
- const struct watch_entry *e1, *e2;
-
- e1 = container_of(eptr, const struct watch_entry, ent);
- e2 = container_of(entry_or_key, const struct watch_entry, ent);
- return e1->wd != e2->wd;
-}
-
-static int revwatches_entry_cmp(const void *cmp_data UNUSED,
- const struct hashmap_entry *eptr,
- const struct hashmap_entry *entry_or_key,
- const void *keydata UNUSED)
-{
- const struct watch_entry *e1, *e2;
-
- e1 = container_of(eptr, const struct watch_entry, ent);
- e2 = container_of(entry_or_key, const struct watch_entry, ent);
- return strcmp(e1->dir, e2->dir);
-}
-
-static int rename_entry_cmp(const void *cmp_data UNUSED,
- const struct hashmap_entry *eptr,
- const struct hashmap_entry *entry_or_key,
- const void *keydata UNUSED)
-{
- const struct rename_entry *e1, *e2;
-
- e1 = container_of(eptr, const struct rename_entry, ent);
- e2 = container_of(entry_or_key, const struct rename_entry, ent);
- return e1->cookie != e2->cookie;
-}
-
-/*
- * Register an inotify watch, add watch descriptor to path mapping
- * and the reverse mapping.
- */
-static int add_watch(const char *path, struct fsm_listen_data *data)
-{
- const char *interned = strintern(path);
- struct watch_entry *w1, *w2;
-
- /* add the inotify watch, don't allow watches to be modified */
- int wd = inotify_add_watch(data->fd_inotify, interned,
- (IN_ALL_EVENTS | IN_ONLYDIR | IN_MASK_CREATE)
- ^ IN_ACCESS ^ IN_CLOSE ^ IN_OPEN);
- if (wd < 0) {
- if (errno == ENOENT || errno == ENOTDIR)
- return 0; /* directory was deleted or is not a directory */
- if (errno == EEXIST)
- return 0; /* watch already exists, no action needed */
- if (errno == ENOSPC)
- return error(_("inotify watch limit reached; "
- "increase fs.inotify.max_user_watches"));
- return error_errno(_("inotify_add_watch('%s') failed"), interned);
- }
-
- /* add watch descriptor -> directory mapping */
- CALLOC_ARRAY(w1, 1);
- w1->wd = wd;
- w1->dir = interned;
- hashmap_entry_init(&w1->ent, memhash(&w1->wd, sizeof(int)));
- hashmap_add(&data->watches, &w1->ent);
-
- /* add directory -> watch descriptor mapping */
- CALLOC_ARRAY(w2, 1);
- w2->wd = wd;
- w2->dir = interned;
- hashmap_entry_init(&w2->ent, strhash(w2->dir));
- hashmap_add(&data->revwatches, &w2->ent);
-
- return 0;
-}
-
-/*
- * Remove the inotify watch, the watch descriptor to path mapping
- * and the reverse mapping.
- */
-static void remove_watch(struct watch_entry *w, struct fsm_listen_data *data)
-{
- struct watch_entry k1, k2, *w1, *w2;
-
- /* remove watch, ignore error if kernel already did it */
- if (inotify_rm_watch(data->fd_inotify, w->wd) && errno != EINVAL)
- error_errno(_("inotify_rm_watch() failed"));
-
- k1.wd = w->wd;
- hashmap_entry_init(&k1.ent, memhash(&k1.wd, sizeof(int)));
- w1 = hashmap_remove_entry(&data->watches, &k1, ent, NULL);
- if (!w1)
- BUG("double remove of watch for '%s'", w->dir);
-
- if (w1->cookie)
- BUG("removing watch for '%s' which has a pending rename", w1->dir);
-
- k2.dir = w->dir;
- hashmap_entry_init(&k2.ent, strhash(k2.dir));
- w2 = hashmap_remove_entry(&data->revwatches, &k2, ent, NULL);
- if (!w2)
- BUG("double remove of reverse watch for '%s'", w->dir);
-
- /* w1->dir and w2->dir are interned strings, we don't own them */
- free(w1);
- free(w2);
-}
-
-/*
- * Check for stale directory renames.
- *
- * https://man7.org/linux/man-pages/man7/inotify.7.html
- *
- * Allow for some small timeout to account for the fact that insertion of the
- * IN_MOVED_FROM+IN_MOVED_TO event pair is not atomic, and the possibility that
- * there may not be any IN_MOVED_TO event.
- *
- * If the IN_MOVED_TO event is not received within the timeout then events have
- * been missed and the monitor is in an inconsistent state with respect to the
- * filesystem.
- */
-static int check_stale_dir_renames(struct hashmap *renames, time_t max_age)
-{
- struct rename_entry *re;
- struct hashmap_iter iter;
-
- hashmap_for_each_entry(renames, &iter, re, ent) {
- if (re->whence <= max_age)
- return -1;
- }
- return 0;
-}
-
-/*
- * Track pending renames.
- *
- * Tracking is done via an event cookie to watch descriptor mapping.
- *
- * A rename is not complete until matching an IN_MOVED_TO event is received
- * for a corresponding IN_MOVED_FROM event.
- */
-static void add_dir_rename(uint32_t cookie, const char *path,
- struct fsm_listen_data *data)
-{
- struct watch_entry k, *w;
- struct rename_entry *re;
-
- /* lookup the watch descriptor for the given path */
- k.dir = path;
- hashmap_entry_init(&k.ent, strhash(path));
- w = hashmap_get_entry(&data->revwatches, &k, ent, NULL);
- if (!w) {
- /*
- * This can happen in rare cases where the directory was
- * moved before we had a chance to add a watch on it.
- * Just ignore this rename.
- */
- trace_printf_key(&trace_fsmonitor,
- "no watch found for rename from '%s'", path);
- return;
- }
- w->cookie = cookie;
-
- /* add the pending rename to match against later */
- CALLOC_ARRAY(re, 1);
- re->dir = w->dir;
- re->cookie = w->cookie;
- re->whence = time(NULL);
- hashmap_entry_init(&re->ent, memhash(&re->cookie, sizeof(uint32_t)));
- hashmap_add(&data->renames, &re->ent);
-}
-
-/*
- * Handle directory renames
- *
- * Once an IN_MOVED_TO event is received, lookup the rename tracking information
- * via the event cookie and use this information to update the watch.
- */
-static void rename_dir(uint32_t cookie, const char *path,
- struct fsm_listen_data *data)
-{
- struct rename_entry rek, *re;
- struct watch_entry k, *w;
-
- /* lookup a pending rename to match */
- rek.cookie = cookie;
- hashmap_entry_init(&rek.ent, memhash(&rek.cookie, sizeof(uint32_t)));
- re = hashmap_get_entry(&data->renames, &rek, ent, NULL);
- if (re) {
- k.dir = re->dir;
- hashmap_entry_init(&k.ent, strhash(k.dir));
- w = hashmap_get_entry(&data->revwatches, &k, ent, NULL);
- if (w) {
- w->cookie = 0; /* rename handled */
- remove_watch(w, data);
- if (add_watch(path, data))
- trace_printf_key(&trace_fsmonitor,
- "failed to add watch for renamed dir '%s'",
- path);
- } else {
- /* Directory was moved out of watch tree */
- trace_printf_key(&trace_fsmonitor,
- "no matching watch for rename to '%s'", path);
- }
- hashmap_remove_entry(&data->renames, &rek, ent, NULL);
- free(re);
- } else {
- /* Directory was moved from outside the watch tree */
- trace_printf_key(&trace_fsmonitor,
- "no matching cookie for rename to '%s'", path);
- }
-}
-
-/*
- * Recursively add watches to every directory under path
- */
-static int register_inotify(const char *path,
- struct fsmonitor_daemon_state *state,
- struct fsmonitor_batch *batch)
-{
- DIR *dir;
- const char *rel;
- struct strbuf current = STRBUF_INIT;
- struct dirent *de;
- struct stat fs;
- int ret = -1;
-
- dir = opendir(path);
- if (!dir) {
- if (errno == ENOENT || errno == ENOTDIR)
- return 0; /* directory was deleted */
- return error_errno(_("opendir('%s') failed"), path);
- }
-
- while ((de = readdir_skip_dot_and_dotdot(dir)) != NULL) {
- strbuf_reset(¤t);
- strbuf_addf(¤t, "%s/%s", path, de->d_name);
- if (lstat(current.buf, &fs)) {
- if (errno == ENOENT)
- continue; /* file was deleted */
- error_errno(_("lstat('%s') failed"), current.buf);
- goto failed;
- }
-
- /* recurse into directory */
- if (S_ISDIR(fs.st_mode)) {
- if (add_watch(current.buf, state->listen_data))
- goto failed;
- if (register_inotify(current.buf, state, batch))
- goto failed;
- } else if (batch) {
- rel = current.buf + state->path_worktree_watch.len + 1;
- trace_printf_key(&trace_fsmonitor, "explicitly adding '%s'", rel);
- fsmonitor_batch__add_path(batch, rel);
- }
- }
- ret = 0;
-
-failed:
- strbuf_release(¤t);
- if (closedir(dir) < 0)
- return error_errno(_("closedir('%s') failed"), path);
- return ret;
-}
-
-static int em_rename_dir_from(uint32_t mask)
-{
- return ((mask & IN_ISDIR) && (mask & IN_MOVED_FROM));
-}
-
-static int em_rename_dir_to(uint32_t mask)
-{
- return ((mask & IN_ISDIR) && (mask & IN_MOVED_TO));
-}
-
-static int em_remove_watch(uint32_t mask)
-{
- return (mask & IN_DELETE_SELF);
-}
-
-static int em_dir_renamed(uint32_t mask)
-{
- return ((mask & IN_ISDIR) && (mask & IN_MOVE));
-}
-
-static int em_dir_created(uint32_t mask)
-{
- return ((mask & IN_ISDIR) && (mask & IN_CREATE));
-}
-
-static int em_dir_deleted(uint32_t mask)
-{
- return ((mask & IN_ISDIR) && (mask & IN_DELETE));
-}
-
-static int em_force_shutdown(uint32_t mask)
-{
- return (mask & IN_UNMOUNT) || (mask & IN_Q_OVERFLOW);
-}
-
-static int em_ignore(uint32_t mask)
-{
- return (mask & IN_IGNORED) || (mask & IN_MOVE_SELF);
-}
-
-static void log_mask_set(const char *path, uint32_t mask)
-{
- struct strbuf msg = STRBUF_INIT;
-
- if (mask & IN_ACCESS)
- strbuf_addstr(&msg, "IN_ACCESS|");
- if (mask & IN_MODIFY)
- strbuf_addstr(&msg, "IN_MODIFY|");
- if (mask & IN_ATTRIB)
- strbuf_addstr(&msg, "IN_ATTRIB|");
- if (mask & IN_CLOSE_WRITE)
- strbuf_addstr(&msg, "IN_CLOSE_WRITE|");
- if (mask & IN_CLOSE_NOWRITE)
- strbuf_addstr(&msg, "IN_CLOSE_NOWRITE|");
- if (mask & IN_OPEN)
- strbuf_addstr(&msg, "IN_OPEN|");
- if (mask & IN_MOVED_FROM)
- strbuf_addstr(&msg, "IN_MOVED_FROM|");
- if (mask & IN_MOVED_TO)
- strbuf_addstr(&msg, "IN_MOVED_TO|");
- if (mask & IN_CREATE)
- strbuf_addstr(&msg, "IN_CREATE|");
- if (mask & IN_DELETE)
- strbuf_addstr(&msg, "IN_DELETE|");
- if (mask & IN_DELETE_SELF)
- strbuf_addstr(&msg, "IN_DELETE_SELF|");
- if (mask & IN_MOVE_SELF)
- strbuf_addstr(&msg, "IN_MOVE_SELF|");
- if (mask & IN_UNMOUNT)
- strbuf_addstr(&msg, "IN_UNMOUNT|");
- if (mask & IN_Q_OVERFLOW)
- strbuf_addstr(&msg, "IN_Q_OVERFLOW|");
- if (mask & IN_IGNORED)
- strbuf_addstr(&msg, "IN_IGNORED|");
- if (mask & IN_ISDIR)
- strbuf_addstr(&msg, "IN_ISDIR|");
-
- strbuf_strip_suffix(&msg, "|");
-
- trace_printf_key(&trace_fsmonitor, "inotify_event: '%s', mask=%#8.8x %s",
- path, mask, msg.buf);
-
- strbuf_release(&msg);
-}
-
-int fsm_listen__ctor(struct fsmonitor_daemon_state *state)
-{
- int fd;
- int ret = 0;
- struct fsm_listen_data *data;
-
- CALLOC_ARRAY(data, 1);
- state->listen_data = data;
- state->listen_error_code = -1;
- data->fd_inotify = -1;
- data->shutdown = SHUTDOWN_ERROR;
-
- fd = inotify_init1(O_NONBLOCK);
- if (fd < 0) {
- FREE_AND_NULL(state->listen_data);
- return error_errno(_("inotify_init1() failed"));
- }
-
- data->fd_inotify = fd;
-
- hashmap_init(&data->watches, watch_entry_cmp, NULL, 0);
- hashmap_init(&data->renames, rename_entry_cmp, NULL, 0);
- hashmap_init(&data->revwatches, revwatches_entry_cmp, NULL, 0);
-
- if (add_watch(state->path_worktree_watch.buf, data))
- ret = -1;
- else if (register_inotify(state->path_worktree_watch.buf, state, NULL))
- ret = -1;
- else if (state->nr_paths_watching > 1) {
- if (add_watch(state->path_gitdir_watch.buf, data))
- ret = -1;
- else if (register_inotify(state->path_gitdir_watch.buf, state, NULL))
- ret = -1;
- }
-
- if (!ret) {
- state->listen_error_code = 0;
- data->shutdown = SHUTDOWN_CONTINUE;
- }
-
- return ret;
-}
-
-void fsm_listen__dtor(struct fsmonitor_daemon_state *state)
-{
- struct fsm_listen_data *data;
- struct hashmap_iter iter;
- struct watch_entry *w;
- struct watch_entry **to_remove;
- size_t nr_to_remove = 0, alloc_to_remove = 0;
- size_t i;
- int fd;
-
- if (!state || !state->listen_data)
- return;
-
- data = state->listen_data;
- fd = data->fd_inotify;
-
- /*
- * Collect all entries first, then remove them.
- * We can't modify the hashmap while iterating over it.
- */
- to_remove = NULL;
- hashmap_for_each_entry(&data->watches, &iter, w, ent) {
- ALLOC_GROW(to_remove, nr_to_remove + 1, alloc_to_remove);
- to_remove[nr_to_remove++] = w;
- }
-
- for (i = 0; i < nr_to_remove; i++) {
- to_remove[i]->cookie = 0; /* ignore any pending renames */
- remove_watch(to_remove[i], data);
- }
- free(to_remove);
-
- hashmap_clear(&data->watches);
-
- hashmap_clear(&data->revwatches); /* remove_watch freed the entries */
-
- hashmap_clear_and_free(&data->renames, struct rename_entry, ent);
-
- FREE_AND_NULL(state->listen_data);
-
- if (fd >= 0 && (close(fd) < 0))
- error_errno(_("closing inotify file descriptor failed"));
-}
-
-void fsm_listen__stop_async(struct fsmonitor_daemon_state *state)
-{
- if (state && state->listen_data &&
- state->listen_data->shutdown == SHUTDOWN_CONTINUE)
- state->listen_data->shutdown = SHUTDOWN_STOP;
-}
-
-/*
- * Process a single inotify event and queue for publication.
- */
-static int process_event(const char *path,
- const struct inotify_event *event,
- struct fsmonitor_batch **batch,
- struct string_list *cookie_list,
- struct fsmonitor_daemon_state *state)
-{
- const char *rel;
- const char *last_sep;
-
- switch (fsmonitor_classify_path_absolute(state, path)) {
- case IS_INSIDE_DOT_GIT_WITH_COOKIE_PREFIX:
- case IS_INSIDE_GITDIR_WITH_COOKIE_PREFIX:
- /* Use just the filename of the cookie file. */
- last_sep = find_last_dir_sep(path);
- string_list_append(cookie_list,
- last_sep ? last_sep + 1 : path);
- break;
- case IS_INSIDE_DOT_GIT:
- case IS_INSIDE_GITDIR:
- break;
- case IS_DOT_GIT:
- case IS_GITDIR:
- /*
- * If .git directory is deleted or renamed away,
- * we have to quit.
- */
- if (em_dir_deleted(event->mask)) {
- trace_printf_key(&trace_fsmonitor,
- "event: gitdir removed");
- state->listen_data->shutdown = SHUTDOWN_FORCE;
- goto done;
- }
-
- if (em_dir_renamed(event->mask)) {
- trace_printf_key(&trace_fsmonitor,
- "event: gitdir renamed");
- state->listen_data->shutdown = SHUTDOWN_FORCE;
- goto done;
- }
- break;
- case IS_WORKDIR_PATH:
- /* normal events in the working directory */
- if (trace_pass_fl(&trace_fsmonitor))
- log_mask_set(path, event->mask);
-
- if (!*batch)
- *batch = fsmonitor_batch__new();
-
- rel = path + state->path_worktree_watch.len + 1;
- fsmonitor_batch__add_path(*batch, rel);
-
- if (em_dir_deleted(event->mask))
- break;
-
- /* received IN_MOVE_FROM, add tracking for expected IN_MOVE_TO */
- if (em_rename_dir_from(event->mask))
- add_dir_rename(event->cookie, path, state->listen_data);
-
- /* received IN_MOVE_TO, update watch to reflect new path */
- if (em_rename_dir_to(event->mask)) {
- rename_dir(event->cookie, path, state->listen_data);
- if (register_inotify(path, state, *batch)) {
- state->listen_data->shutdown = SHUTDOWN_ERROR;
- goto done;
- }
- }
-
- if (em_dir_created(event->mask)) {
- if (add_watch(path, state->listen_data)) {
- state->listen_data->shutdown = SHUTDOWN_ERROR;
- goto done;
- }
- if (register_inotify(path, state, *batch)) {
- state->listen_data->shutdown = SHUTDOWN_ERROR;
- goto done;
- }
- }
- break;
- case IS_OUTSIDE_CONE:
- default:
- trace_printf_key(&trace_fsmonitor,
- "ignoring '%s'", path);
- break;
- }
- return 0;
-done:
- return -1;
-}
-
-/*
- * Read the inotify event stream and pre-process events before further
- * processing and eventual publishing.
- */
-static void handle_events(struct fsmonitor_daemon_state *state)
-{
- /* See https://man7.org/linux/man-pages/man7/inotify.7.html */
- char buf[4096]
- __attribute__ ((aligned(__alignof__(struct inotify_event))));
-
- struct hashmap *watches = &state->listen_data->watches;
- struct fsmonitor_batch *batch = NULL;
- struct string_list cookie_list = STRING_LIST_INIT_DUP;
- struct watch_entry k, *w;
- struct strbuf path = STRBUF_INIT;
- const struct inotify_event *event;
- int fd = state->listen_data->fd_inotify;
- ssize_t len;
- char *ptr, *p;
-
- for (;;) {
- len = read(fd, buf, sizeof(buf));
- if (len == -1) {
- if (errno == EAGAIN || errno == EINTR)
- goto done;
- error_errno(_("reading inotify message stream failed"));
- state->listen_data->shutdown = SHUTDOWN_ERROR;
- goto done;
- }
-
- /* nothing to read */
- if (len == 0)
- goto done;
-
- /* Loop over all events in the buffer. */
- for (ptr = buf; ptr < buf + len;
- ptr += sizeof(struct inotify_event) + event->len) {
-
- event = (const struct inotify_event *)ptr;
-
- if (em_ignore(event->mask))
- continue;
-
- /* File system was unmounted or event queue overflowed */
- if (em_force_shutdown(event->mask)) {
- if (trace_pass_fl(&trace_fsmonitor))
- log_mask_set("forcing shutdown", event->mask);
- state->listen_data->shutdown = SHUTDOWN_FORCE;
- goto done;
- }
-
- k.wd = event->wd;
- hashmap_entry_init(&k.ent, memhash(&k.wd, sizeof(int)));
-
- w = hashmap_get_entry(watches, &k, ent, NULL);
- if (!w) {
- /* Watch was removed, skip event */
- continue;
- }
-
- /* directory watch was removed */
- if (em_remove_watch(event->mask)) {
- remove_watch(w, state->listen_data);
- continue;
- }
-
- strbuf_reset(&path);
- strbuf_addf(&path, "%s/%s", w->dir, event->name);
-
- p = fsmonitor__resolve_alias(path.buf, &state->alias);
- if (!p)
- p = strbuf_detach(&path, NULL);
-
- if (process_event(p, event, &batch, &cookie_list, state)) {
- free(p);
- goto done;
- }
- free(p);
- }
- strbuf_reset(&path);
- fsmonitor_publish(state, batch, &cookie_list);
- string_list_clear(&cookie_list, 0);
- batch = NULL;
- }
-done:
- strbuf_release(&path);
- fsmonitor_batch__free_list(batch);
- string_list_clear(&cookie_list, 0);
-}
-
-/*
- * Non-blocking read of the inotify events stream. The inotify fd is polled
- * frequently to help minimize the number of queue overflows.
- */
-void fsm_listen__loop(struct fsmonitor_daemon_state *state)
-{
- int poll_num;
- /*
- * Interval in seconds between checks for stale directory renames.
- * A directory rename that is not completed within this window
- * (i.e. no matching IN_MOVED_TO for an IN_MOVED_FROM) indicates
- * missed events, forcing a shutdown.
- */
- const int interval = 1;
- time_t checked = time(NULL);
- struct pollfd fds[1];
-
- fds[0].fd = state->listen_data->fd_inotify;
- fds[0].events = POLLIN;
-
- /*
- * Our fs event listener is now running, so it's safe to start
- * serving client requests.
- */
- ipc_server_start_async(state->ipc_server_data);
-
- for (;;) {
- switch (state->listen_data->shutdown) {
- case SHUTDOWN_CONTINUE:
- poll_num = poll(fds, 1, 50);
- if (poll_num == -1) {
- if (errno == EINTR)
- continue;
- error_errno(_("polling inotify message stream failed"));
- state->listen_data->shutdown = SHUTDOWN_ERROR;
- continue;
- }
-
- if ((time(NULL) - checked) >= interval) {
- checked = time(NULL);
- if (check_stale_dir_renames(&state->listen_data->renames,
- checked - interval)) {
- trace_printf_key(&trace_fsmonitor,
- "missed IN_MOVED_TO events, forcing shutdown");
- state->listen_data->shutdown = SHUTDOWN_FORCE;
- continue;
- }
- }
-
- if (poll_num > 0 && (fds[0].revents & POLLIN))
- handle_events(state);
-
- continue;
- case SHUTDOWN_ERROR:
- state->listen_error_code = -1;
- ipc_server_stop_async(state->ipc_server_data);
- break;
- case SHUTDOWN_FORCE:
- state->listen_error_code = 0;
- ipc_server_stop_async(state->ipc_server_data);
- break;
- case SHUTDOWN_STOP:
- default:
- state->listen_error_code = 0;
- break;
- }
- return;
- }
-}
+++ /dev/null
-#include "git-compat-util.h"
-#include "fsmonitor-ll.h"
-#include "fsmonitor-path-utils.h"
-#include "gettext.h"
-#include "trace.h"
-
-#include <sys/statfs.h>
-
-#ifdef HAVE_LINUX_MAGIC_H
-#include <linux/magic.h>
-#endif
-
-/*
- * Filesystem magic numbers for remote filesystems.
- * Defined here if not available in linux/magic.h.
- */
-#ifndef CIFS_SUPER_MAGIC
-#define CIFS_SUPER_MAGIC 0xff534d42
-#endif
-#ifndef SMB_SUPER_MAGIC
-#define SMB_SUPER_MAGIC 0x517b
-#endif
-#ifndef SMB2_SUPER_MAGIC
-#define SMB2_SUPER_MAGIC 0xfe534d42
-#endif
-#ifndef NFS_SUPER_MAGIC
-#define NFS_SUPER_MAGIC 0x6969
-#endif
-#ifndef AFS_SUPER_MAGIC
-#define AFS_SUPER_MAGIC 0x5346414f
-#endif
-#ifndef CODA_SUPER_MAGIC
-#define CODA_SUPER_MAGIC 0x73757245
-#endif
-#ifndef FUSE_SUPER_MAGIC
-#define FUSE_SUPER_MAGIC 0x65735546
-#endif
-
-/*
- * Check if filesystem type is a remote filesystem.
- */
-static int is_remote_fs(unsigned long f_type)
-{
- switch (f_type) {
- case CIFS_SUPER_MAGIC:
- case SMB_SUPER_MAGIC:
- case SMB2_SUPER_MAGIC:
- case NFS_SUPER_MAGIC:
- case AFS_SUPER_MAGIC:
- case CODA_SUPER_MAGIC:
- case FUSE_SUPER_MAGIC:
- return 1;
- default:
- return 0;
- }
-}
-
-/*
- * Map filesystem magic numbers to human-readable names as a fallback
- * when /proc/mounts is unavailable. This only covers the remote and
- * special filesystems in is_remote_fs() above; local filesystems are
- * never flagged as incompatible, so we do not need their names here.
- */
-static const char *get_fs_typename(unsigned long f_type)
-{
- switch (f_type) {
- case CIFS_SUPER_MAGIC:
- return "cifs";
- case SMB_SUPER_MAGIC:
- return "smb";
- case SMB2_SUPER_MAGIC:
- return "smb2";
- case NFS_SUPER_MAGIC:
- return "nfs";
- case AFS_SUPER_MAGIC:
- return "afs";
- case CODA_SUPER_MAGIC:
- return "coda";
- case FUSE_SUPER_MAGIC:
- return "fuse";
- default:
- return "unknown";
- }
-}
-
-/*
- * Find the mount point for a given path by reading /proc/mounts.
- *
- * statfs(2) gives us f_type (the magic number) but not the human-readable
- * filesystem type string. We scan /proc/mounts to find the mount entry
- * whose path is the longest prefix of ours and whose f_fsid matches,
- * which gives us the fstype string (e.g. "nfs", "ext4") for logging.
- */
-static char *find_mount(const char *path, const struct statfs *path_fs)
-{
- FILE *fp;
- struct strbuf line = STRBUF_INIT;
- struct strbuf match = STRBUF_INIT;
- struct strbuf fstype = STRBUF_INIT;
- char *result = NULL;
-
- fp = fopen("/proc/mounts", "r");
- if (!fp)
- return NULL;
-
- while (strbuf_getline(&line, fp) != EOF) {
- char *fields[6];
- char *p = line.buf;
- int i;
-
- /* Parse mount entry: device mountpoint fstype options dump pass */
- for (i = 0; i < 6 && p; i++) {
- fields[i] = p;
- p = strchr(p, ' ');
- if (p)
- *p++ = '\0';
- }
-
- if (i >= 3) {
- const char *mountpoint = fields[1];
- const char *type = fields[2];
- struct statfs mount_fs;
-
- /* Check if this mount point is a prefix of our path */
- if (starts_with(path, mountpoint) &&
- (path[strlen(mountpoint)] == '/' ||
- path[strlen(mountpoint)] == '\0')) {
- /* Check if filesystem ID matches */
- if (statfs(mountpoint, &mount_fs) == 0 &&
- !memcmp(&mount_fs.f_fsid, &path_fs->f_fsid,
- sizeof(mount_fs.f_fsid))) {
- /* Keep the longest matching mount point */
- if (strlen(mountpoint) > match.len) {
- strbuf_reset(&match);
- strbuf_addstr(&match, mountpoint);
- strbuf_reset(&fstype);
- strbuf_addstr(&fstype, type);
- }
- }
- }
- }
- }
-
- fclose(fp);
- strbuf_release(&line);
- strbuf_release(&match);
-
- if (fstype.len)
- result = strbuf_detach(&fstype, NULL);
- else
- strbuf_release(&fstype);
-
- return result;
-}
-
-int fsmonitor__get_fs_info(const char *path, struct fs_info *fs_info)
-{
- struct statfs fs;
-
- if (statfs(path, &fs) == -1) {
- int saved_errno = errno;
- trace_printf_key(&trace_fsmonitor, "statfs('%s') failed: %s",
- path, strerror(saved_errno));
- errno = saved_errno;
- return -1;
- }
-
- trace_printf_key(&trace_fsmonitor,
- "statfs('%s') [type 0x%08lx]",
- path, (unsigned long)fs.f_type);
-
- fs_info->is_remote = is_remote_fs(fs.f_type);
-
- /*
- * Try to get filesystem type from /proc/mounts for a more
- * descriptive name.
- */
- fs_info->typename = find_mount(path, &fs);
- if (!fs_info->typename)
- fs_info->typename = xstrdup(get_fs_typename(fs.f_type));
-
- trace_printf_key(&trace_fsmonitor,
- "'%s' is_remote: %d, typename: %s",
- path, fs_info->is_remote, fs_info->typename);
-
- return 0;
-}
-
-int fsmonitor__is_fs_remote(const char *path)
-{
- struct fs_info fs;
-
- if (fsmonitor__get_fs_info(path, &fs))
- return -1;
-
- free(fs.typename);
-
- return fs.is_remote;
-}
-
-/*
- * No-op for Linux - we don't have firmlinks like macOS.
- */
-int fsmonitor__get_alias(const char *path UNUSED,
- struct alias_info *info UNUSED)
-{
- return 0;
-}
-
-/*
- * No-op for Linux - we don't have firmlinks like macOS.
- */
-char *fsmonitor__resolve_alias(const char *path UNUSED,
- const struct alias_info *info UNUSED)
-{
- return NULL;
-}
return err_win_to_posix(GetLastError());
return 0;
}
-
-int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
- const struct timespec *abstime)
-{
- struct timeval now;
- long long now_ms, deadline_ms;
- DWORD timeout_ms;
-
- gettimeofday(&now, NULL);
- now_ms = (long long)now.tv_sec * 1000 + now.tv_usec / 1000;
- deadline_ms = (long long)abstime->tv_sec * 1000 +
- abstime->tv_nsec / 1000000;
-
- if (deadline_ms <= now_ms)
- return ETIMEDOUT;
- else
- timeout_ms = (DWORD)(deadline_ms - now_ms);
-
- if (SleepConditionVariableCS(cond, mutex, timeout_ms) == 0) {
- DWORD err = GetLastError();
- if (err == ERROR_TIMEOUT)
- return ETIMEDOUT;
- return err_win_to_posix(err);
- }
- return 0;
-}
pthread_t pthread_self(void);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
-int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
- const struct timespec *abstime);
static inline void NORETURN pthread_exit(void *ret)
{
BASIC_CFLAGS += -std=c99
endif
LINK_FUZZ_PROGRAMS = YesPlease
-
- # The builtin FSMonitor on Linux builds upon Simple-IPC. Both require
- # Unix domain sockets and PThreads.
- ifndef NO_PTHREADS
- ifndef NO_UNIX_SOCKETS
- FSMONITOR_DAEMON_BACKEND = linux
- FSMONITOR_OS_SETTINGS = unix
- BASIC_CFLAGS += -DHAVE_LINUX_MAGIC_H
- endif
- endif
endif
ifeq ($(uname_S),GNU/kFreeBSD)
HAVE_ALLOCA_H = YesPlease
ifndef NO_PTHREADS
ifndef NO_UNIX_SOCKETS
FSMONITOR_DAEMON_BACKEND = darwin
- FSMONITOR_OS_SETTINGS = unix
+ FSMONITOR_OS_SETTINGS = darwin
endif
endif
if(SUPPORTS_SIMPLE_IPC)
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
- set(FSMONITOR_DAEMON_BACKEND "win32")
- set(FSMONITOR_OS_SETTINGS "win32")
- elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
- set(FSMONITOR_DAEMON_BACKEND "darwin")
- set(FSMONITOR_OS_SETTINGS "unix")
- elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux")
- set(FSMONITOR_DAEMON_BACKEND "linux")
- set(FSMONITOR_OS_SETTINGS "unix")
- add_compile_definitions(HAVE_LINUX_MAGIC_H)
- endif()
+ add_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)
+ list(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-win32.c)
+ list(APPEND compat_SOURCES compat/fsmonitor/fsm-health-win32.c)
+ list(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-win32.c)
+ list(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-win32.c)
- if(FSMONITOR_DAEMON_BACKEND)
+ add_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)
+ list(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-win32.c)
+ elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
add_compile_definitions(HAVE_FSMONITOR_DAEMON_BACKEND)
- list(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-${FSMONITOR_DAEMON_BACKEND}.c)
- list(APPEND compat_SOURCES compat/fsmonitor/fsm-health-${FSMONITOR_DAEMON_BACKEND}.c)
- list(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-${FSMONITOR_OS_SETTINGS}.c)
- list(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-${FSMONITOR_DAEMON_BACKEND}.c)
+ list(APPEND compat_SOURCES compat/fsmonitor/fsm-listen-darwin.c)
+ list(APPEND compat_SOURCES compat/fsmonitor/fsm-health-darwin.c)
+ list(APPEND compat_SOURCES compat/fsmonitor/fsm-ipc-darwin.c)
+ list(APPEND compat_SOURCES compat/fsmonitor/fsm-path-utils-darwin.c)
add_compile_definitions(HAVE_FSMONITOR_OS_SETTINGS)
- list(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-${FSMONITOR_OS_SETTINGS}.c)
+ list(APPEND compat_SOURCES compat/fsmonitor/fsm-settings-darwin.c)
endif()
endif()
file(STRINGS ${CMAKE_SOURCE_DIR}/GIT-BUILD-OPTIONS.in git_build_options NEWLINE_CONSUME)
string(REPLACE "@BROKEN_PATH_FIX@" "" git_build_options "${git_build_options}")
string(REPLACE "@DIFF@" "'${DIFF}'" git_build_options "${git_build_options}")
-string(REPLACE "@FSMONITOR_DAEMON_BACKEND@" "${FSMONITOR_DAEMON_BACKEND}" git_build_options "${git_build_options}")
-string(REPLACE "@FSMONITOR_OS_SETTINGS@" "${FSMONITOR_OS_SETTINGS}" git_build_options "${git_build_options}")
+string(REPLACE "@FSMONITOR_DAEMON_BACKEND@" "win32" git_build_options "${git_build_options}")
+string(REPLACE "@FSMONITOR_OS_SETTINGS@" "win32" git_build_options "${git_build_options}")
string(REPLACE "@GITWEBDIR@" "'${GITWEBDIR}'" git_build_options "${git_build_options}")
string(REPLACE "@GIT_INTEROP_MAKE_OPTS@" "" git_build_options "${git_build_options}")
string(REPLACE "@GIT_PERF_LARGE_REPO@" "" git_build_options "${git_build_options}")
cmd.git_cmd = 1;
cmd.no_stdin = 1;
- cmd.no_stdout = 1;
- cmd.no_stderr = 1;
- cmd.close_fd_above_stderr = 1;
cmd.trace2_child_class = "fsmonitor";
strvec_pushl(&cmd.args, "fsmonitor--daemon", "start", NULL);
endif
fsmonitor_backend = ''
-fsmonitor_os = ''
if host_machine.system() == 'windows'
fsmonitor_backend = 'win32'
- fsmonitor_os = 'win32'
-elif host_machine.system() == 'linux' and threads.found() and compiler.has_header('linux/magic.h')
- fsmonitor_backend = 'linux'
- fsmonitor_os = 'unix'
- libgit_c_args += '-DHAVE_LINUX_MAGIC_H'
elif host_machine.system() == 'darwin'
fsmonitor_backend = 'darwin'
- fsmonitor_os = 'unix'
libgit_dependencies += dependency('CoreServices')
endif
if fsmonitor_backend != ''
compat_sources += [
'compat/fsmonitor/fsm-health-' + fsmonitor_backend + '.c',
- 'compat/fsmonitor/fsm-ipc-' + fsmonitor_os + '.c',
+ 'compat/fsmonitor/fsm-ipc-' + fsmonitor_backend + '.c',
'compat/fsmonitor/fsm-listen-' + fsmonitor_backend + '.c',
'compat/fsmonitor/fsm-path-utils-' + fsmonitor_backend + '.c',
- 'compat/fsmonitor/fsm-settings-' + fsmonitor_os + '.c',
+ 'compat/fsmonitor/fsm-settings-' + fsmonitor_backend + '.c',
]
endif
build_options_config.set_quoted('FSMONITOR_DAEMON_BACKEND', fsmonitor_backend)
-build_options_config.set_quoted('FSMONITOR_OS_SETTINGS', fsmonitor_os)
+build_options_config.set_quoted('FSMONITOR_OS_SETTINGS', fsmonitor_backend)
if not get_option('b_sanitize').contains('address') and get_option('regex').allowed() and compiler.has_header('regex.h') and compiler.get_define('REG_STARTEND', prefix: '#include <regex.h>') != ''
build_options_config.set('NO_REGEX', '')
"restoring signal mask");
#endif
}
-
#endif /* GIT_WINDOWS_NATIVE */
static inline void set_cloexec(int fd)
child_close(cmd->out);
}
- if (cmd->close_fd_above_stderr) {
- long max_fd = sysconf(_SC_OPEN_MAX);
- int fd;
- if (max_fd < 0 || max_fd > 4096)
- max_fd = 4096;
- for (fd = 3; fd < max_fd; fd++) {
- if (fd != child_notifier)
- close(fd);
- }
- }
-
if (cmd->dir && chdir(cmd->dir))
child_die(CHILD_ERR_CHDIR);
unsigned stdout_to_stderr:1;
unsigned clean_on_exit:1;
unsigned wait_after_clean:1;
-
- /**
- * Close file descriptors 3 and above in the child after forking
- * but before exec. This prevents the child from inheriting
- * pipe endpoints or other descriptors from the parent
- * environment (e.g., the test harness).
- */
- unsigned close_fd_above_stderr:1;
-
void (*clean_on_exit_handler)(struct child_process *process);
};
test_done
fi
-# Verify that the filesystem delivers events to the daemon.
-# On some configurations (e.g., overlayfs with older kernels),
-# inotify watches succeed but events are never delivered. The
-# cookie wait will time out and the daemon logs a trace message.
-#
-# Use "timeout" (if available) to guard each step against hangs.
-maybe_timeout () {
- if type timeout >/dev/null 2>&1
- then
- timeout "$@"
- else
- shift
- "$@"
- fi
-}
-verify_fsmonitor_works () {
- git init test_fsmonitor_smoke || return 1
-
- GIT_TRACE_FSMONITOR="$PWD/smoke.trace" &&
- export GIT_TRACE_FSMONITOR &&
- maybe_timeout 30 \
- git -C test_fsmonitor_smoke fsmonitor--daemon start \
- --start-timeout=10
- ret=$?
- unset GIT_TRACE_FSMONITOR
- if test $ret -ne 0
- then
- rm -rf test_fsmonitor_smoke smoke.trace
- return 1
- fi
-
- maybe_timeout 10 \
- test-tool -C test_fsmonitor_smoke fsmonitor-client query \
- --token 0 >/dev/null 2>&1
- maybe_timeout 5 \
- git -C test_fsmonitor_smoke fsmonitor--daemon stop 2>/dev/null
- ! grep -q "cookie_wait timed out" "$PWD/smoke.trace" 2>/dev/null
- ret=$?
- rm -rf test_fsmonitor_smoke smoke.trace
- return $ret
-}
-
-if ! verify_fsmonitor_works
-then
- skip_all="filesystem does not deliver fsmonitor events (container/overlayfs?)"
- test_done
-fi
-
stop_daemon_delete_repo () {
r=$1 &&
- test_might_fail maybe_timeout 30 \
- git -C $r fsmonitor--daemon stop 2>/dev/null
+ test_might_fail git -C $r fsmonitor--daemon stop &&
rm -rf $1
}
export GIT_TEST_FSMONITOR_TOKEN
fi &&
- git $r fsmonitor--daemon start --start-timeout=10 &&
+ git $r fsmonitor--daemon start &&
git $r fsmonitor--daemon status
)
}
retry_grep "^event: dir1$" .git/trace
'
-test_expect_success 'rapid nested directory creation' '
- test_when_finished "git fsmonitor--daemon stop; rm -rf rapid" &&
-
- start_daemon --tf "$PWD/.git/trace" &&
-
- # Rapidly create nested directories to exercise race conditions
- # where directory watches may be added concurrently during
- # event processing and recursive scanning.
- for i in $(test_seq 1 20)
- do
- mkdir -p "rapid/nested/dir$i/subdir/deep" || return 1
- done &&
-
- # Give the daemon time to process all events
- sleep 1 &&
-
- test-tool fsmonitor-client query --token 0 &&
-
- # Verify daemon is still running (did not crash)
- git fsmonitor--daemon status
-'
-
# The next few test cases exercise the token-resync code. When filesystem
# drops events (because of filesystem velocity or because the daemon isn't
# polling fast enough), we need to discard the cached data (relative to the
start_git_in_background () {
git "$@" &
git_pid=$!
- git_pgid=$(ps -o pgid= -p $git_pid 2>/dev/null ||
- awk '{print $5}' /proc/$git_pid/stat 2>/dev/null) &&
- git_pgid="${git_pgid## }" &&
- git_pgid="${git_pgid%% }"
+ git_pgid=$(ps -o pgid= -p $git_pid)
nr_tries_left=10
while true
do
fi
sleep 1
nr_tries_left=$(($nr_tries_left - 1))
- done >/dev/null 2>&1 3>&- 4>&- 5>&- 6>&- 7>&- &
+ done >/dev/null 2>&1 &
watchdog_pid=$!
wait $git_pid
}
stop_git () {
- test -n "$git_pgid" || return 0
- while kill -0 -- -$git_pgid 2>/dev/null
+ while kill -0 -- -$git_pgid
do
- kill -- -$git_pgid 2>/dev/null
+ kill -- -$git_pgid
sleep 1
done
}
test_expect_success !MINGW "submodule implicitly starts daemon by pull" '
test_atexit "stop_watchdog" &&
- test_when_finished "set +m; stop_git; rm -rf cloned super sub" &&
+ test_when_finished "stop_git; rm -rf cloned super sub" &&
create_super super &&
create_sub sub &&
'
test_expect_success 'scalar clone' '
- # index.skipHash (Scalar default) and GIT_TEST_SPLIT_INDEX are
- # incompatible: the shared index gets a null OID and fails to
- # load on re-read.
- sane_unset GIT_TEST_SPLIT_INDEX &&
second=$(git rev-parse --verify second:second.t) &&
scalar clone "file://$(pwd)" cloned --single-branch &&
(
'
test_expect_success 'scalar clone --no-... opts' '
- sane_unset GIT_TEST_SPLIT_INDEX &&
# Note: redirect stderr always to avoid having a verbose test
# run result in a difference in the --[no-]progress option.
GIT_TRACE2_EVENT="$(pwd)/no-opt-trace" scalar clone \
SQ="'"
test_expect_success UNZIP 'scalar diagnose' '
- sane_unset GIT_TEST_SPLIT_INDEX &&
scalar clone "file://$(pwd)" cloned --single-branch &&
git repack &&
echo "$(pwd)/.git/objects/" >>cloned/src/.git/objects/info/alternates &&
GIT_TEST_MAINT_SCHEDULER="crontab:test-tool crontab cron.txt,launchctl:true,schtasks:true"
export GIT_TEST_MAINT_SCHEDULER
-# index.skipHash (Scalar default) and GIT_TEST_SPLIT_INDEX are
-# incompatible: the shared index gets a null OID and fails to
-# load on re-read. Every test here uses scalar clone.
-sane_unset GIT_TEST_SPLIT_INDEX
-
test_expect_success 'set up repository to clone' '
rm -rf .git &&
git init to-clone &&