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