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