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