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