9
9
#include "khash.h"
10
10
11
11
static const char * const builtin_fsmonitor__daemon_usage [] = {
12
+ N_ ("git fsmonitor--daemon start [<options>]" ),
12
13
N_ ("git fsmonitor--daemon run [<options>]" ),
13
14
N_ ("git fsmonitor--daemon stop" ),
14
15
N_ ("git fsmonitor--daemon status" ),
@@ -22,6 +23,9 @@ static const char * const builtin_fsmonitor__daemon_usage[] = {
22
23
#define FSMONITOR__IPC_THREADS "fsmonitor.ipcthreads"
23
24
static int fsmonitor__ipc_threads = 8 ;
24
25
26
+ #define FSMONITOR__START_TIMEOUT "fsmonitor.starttimeout"
27
+ static int fsmonitor__start_timeout_sec = 60 ;
28
+
25
29
static int fsmonitor_config (const char * var , const char * value , void * cb )
26
30
{
27
31
if (!strcmp (var , FSMONITOR__IPC_THREADS )) {
@@ -33,6 +37,15 @@ static int fsmonitor_config(const char *var, const char *value, void *cb)
33
37
return 0 ;
34
38
}
35
39
40
+ if (!strcmp (var , FSMONITOR__START_TIMEOUT )) {
41
+ int i = git_config_int (var , value );
42
+ if (i < 0 )
43
+ return error (_ ("value of '%s' out of range: %d" ),
44
+ FSMONITOR__START_TIMEOUT , i );
45
+ fsmonitor__start_timeout_sec = i ;
46
+ return 0 ;
47
+ }
48
+
36
49
return git_default_config (var , value , cb );
37
50
}
38
51
@@ -256,6 +269,194 @@ static int try_to_run_foreground_daemon(void)
256
269
return !!fsmonitor_run_daemon ();
257
270
}
258
271
272
+ #ifdef GIT_WINDOWS_NATIVE
273
+ /*
274
+ * Create a background process to run the daemon. It should be completely
275
+ * disassociated from the terminal.
276
+ *
277
+ * Conceptually like `daemonize()` but different because Windows does not
278
+ * have `fork(2)`. Spawn a normal Windows child process but without the
279
+ * limitations of `start_command()` and `finish_command()`.
280
+ *
281
+ * The child process execs the "git fsmonitor--daemon run" command.
282
+ *
283
+ * The current process returns so that the caller can wait for the child
284
+ * to startup before exiting.
285
+ */
286
+ static int spawn_fsmonitor (pid_t * pid )
287
+ {
288
+ char git_exe [MAX_PATH ];
289
+ struct strvec args = STRVEC_INIT ;
290
+ int in , out ;
291
+
292
+ GetModuleFileNameA (NULL , git_exe , MAX_PATH );
293
+
294
+ in = open ("/dev/null" , O_RDONLY );
295
+ out = open ("/dev/null" , O_WRONLY );
296
+
297
+ strvec_push (& args , git_exe );
298
+ strvec_push (& args , "fsmonitor--daemon" );
299
+ strvec_push (& args , "run" );
300
+ strvec_pushf (& args , "--ipc-threads=%d" , fsmonitor__ipc_threads );
301
+
302
+ * pid = mingw_spawnvpe (args .v [0 ], args .v , NULL , NULL , in , out , out );
303
+ close (in );
304
+ close (out );
305
+
306
+ strvec_clear (& args );
307
+
308
+ if (* pid < 0 )
309
+ return error (_ ("could not spawn fsmonitor--daemon in the background" ));
310
+
311
+ return 0 ;
312
+ }
313
+ #else
314
+ /*
315
+ * Create a background process to run the daemon. It should be completely
316
+ * disassociated from the terminal.
317
+ *
318
+ * This is adapted from `daemonize()`. Use `fork()` to directly
319
+ * create and run the daemon in the child process.
320
+ *
321
+ * The fork-child can just call the run code; it does not need to exec
322
+ * it.
323
+ *
324
+ * The fork-parent returns the child PID so that we can wait for the
325
+ * child to startup before exiting.
326
+ */
327
+ static int spawn_fsmonitor (pid_t * pid )
328
+ {
329
+ * pid = fork ();
330
+
331
+ switch (* pid ) {
332
+ case 0 :
333
+ if (setsid () == -1 )
334
+ error_errno (_ ("setsid failed" ));
335
+ close (0 );
336
+ close (1 );
337
+ close (2 );
338
+ sanitize_stdfds ();
339
+
340
+ return !!fsmonitor_run_daemon ();
341
+
342
+ case -1 :
343
+ return error_errno (_ ("could not spawn fsmonitor--daemon in the background" ));
344
+
345
+ default :
346
+ return 0 ;
347
+ }
348
+ }
349
+ #endif
350
+
351
+ /*
352
+ * This is adapted from `wait_or_whine()`. Watch the child process and
353
+ * let it get started and begin listening for requests on the socket
354
+ * before reporting our success.
355
+ */
356
+ static int wait_for_startup (pid_t pid_child )
357
+ {
358
+ int status ;
359
+ pid_t pid_seen ;
360
+ enum ipc_active_state s ;
361
+ time_t time_limit , now ;
362
+
363
+ time (& time_limit );
364
+ time_limit += fsmonitor__start_timeout_sec ;
365
+
366
+ for (;;) {
367
+ pid_seen = waitpid (pid_child , & status , WNOHANG );
368
+
369
+ if (pid_seen == -1 )
370
+ return error_errno (_ ("waitpid failed" ));
371
+ else if (pid_seen == 0 ) {
372
+ /*
373
+ * The child is still running (this should be
374
+ * the normal case). Try to connect to it on
375
+ * the socket and see if it is ready for
376
+ * business.
377
+ *
378
+ * If there is another daemon already running,
379
+ * our child will fail to start (possibly
380
+ * after a timeout on the lock), but we don't
381
+ * care (who responds) if the socket is live.
382
+ */
383
+ s = fsmonitor_ipc__get_state ();
384
+ if (s == IPC_STATE__LISTENING )
385
+ return 0 ;
386
+
387
+ time (& now );
388
+ if (now > time_limit )
389
+ return error (_ ("fsmonitor--daemon not online yet" ));
390
+ } else if (pid_seen == pid_child ) {
391
+ /*
392
+ * The new child daemon process shutdown while
393
+ * it was starting up, so it is not listening
394
+ * on the socket.
395
+ *
396
+ * Try to ping the socket in the odd chance
397
+ * that another daemon started (or was already
398
+ * running) while our child was starting.
399
+ *
400
+ * Again, we don't care who services the socket.
401
+ */
402
+ s = fsmonitor_ipc__get_state ();
403
+ if (s == IPC_STATE__LISTENING )
404
+ return 0 ;
405
+
406
+ /*
407
+ * We don't care about the WEXITSTATUS() nor
408
+ * any of the WIF*(status) values because
409
+ * `cmd_fsmonitor__daemon()` does the `!!result`
410
+ * trick on all function return values.
411
+ *
412
+ * So it is sufficient to just report the
413
+ * early shutdown as an error.
414
+ */
415
+ return error (_ ("fsmonitor--daemon failed to start" ));
416
+ } else
417
+ return error (_ ("waitpid is confused" ));
418
+ }
419
+ }
420
+
421
+ static int try_to_start_background_daemon (void )
422
+ {
423
+ pid_t pid_child ;
424
+ int ret ;
425
+
426
+ /*
427
+ * Before we try to create a background daemon process, see
428
+ * if a daemon process is already listening. This makes it
429
+ * easier for us to report an already-listening error to the
430
+ * console, since our spawn/daemon can only report the success
431
+ * of creating the background process (and not whether it
432
+ * immediately exited).
433
+ */
434
+ if (fsmonitor_ipc__get_state () == IPC_STATE__LISTENING )
435
+ die ("fsmonitor--daemon is already running '%s'" ,
436
+ the_repository -> worktree );
437
+
438
+ printf (_ ("starting fsmonitor-daemon in '%s'\n" ),
439
+ the_repository -> worktree );
440
+ fflush (stdout );
441
+
442
+ /*
443
+ * Run the actual daemon in a background process.
444
+ */
445
+ ret = spawn_fsmonitor (& pid_child );
446
+ if (pid_child <= 0 )
447
+ return ret ;
448
+
449
+ /*
450
+ * Wait (with timeout) for the background child process get
451
+ * started and begin listening on the socket/pipe. This makes
452
+ * the "start" command more synchronous and more reliable in
453
+ * tests.
454
+ */
455
+ ret = wait_for_startup (pid_child );
456
+
457
+ return ret ;
458
+ }
459
+
259
460
int cmd_fsmonitor__daemon (int argc , const char * * argv , const char * prefix )
260
461
{
261
462
const char * subcmd ;
@@ -264,6 +465,10 @@ int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
264
465
OPT_INTEGER (0 , "ipc-threads" ,
265
466
& fsmonitor__ipc_threads ,
266
467
N_ ("use <n> ipc worker threads" )),
468
+ OPT_INTEGER (0 , "start-timeout" ,
469
+ & fsmonitor__start_timeout_sec ,
470
+ N_ ("Max seconds to wait for background daemon startup" )),
471
+
267
472
OPT_END ()
268
473
};
269
474
@@ -285,6 +490,9 @@ int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
285
490
die (_ ("invalid 'ipc-threads' value (%d)" ),
286
491
fsmonitor__ipc_threads );
287
492
493
+ if (!strcmp (subcmd , "start" ))
494
+ return !!try_to_start_background_daemon ();
495
+
288
496
if (!strcmp (subcmd , "run" ))
289
497
return !!try_to_run_foreground_daemon ();
290
498
0 commit comments