]> git.ipfire.org Git - thirdparty/git.git/blob - fsmonitor-settings.c
artifacts-tar: when including `.dll` files, don't forget the unit-tests
[thirdparty/git.git] / fsmonitor-settings.c
1 #include "git-compat-util.h"
2 #include "config.h"
3 #include "gettext.h"
4 #include "repository.h"
5 #include "fsmonitor-ipc.h"
6 #include "fsmonitor-settings.h"
7 #include "fsmonitor-path-utils.h"
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;
15 enum fsmonitor_reason reason;
16 char *hook_path;
17 };
18
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
65 static enum fsmonitor_reason check_for_incompatible(struct repository *r, int ipc)
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
75 #ifdef HAVE_FSMONITOR_OS_SETTINGS
76 {
77 enum fsmonitor_reason reason;
78
79 reason = check_remote(r);
80 if (reason != FSMONITOR_REASON_OK)
81 return reason;
82 reason = fsm_os__incompatible(r, ipc);
83 if (reason != FSMONITOR_REASON_OK)
84 return reason;
85 }
86 #endif
87
88 return FSMONITOR_REASON_OK;
89 }
90
91 static struct fsmonitor_settings *alloc_settings(void)
92 {
93 struct fsmonitor_settings *s;
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 {
104 const char *const_str;
105 int bool_value;
106
107 if (r->settings.fsmonitor)
108 return;
109
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);
122 else
123 fsm_settings__set_disabled(r);
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
139 if (const_str && *const_str)
140 fsm_settings__set_hook(r, const_str);
141 else
142 fsm_settings__set_disabled(r);
143 }
144
145 enum fsmonitor_mode fsm_settings__get_mode(struct repository *r)
146 {
147 if (!r->settings.fsmonitor)
148 lookup_fsmonitor_settings(r);
149
150 return r->settings.fsmonitor->mode;
151 }
152
153 const char *fsm_settings__get_hook_path(struct repository *r)
154 {
155 if (!r->settings.fsmonitor)
156 lookup_fsmonitor_settings(r);
157
158 return r->settings.fsmonitor->hook_path;
159 }
160
161 void fsm_settings__set_ipc(struct repository *r)
162 {
163 enum fsmonitor_reason reason = check_for_incompatible(r, 1);
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 */
174 if (!r->settings.fsmonitor)
175 r->settings.fsmonitor = alloc_settings();
176
177 r->settings.fsmonitor->mode = FSMONITOR_MODE_IPC;
178 r->settings.fsmonitor->reason = reason;
179 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
180 }
181
182 void fsm_settings__set_hook(struct repository *r, const char *path)
183 {
184 enum fsmonitor_reason reason = check_for_incompatible(r, 0);
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 */
195 if (!r->settings.fsmonitor)
196 r->settings.fsmonitor = alloc_settings();
197
198 r->settings.fsmonitor->mode = FSMONITOR_MODE_HOOK;
199 r->settings.fsmonitor->reason = reason;
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 {
206 if (!r->settings.fsmonitor)
207 r->settings.fsmonitor = alloc_settings();
208
209 r->settings.fsmonitor->mode = FSMONITOR_MODE_DISABLED;
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 {
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;
222 FREE_AND_NULL(r->settings.fsmonitor->hook_path);
223 }
224
225 enum fsmonitor_reason fsm_settings__get_reason(struct repository *r)
226 {
227 if (!r->settings.fsmonitor)
228 lookup_fsmonitor_settings(r);
229
230 return r->settings.fsmonitor->reason;
231 }
232
233 char *fsm_settings__get_incompatible_msg(struct repository *r,
234 enum fsmonitor_reason reason)
235 {
236 struct strbuf msg = STRBUF_INIT;
237 const char *socket_dir;
238
239 switch (reason) {
240 case FSMONITOR_REASON_UNTESTED:
241 case FSMONITOR_REASON_OK:
242 goto done;
243
244 case FSMONITOR_REASON_BARE: {
245 char *cwd = xgetcwd();
246
247 strbuf_addf(&msg,
248 _("bare repository '%s' is incompatible with fsmonitor"),
249 cwd);
250 free(cwd);
251 goto done;
252 }
253
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
266 case FSMONITOR_REASON_VFS4GIT:
267 strbuf_addf(&msg,
268 _("virtual repository '%s' is incompatible with fsmonitor"),
269 r->worktree);
270 goto done;
271
272 case FSMONITOR_REASON_NOSOCKETS:
273 socket_dir = dirname((char *)fsmonitor_ipc__get_path(r));
274 strbuf_addf(&msg,
275 _("socket directory '%s' is incompatible with fsmonitor due"
276 " to lack of Unix sockets support"),
277 socket_dir);
278 goto done;
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 }