Skip to content

Commit c284e27

Browse files
jeffhostetlergitster
authored andcommitted
fsmonitor--daemon: implement 'start' command
Implement 'git fsmonitor--daemon start' command. This command starts an instance of 'git fsmonitor--daemon run' in the background using the new 'start_bg_command()' function. We avoid the fork-and-call technique on Unix systems in favor of a fork-and-exec technique. This gives us more uniform Trace2 child-* events. It also makes our usage more consistent with Windows usage. On Windows, teach 'git fsmonitor--daemon run' to optionally call 'FreeConsole()' to release handles to the inherited Win32 console (despite being passed invalid handles for stdin/out/err). Without this, command prompts and powershell terminal windows could hang in "exit" until the last background child process exited or released their Win32 console handle. (This was not seen with git-bash shells because they don't have a Win32 console attached to them.) Signed-off-by: Jeff Hostetler <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 9dcba0b commit c284e27

File tree

1 file changed

+107
-2
lines changed

1 file changed

+107
-2
lines changed

builtin/fsmonitor--daemon.c

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "khash.h"
1010

1111
static const char * const builtin_fsmonitor__daemon_usage[] = {
12+
N_("git fsmonitor--daemon start [<options>]"),
1213
N_("git fsmonitor--daemon run [<options>]"),
1314
N_("git fsmonitor--daemon stop"),
1415
N_("git fsmonitor--daemon status"),
@@ -22,6 +23,9 @@ static const char * const builtin_fsmonitor__daemon_usage[] = {
2223
#define FSMONITOR__IPC_THREADS "fsmonitor.ipcthreads"
2324
static int fsmonitor__ipc_threads = 8;
2425

26+
#define FSMONITOR__START_TIMEOUT "fsmonitor.starttimeout"
27+
static int fsmonitor__start_timeout_sec = 60;
28+
2529
#define FSMONITOR__ANNOUNCE_STARTUP "fsmonitor.announcestartup"
2630
static int fsmonitor__announce_startup = 0;
2731

@@ -36,6 +40,15 @@ static int fsmonitor_config(const char *var, const char *value, void *cb)
3640
return 0;
3741
}
3842

43+
if (!strcmp(var, FSMONITOR__START_TIMEOUT)) {
44+
int i = git_config_int(var, value);
45+
if (i < 0)
46+
return error(_("value of '%s' out of range: %d"),
47+
FSMONITOR__START_TIMEOUT, i);
48+
fsmonitor__start_timeout_sec = i;
49+
return 0;
50+
}
51+
3952
if (!strcmp(var, FSMONITOR__ANNOUNCE_STARTUP)) {
4053
int is_bool;
4154
int i = git_config_bool_or_int(var, value, &is_bool);
@@ -250,7 +263,7 @@ static int fsmonitor_run_daemon(void)
250263
return err;
251264
}
252265

253-
static int try_to_run_foreground_daemon(void)
266+
static int try_to_run_foreground_daemon(int detach_console)
254267
{
255268
/*
256269
* Technically, we don't need to probe for an existing daemon
@@ -270,17 +283,106 @@ static int try_to_run_foreground_daemon(void)
270283
fflush(stderr);
271284
}
272285

286+
#ifdef GIT_WINDOWS_NATIVE
287+
if (detach_console)
288+
FreeConsole();
289+
#endif
290+
273291
return !!fsmonitor_run_daemon();
274292
}
275293

294+
static start_bg_wait_cb bg_wait_cb;
295+
296+
static int bg_wait_cb(const struct child_process *cp, void *cb_data)
297+
{
298+
enum ipc_active_state s = fsmonitor_ipc__get_state();
299+
300+
switch (s) {
301+
case IPC_STATE__LISTENING:
302+
/* child is "ready" */
303+
return 0;
304+
305+
case IPC_STATE__NOT_LISTENING:
306+
case IPC_STATE__PATH_NOT_FOUND:
307+
/* give child more time */
308+
return 1;
309+
310+
default:
311+
case IPC_STATE__INVALID_PATH:
312+
case IPC_STATE__OTHER_ERROR:
313+
/* all the time in world won't help */
314+
return -1;
315+
}
316+
}
317+
318+
static int try_to_start_background_daemon(void)
319+
{
320+
struct child_process cp = CHILD_PROCESS_INIT;
321+
enum start_bg_result sbgr;
322+
323+
/*
324+
* Before we try to create a background daemon process, see
325+
* if a daemon process is already listening. This makes it
326+
* easier for us to report an already-listening error to the
327+
* console, since our spawn/daemon can only report the success
328+
* of creating the background process (and not whether it
329+
* immediately exited).
330+
*/
331+
if (fsmonitor_ipc__get_state() == IPC_STATE__LISTENING)
332+
die(_("fsmonitor--daemon is already running '%s'"),
333+
the_repository->worktree);
334+
335+
if (fsmonitor__announce_startup) {
336+
fprintf(stderr, _("starting fsmonitor-daemon in '%s'\n"),
337+
the_repository->worktree);
338+
fflush(stderr);
339+
}
340+
341+
cp.git_cmd = 1;
342+
343+
strvec_push(&cp.args, "fsmonitor--daemon");
344+
strvec_push(&cp.args, "run");
345+
strvec_push(&cp.args, "--detach");
346+
strvec_pushf(&cp.args, "--ipc-threads=%d", fsmonitor__ipc_threads);
347+
348+
cp.no_stdin = 1;
349+
cp.no_stdout = 1;
350+
cp.no_stderr = 1;
351+
352+
sbgr = start_bg_command(&cp, bg_wait_cb, NULL,
353+
fsmonitor__start_timeout_sec);
354+
355+
switch (sbgr) {
356+
case SBGR_READY:
357+
return 0;
358+
359+
default:
360+
case SBGR_ERROR:
361+
case SBGR_CB_ERROR:
362+
return error(_("daemon failed to start"));
363+
364+
case SBGR_TIMEOUT:
365+
return error(_("daemon not online yet"));
366+
367+
case SBGR_DIED:
368+
return error(_("daemon terminated"));
369+
}
370+
}
371+
276372
int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
277373
{
278374
const char *subcmd;
375+
int detach_console = 0;
279376

280377
struct option options[] = {
378+
OPT_BOOL(0, "detach", &detach_console, N_("detach from console")),
281379
OPT_INTEGER(0, "ipc-threads",
282380
&fsmonitor__ipc_threads,
283381
N_("use <n> ipc worker threads")),
382+
OPT_INTEGER(0, "start-timeout",
383+
&fsmonitor__start_timeout_sec,
384+
N_("max seconds to wait for background daemon startup")),
385+
284386
OPT_END()
285387
};
286388

@@ -296,8 +398,11 @@ int cmd_fsmonitor__daemon(int argc, const char **argv, const char *prefix)
296398
die(_("invalid 'ipc-threads' value (%d)"),
297399
fsmonitor__ipc_threads);
298400

401+
if (!strcmp(subcmd, "start"))
402+
return !!try_to_start_background_daemon();
403+
299404
if (!strcmp(subcmd, "run"))
300-
return !!try_to_run_foreground_daemon();
405+
return !!try_to_run_foreground_daemon(detach_console);
301406

302407
if (!strcmp(subcmd, "stop"))
303408
return !!do_as_client__send_stop();

0 commit comments

Comments
 (0)