]>
Commit | Line | Data |
---|---|---|
1e0ea5c4 JH |
1 | #include "cache.h" |
2 | #include "config.h" | |
3 | #include "repository.h" | |
25c2cab0 | 4 | #include "fsmonitor-ipc.h" |
1e0ea5c4 | 5 | #include "fsmonitor-settings.h" |
508c1a57 | 6 | #include "fsmonitor-path-utils.h" |
1e0ea5c4 JH |
7 | |
8 | /* | |
9 | * We keep this structure defintion private and have getters | |
10 | * for all fields so that we can lazy load it as needed. | |
11 | */ | |
12 | struct fsmonitor_settings { | |
13 | enum fsmonitor_mode mode; | |
62a62a28 | 14 | enum fsmonitor_reason reason; |
1e0ea5c4 JH |
15 | char *hook_path; |
16 | }; | |
17 | ||
508c1a57 ED |
18 | /* |
19 | * Remote working directories are problematic for FSMonitor. | |
20 | * | |
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. | |
24 | * | |
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. | |
28 | * | |
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. | |
35 | * | |
36 | * In theory, the above issues need to be addressed whether we are | |
37 | * using the Hook or IPC API. | |
38 | * | |
39 | * So (for now at least), mark remote working directories as | |
40 | * incompatible unless 'fsmonitor.allowRemote' is true. | |
41 | * | |
42 | */ | |
43 | #ifdef HAVE_FSMONITOR_OS_SETTINGS | |
44 | static enum fsmonitor_reason check_remote(struct repository *r) | |
45 | { | |
46 | int allow_remote = -1; /* -1 unset, 0 not allowed, 1 allowed */ | |
47 | int is_remote = fsmonitor__is_fs_remote(r->worktree); | |
48 | ||
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; | |
60 | } | |
61 | } | |
62 | #endif | |
63 | ||
8f449768 | 64 | static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc) |
62a62a28 JH |
65 | { |
66 | if (!r->worktree) { | |
67 | /* | |
68 | * Bare repositories don't have a working directory and | |
69 | * therefore have nothing to watch. | |
70 | */ | |
71 | return FSMONITOR_REASON_BARE; | |
72 | } | |
73 | ||
d33c804d JH |
74 | #ifdef HAVE_FSMONITOR_OS_SETTINGS |
75 | { | |
76 | enum fsmonitor_reason reason; | |
77 | ||
508c1a57 ED |
78 | reason = check_remote(r); |
79 | if (reason != FSMONITOR_REASON_OK) | |
80 | return reason; | |
8f449768 | 81 | reason = fsm_os__incompatible(r, ipc); |
d33c804d JH |
82 | if (reason != FSMONITOR_REASON_OK) |
83 | return reason; | |
84 | } | |
85 | #endif | |
86 | ||
62a62a28 JH |
87 | return FSMONITOR_REASON_OK; |
88 | } | |
89 | ||
90 | static struct fsmonitor_settings *alloc_settings(void) | |
1e0ea5c4 JH |
91 | { |
92 | struct fsmonitor_settings *s; | |
62a62a28 JH |
93 | |
94 | CALLOC_ARRAY(s, 1); | |
95 | s->mode = FSMONITOR_MODE_DISABLED; | |
96 | s->reason = FSMONITOR_REASON_UNTESTED; | |
97 | ||
98 | return s; | |
99 | } | |
100 | ||
101 | static void lookup_fsmonitor_settings(struct repository *r) | |
102 | { | |
1e0ea5c4 JH |
103 | const char *const_str; |
104 | int bool_value; | |
105 | ||
106 | if (r->settings.fsmonitor) | |
107 | return; | |
108 | ||
1e0ea5c4 JH |
109 | /* |
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.) | |
115 | */ | |
116 | switch (repo_config_get_maybe_bool(r, "core.fsmonitor", &bool_value)) { | |
117 | ||
118 | case 0: /* config value was set to <bool> */ | |
119 | if (bool_value) | |
120 | fsm_settings__set_ipc(r); | |
62a62a28 JH |
121 | else |
122 | fsm_settings__set_disabled(r); | |
1e0ea5c4 JH |
123 | return; |
124 | ||
125 | case 1: /* config value was unset */ | |
126 | const_str = getenv("GIT_TEST_FSMONITOR"); | |
127 | break; | |
128 | ||
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; | |
133 | ||
134 | default: /* should not happen */ | |
135 | return; | |
136 | } | |
137 | ||
62a62a28 JH |
138 | if (const_str && *const_str) |
139 | fsm_settings__set_hook(r, const_str); | |
140 | else | |
141 | fsm_settings__set_disabled(r); | |
1e0ea5c4 JH |
142 | } |
143 | ||
144 | enum fsmonitor_mode fsm_settings__get_mode(struct repository *r) | |
145 | { | |
146 | if (!r) | |
147 | r = the_repository; | |
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 | { | |
156 | if (!r) | |
157 | r = the_repository; | |
62a62a28 JH |
158 | if (!r->settings.fsmonitor) |
159 | lookup_fsmonitor_settings(r); | |
1e0ea5c4 JH |
160 | |
161 | return r->settings.fsmonitor->hook_path; | |
162 | } | |
163 | ||
164 | void fsm_settings__set_ipc(struct repository *r) | |
165 | { | |
8f449768 | 166 | enum fsmonitor_reason reason = check_for_incompatible(r, 1); |
62a62a28 JH |
167 | |
168 | if (reason != FSMONITOR_REASON_OK) { | |
169 | fsm_settings__set_incompatible(r, reason); | |
170 | return; | |
171 | } | |
172 | ||
173 | /* | |
174 | * Caller requested IPC explicitly, so avoid (possibly | |
175 | * recursive) config lookup. | |
176 | */ | |
1e0ea5c4 JH |
177 | if (!r) |
178 | r = the_repository; | |
62a62a28 JH |
179 | if (!r->settings.fsmonitor) |
180 | r->settings.fsmonitor = alloc_settings(); | |
1e0ea5c4 JH |
181 | |
182 | r->settings.fsmonitor->mode = FSMONITOR_MODE_IPC; | |
62a62a28 | 183 | r->settings.fsmonitor->reason = reason; |
1e0ea5c4 JH |
184 | FREE_AND_NULL(r->settings.fsmonitor->hook_path); |
185 | } | |
186 | ||
187 | void fsm_settings__set_hook(struct repository *r, const char *path) | |
188 | { | |
8f449768 | 189 | enum fsmonitor_reason reason = check_for_incompatible(r, 0); |
62a62a28 JH |
190 | |
191 | if (reason != FSMONITOR_REASON_OK) { | |
192 | fsm_settings__set_incompatible(r, reason); | |
193 | return; | |
194 | } | |
195 | ||
196 | /* | |
197 | * Caller requested hook explicitly, so avoid (possibly | |
198 | * recursive) config lookup. | |
199 | */ | |
1e0ea5c4 JH |
200 | if (!r) |
201 | r = the_repository; | |
62a62a28 JH |
202 | if (!r->settings.fsmonitor) |
203 | r->settings.fsmonitor = alloc_settings(); | |
1e0ea5c4 JH |
204 | |
205 | r->settings.fsmonitor->mode = FSMONITOR_MODE_HOOK; | |
62a62a28 | 206 | r->settings.fsmonitor->reason = reason; |
1e0ea5c4 JH |
207 | FREE_AND_NULL(r->settings.fsmonitor->hook_path); |
208 | r->settings.fsmonitor->hook_path = strdup(path); | |
209 | } | |
210 | ||
211 | void fsm_settings__set_disabled(struct repository *r) | |
212 | { | |
213 | if (!r) | |
214 | r = the_repository; | |
62a62a28 JH |
215 | if (!r->settings.fsmonitor) |
216 | r->settings.fsmonitor = alloc_settings(); | |
1e0ea5c4 JH |
217 | |
218 | r->settings.fsmonitor->mode = FSMONITOR_MODE_DISABLED; | |
62a62a28 JH |
219 | r->settings.fsmonitor->reason = FSMONITOR_REASON_OK; |
220 | FREE_AND_NULL(r->settings.fsmonitor->hook_path); | |
221 | } | |
222 | ||
223 | void fsm_settings__set_incompatible(struct repository *r, | |
224 | enum fsmonitor_reason reason) | |
225 | { | |
226 | if (!r) | |
227 | r = the_repository; | |
228 | if (!r->settings.fsmonitor) | |
229 | r->settings.fsmonitor = alloc_settings(); | |
230 | ||
231 | r->settings.fsmonitor->mode = FSMONITOR_MODE_INCOMPATIBLE; | |
232 | r->settings.fsmonitor->reason = reason; | |
1e0ea5c4 JH |
233 | FREE_AND_NULL(r->settings.fsmonitor->hook_path); |
234 | } | |
62a62a28 JH |
235 | |
236 | enum fsmonitor_reason fsm_settings__get_reason(struct repository *r) | |
237 | { | |
238 | if (!r) | |
239 | r = the_repository; | |
240 | if (!r->settings.fsmonitor) | |
241 | lookup_fsmonitor_settings(r); | |
242 | ||
243 | return r->settings.fsmonitor->reason; | |
244 | } | |
245 | ||
25c2cab0 | 246 | char *fsm_settings__get_incompatible_msg(struct repository *r, |
62a62a28 JH |
247 | enum fsmonitor_reason reason) |
248 | { | |
249 | struct strbuf msg = STRBUF_INIT; | |
25c2cab0 | 250 | const char *socket_dir; |
62a62a28 JH |
251 | |
252 | switch (reason) { | |
253 | case FSMONITOR_REASON_UNTESTED: | |
254 | case FSMONITOR_REASON_OK: | |
255 | goto done; | |
256 | ||
5a09991e JS |
257 | case FSMONITOR_REASON_BARE: { |
258 | char *cwd = xgetcwd(); | |
259 | ||
62a62a28 JH |
260 | strbuf_addf(&msg, |
261 | _("bare repository '%s' is incompatible with fsmonitor"), | |
5a09991e JS |
262 | cwd); |
263 | free(cwd); | |
62a62a28 | 264 | goto done; |
5a09991e | 265 | } |
5c58fbd2 | 266 | |
1e7be10d JH |
267 | case FSMONITOR_REASON_ERROR: |
268 | strbuf_addf(&msg, | |
269 | _("repository '%s' is incompatible with fsmonitor due to errors"), | |
270 | r->worktree); | |
271 | goto done; | |
272 | ||
273 | case FSMONITOR_REASON_REMOTE: | |
274 | strbuf_addf(&msg, | |
275 | _("remote repository '%s' is incompatible with fsmonitor"), | |
276 | r->worktree); | |
277 | goto done; | |
278 | ||
5c58fbd2 JH |
279 | case FSMONITOR_REASON_VFS4GIT: |
280 | strbuf_addf(&msg, | |
281 | _("virtual repository '%s' is incompatible with fsmonitor"), | |
282 | r->worktree); | |
283 | goto done; | |
ddc5dacf JH |
284 | |
285 | case FSMONITOR_REASON_NOSOCKETS: | |
25c2cab0 | 286 | socket_dir = dirname((char *)fsmonitor_ipc__get_path(r)); |
ddc5dacf | 287 | strbuf_addf(&msg, |
25c2cab0 ED |
288 | _("socket directory '%s' is incompatible with fsmonitor due" |
289 | " to lack of Unix sockets support"), | |
290 | socket_dir); | |
ddc5dacf | 291 | goto done; |
62a62a28 JH |
292 | } |
293 | ||
294 | BUG("Unhandled case in fsm_settings__get_incompatible_msg: '%d'", | |
295 | reason); | |
296 | ||
297 | done: | |
298 | return strbuf_detach(&msg, NULL); | |
299 | } |