comparison contrib/chg/chg.c @ 28357:2f0f352d4196

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).
author Jun Wu <quark@fb.com>
date Sun, 06 Mar 2016 14:22:37 +0000
parents 3ab370f84a23
children ffd3ac07b1d7
comparison
equal deleted inserted replaced
28356:a5c773acb018 28357:2f0f352d4196
29 #define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)NULL)->sun_path)) 29 #define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)NULL)->sun_path))
30 #endif 30 #endif
31 31
32 struct cmdserveropts { 32 struct cmdserveropts {
33 char sockname[UNIX_PATH_MAX]; 33 char sockname[UNIX_PATH_MAX];
34 char redirectsockname[UNIX_PATH_MAX];
34 char lockfile[UNIX_PATH_MAX]; 35 char lockfile[UNIX_PATH_MAX];
35 char pidfile[UNIX_PATH_MAX]; 36 char pidfile[UNIX_PATH_MAX];
36 size_t argsize; 37 size_t argsize;
37 const char **args; 38 const char **args;
38 int lockfd; 39 int lockfd;
268 } 269 }
269 270
270 /* Connect to a cmdserver. Will start a new server on demand. */ 271 /* Connect to a cmdserver. Will start a new server on demand. */
271 static hgclient_t *connectcmdserver(struct cmdserveropts *opts) 272 static hgclient_t *connectcmdserver(struct cmdserveropts *opts)
272 { 273 {
273 hgclient_t *hgc = hgc_open(opts->sockname); 274 const char *sockname = opts->redirectsockname[0] ?
275 opts->redirectsockname : opts->sockname;
276 hgclient_t *hgc = hgc_open(sockname);
274 if (hgc) 277 if (hgc)
275 return hgc; 278 return hgc;
276 279
277 lockcmdserver(opts); 280 lockcmdserver(opts);
278 hgc = hgc_open(opts->sockname); 281 hgc = hgc_open(sockname);
279 if (hgc) { 282 if (hgc) {
280 unlockcmdserver(opts); 283 unlockcmdserver(opts);
281 debugmsg("cmdserver is started by another process"); 284 debugmsg("cmdserver is started by another process");
282 return hgc; 285 return hgc;
283 } 286 }
287
288 /* prevent us from being connected to an outdated server: we were
289 * told by a server to redirect to opts->redirectsockname and that
290 * address does not work. we do not want to connect to the server
291 * again because it will probably tell us the same thing. */
292 if (sockname == opts->redirectsockname)
293 unlink(opts->sockname);
284 294
285 debugmsg("start cmdserver at %s", opts->sockname); 295 debugmsg("start cmdserver at %s", opts->sockname);
286 296
287 pid_t pid = fork(); 297 pid_t pid = fork();
288 if (pid < 0) 298 if (pid < 0)
444 454
445 error: 455 error:
446 close(pipefds[0]); 456 close(pipefds[0]);
447 close(pipefds[1]); 457 close(pipefds[1]);
448 abortmsg("failed to prepare pager (errno = %d)", errno); 458 abortmsg("failed to prepare pager (errno = %d)", errno);
459 }
460
461 /* Run instructions sent from the server like unlink and set redirect path */
462 static void runinstructions(struct cmdserveropts *opts, const char **insts)
463 {
464 assert(insts);
465 opts->redirectsockname[0] = '\0';
466 const char **pinst;
467 for (pinst = insts; *pinst; pinst++) {
468 debugmsg("instruction: %s", *pinst);
469 if (strncmp(*pinst, "unlink ", 7) == 0) {
470 unlink(*pinst + 7);
471 } else if (strncmp(*pinst, "redirect ", 9) == 0) {
472 int r = snprintf(opts->redirectsockname,
473 sizeof(opts->redirectsockname),
474 "%s", *pinst + 9);
475 if (r < 0 || r >= (int)sizeof(opts->redirectsockname))
476 abortmsg("redirect path is too long (%d)", r);
477 } else {
478 abortmsg("unknown instruction: %s", *pinst);
479 }
480 }
449 } 481 }
450 482
451 /* 483 /*
452 * Test whether the command is unsupported or not. This is not designed to 484 * Test whether the command is unsupported or not. This is not designed to
453 * cover all cases. But it's fast, does not depend on the server and does 485 * cover all cases. But it's fast, does not depend on the server and does
514 killcmdserver(&opts, sig); 546 killcmdserver(&opts, sig);
515 return 0; 547 return 0;
516 } 548 }
517 } 549 }
518 550
519 hgclient_t *hgc = connectcmdserver(&opts); 551 hgclient_t *hgc;
520 if (!hgc) 552 while (1) {
521 abortmsg("cannot open hg client"); 553 hgc = connectcmdserver(&opts);
554 if (!hgc)
555 abortmsg("cannot open hg client");
556 hgc_setenv(hgc, envp);
557 const char **insts = hgc_validate(hgc, argv + 1, argc - 1);
558 if (insts == NULL)
559 break;
560 runinstructions(&opts, insts);
561 free(insts);
562 hgc_close(hgc);
563 }
522 564
523 setupsignalhandler(hgc_peerpid(hgc)); 565 setupsignalhandler(hgc_peerpid(hgc));
524 hgc_setenv(hgc, envp);
525 setuppager(hgc, argv + 1, argc - 1); 566 setuppager(hgc, argv + 1, argc - 1);
526 int exitcode = hgc_runcommand(hgc, argv + 1, argc - 1); 567 int exitcode = hgc_runcommand(hgc, argv + 1, argc - 1);
527 hgc_close(hgc); 568 hgc_close(hgc);
528 freecmdserveropts(&opts); 569 freecmdserveropts(&opts);
529 return exitcode; 570 return exitcode;