test-ctype: check EOF
[git/gitster.git] / fsmonitor-settings.c
blob899bfe9c8138e1ae46f1fd0f0dfc7aebba05b81a
1 #include "cache.h"
2 #include "config.h"
3 #include "repository.h"
4 #include "fsmonitor-ipc.h"
5 #include "fsmonitor-settings.h"
6 #include "fsmonitor-path-utils.h"
8 /*
9 * We keep this structure defintion private and have getters
10 * for all fields so that we can lazy load it as needed.
12 struct fsmonitor_settings {
13 enum fsmonitor_mode mode;
14 enum fsmonitor_reason reason;
15 char *hook_path;
19 * Remote working directories are problematic for FSMonitor.
21 * The underlying file system on the server machine and/or the remote
22 * mount type dictates whether notification events are available at
23 * all to remote client machines.
25 * Kernel differences between the server and client machines also
26 * dictate the how (buffering, frequency, de-dup) the events are
27 * delivered to client machine processes.
29 * A client machine (such as a laptop) may choose to suspend/resume
30 * and it is unclear (without lots of testing) whether the watcher can
31 * resync after a resume. We might be able to treat this as a normal
32 * "events were dropped by the kernel" event and do our normal "flush
33 * and resync" --or-- we might need to close the existing (zombie?)
34 * notification fd and create a new one.
36 * In theory, the above issues need to be addressed whether we are
37 * using the Hook or IPC API.
39 * So (for now at least), mark remote working directories as
40 * incompatible unless 'fsmonitor.allowRemote' is true.
43 #ifdef HAVE_FSMONITOR_OS_SETTINGS
44 static enum fsmonitor_reason check_remote(struct repository *r)
46 int allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */
47 int is_remote = fsmonitor__is_fs_remote(r->worktree);
49 switch (is_remote) {
50 case 0:
51 return FSMONITOR_REASON_OK;
52 case 1:
53 repo_config_get_bool(r, "fsmonitor.allowremote", &allow_remote);
54 if (allow_remote < 1)
55 return FSMONITOR_REASON_REMOTE;
56 else
57 return FSMONITOR_REASON_OK;
58 default:
59 return FSMONITOR_REASON_ERROR;
62 #endif
64 static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)
66 if (!r->worktree) {
68 * Bare repositories don't have a working directory and
69 * therefore have nothing to watch.
71 return FSMONITOR_REASON_BARE;
74 #ifdef HAVE_FSMONITOR_OS_SETTINGS
76 enum fsmonitor_reason reason;
78 reason = check_remote(r);
79 if (reason != FSMONITOR_REASON_OK)
80 return reason;
81 reason = fsm_os__incompatible(r, ipc);
82 if (reason != FSMONITOR_REASON_OK)
83 return reason;
85 #endif
87 return FSMONITOR_REASON_OK;
90 static struct fsmonitor_settings *alloc_settings(void)
92 struct fsmonitor_settings *s;
94 CALLOC_ARRAY(s, 1);
95 s->mode = FSMONITOR_MODE_DISABLED;
96 s->reason = FSMONITOR_REASON_UNTESTED;
98 return s;
101 static void lookup_fsmonitor_settings(struct repository *r)
103 const char *const_str;
104 int bool_value;
106 if (r->settings.fsmonitor)
107 return;
110 * Overload the existing "core.fsmonitor" config setting (which
111 * has historically been either unset or a hook pathname) to
112 * now allow a boolean value to enable the builtin FSMonitor
113 * or to turn everything off. (This does imply that you can't
114 * use a hook script named "true" or "false", but that's OK.)
116 switch (repo_config_get_maybe_bool(r, "core.fsmonitor", &bool_value)) {
118 case 0: /* config value was set to <bool> */
119 if (bool_value)
120 fsm_settings__set_ipc(r);
121 else
122 fsm_settings__set_disabled(r);
123 return;
125 case 1: /* config value was unset */
126 const_str = getenv("GIT_TEST_FSMONITOR");
127 break;
129 case -1: /* config value set to an arbitrary string */
130 if (repo_config_get_pathname(r, "core.fsmonitor", &const_str))
131 return; /* should not happen */
132 break;
134 default: /* should not happen */
135 return;
138 if (const_str && *const_str)
139 fsm_settings__set_hook(r, const_str);
140 else
141 fsm_settings__set_disabled(r);
144 enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)
146 if (!r->settings.fsmonitor)
147 lookup_fsmonitor_settings(r);
149 return r->settings.fsmonitor->mode;
152 const char *fsm_settings__get_hook_path(struct repository *r)
154 if (!r->settings.fsmonitor)
155 lookup_fsmonitor_settings(r);
157 return r->settings.fsmonitor->hook_path;
160 void fsm_settings__set_ipc(struct repository *r)
162 enum fsmonitor_reason reason = check_for_incompatible(r, 1);
164 if (reason != FSMONITOR_REASON_OK) {
165 fsm_settings__set_incompatible(r, reason);
166 return;
170 * Caller requested IPC explicitly, so avoid (possibly
171 * recursive) config lookup.
173 if (!r->settings.fsmonitor)
174 r->settings.fsmonitor = alloc_settings();
176 r->settings.fsmonitor->mode = FSMONITOR_MODE_IPC;
177 r->settings.fsmonitor->reason = reason;
178 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
181 void fsm_settings__set_hook(struct repository *r, const char *path)
183 enum fsmonitor_reason reason = check_for_incompatible(r, 0);
185 if (reason != FSMONITOR_REASON_OK) {
186 fsm_settings__set_incompatible(r, reason);
187 return;
191 * Caller requested hook explicitly, so avoid (possibly
192 * recursive) config lookup.
194 if (!r->settings.fsmonitor)
195 r->settings.fsmonitor = alloc_settings();
197 r->settings.fsmonitor->mode = FSMONITOR_MODE_HOOK;
198 r->settings.fsmonitor->reason = reason;
199 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
200 r->settings.fsmonitor->hook_path = strdup(path);
203 void fsm_settings__set_disabled(struct repository *r)
205 if (!r->settings.fsmonitor)
206 r->settings.fsmonitor = alloc_settings();
208 r->settings.fsmonitor->mode = FSMONITOR_MODE_DISABLED;
209 r->settings.fsmonitor->reason = FSMONITOR_REASON_OK;
210 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
213 void fsm_settings__set_incompatible(struct repository *r,
214 enum fsmonitor_reason reason)
216 if (!r->settings.fsmonitor)
217 r->settings.fsmonitor = alloc_settings();
219 r->settings.fsmonitor->mode = FSMONITOR_MODE_INCOMPATIBLE;
220 r->settings.fsmonitor->reason = reason;
221 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
224 enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)
226 if (!r->settings.fsmonitor)
227 lookup_fsmonitor_settings(r);
229 return r->settings.fsmonitor->reason;
232 char *fsm_settings__get_incompatible_msg(struct repository *r,
233 enum fsmonitor_reason reason)
235 struct strbuf msg = STRBUF_INIT;
236 const char *socket_dir;
238 switch (reason) {
239 case FSMONITOR_REASON_UNTESTED:
240 case FSMONITOR_REASON_OK:
241 goto done;
243 case FSMONITOR_REASON_BARE: {
244 char *cwd = xgetcwd();
246 strbuf_addf(&msg,
247 _("bare repository '%s' is incompatible with fsmonitor"),
248 cwd);
249 free(cwd);
250 goto done;
253 case FSMONITOR_REASON_ERROR:
254 strbuf_addf(&msg,
255 _("repository '%s' is incompatible with fsmonitor due to errors"),
256 r->worktree);
257 goto done;
259 case FSMONITOR_REASON_REMOTE:
260 strbuf_addf(&msg,
261 _("remote repository '%s' is incompatible with fsmonitor"),
262 r->worktree);
263 goto done;
265 case FSMONITOR_REASON_VFS4GIT:
266 strbuf_addf(&msg,
267 _("virtual repository '%s' is incompatible with fsmonitor"),
268 r->worktree);
269 goto done;
271 case FSMONITOR_REASON_NOSOCKETS:
272 socket_dir = dirname((char *)fsmonitor_ipc__get_path(r));
273 strbuf_addf(&msg,
274 _("socket directory '%s' is incompatible with fsmonitor due"
275 " to lack of Unix sockets support"),
276 socket_dir);
277 goto done;
280 BUG("Unhandled case in fsm_settings__get_incompatible_msg: '%d'",
281 reason);
283 done:
284 return strbuf_detach(&msg, NULL);