Comments
Patch
@@ -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);