1 #include "git-compat-util.h"
4 #include "repository.h"
5 #include "fsmonitor-ipc.h"
6 #include "fsmonitor-settings.h"
7 #include "fsmonitor-path-utils.h"
10 * We keep this structure defintion private and have getters
11 * for all fields so that we can lazy load it as needed.
13 struct fsmonitor_settings
{
14 enum fsmonitor_mode mode
;
15 enum fsmonitor_reason reason
;
20 * Remote working directories are problematic for FSMonitor.
22 * The underlying file system on the server machine and/or the remote
23 * mount type dictates whether notification events are available at
24 * all to remote client machines.
26 * Kernel differences between the server and client machines also
27 * dictate the how (buffering, frequency, de-dup) the events are
28 * delivered to client machine processes.
30 * A client machine (such as a laptop) may choose to suspend/resume
31 * and it is unclear (without lots of testing) whether the watcher can
32 * resync after a resume. We might be able to treat this as a normal
33 * "events were dropped by the kernel" event and do our normal "flush
34 * and resync" --or-- we might need to close the existing (zombie?)
35 * notification fd and create a new one.
37 * In theory, the above issues need to be addressed whether we are
38 * using the Hook or IPC API.
40 * So (for now at least), mark remote working directories as
41 * incompatible unless 'fsmonitor.allowRemote' is true.
44 #ifdef HAVE_FSMONITOR_OS_SETTINGS
45 static enum fsmonitor_reason
check_remote(struct repository
*r
)
47 int allow_remote
= -1; /* -1 unset, 0 not allowed, 1 allowed */
48 int is_remote
= fsmonitor__is_fs_remote(r
->worktree
);
52 return FSMONITOR_REASON_OK
;
54 repo_config_get_bool(r
, "fsmonitor.allowremote", &allow_remote
);
56 return FSMONITOR_REASON_REMOTE
;
58 return FSMONITOR_REASON_OK
;
60 return FSMONITOR_REASON_ERROR
;
65 static enum fsmonitor_reason
check_for_incompatible(struct repository
*r
,
70 * Bare repositories don't have a working directory and
71 * therefore have nothing to watch.
73 return FSMONITOR_REASON_BARE
;
76 #ifdef HAVE_FSMONITOR_OS_SETTINGS
78 enum fsmonitor_reason reason
;
80 reason
= check_remote(r
);
81 if (reason
!= FSMONITOR_REASON_OK
)
83 reason
= fsm_os__incompatible(r
, ipc
);
84 if (reason
!= FSMONITOR_REASON_OK
)
89 return FSMONITOR_REASON_OK
;
92 static struct fsmonitor_settings
*alloc_settings(void)
94 struct fsmonitor_settings
*s
;
97 s
->mode
= FSMONITOR_MODE_DISABLED
;
98 s
->reason
= FSMONITOR_REASON_UNTESTED
;
103 static void lookup_fsmonitor_settings(struct repository
*r
)
105 const char *const_str
;
106 char *to_free
= NULL
;
109 if (r
->settings
.fsmonitor
)
113 * Overload the existing "core.fsmonitor" config setting (which
114 * has historically been either unset or a hook pathname) to
115 * now allow a boolean value to enable the builtin FSMonitor
116 * or to turn everything off. (This does imply that you can't
117 * use a hook script named "true" or "false", but that's OK.)
119 switch (repo_config_get_maybe_bool(r
, "core.fsmonitor", &bool_value
)) {
121 case 0: /* config value was set to <bool> */
123 fsm_settings__set_ipc(r
);
125 fsm_settings__set_disabled(r
);
128 case 1: /* config value was unset */
129 const_str
= getenv("GIT_TEST_FSMONITOR");
132 case -1: /* config value set to an arbitrary string */
133 if (repo_config_get_pathname(r
, "core.fsmonitor", &to_free
))
134 return; /* should not happen */
138 default: /* should not happen */
142 if (const_str
&& *const_str
)
143 fsm_settings__set_hook(r
, const_str
);
145 fsm_settings__set_disabled(r
);
149 enum fsmonitor_mode
fsm_settings__get_mode(struct repository
*r
)
151 if (!r
->settings
.fsmonitor
)
152 lookup_fsmonitor_settings(r
);
154 return r
->settings
.fsmonitor
->mode
;
157 const char *fsm_settings__get_hook_path(struct repository
*r
)
159 if (!r
->settings
.fsmonitor
)
160 lookup_fsmonitor_settings(r
);
162 return r
->settings
.fsmonitor
->hook_path
;
165 void fsm_settings__set_ipc(struct repository
*r
)
167 enum fsmonitor_reason reason
= check_for_incompatible(r
, 1);
169 if (reason
!= FSMONITOR_REASON_OK
) {
170 fsm_settings__set_incompatible(r
, reason
);
175 * Caller requested IPC explicitly, so avoid (possibly
176 * recursive) config lookup.
178 if (!r
->settings
.fsmonitor
)
179 r
->settings
.fsmonitor
= alloc_settings();
181 r
->settings
.fsmonitor
->mode
= FSMONITOR_MODE_IPC
;
182 r
->settings
.fsmonitor
->reason
= reason
;
183 FREE_AND_NULL(r
->settings
.fsmonitor
->hook_path
);
186 void fsm_settings__set_hook(struct repository
*r
, const char *path
)
188 enum fsmonitor_reason reason
= check_for_incompatible(r
, 0);
190 if (reason
!= FSMONITOR_REASON_OK
) {
191 fsm_settings__set_incompatible(r
, reason
);
196 * Caller requested hook explicitly, so avoid (possibly
197 * recursive) config lookup.
199 if (!r
->settings
.fsmonitor
)
200 r
->settings
.fsmonitor
= alloc_settings();
202 r
->settings
.fsmonitor
->mode
= FSMONITOR_MODE_HOOK
;
203 r
->settings
.fsmonitor
->reason
= reason
;
204 FREE_AND_NULL(r
->settings
.fsmonitor
->hook_path
);
205 r
->settings
.fsmonitor
->hook_path
= strdup(path
);
208 void fsm_settings__set_disabled(struct repository
*r
)
210 if (!r
->settings
.fsmonitor
)
211 r
->settings
.fsmonitor
= alloc_settings();
213 r
->settings
.fsmonitor
->mode
= FSMONITOR_MODE_DISABLED
;
214 r
->settings
.fsmonitor
->reason
= FSMONITOR_REASON_OK
;
215 FREE_AND_NULL(r
->settings
.fsmonitor
->hook_path
);
218 void fsm_settings__set_incompatible(struct repository
*r
,
219 enum fsmonitor_reason reason
)
221 if (!r
->settings
.fsmonitor
)
222 r
->settings
.fsmonitor
= alloc_settings();
224 r
->settings
.fsmonitor
->mode
= FSMONITOR_MODE_INCOMPATIBLE
;
225 r
->settings
.fsmonitor
->reason
= reason
;
226 FREE_AND_NULL(r
->settings
.fsmonitor
->hook_path
);
229 enum fsmonitor_reason
fsm_settings__get_reason(struct repository
*r
)
231 if (!r
->settings
.fsmonitor
)
232 lookup_fsmonitor_settings(r
);
234 return r
->settings
.fsmonitor
->reason
;
237 char *fsm_settings__get_incompatible_msg(struct repository
*r
,
238 enum fsmonitor_reason reason
)
240 struct strbuf msg
= STRBUF_INIT
;
241 const char *socket_dir
;
244 case FSMONITOR_REASON_UNTESTED
:
245 case FSMONITOR_REASON_OK
:
248 case FSMONITOR_REASON_BARE
: {
249 char *cwd
= xgetcwd();
252 _("bare repository '%s' is incompatible with fsmonitor"),
258 case FSMONITOR_REASON_ERROR
:
260 _("repository '%s' is incompatible with fsmonitor due to errors"),
264 case FSMONITOR_REASON_REMOTE
:
266 _("remote repository '%s' is incompatible with fsmonitor"),
270 case FSMONITOR_REASON_VFS4GIT
:
272 _("virtual repository '%s' is incompatible with fsmonitor"),
276 case FSMONITOR_REASON_NOSOCKETS
:
277 socket_dir
= dirname((char *)fsmonitor_ipc__get_path(r
));
279 _("socket directory '%s' is incompatible with fsmonitor due"
280 " to lack of Unix sockets support"),
285 BUG("Unhandled case in fsm_settings__get_incompatible_msg: '%d'",
289 return strbuf_detach(&msg
, NULL
);