chg: use validate to make sure the server is up to date
authorJun Wu <quark@fb.com>
Sun, 06 Mar 2016 14:22:37 +0000
changeset 28357 2f0f352d4196
parent 28356 a5c773acb018
child 28358 ffd3ac07b1d7
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, the client will receive instructions and follow them 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 (stops an out-dated server).
contrib/chg/chg.c
--- a/contrib/chg/chg.c	Sun Mar 06 14:21:52 2016 +0000
+++ b/contrib/chg/chg.c	Sun Mar 06 14:22:37 2016 +0000
@@ -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,28 @@
 	abortmsg("failed to prepare pager (errno = %d)", errno);
 }
 
+/* Run instructions sent from the server like unlink and set redirect path */
+static void runinstructions(struct cmdserveropts *opts, const char **insts)
+{
+	assert(insts);
+	opts->redirectsockname[0] = '\0';
+	const char **pinst;
+	for (pinst = insts; *pinst; pinst++) {
+		debugmsg("instruction: %s", *pinst);
+		if (strncmp(*pinst, "unlink ", 7) == 0) {
+			unlink(*pinst + 7);
+		} else if (strncmp(*pinst, "redirect ", 9) == 0) {
+			int r = snprintf(opts->redirectsockname,
+					 sizeof(opts->redirectsockname),
+					 "%s", *pinst + 9);
+			if (r < 0 || r >= (int)sizeof(opts->redirectsockname))
+				abortmsg("redirect path is too long (%d)", r);
+		} else {
+			abortmsg("unknown instruction: %s", *pinst);
+		}
+	}
+}
+
 /*
  * 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 +548,21 @@
 		}
 	}
 
-	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);
+		const char **insts = hgc_validate(hgc, argv + 1, argc - 1);
+		if (insts == NULL)
+			break;
+		runinstructions(&opts, insts);
+		free(insts);
+		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);