##// END OF EJS Templates
chg: change default connect timeout to 60 seconds...
Jun Wu -
r29357:66d41c9e default
parent child Browse files
Show More
@@ -1,32 +1,32 b''
1 1 cHg
2 2 ===
3 3
4 4 A fast client for Mercurial command server running on Unix.
5 5
6 6 Install:
7 7
8 8 $ make
9 9 $ make install
10 10
11 11 Usage:
12 12
13 13 $ chg help # show help of Mercurial
14 14 $ alias hg=chg # replace hg command
15 15 $ chg --kill-chg-daemon # terminate background server
16 16
17 17 Environment variables:
18 18
19 19 Although cHg tries to update environment variables, some of them cannot be
20 20 changed after spawning the server. The following variables are specially
21 21 handled:
22 22
23 23 * configuration files are reloaded automatically by default.
24 24 * CHGHG or HG specifies the path to the hg executable spawned as the
25 25 background command server.
26 26
27 27 The following variables are available for testing:
28 28
29 29 * CHGDEBUG enables debug messages.
30 30 * CHGSOCKNAME specifies the socket path of the background cmdserver.
31 31 * CHGTIMEOUT specifies how many seconds chg will wait before giving up
32 connecting to a cmdserver. If it is 0, chg will wait forever. Default: 10
32 connecting to a cmdserver. If it is 0, chg will wait forever. Default: 60
@@ -1,617 +1,617 b''
1 1 /*
2 2 * A fast client for Mercurial command server
3 3 *
4 4 * Copyright (c) 2011 Yuya Nishihara <yuya@tcha.org>
5 5 *
6 6 * This software may be used and distributed according to the terms of the
7 7 * GNU General Public License version 2 or any later version.
8 8 */
9 9
10 10 #include <assert.h>
11 11 #include <errno.h>
12 12 #include <fcntl.h>
13 13 #include <signal.h>
14 14 #include <stdio.h>
15 15 #include <stdlib.h>
16 16 #include <string.h>
17 17 #include <sys/file.h>
18 18 #include <sys/stat.h>
19 19 #include <sys/types.h>
20 20 #include <sys/un.h>
21 21 #include <sys/wait.h>
22 22 #include <time.h>
23 23 #include <unistd.h>
24 24
25 25 #include "hgclient.h"
26 26 #include "util.h"
27 27
28 28 #ifndef UNIX_PATH_MAX
29 29 #define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)NULL)->sun_path))
30 30 #endif
31 31
32 32 struct cmdserveropts {
33 33 char sockname[UNIX_PATH_MAX];
34 34 char redirectsockname[UNIX_PATH_MAX];
35 35 char lockfile[UNIX_PATH_MAX];
36 36 size_t argsize;
37 37 const char **args;
38 38 int lockfd;
39 39 int sockdirfd;
40 40 };
41 41
42 42 static void initcmdserveropts(struct cmdserveropts *opts) {
43 43 memset(opts, 0, sizeof(struct cmdserveropts));
44 44 opts->lockfd = -1;
45 45 opts->sockdirfd = -1;
46 46 }
47 47
48 48 static void freecmdserveropts(struct cmdserveropts *opts) {
49 49 free(opts->args);
50 50 opts->args = NULL;
51 51 opts->argsize = 0;
52 52 assert(opts->lockfd == -1 && "should be closed by unlockcmdserver()");
53 53 if (opts->sockdirfd >= 0) {
54 54 close(opts->sockdirfd);
55 55 opts->sockdirfd = -1;
56 56 }
57 57 }
58 58
59 59 /*
60 60 * Test if an argument is a sensitive flag that should be passed to the server.
61 61 * Return 0 if not, otherwise the number of arguments starting from the current
62 62 * one that should be passed to the server.
63 63 */
64 64 static size_t testsensitiveflag(const char *arg)
65 65 {
66 66 static const struct {
67 67 const char *name;
68 68 size_t narg;
69 69 } flags[] = {
70 70 {"--config", 1},
71 71 {"--cwd", 1},
72 72 {"--repo", 1},
73 73 {"--repository", 1},
74 74 {"--traceback", 0},
75 75 {"-R", 1},
76 76 };
77 77 size_t i;
78 78 for (i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i) {
79 79 size_t len = strlen(flags[i].name);
80 80 size_t narg = flags[i].narg;
81 81 if (memcmp(arg, flags[i].name, len) == 0) {
82 82 if (arg[len] == '\0') {
83 83 /* --flag (value) */
84 84 return narg + 1;
85 85 } else if (arg[len] == '=' && narg > 0) {
86 86 /* --flag=value */
87 87 return 1;
88 88 } else if (flags[i].name[1] != '-') {
89 89 /* short flag */
90 90 return 1;
91 91 }
92 92 }
93 93 }
94 94 return 0;
95 95 }
96 96
97 97 /*
98 98 * Parse argv[] and put sensitive flags to opts->args
99 99 */
100 100 static void setcmdserverargs(struct cmdserveropts *opts,
101 101 int argc, const char *argv[])
102 102 {
103 103 size_t i, step;
104 104 opts->argsize = 0;
105 105 for (i = 0, step = 1; i < (size_t)argc; i += step, step = 1) {
106 106 if (!argv[i])
107 107 continue; /* pass clang-analyse */
108 108 if (strcmp(argv[i], "--") == 0)
109 109 break;
110 110 size_t n = testsensitiveflag(argv[i]);
111 111 if (n == 0 || i + n > (size_t)argc)
112 112 continue;
113 113 opts->args = reallocx(opts->args,
114 114 (n + opts->argsize) * sizeof(char *));
115 115 memcpy(opts->args + opts->argsize, argv + i,
116 116 sizeof(char *) * n);
117 117 opts->argsize += n;
118 118 step = n;
119 119 }
120 120 }
121 121
122 122 static void preparesockdir(const char *sockdir)
123 123 {
124 124 int r;
125 125 r = mkdir(sockdir, 0700);
126 126 if (r < 0 && errno != EEXIST)
127 127 abortmsgerrno("cannot create sockdir %s", sockdir);
128 128
129 129 struct stat st;
130 130 r = lstat(sockdir, &st);
131 131 if (r < 0)
132 132 abortmsgerrno("cannot stat %s", sockdir);
133 133 if (!S_ISDIR(st.st_mode))
134 134 abortmsg("cannot create sockdir %s (file exists)", sockdir);
135 135 if (st.st_uid != geteuid() || st.st_mode & 0077)
136 136 abortmsg("insecure sockdir %s", sockdir);
137 137 }
138 138
139 139 static void setcmdserveropts(struct cmdserveropts *opts)
140 140 {
141 141 int r;
142 142 char sockdir[UNIX_PATH_MAX];
143 143 const char *envsockname = getenv("CHGSOCKNAME");
144 144 if (!envsockname) {
145 145 /* by default, put socket file in secure directory
146 146 * (permission of socket file may be ignored on some Unices) */
147 147 const char *tmpdir = getenv("TMPDIR");
148 148 if (!tmpdir)
149 149 tmpdir = "/tmp";
150 150 r = snprintf(sockdir, sizeof(sockdir), "%s/chg%d",
151 151 tmpdir, geteuid());
152 152 if (r < 0 || (size_t)r >= sizeof(sockdir))
153 153 abortmsg("too long TMPDIR (r = %d)", r);
154 154 preparesockdir(sockdir);
155 155 }
156 156
157 157 const char *basename = (envsockname) ? envsockname : sockdir;
158 158 const char *sockfmt = (envsockname) ? "%s" : "%s/server";
159 159 const char *lockfmt = (envsockname) ? "%s.lock" : "%s/lock";
160 160 r = snprintf(opts->sockname, sizeof(opts->sockname), sockfmt, basename);
161 161 if (r < 0 || (size_t)r >= sizeof(opts->sockname))
162 162 abortmsg("too long TMPDIR or CHGSOCKNAME (r = %d)", r);
163 163 r = snprintf(opts->lockfile, sizeof(opts->lockfile), lockfmt, basename);
164 164 if (r < 0 || (size_t)r >= sizeof(opts->lockfile))
165 165 abortmsg("too long TMPDIR or CHGSOCKNAME (r = %d)", r);
166 166 }
167 167
168 168 /*
169 169 * Acquire a file lock that indicates a client is trying to start and connect
170 170 * to a server, before executing a command. The lock is released upon exit or
171 171 * explicit unlock. Will block if the lock is held by another process.
172 172 */
173 173 static void lockcmdserver(struct cmdserveropts *opts)
174 174 {
175 175 if (opts->lockfd == -1) {
176 176 opts->lockfd = open(opts->lockfile,
177 177 O_RDWR | O_CREAT | O_NOFOLLOW, 0600);
178 178 if (opts->lockfd == -1)
179 179 abortmsgerrno("cannot create lock file %s",
180 180 opts->lockfile);
181 181 fsetcloexec(opts->lockfd);
182 182 }
183 183 int r = flock(opts->lockfd, LOCK_EX);
184 184 if (r == -1)
185 185 abortmsgerrno("cannot acquire lock");
186 186 }
187 187
188 188 /*
189 189 * Release the file lock held by calling lockcmdserver. Will do nothing if
190 190 * lockcmdserver is not called.
191 191 */
192 192 static void unlockcmdserver(struct cmdserveropts *opts)
193 193 {
194 194 if (opts->lockfd == -1)
195 195 return;
196 196 flock(opts->lockfd, LOCK_UN);
197 197 close(opts->lockfd);
198 198 opts->lockfd = -1;
199 199 }
200 200
201 201 static const char *gethgcmd(void)
202 202 {
203 203 static const char *hgcmd = NULL;
204 204 if (!hgcmd) {
205 205 hgcmd = getenv("CHGHG");
206 206 if (!hgcmd || hgcmd[0] == '\0')
207 207 hgcmd = getenv("HG");
208 208 if (!hgcmd || hgcmd[0] == '\0')
209 209 #ifdef HGPATH
210 210 hgcmd = (HGPATH);
211 211 #else
212 212 hgcmd = "hg";
213 213 #endif
214 214 }
215 215 return hgcmd;
216 216 }
217 217
218 218 static void execcmdserver(const struct cmdserveropts *opts)
219 219 {
220 220 const char *hgcmd = gethgcmd();
221 221
222 222 const char *baseargv[] = {
223 223 hgcmd,
224 224 "serve",
225 225 "--cmdserver", "chgunix",
226 226 "--address", opts->sockname,
227 227 "--daemon-postexec", "chdir:/",
228 228 "--config", "extensions.chgserver=",
229 229 };
230 230 size_t baseargvsize = sizeof(baseargv) / sizeof(baseargv[0]);
231 231 size_t argsize = baseargvsize + opts->argsize + 1;
232 232
233 233 const char **argv = mallocx(sizeof(char *) * argsize);
234 234 memcpy(argv, baseargv, sizeof(baseargv));
235 235 memcpy(argv + baseargvsize, opts->args, sizeof(char *) * opts->argsize);
236 236 argv[argsize - 1] = NULL;
237 237
238 238 if (putenv("CHGINTERNALMARK=") != 0)
239 239 abortmsgerrno("failed to putenv");
240 240 if (execvp(hgcmd, (char **)argv) < 0)
241 241 abortmsgerrno("failed to exec cmdserver");
242 242 free(argv);
243 243 }
244 244
245 245 /* Retry until we can connect to the server. Give up after some time. */
246 246 static hgclient_t *retryconnectcmdserver(struct cmdserveropts *opts, pid_t pid)
247 247 {
248 248 static const struct timespec sleepreq = {0, 10 * 1000000};
249 249 int pst = 0;
250 250
251 251 debugmsg("try connect to %s repeatedly", opts->sockname);
252 252
253 unsigned int timeoutsec = 10; /* default: 10 seconds */
253 unsigned int timeoutsec = 60; /* default: 60 seconds */
254 254 const char *timeoutenv = getenv("CHGTIMEOUT");
255 255 if (timeoutenv)
256 256 sscanf(timeoutenv, "%u", &timeoutsec);
257 257
258 258 for (unsigned int i = 0; !timeoutsec || i < timeoutsec * 100; i++) {
259 259 hgclient_t *hgc = hgc_open(opts->sockname);
260 260 if (hgc)
261 261 return hgc;
262 262
263 263 if (pid > 0) {
264 264 /* collect zombie if child process fails to start */
265 265 int r = waitpid(pid, &pst, WNOHANG);
266 266 if (r != 0)
267 267 goto cleanup;
268 268 }
269 269
270 270 nanosleep(&sleepreq, NULL);
271 271 }
272 272
273 273 abortmsg("timed out waiting for cmdserver %s", opts->sockname);
274 274 return NULL;
275 275
276 276 cleanup:
277 277 if (WIFEXITED(pst)) {
278 278 if (WEXITSTATUS(pst) == 0)
279 279 abortmsg("could not connect to cmdserver "
280 280 "(exited with status 0)");
281 281 debugmsg("cmdserver exited with status %d", WEXITSTATUS(pst));
282 282 exit(WEXITSTATUS(pst));
283 283 } else if (WIFSIGNALED(pst)) {
284 284 abortmsg("cmdserver killed by signal %d", WTERMSIG(pst));
285 285 } else {
286 286 abortmsg("error while waiting for cmdserver");
287 287 }
288 288 return NULL;
289 289 }
290 290
291 291 /* Connect to a cmdserver. Will start a new server on demand. */
292 292 static hgclient_t *connectcmdserver(struct cmdserveropts *opts)
293 293 {
294 294 const char *sockname = opts->redirectsockname[0] ?
295 295 opts->redirectsockname : opts->sockname;
296 296 debugmsg("try connect to %s", sockname);
297 297 hgclient_t *hgc = hgc_open(sockname);
298 298 if (hgc)
299 299 return hgc;
300 300
301 301 lockcmdserver(opts);
302 302 hgc = hgc_open(sockname);
303 303 if (hgc) {
304 304 unlockcmdserver(opts);
305 305 debugmsg("cmdserver is started by another process");
306 306 return hgc;
307 307 }
308 308
309 309 /* prevent us from being connected to an outdated server: we were
310 310 * told by a server to redirect to opts->redirectsockname and that
311 311 * address does not work. we do not want to connect to the server
312 312 * again because it will probably tell us the same thing. */
313 313 if (sockname == opts->redirectsockname)
314 314 unlink(opts->sockname);
315 315
316 316 debugmsg("start cmdserver at %s", opts->sockname);
317 317
318 318 pid_t pid = fork();
319 319 if (pid < 0)
320 320 abortmsg("failed to fork cmdserver process");
321 321 if (pid == 0) {
322 322 execcmdserver(opts);
323 323 } else {
324 324 hgc = retryconnectcmdserver(opts, pid);
325 325 }
326 326
327 327 unlockcmdserver(opts);
328 328 return hgc;
329 329 }
330 330
331 331 static void killcmdserver(const struct cmdserveropts *opts)
332 332 {
333 333 /* resolve config hash */
334 334 char *resolvedpath = realpath(opts->sockname, NULL);
335 335 if (resolvedpath) {
336 336 unlink(resolvedpath);
337 337 free(resolvedpath);
338 338 }
339 339 }
340 340
341 341 static pid_t peerpid = 0;
342 342
343 343 static void forwardsignal(int sig)
344 344 {
345 345 assert(peerpid > 0);
346 346 if (kill(peerpid, sig) < 0)
347 347 abortmsgerrno("cannot kill %d", peerpid);
348 348 debugmsg("forward signal %d", sig);
349 349 }
350 350
351 351 static void handlestopsignal(int sig)
352 352 {
353 353 sigset_t unblockset, oldset;
354 354 struct sigaction sa, oldsa;
355 355 if (sigemptyset(&unblockset) < 0)
356 356 goto error;
357 357 if (sigaddset(&unblockset, sig) < 0)
358 358 goto error;
359 359 memset(&sa, 0, sizeof(sa));
360 360 sa.sa_handler = SIG_DFL;
361 361 sa.sa_flags = SA_RESTART;
362 362 if (sigemptyset(&sa.sa_mask) < 0)
363 363 goto error;
364 364
365 365 forwardsignal(sig);
366 366 if (raise(sig) < 0) /* resend to self */
367 367 goto error;
368 368 if (sigaction(sig, &sa, &oldsa) < 0)
369 369 goto error;
370 370 if (sigprocmask(SIG_UNBLOCK, &unblockset, &oldset) < 0)
371 371 goto error;
372 372 /* resent signal will be handled before sigprocmask() returns */
373 373 if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0)
374 374 goto error;
375 375 if (sigaction(sig, &oldsa, NULL) < 0)
376 376 goto error;
377 377 return;
378 378
379 379 error:
380 380 abortmsgerrno("failed to handle stop signal");
381 381 }
382 382
383 383 static void setupsignalhandler(pid_t pid)
384 384 {
385 385 if (pid <= 0)
386 386 return;
387 387 peerpid = pid;
388 388
389 389 struct sigaction sa;
390 390 memset(&sa, 0, sizeof(sa));
391 391 sa.sa_handler = forwardsignal;
392 392 sa.sa_flags = SA_RESTART;
393 393 if (sigemptyset(&sa.sa_mask) < 0)
394 394 goto error;
395 395
396 396 if (sigaction(SIGHUP, &sa, NULL) < 0)
397 397 goto error;
398 398 if (sigaction(SIGINT, &sa, NULL) < 0)
399 399 goto error;
400 400
401 401 /* terminate frontend by double SIGTERM in case of server freeze */
402 402 sa.sa_flags |= SA_RESETHAND;
403 403 if (sigaction(SIGTERM, &sa, NULL) < 0)
404 404 goto error;
405 405
406 406 /* notify the worker about window resize events */
407 407 sa.sa_flags = SA_RESTART;
408 408 if (sigaction(SIGWINCH, &sa, NULL) < 0)
409 409 goto error;
410 410 /* propagate job control requests to worker */
411 411 sa.sa_handler = forwardsignal;
412 412 sa.sa_flags = SA_RESTART;
413 413 if (sigaction(SIGCONT, &sa, NULL) < 0)
414 414 goto error;
415 415 sa.sa_handler = handlestopsignal;
416 416 sa.sa_flags = SA_RESTART;
417 417 if (sigaction(SIGTSTP, &sa, NULL) < 0)
418 418 goto error;
419 419
420 420 return;
421 421
422 422 error:
423 423 abortmsgerrno("failed to set up signal handlers");
424 424 }
425 425
426 426 /* This implementation is based on hgext/pager.py (post 369741ef7253)
427 427 * Return 0 if pager is not started, or pid of the pager */
428 428 static pid_t setuppager(hgclient_t *hgc, const char *const args[],
429 429 size_t argsize)
430 430 {
431 431 const char *pagercmd = hgc_getpager(hgc, args, argsize);
432 432 if (!pagercmd)
433 433 return 0;
434 434
435 435 int pipefds[2];
436 436 if (pipe(pipefds) < 0)
437 437 return 0;
438 438 pid_t pid = fork();
439 439 if (pid < 0)
440 440 goto error;
441 441 if (pid > 0) {
442 442 close(pipefds[0]);
443 443 if (dup2(pipefds[1], fileno(stdout)) < 0)
444 444 goto error;
445 445 if (isatty(fileno(stderr))) {
446 446 if (dup2(pipefds[1], fileno(stderr)) < 0)
447 447 goto error;
448 448 }
449 449 close(pipefds[1]);
450 450 hgc_attachio(hgc); /* reattach to pager */
451 451 return pid;
452 452 } else {
453 453 dup2(pipefds[0], fileno(stdin));
454 454 close(pipefds[0]);
455 455 close(pipefds[1]);
456 456
457 457 int r = execlp("/bin/sh", "/bin/sh", "-c", pagercmd, NULL);
458 458 if (r < 0) {
459 459 abortmsgerrno("cannot start pager '%s'", pagercmd);
460 460 }
461 461 return 0;
462 462 }
463 463
464 464 error:
465 465 close(pipefds[0]);
466 466 close(pipefds[1]);
467 467 abortmsgerrno("failed to prepare pager");
468 468 return 0;
469 469 }
470 470
471 471 static void waitpager(pid_t pid)
472 472 {
473 473 /* close output streams to notify the pager its input ends */
474 474 fclose(stdout);
475 475 fclose(stderr);
476 476 while (1) {
477 477 pid_t ret = waitpid(pid, NULL, 0);
478 478 if (ret == -1 && errno == EINTR)
479 479 continue;
480 480 break;
481 481 }
482 482 }
483 483
484 484 /* Run instructions sent from the server like unlink and set redirect path
485 485 * Return 1 if reconnect is needed, otherwise 0 */
486 486 static int runinstructions(struct cmdserveropts *opts, const char **insts)
487 487 {
488 488 int needreconnect = 0;
489 489 if (!insts)
490 490 return needreconnect;
491 491
492 492 assert(insts);
493 493 opts->redirectsockname[0] = '\0';
494 494 const char **pinst;
495 495 for (pinst = insts; *pinst; pinst++) {
496 496 debugmsg("instruction: %s", *pinst);
497 497 if (strncmp(*pinst, "unlink ", 7) == 0) {
498 498 unlink(*pinst + 7);
499 499 } else if (strncmp(*pinst, "redirect ", 9) == 0) {
500 500 int r = snprintf(opts->redirectsockname,
501 501 sizeof(opts->redirectsockname),
502 502 "%s", *pinst + 9);
503 503 if (r < 0 || r >= (int)sizeof(opts->redirectsockname))
504 504 abortmsg("redirect path is too long (%d)", r);
505 505 needreconnect = 1;
506 506 } else if (strncmp(*pinst, "exit ", 5) == 0) {
507 507 int n = 0;
508 508 if (sscanf(*pinst + 5, "%d", &n) != 1)
509 509 abortmsg("cannot read the exit code");
510 510 exit(n);
511 511 } else if (strcmp(*pinst, "reconnect") == 0) {
512 512 needreconnect = 1;
513 513 } else {
514 514 abortmsg("unknown instruction: %s", *pinst);
515 515 }
516 516 }
517 517 return needreconnect;
518 518 }
519 519
520 520 /*
521 521 * Test whether the command is unsupported or not. This is not designed to
522 522 * cover all cases. But it's fast, does not depend on the server and does
523 523 * not return false positives.
524 524 */
525 525 static int isunsupported(int argc, const char *argv[])
526 526 {
527 527 enum {
528 528 SERVE = 1,
529 529 DAEMON = 2,
530 530 SERVEDAEMON = SERVE | DAEMON,
531 531 TIME = 4,
532 532 };
533 533 unsigned int state = 0;
534 534 int i;
535 535 for (i = 0; i < argc; ++i) {
536 536 if (strcmp(argv[i], "--") == 0)
537 537 break;
538 538 if (i == 0 && strcmp("serve", argv[i]) == 0)
539 539 state |= SERVE;
540 540 else if (strcmp("-d", argv[i]) == 0 ||
541 541 strcmp("--daemon", argv[i]) == 0)
542 542 state |= DAEMON;
543 543 else if (strcmp("--time", argv[i]) == 0)
544 544 state |= TIME;
545 545 }
546 546 return (state & TIME) == TIME ||
547 547 (state & SERVEDAEMON) == SERVEDAEMON;
548 548 }
549 549
550 550 static void execoriginalhg(const char *argv[])
551 551 {
552 552 debugmsg("execute original hg");
553 553 if (execvp(gethgcmd(), (char **)argv) < 0)
554 554 abortmsgerrno("failed to exec original hg");
555 555 }
556 556
557 557 int main(int argc, const char *argv[], const char *envp[])
558 558 {
559 559 if (getenv("CHGDEBUG"))
560 560 enabledebugmsg();
561 561
562 562 if (!getenv("HGPLAIN") && isatty(fileno(stderr)))
563 563 enablecolor();
564 564
565 565 if (getenv("CHGINTERNALMARK"))
566 566 abortmsg("chg started by chg detected.\n"
567 567 "Please make sure ${HG:-hg} is not a symlink or "
568 568 "wrapper to chg. Alternatively, set $CHGHG to the "
569 569 "path of real hg.");
570 570
571 571 if (isunsupported(argc - 1, argv + 1))
572 572 execoriginalhg(argv);
573 573
574 574 struct cmdserveropts opts;
575 575 initcmdserveropts(&opts);
576 576 setcmdserveropts(&opts);
577 577 setcmdserverargs(&opts, argc, argv);
578 578
579 579 if (argc == 2) {
580 580 if (strcmp(argv[1], "--kill-chg-daemon") == 0) {
581 581 killcmdserver(&opts);
582 582 return 0;
583 583 }
584 584 }
585 585
586 586 hgclient_t *hgc;
587 587 size_t retry = 0;
588 588 while (1) {
589 589 hgc = connectcmdserver(&opts);
590 590 if (!hgc)
591 591 abortmsg("cannot open hg client");
592 592 hgc_setenv(hgc, envp);
593 593 const char **insts = hgc_validate(hgc, argv + 1, argc - 1);
594 594 int needreconnect = runinstructions(&opts, insts);
595 595 free(insts);
596 596 if (!needreconnect)
597 597 break;
598 598 hgc_close(hgc);
599 599 if (++retry > 10)
600 600 abortmsg("too many redirections.\n"
601 601 "Please make sure %s is not a wrapper which "
602 602 "changes sensitive environment variables "
603 603 "before executing hg. If you have to use a "
604 604 "wrapper, wrap chg instead of hg.",
605 605 gethgcmd());
606 606 }
607 607
608 608 setupsignalhandler(hgc_peerpid(hgc));
609 609 pid_t pagerpid = setuppager(hgc, argv + 1, argc - 1);
610 610 int exitcode = hgc_runcommand(hgc, argv + 1, argc - 1);
611 611 hgc_close(hgc);
612 612 freecmdserveropts(&opts);
613 613 if (pagerpid)
614 614 waitpager(pagerpid);
615 615
616 616 return exitcode;
617 617 }
General Comments 0
You need to be logged in to leave comments. Login now