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