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