[PATCH 4 of 5 V2] chg: use validate to make sure the server is up to date

Jun Wu quark at fb.com
Fri Mar 4 08:28:57 EST 2016


# HG changeset patch
# User Jun Wu <quark at fb.com>
# Date 1456913782 0
#      Wed Mar 02 10:16:22 2016 +0000
# Node ID 6a5b979dd315c60b0eb0f3c3e87fcaf1c892e6e1
# Parent  314e2f98e29e590d2bae1d3604f54478ce47cbc6
chg: use validate to make sure the server is up to date

This patch uses the newly added validate method to make sure the server has
loaded the up-to-date config and extensions. It will follow instructions from
the server like redirecting to another server or unlinking a socket file (
effectively stopping an out-dated server).

diff --git a/contrib/chg/chg.c b/contrib/chg/chg.c
--- a/contrib/chg/chg.c
+++ b/contrib/chg/chg.c
@@ -31,6 +31,7 @@
 
 struct cmdserveropts {
 	char sockname[UNIX_PATH_MAX];
+	char redirectsockname[UNIX_PATH_MAX];
 	char lockfile[UNIX_PATH_MAX];
 	char pidfile[UNIX_PATH_MAX];
 	size_t argsize;
@@ -270,18 +271,24 @@
 /* Connect to a cmdserver. Will start a new server on demand. */
 static hgclient_t *connectcmdserver(struct cmdserveropts *opts)
 {
-	hgclient_t *hgc = hgc_open(opts->sockname);
+	const char *sockname = opts->redirectsockname[0] ?
+		opts->redirectsockname : opts->sockname;
+	hgclient_t *hgc = hgc_open(sockname);
 	if (hgc)
 		return hgc;
 
 	lockcmdserver(opts);
-	hgc = hgc_open(opts->sockname);
+	hgc = hgc_open(sockname);
 	if (hgc) {
 		unlockcmdserver(opts);
 		debugmsg("cmdserver is started by another process");
 		return hgc;
 	}
 
+	/* prevent us from being connected to an outdated server */
+	if (sockname == opts->redirectsockname)
+		unlink(opts->sockname);
+
 	debugmsg("start cmdserver at %s", opts->sockname);
 
 	pid_t pid = fork();
@@ -448,6 +455,39 @@
 	abortmsg("failed to prepare pager (errno = %d)", errno);
 }
 
+/* Run instructions sent from the server like unlink and set redirect path */
+static void runinstructions(hgclient_t *hgc, struct cmdserveropts *opts,
+			    size_t instsize)
+{
+	assert(instsize > 0);
+	const char **instbuf = mallocx(instsize * sizeof(char *));
+
+	size_t n = hgc_parselist(hgc, instbuf, instsize);
+	if (n >= instsize || n == 0)
+		abortmsg("unexpected instruction count (%u)", (unsigned) n);
+
+	opts->redirectsockname[0] = '\0';
+
+	static const char UNLINK[] = "unlink ";
+	static const char REDIRECT[] = "redirect ";
+	const char **pi;
+	for (pi = instbuf; *pi; pi++) {
+		debugmsg("instruction: %s", *pi);
+		if (strncmp(*pi, UNLINK, sizeof(UNLINK) - 1) == 0) {
+			unlink(*pi + 7);
+		} else if (strncmp(*pi, REDIRECT, sizeof(REDIRECT) - 1) == 0) {
+			int r = snprintf(opts->redirectsockname,
+					 sizeof(opts->redirectsockname),
+					 "%s", *pi + 9);
+			if (r < 0 || r >= (int)sizeof(opts->redirectsockname))
+				abortmsg("redirect path too long (r = %d)", r);
+		} else {
+			abortmsg("unknown instruction: %s", *pi);
+		}
+	}
+	free(instbuf);
+}
+
 /*
  * Test whether the command is unsupported or not. This is not designed to
  * cover all cases. But it's fast, does not depend on the server and does
@@ -516,12 +556,20 @@
 		}
 	}
 
-	hgclient_t *hgc = connectcmdserver(&opts);
-	if (!hgc)
-		abortmsg("cannot open hg client");
+	hgclient_t *hgc;
+	while (1) {
+		hgc = connectcmdserver(&opts);
+		if (!hgc)
+			abortmsg("cannot open hg client");
+		hgc_setenv(hgc, envp);
+		size_t instsize = hgc_validate(hgc, argv + 1, argc - 1);
+		if (instsize == 0)
+			break;
+		runinstructions(hgc, &opts, instsize);
+		hgc_close(hgc);
+	}
 
 	setupsignalhandler(hgc_peerpid(hgc));
-	hgc_setenv(hgc, envp);
 	setuppager(hgc, argv + 1, argc - 1);
 	int exitcode = hgc_runcommand(hgc, argv + 1, argc - 1);
 	hgc_close(hgc);


More information about the Mercurial-devel mailing list