]>
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 | { | |
62a62a28 JH |
146 | if (!r->settings.fsmonitor) |
147 | lookup_fsmonitor_settings(r); | |
1e0ea5c4 JH |
148 | |
149 | return r->settings.fsmonitor->mode; | |
150 | } | |
151 | ||
152 | const char *fsm_settings__get_hook_path(struct repository *r) | |
153 | { | |
62a62a28 JH |
154 | if (!r->settings.fsmonitor) |
155 | lookup_fsmonitor_settings(r); | |
1e0ea5c4 JH |
156 | |
157 | return r->settings.fsmonitor->hook_path; | |
158 | } | |
159 | ||
160 | void fsm_settings__set_ipc(struct repository *r) | |
161 | { | |
8f449768 | 162 | enum fsmonitor_reason reason = check_for_incompatible(r, 1); |
62a62a28 JH |
163 | |
164 | if (reason != FSMONITOR_REASON_OK) { | |
165 | fsm_settings__set_incompatible(r, reason); | |
166 | return; | |
167 | } | |
168 | ||
169 | /* | |
170 | * Caller requested IPC explicitly, so avoid (possibly | |
171 | * recursive) config lookup. | |
172 | */ | |
62a62a28 JH |
173 | if (!r->settings.fsmonitor) |
174 | r->settings.fsmonitor = alloc_settings(); | |
1e0ea5c4 JH |
175 | |
176 | r->settings.fsmonitor->mode = FSMONITOR_MODE_IPC; | |
62a62a28 | 177 | r->settings.fsmonitor->reason = reason; |
1e0ea5c4 JH |
178 | FREE_AND_NULL(r->settings.fsmonitor->hook_path); |
179 | } | |
180 | ||
181 | void fsm_settings__set_hook(struct repository *r, const char *path) | |
182 | { | |
8f449768 | 183 | enum fsmonitor_reason reason = check_for_incompatible(r, 0); |
62a62a28 JH |
184 | |
185 | if (reason != FSMONITOR_REASON_OK) { | |
186 | fsm_settings__set_incompatible(r, reason); | |
187 | return; | |
188 | } | |
189 | ||
190 | /* | |
191 | * Caller requested hook explicitly, so avoid (possibly | |
192 | * recursive) config lookup. | |
193 | */ | |
62a62a28 JH |
194 | if (!r->settings.fsmonitor) |
195 | r->settings.fsmonitor = alloc_settings(); | |
1e0ea5c4 JH |
196 | |
197 | r->settings.fsmonitor->mode = FSMONITOR_MODE_HOOK; | |
62a62a28 | 198 | r->settings.fsmonitor->reason = reason; |
1e0ea5c4 JH |
199 | FREE_AND_NULL(r->settings.fsmonitor->hook_path); |
200 | r->settings.fsmonitor->hook_path = strdup(path); | |
201 | } | |
202 | ||
203 | void fsm_settings__set_disabled(struct repository *r) | |
204 | { | |
62a62a28 JH |
205 | if (!r->settings.fsmonitor) |
206 | r->settings.fsmonitor = alloc_settings(); | |
1e0ea5c4 JH |
207 | |
208 | r->settings.fsmonitor->mode = FSMONITOR_MODE_DISABLED; | |
62a62a28 JH |
209 | r->settings.fsmonitor->reason = FSMONITOR_REASON_OK; |
210 | FREE_AND_NULL(r->settings.fsmonitor->hook_path); | |
211 | } | |
212 | ||
213 | void fsm_settings__set_incompatible(struct repository *r, | |
214 | enum fsmonitor_reason reason) | |
215 | { | |
62a62a28 JH |
216 | if (!r->settings.fsmonitor) |
217 | r->settings.fsmonitor = alloc_settings(); | |
218 | ||
219 | r->settings.fsmonitor->mode = FSMONITOR_MODE_INCOMPATIBLE; | |
220 | r->settings.fsmonitor->reason = reason; | |
1e0ea5c4 JH |
221 | FREE_AND_NULL(r->settings.fsmonitor->hook_path); |
222 | } | |
62a62a28 JH |
223 | |
224 | enum fsmonitor_reason fsm_settings__get_reason(struct repository *r) | |
225 | { | |
62a62a28 JH |
226 | if (!r->settings.fsmonitor) |
227 | lookup_fsmonitor_settings(r); | |
228 | ||
229 | return r->settings.fsmonitor->reason; | |
230 | } | |
231 | ||
25c2cab0 | 232 | char *fsm_settings__get_incompatible_msg(struct repository *r, |
62a62a28 JH |
233 | enum fsmonitor_reason reason) |
234 | { | |
235 | struct strbuf msg = STRBUF_INIT; | |
25c2cab0 | 236 | const char *socket_dir; |
62a62a28 JH |
237 | |
238 | switch (reason) { | |
239 | case FSMONITOR_REASON_UNTESTED: | |
240 | case FSMONITOR_REASON_OK: | |
241 | goto done; | |
242 | ||
5a09991e JS |
243 | case FSMONITOR_REASON_BARE: { |
244 | char *cwd = xgetcwd(); | |
245 | ||
62a62a28 JH |
246 | strbuf_addf(&msg, |
247 | _("bare repository '%s' is incompatible with fsmonitor"), | |
5a09991e JS |
248 | cwd); |
249 | free(cwd); | |
62a62a28 | 250 | goto done; |
5a09991e | 251 | } |
5c58fbd2 | 252 | |
1e7be10d JH |
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; | |
258 | ||
259 | case FSMONITOR_REASON_REMOTE: | |
260 | strbuf_addf(&msg, | |
261 | _("remote repository '%s' is incompatible with fsmonitor"), | |
262 | r->worktree); | |
263 | goto done; | |
264 | ||
5c58fbd2 JH |
265 | case FSMONITOR_REASON_VFS4GIT: |
266 | strbuf_addf(&msg, | |
267 | _("virtual repository '%s' is incompatible with fsmonitor"), | |
268 | r->worktree); | |
269 | goto done; | |
ddc5dacf JH |
270 | |
271 | case FSMONITOR_REASON_NOSOCKETS: | |
25c2cab0 | 272 | socket_dir = dirname((char *)fsmonitor_ipc__get_path(r)); |
ddc5dacf | 273 | strbuf_addf(&msg, |
25c2cab0 ED |
274 | _("socket directory '%s' is incompatible with fsmonitor due" |
275 | " to lack of Unix sockets support"), | |
276 | socket_dir); | |
ddc5dacf | 277 | goto done; |
62a62a28 JH |
278 | } |
279 | ||
280 | BUG("Unhandled case in fsm_settings__get_incompatible_msg: '%d'", | |
281 | reason); | |
282 | ||
283 | done: | |
284 | return strbuf_detach(&msg, NULL); | |
285 | } |