]> git.ipfire.org Git - thirdparty/git.git/blame - fsmonitor-settings.c
Start the 2.46 cycle
[thirdparty/git.git] / fsmonitor-settings.c
CommitLineData
36bf1958 1#include "git-compat-util.h"
1e0ea5c4 2#include "config.h"
36bf1958 3#include "gettext.h"
1e0ea5c4 4#include "repository.h"
25c2cab0 5#include "fsmonitor-ipc.h"
1e0ea5c4 6#include "fsmonitor-settings.h"
508c1a57 7#include "fsmonitor-path-utils.h"
1e0ea5c4
JH
8
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;
62a62a28 15 enum fsmonitor_reason reason;
1e0ea5c4
JH
16 char *hook_path;
17};
18
508c1a57
ED
19/*
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
f4c5778b
JK
65static enum fsmonitor_reason check_for_incompatible(struct repository *r,
66 int ipc MAYBE_UNUSED)
62a62a28
JH
67{
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
d33c804d
JH
76#ifdef HAVE_FSMONITOR_OS_SETTINGS
77 {
78 enum fsmonitor_reason reason;
79
508c1a57
ED
80 reason = check_remote(r);
81 if (reason != FSMONITOR_REASON_OK)
82 return reason;
8f449768 83 reason = fsm_os__incompatible(r, ipc);
d33c804d
JH
84 if (reason != FSMONITOR_REASON_OK)
85 return reason;
86 }
87#endif
88
62a62a28
JH
89 return FSMONITOR_REASON_OK;
90}
91
92static struct fsmonitor_settings *alloc_settings(void)
1e0ea5c4
JH
93{
94 struct fsmonitor_settings *s;
62a62a28
JH
95
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{
1e0ea5c4
JH
105 const char *const_str;
106 int bool_value;
107
108 if (r->settings.fsmonitor)
109 return;
110
1e0ea5c4
JH
111 /*
112 * Overload the existing "core.fsmonitor" config setting (which
113 * has historically been either unset or a hook pathname) to
114 * now allow a boolean value to enable the builtin FSMonitor
115 * or to turn everything off. (This does imply that you can't
116 * use a hook script named "true" or "false", but that's OK.)
117 */
118 switch (repo_config_get_maybe_bool(r, "core.fsmonitor", &bool_value)) {
119
120 case 0: /* config value was set to <bool> */
121 if (bool_value)
122 fsm_settings__set_ipc(r);
62a62a28
JH
123 else
124 fsm_settings__set_disabled(r);
1e0ea5c4
JH
125 return;
126
127 case 1: /* config value was unset */
128 const_str = getenv("GIT_TEST_FSMONITOR");
129 break;
130
131 case -1: /* config value set to an arbitrary string */
132 if (repo_config_get_pathname(r, "core.fsmonitor", &const_str))
133 return; /* should not happen */
134 break;
135
136 default: /* should not happen */
137 return;
138 }
139
62a62a28
JH
140 if (const_str && *const_str)
141 fsm_settings__set_hook(r, const_str);
142 else
143 fsm_settings__set_disabled(r);
1e0ea5c4
JH
144}
145
146enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)
147{
62a62a28
JH
148 if (!r->settings.fsmonitor)
149 lookup_fsmonitor_settings(r);
1e0ea5c4
JH
150
151 return r->settings.fsmonitor->mode;
152}
153
154const char *fsm_settings__get_hook_path(struct repository *r)
155{
62a62a28
JH
156 if (!r->settings.fsmonitor)
157 lookup_fsmonitor_settings(r);
1e0ea5c4
JH
158
159 return r->settings.fsmonitor->hook_path;
160}
161
162void fsm_settings__set_ipc(struct repository *r)
163{
8f449768 164 enum fsmonitor_reason reason = check_for_incompatible(r, 1);
62a62a28
JH
165
166 if (reason != FSMONITOR_REASON_OK) {
167 fsm_settings__set_incompatible(r, reason);
168 return;
169 }
170
171 /*
172 * Caller requested IPC explicitly, so avoid (possibly
173 * recursive) config lookup.
174 */
62a62a28
JH
175 if (!r->settings.fsmonitor)
176 r->settings.fsmonitor = alloc_settings();
1e0ea5c4
JH
177
178 r->settings.fsmonitor->mode = FSMONITOR_MODE_IPC;
62a62a28 179 r->settings.fsmonitor->reason = reason;
1e0ea5c4
JH
180 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
181}
182
183void fsm_settings__set_hook(struct repository *r, const char *path)
184{
8f449768 185 enum fsmonitor_reason reason = check_for_incompatible(r, 0);
62a62a28
JH
186
187 if (reason != FSMONITOR_REASON_OK) {
188 fsm_settings__set_incompatible(r, reason);
189 return;
190 }
191
192 /*
193 * Caller requested hook explicitly, so avoid (possibly
194 * recursive) config lookup.
195 */
62a62a28
JH
196 if (!r->settings.fsmonitor)
197 r->settings.fsmonitor = alloc_settings();
1e0ea5c4
JH
198
199 r->settings.fsmonitor->mode = FSMONITOR_MODE_HOOK;
62a62a28 200 r->settings.fsmonitor->reason = reason;
1e0ea5c4
JH
201 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
202 r->settings.fsmonitor->hook_path = strdup(path);
203}
204
205void fsm_settings__set_disabled(struct repository *r)
206{
62a62a28
JH
207 if (!r->settings.fsmonitor)
208 r->settings.fsmonitor = alloc_settings();
1e0ea5c4
JH
209
210 r->settings.fsmonitor->mode = FSMONITOR_MODE_DISABLED;
62a62a28
JH
211 r->settings.fsmonitor->reason = FSMONITOR_REASON_OK;
212 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
213}
214
215void fsm_settings__set_incompatible(struct repository *r,
216 enum fsmonitor_reason reason)
217{
62a62a28
JH
218 if (!r->settings.fsmonitor)
219 r->settings.fsmonitor = alloc_settings();
220
221 r->settings.fsmonitor->mode = FSMONITOR_MODE_INCOMPATIBLE;
222 r->settings.fsmonitor->reason = reason;
1e0ea5c4
JH
223 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
224}
62a62a28
JH
225
226enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)
227{
62a62a28
JH
228 if (!r->settings.fsmonitor)
229 lookup_fsmonitor_settings(r);
230
231 return r->settings.fsmonitor->reason;
232}
233
25c2cab0 234char *fsm_settings__get_incompatible_msg(struct repository *r,
62a62a28
JH
235 enum fsmonitor_reason reason)
236{
237 struct strbuf msg = STRBUF_INIT;
25c2cab0 238 const char *socket_dir;
62a62a28
JH
239
240 switch (reason) {
241 case FSMONITOR_REASON_UNTESTED:
242 case FSMONITOR_REASON_OK:
243 goto done;
244
5a09991e
JS
245 case FSMONITOR_REASON_BARE: {
246 char *cwd = xgetcwd();
247
62a62a28
JH
248 strbuf_addf(&msg,
249 _("bare repository '%s' is incompatible with fsmonitor"),
5a09991e
JS
250 cwd);
251 free(cwd);
62a62a28 252 goto done;
5a09991e 253 }
5c58fbd2 254
1e7be10d
JH
255 case FSMONITOR_REASON_ERROR:
256 strbuf_addf(&msg,
257 _("repository '%s' is incompatible with fsmonitor due to errors"),
258 r->worktree);
259 goto done;
260
261 case FSMONITOR_REASON_REMOTE:
262 strbuf_addf(&msg,
263 _("remote repository '%s' is incompatible with fsmonitor"),
264 r->worktree);
265 goto done;
266
5c58fbd2
JH
267 case FSMONITOR_REASON_VFS4GIT:
268 strbuf_addf(&msg,
269 _("virtual repository '%s' is incompatible with fsmonitor"),
270 r->worktree);
271 goto done;
ddc5dacf
JH
272
273 case FSMONITOR_REASON_NOSOCKETS:
25c2cab0 274 socket_dir = dirname((char *)fsmonitor_ipc__get_path(r));
ddc5dacf 275 strbuf_addf(&msg,
25c2cab0
ED
276 _("socket directory '%s' is incompatible with fsmonitor due"
277 " to lack of Unix sockets support"),
278 socket_dir);
ddc5dacf 279 goto done;
62a62a28
JH
280 }
281
282 BUG("Unhandled case in fsm_settings__get_incompatible_msg: '%d'",
283 reason);
284
285done:
286 return strbuf_detach(&msg, NULL);
287}