Subject: [PATCH/WIP v2 10/14] file-watcher: automatically quit

From: Nguyễn Thái Ngọc Duy <[email-masked]>
Date: Fri, 17 Jan 2014 16:47:36 +0700

If $GIT_DIR/index.watcher or $GIT_DIR/index is gone, exit. We could
watch this path too, but we'll waste precious resources (at least with
inotify). And with inotify, it seems to miss the case when $GIT_DIR is
moved. Just check if the socket path still exists every minute.

As the last resort, if we do not receive any commands in the last 6
hours, exit. The code is structured this way because later on inotify
is also polled. On an busy watched directory, the timeout may never
happen for us to kil the watcher, even if index.watcher is already
gone.

For mass cleanup, "killall -USR1 git-file-watcher" asks every watcher
process to question the purpose of its existence.

Signed-off-by: Nguyễn Thái Ngọc Duy <[email-masked]>
---
 file-watcher.c | 38 +++++++++++++++++++++++++++++++++++---
 1 file changed, 35 insertions(+), 3 deletions(-)

diff --git a/file-watcher.c b/file-watcher.c
index df06529..f334e23 100644
--- a/file-watcher.c
+++ b/file-watcher.c
@@ -163,6 +163,12 @@ static void daemonize(void)
 #endif
 }
 
+static int check_exit_please;
+static void check_exit_signal(int signo)
+{
+	check_exit_please = 1;
+}
+
 int main(int argc, const char **argv)
 {
 	struct strbuf sb = STRBUF_INIT;
@@ -172,6 +178,8 @@ int main(int argc, const char **argv)
 	const char *prefix;
 	int daemon = 0, quiet = 0, shutdown = 0;
 	const char *log_string = NULL;
+	struct stat socket_st;
+	struct timeval tv_last_command;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet")),
 		OPT_BOOL(0, "daemon", &daemon,
@@ -217,6 +225,10 @@ int main(int argc, const char **argv)
 
 	atexit(cleanup);
 	sigchain_push_common(cleanup_on_signal);
+	sigchain_push(SIGUSR1, check_exit_signal);
+
+	if (stat(socket_path, &socket_st))
+		die_errno("failed to stat %s", socket_path);
 
 	if (daemon) {
 		strbuf_addf(&sb, "%s.log", socket_path);
@@ -234,17 +246,37 @@ int main(int argc, const char **argv)
 	pfd[nr].fd = fd;
 	pfd[nr++].events = POLLIN;
 
+	gettimeofday(&tv_last_command, NULL);
 	for (;;) {
-		if (poll(pfd, nr, -1) < 0) {
+		int check_exit = check_exit_please;
+		int ret = poll(pfd, nr, check_exit ? 0 : 60 * 1000);
+		if (ret < 0) {
 			if (errno != EINTR) {
 				error("Poll failed, resuming: %s", strerror(errno));
 				sleep(1);
 			}
 			continue;
+		} else if (!ret)
+			check_exit = 1;
+
+		if ((pfd[0].revents & POLLIN)) {
+			if (handle_command(fd))
+				break;
+			gettimeofday(&tv_last_command, NULL);
 		}
 
-		if ((pfd[0].revents & POLLIN) && handle_command(fd))
-			break;
+		if (check_exit) {
+			struct stat st;
+			struct timeval now;
+			gettimeofday(&now, NULL);
+			if (tv_last_command.tv_sec + 6 * 60 < now.tv_sec)
+				break;
+			if (stat(socket_path, &st) ||
+			    st.st_ino != socket_st.st_ino ||
+			    stat(get_index_file(), &st))
+				break;
+			check_exit_please = 0;
+		}
 	}
 	return 0;
 }
-- 
1.8.5.1.208.g05b12ea