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