]>
Commit | Line | Data |
---|---|---|
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 | */ | |
13 | struct 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 | |
45 | static 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 |
65 | static 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 | ||
92 | static 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 | ||
103 | static 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 | ||
146 | enum 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 | ||
154 | const 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 | ||
162 | void 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 | ||
183 | void 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 | ||
205 | void 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 | ||
215 | void 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 | |
226 | enum 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 | 234 | char *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 | ||
285 | done: | |
286 | return strbuf_detach(&msg, NULL); | |
287 | } |