[PATCH V3] chg: use validate to make sure the server is up to date

Jun Wu quark at fb.com
Fri Mar 4 14:14:37 UTC 2016


# HG changeset patch
# User Jun Wu <quark at fb.com>
# Date 1457100004 0
#      Fri Mar 04 14:00:04 2016 +0000
# Node ID 329ae1c21f95f22d133df541041541d94d08c8c6
# 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. If the server cannot validate
itself, it will send instructions which the client will follow to try to reach
another server that is more likely to validate itself. The instructions can
be a redirect (connect to another server address) and/or an unlink (effectively
stops 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,27 @@
 /* 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: we were
+	 * told by a server to redirect to opts->redirectsockname and that
+	 * address does not work. we do not want to connect to the server
+	 * again because it will probably tell us the same thing. */
+	if (sockname == opts->redirectsockname)
+		unlink(opts->sockname);
+
 	debugmsg("start cmdserver at %s", opts->sockname);
 
 	pid_t pid = fork();
@@ -448,6 +458,40 @@
 	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 ";
+	size_t i;
+	for (i = 0; i < n; ++i) {
+		const char *s = instbuf[i];
+		debugmsg("instruction: %s", s);
+		if (strncmp(s, UNLINK, sizeof(UNLINK) - 1) == 0) {
+			unlink(s + sizeof(UNLINK) - 1);
+		} else if (strncmp(s, REDIRECT, sizeof(REDIRECT) - 1) == 0) {
+			int r = snprintf(opts->redirectsockname,
+					 sizeof(opts->redirectsockname),
+					 "%s", s + sizeof(REDIRECT) - 1);
+			if (r < 0 || r >= (int)sizeof(opts->redirectsockname))
+				abortmsg("redirect path too long (r = %d)", r);
+		} else {
+			abortmsg("unknown instruction: %s", s);
+		}
+	}
+	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 +560,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