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