blob: e818583420aa1b0922e2d63e4a05f86c12de1c5d [file] [log] [blame]
Elijah Newren36bf1952023-02-24 00:09:24 +00001#include "git-compat-util.h"
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +00002#include "config.h"
Elijah Newren36bf1952023-02-24 00:09:24 +00003#include "gettext.h"
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +00004#include "repository.h"
Eric DeCosta25c2cab2022-10-04 17:32:30 +00005#include "fsmonitor-ipc.h"
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +00006#include "fsmonitor-settings.h"
Eric DeCosta508c1a52022-10-04 17:32:26 +00007#include "fsmonitor-path-utils.h"
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +00008
9/*
10 * We keep this structure defintion private and have getters
11 * for all fields so that we can lazy load it as needed.
12 */
13struct fsmonitor_settings {
14 enum fsmonitor_mode mode;
Jeff Hostetler62a62a22022-05-26 21:46:58 +000015 enum fsmonitor_reason reason;
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +000016 char *hook_path;
17};
18
Eric DeCosta508c1a52022-10-04 17:32:26 +000019/*
20 * Remote working directories are problematic for FSMonitor.
21 *
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.
25 *
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.
29 *
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.
36 *
37 * In theory, the above issues need to be addressed whether we are
38 * using the Hook or IPC API.
39 *
40 * So (for now at least), mark remote working directories as
41 * incompatible unless 'fsmonitor.allowRemote' is true.
42 *
43 */
44#ifdef HAVE_FSMONITOR_OS_SETTINGS
45static enum fsmonitor_reason check_remote(struct repository *r)
46{
47 int allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */
48 int is_remote = fsmonitor__is_fs_remote(r->worktree);
49
50 switch (is_remote) {
51 case 0:
52 return FSMONITOR_REASON_OK;
53 case 1:
54 repo_config_get_bool(r, "fsmonitor.allowremote", &allow_remote);
55 if (allow_remote < 1)
56 return FSMONITOR_REASON_REMOTE;
57 else
58 return FSMONITOR_REASON_OK;
59 default:
60 return FSMONITOR_REASON_ERROR;
61 }
62}
63#endif
64
Jeff Kingf4c57782023-09-18 18:31:48 -040065static enum fsmonitor_reason check_for_incompatible(struct repository *r,
66 int ipc MAYBE_UNUSED)
Jeff Hostetler62a62a22022-05-26 21:46:58 +000067{
68 if (!r->worktree) {
69 /*
70 * Bare repositories don't have a working directory and
71 * therefore have nothing to watch.
72 */
73 return FSMONITOR_REASON_BARE;
74 }
75
Jeff Hostetlerd33c8042022-05-26 21:46:59 +000076#ifdef HAVE_FSMONITOR_OS_SETTINGS
77 {
78 enum fsmonitor_reason reason;
79
Eric DeCosta508c1a52022-10-04 17:32:26 +000080 reason = check_remote(r);
81 if (reason != FSMONITOR_REASON_OK)
82 return reason;
Eric DeCosta8f449762022-10-04 17:32:28 +000083 reason = fsm_os__incompatible(r, ipc);
Jeff Hostetlerd33c8042022-05-26 21:46:59 +000084 if (reason != FSMONITOR_REASON_OK)
85 return reason;
86 }
87#endif
88
Jeff Hostetler62a62a22022-05-26 21:46:58 +000089 return FSMONITOR_REASON_OK;
90}
91
92static struct fsmonitor_settings *alloc_settings(void)
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +000093{
94 struct fsmonitor_settings *s;
Jeff Hostetler62a62a22022-05-26 21:46:58 +000095
96 CALLOC_ARRAY(s, 1);
97 s->mode = FSMONITOR_MODE_DISABLED;
98 s->reason = FSMONITOR_REASON_UNTESTED;
99
100 return s;
101}
102
103static void lookup_fsmonitor_settings(struct repository *r)
104{
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +0000105 const char *const_str;
Patrick Steinhardt6073b3b2024-05-27 13:46:15 +0200106 char *to_free = NULL;
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +0000107 int bool_value;
108
109 if (r->settings.fsmonitor)
110 return;
111
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +0000112 /*
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.)
118 */
119 switch (repo_config_get_maybe_bool(r, "core.fsmonitor", &bool_value)) {
120
121 case 0: /* config value was set to <bool> */
122 if (bool_value)
123 fsm_settings__set_ipc(r);
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000124 else
125 fsm_settings__set_disabled(r);
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +0000126 return;
127
128 case 1: /* config value was unset */
129 const_str = getenv("GIT_TEST_FSMONITOR");
130 break;
131
132 case -1: /* config value set to an arbitrary string */
Patrick Steinhardt6073b3b2024-05-27 13:46:15 +0200133 if (repo_config_get_pathname(r, "core.fsmonitor", &to_free))
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +0000134 return; /* should not happen */
Patrick Steinhardt6073b3b2024-05-27 13:46:15 +0200135 const_str = to_free;
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +0000136 break;
137
138 default: /* should not happen */
139 return;
140 }
141
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000142 if (const_str && *const_str)
143 fsm_settings__set_hook(r, const_str);
144 else
145 fsm_settings__set_disabled(r);
Patrick Steinhardt6073b3b2024-05-27 13:46:15 +0200146 free(to_free);
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +0000147}
148
149enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)
150{
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000151 if (!r->settings.fsmonitor)
152 lookup_fsmonitor_settings(r);
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +0000153
154 return r->settings.fsmonitor->mode;
155}
156
157const char *fsm_settings__get_hook_path(struct repository *r)
158{
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000159 if (!r->settings.fsmonitor)
160 lookup_fsmonitor_settings(r);
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +0000161
162 return r->settings.fsmonitor->hook_path;
163}
164
165void fsm_settings__set_ipc(struct repository *r)
166{
Eric DeCosta8f449762022-10-04 17:32:28 +0000167 enum fsmonitor_reason reason = check_for_incompatible(r, 1);
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000168
169 if (reason != FSMONITOR_REASON_OK) {
170 fsm_settings__set_incompatible(r, reason);
171 return;
172 }
173
174 /*
175 * Caller requested IPC explicitly, so avoid (possibly
176 * recursive) config lookup.
177 */
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000178 if (!r->settings.fsmonitor)
179 r->settings.fsmonitor = alloc_settings();
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +0000180
181 r->settings.fsmonitor->mode = FSMONITOR_MODE_IPC;
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000182 r->settings.fsmonitor->reason = reason;
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +0000183 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
184}
185
186void fsm_settings__set_hook(struct repository *r, const char *path)
187{
Eric DeCosta8f449762022-10-04 17:32:28 +0000188 enum fsmonitor_reason reason = check_for_incompatible(r, 0);
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000189
190 if (reason != FSMONITOR_REASON_OK) {
191 fsm_settings__set_incompatible(r, reason);
192 return;
193 }
194
195 /*
196 * Caller requested hook explicitly, so avoid (possibly
197 * recursive) config lookup.
198 */
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000199 if (!r->settings.fsmonitor)
200 r->settings.fsmonitor = alloc_settings();
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +0000201
202 r->settings.fsmonitor->mode = FSMONITOR_MODE_HOOK;
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000203 r->settings.fsmonitor->reason = reason;
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +0000204 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
205 r->settings.fsmonitor->hook_path = strdup(path);
206}
207
208void fsm_settings__set_disabled(struct repository *r)
209{
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000210 if (!r->settings.fsmonitor)
211 r->settings.fsmonitor = alloc_settings();
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +0000212
213 r->settings.fsmonitor->mode = FSMONITOR_MODE_DISABLED;
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000214 r->settings.fsmonitor->reason = FSMONITOR_REASON_OK;
Jeff Hostetler1e0ea5c2022-03-25 18:02:46 +0000215 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
216}
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000217
218void fsm_settings__set_incompatible(struct repository *r,
219 enum fsmonitor_reason reason)
220{
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000221 if (!r->settings.fsmonitor)
222 r->settings.fsmonitor = alloc_settings();
223
224 r->settings.fsmonitor->mode = FSMONITOR_MODE_INCOMPATIBLE;
225 r->settings.fsmonitor->reason = reason;
226 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
227}
228
229enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)
230{
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000231 if (!r->settings.fsmonitor)
232 lookup_fsmonitor_settings(r);
233
234 return r->settings.fsmonitor->reason;
235}
236
Eric DeCosta25c2cab2022-10-04 17:32:30 +0000237char *fsm_settings__get_incompatible_msg(struct repository *r,
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000238 enum fsmonitor_reason reason)
239{
240 struct strbuf msg = STRBUF_INIT;
Eric DeCosta25c2cab2022-10-04 17:32:30 +0000241 const char *socket_dir;
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000242
243 switch (reason) {
244 case FSMONITOR_REASON_UNTESTED:
245 case FSMONITOR_REASON_OK:
246 goto done;
247
Johannes Schindelin5a099912022-06-15 23:35:36 +0000248 case FSMONITOR_REASON_BARE: {
249 char *cwd = xgetcwd();
250
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000251 strbuf_addf(&msg,
252 _("bare repository '%s' is incompatible with fsmonitor"),
Johannes Schindelin5a099912022-06-15 23:35:36 +0000253 cwd);
254 free(cwd);
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000255 goto done;
Johannes Schindelin5a099912022-06-15 23:35:36 +0000256 }
Jeff Hostetler5c58fbd2022-05-26 21:47:00 +0000257
Jeff Hostetler1e7be102022-05-26 21:47:02 +0000258 case FSMONITOR_REASON_ERROR:
259 strbuf_addf(&msg,
260 _("repository '%s' is incompatible with fsmonitor due to errors"),
261 r->worktree);
262 goto done;
263
264 case FSMONITOR_REASON_REMOTE:
265 strbuf_addf(&msg,
266 _("remote repository '%s' is incompatible with fsmonitor"),
267 r->worktree);
268 goto done;
269
Jeff Hostetler5c58fbd2022-05-26 21:47:00 +0000270 case FSMONITOR_REASON_VFS4GIT:
271 strbuf_addf(&msg,
272 _("virtual repository '%s' is incompatible with fsmonitor"),
273 r->worktree);
274 goto done;
Jeff Hostetlerddc5dac2022-05-26 21:47:04 +0000275
276 case FSMONITOR_REASON_NOSOCKETS:
Eric DeCosta25c2cab2022-10-04 17:32:30 +0000277 socket_dir = dirname((char *)fsmonitor_ipc__get_path(r));
Jeff Hostetlerddc5dac2022-05-26 21:47:04 +0000278 strbuf_addf(&msg,
Eric DeCosta25c2cab2022-10-04 17:32:30 +0000279 _("socket directory '%s' is incompatible with fsmonitor due"
280 " to lack of Unix sockets support"),
281 socket_dir);
Jeff Hostetlerddc5dac2022-05-26 21:47:04 +0000282 goto done;
Jeff Hostetler62a62a22022-05-26 21:46:58 +0000283 }
284
285 BUG("Unhandled case in fsm_settings__get_incompatible_msg: '%d'",
286 reason);
287
288done:
289 return strbuf_detach(&msg, NULL);
290}