##// END OF EJS Templates
chg: use fsetcloexec instead of closing lockfd manually...
Jun Wu -
r28856:0d530299 default
parent child Browse files
Show More
@@ -1,587 +1,586 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 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 = AT_FDCWD;
45 opts->sockdirfd = AT_FDCWD;
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 != AT_FDCWD) {
53 if (opts->sockdirfd != AT_FDCWD) {
54 close(opts->sockdirfd);
54 close(opts->sockdirfd);
55 opts->sockdirfd = AT_FDCWD;
55 opts->sockdirfd = AT_FDCWD;
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 }
182 }
182 int r = flock(opts->lockfd, LOCK_EX);
183 int r = flock(opts->lockfd, LOCK_EX);
183 if (r == -1)
184 if (r == -1)
184 abortmsgerrno("cannot acquire lock");
185 abortmsgerrno("cannot acquire lock");
185 }
186 }
186
187
187 /*
188 /*
188 * 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
189 * lockcmdserver is not called.
190 * lockcmdserver is not called.
190 */
191 */
191 static void unlockcmdserver(struct cmdserveropts *opts)
192 static void unlockcmdserver(struct cmdserveropts *opts)
192 {
193 {
193 if (opts->lockfd == -1)
194 if (opts->lockfd == -1)
194 return;
195 return;
195 flock(opts->lockfd, LOCK_UN);
196 flock(opts->lockfd, LOCK_UN);
196 close(opts->lockfd);
197 close(opts->lockfd);
197 opts->lockfd = -1;
198 opts->lockfd = -1;
198 }
199 }
199
200
200 static const char *gethgcmd(void)
201 static const char *gethgcmd(void)
201 {
202 {
202 static const char *hgcmd = NULL;
203 static const char *hgcmd = NULL;
203 if (!hgcmd) {
204 if (!hgcmd) {
204 hgcmd = getenv("CHGHG");
205 hgcmd = getenv("CHGHG");
205 if (!hgcmd || hgcmd[0] == '\0')
206 if (!hgcmd || hgcmd[0] == '\0')
206 hgcmd = getenv("HG");
207 hgcmd = getenv("HG");
207 if (!hgcmd || hgcmd[0] == '\0')
208 if (!hgcmd || hgcmd[0] == '\0')
208 #ifdef HGPATH
209 #ifdef HGPATH
209 hgcmd = (HGPATH);
210 hgcmd = (HGPATH);
210 #else
211 #else
211 hgcmd = "hg";
212 hgcmd = "hg";
212 #endif
213 #endif
213 }
214 }
214 return hgcmd;
215 return hgcmd;
215 }
216 }
216
217
217 static void execcmdserver(const struct cmdserveropts *opts)
218 static void execcmdserver(const struct cmdserveropts *opts)
218 {
219 {
219 const char *hgcmd = gethgcmd();
220 const char *hgcmd = gethgcmd();
220
221
221 const char *baseargv[] = {
222 const char *baseargv[] = {
222 hgcmd,
223 hgcmd,
223 "serve",
224 "serve",
224 "--cmdserver", "chgunix",
225 "--cmdserver", "chgunix",
225 "--address", opts->sockname,
226 "--address", opts->sockname,
226 "--daemon-postexec", "chdir:/",
227 "--daemon-postexec", "chdir:/",
227 "--config", "extensions.chgserver=",
228 "--config", "extensions.chgserver=",
228 };
229 };
229 size_t baseargvsize = sizeof(baseargv) / sizeof(baseargv[0]);
230 size_t baseargvsize = sizeof(baseargv) / sizeof(baseargv[0]);
230 size_t argsize = baseargvsize + opts->argsize + 1;
231 size_t argsize = baseargvsize + opts->argsize + 1;
231
232
232 const char **argv = mallocx(sizeof(char *) * argsize);
233 const char **argv = mallocx(sizeof(char *) * argsize);
233 memcpy(argv, baseargv, sizeof(baseargv));
234 memcpy(argv, baseargv, sizeof(baseargv));
234 memcpy(argv + baseargvsize, opts->args, sizeof(char *) * opts->argsize);
235 memcpy(argv + baseargvsize, opts->args, sizeof(char *) * opts->argsize);
235 argv[argsize - 1] = NULL;
236 argv[argsize - 1] = NULL;
236
237
237 if (putenv("CHGINTERNALMARK=") != 0)
238 if (putenv("CHGINTERNALMARK=") != 0)
238 abortmsgerrno("failed to putenv");
239 abortmsgerrno("failed to putenv");
239 if (execvp(hgcmd, (char **)argv) < 0)
240 if (execvp(hgcmd, (char **)argv) < 0)
240 abortmsgerrno("failed to exec cmdserver");
241 abortmsgerrno("failed to exec cmdserver");
241 free(argv);
242 free(argv);
242 }
243 }
243
244
244 /* 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. */
245 static hgclient_t *retryconnectcmdserver(struct cmdserveropts *opts, pid_t pid)
246 static hgclient_t *retryconnectcmdserver(struct cmdserveropts *opts, pid_t pid)
246 {
247 {
247 static const struct timespec sleepreq = {0, 10 * 1000000};
248 static const struct timespec sleepreq = {0, 10 * 1000000};
248 int pst = 0;
249 int pst = 0;
249
250
250 debugmsg("try connect to %s repeatedly", opts->sockname);
251 debugmsg("try connect to %s repeatedly", opts->sockname);
251 for (unsigned int i = 0; i < 10 * 100; i++) {
252 for (unsigned int i = 0; i < 10 * 100; i++) {
252 hgclient_t *hgc = hgc_open(opts->sockname);
253 hgclient_t *hgc = hgc_open(opts->sockname);
253 if (hgc)
254 if (hgc)
254 return hgc;
255 return hgc;
255
256
256 if (pid > 0) {
257 if (pid > 0) {
257 /* collect zombie if child process fails to start */
258 /* collect zombie if child process fails to start */
258 int r = waitpid(pid, &pst, WNOHANG);
259 int r = waitpid(pid, &pst, WNOHANG);
259 if (r != 0)
260 if (r != 0)
260 goto cleanup;
261 goto cleanup;
261 }
262 }
262
263
263 nanosleep(&sleepreq, NULL);
264 nanosleep(&sleepreq, NULL);
264 }
265 }
265
266
266 abortmsg("timed out waiting for cmdserver %s", opts->sockname);
267 abortmsg("timed out waiting for cmdserver %s", opts->sockname);
267 return NULL;
268 return NULL;
268
269
269 cleanup:
270 cleanup:
270 if (WIFEXITED(pst)) {
271 if (WIFEXITED(pst)) {
271 debugmsg("cmdserver exited with status %d", WEXITSTATUS(pst));
272 debugmsg("cmdserver exited with status %d", WEXITSTATUS(pst));
272 exit(WEXITSTATUS(pst));
273 exit(WEXITSTATUS(pst));
273 } else if (WIFSIGNALED(pst)) {
274 } else if (WIFSIGNALED(pst)) {
274 abortmsg("cmdserver killed by signal %d", WTERMSIG(pst));
275 abortmsg("cmdserver killed by signal %d", WTERMSIG(pst));
275 } else {
276 } else {
276 abortmsg("error while waiting for cmdserver");
277 abortmsg("error while waiting for cmdserver");
277 }
278 }
278 return NULL;
279 return NULL;
279 }
280 }
280
281
281 /* Connect to a cmdserver. Will start a new server on demand. */
282 /* Connect to a cmdserver. Will start a new server on demand. */
282 static hgclient_t *connectcmdserver(struct cmdserveropts *opts)
283 static hgclient_t *connectcmdserver(struct cmdserveropts *opts)
283 {
284 {
284 const char *sockname = opts->redirectsockname[0] ?
285 const char *sockname = opts->redirectsockname[0] ?
285 opts->redirectsockname : opts->sockname;
286 opts->redirectsockname : opts->sockname;
286 debugmsg("try connect to %s", sockname);
287 debugmsg("try connect to %s", sockname);
287 hgclient_t *hgc = hgc_open(sockname);
288 hgclient_t *hgc = hgc_open(sockname);
288 if (hgc)
289 if (hgc)
289 return hgc;
290 return hgc;
290
291
291 lockcmdserver(opts);
292 lockcmdserver(opts);
292 hgc = hgc_open(sockname);
293 hgc = hgc_open(sockname);
293 if (hgc) {
294 if (hgc) {
294 unlockcmdserver(opts);
295 unlockcmdserver(opts);
295 debugmsg("cmdserver is started by another process");
296 debugmsg("cmdserver is started by another process");
296 return hgc;
297 return hgc;
297 }
298 }
298
299
299 /* prevent us from being connected to an outdated server: we were
300 /* prevent us from being connected to an outdated server: we were
300 * told by a server to redirect to opts->redirectsockname and that
301 * told by a server to redirect to opts->redirectsockname and that
301 * address does not work. we do not want to connect to the server
302 * address does not work. we do not want to connect to the server
302 * again because it will probably tell us the same thing. */
303 * again because it will probably tell us the same thing. */
303 if (sockname == opts->redirectsockname)
304 if (sockname == opts->redirectsockname)
304 unlink(opts->sockname);
305 unlink(opts->sockname);
305
306
306 debugmsg("start cmdserver at %s", opts->sockname);
307 debugmsg("start cmdserver at %s", opts->sockname);
307
308
308 pid_t pid = fork();
309 pid_t pid = fork();
309 if (pid < 0)
310 if (pid < 0)
310 abortmsg("failed to fork cmdserver process");
311 abortmsg("failed to fork cmdserver process");
311 if (pid == 0) {
312 if (pid == 0) {
312 /* do not leak lockfd to hg */
313 close(opts->lockfd);
314 execcmdserver(opts);
313 execcmdserver(opts);
315 } else {
314 } else {
316 hgc = retryconnectcmdserver(opts, pid);
315 hgc = retryconnectcmdserver(opts, pid);
317 }
316 }
318
317
319 unlockcmdserver(opts);
318 unlockcmdserver(opts);
320 return hgc;
319 return hgc;
321 }
320 }
322
321
323 static void killcmdserver(const struct cmdserveropts *opts)
322 static void killcmdserver(const struct cmdserveropts *opts)
324 {
323 {
325 /* resolve config hash */
324 /* resolve config hash */
326 char *resolvedpath = realpath(opts->sockname, NULL);
325 char *resolvedpath = realpath(opts->sockname, NULL);
327 if (resolvedpath) {
326 if (resolvedpath) {
328 unlink(resolvedpath);
327 unlink(resolvedpath);
329 free(resolvedpath);
328 free(resolvedpath);
330 }
329 }
331 }
330 }
332
331
333 static pid_t peerpid = 0;
332 static pid_t peerpid = 0;
334
333
335 static void forwardsignal(int sig)
334 static void forwardsignal(int sig)
336 {
335 {
337 assert(peerpid > 0);
336 assert(peerpid > 0);
338 if (kill(peerpid, sig) < 0)
337 if (kill(peerpid, sig) < 0)
339 abortmsgerrno("cannot kill %d", peerpid);
338 abortmsgerrno("cannot kill %d", peerpid);
340 debugmsg("forward signal %d", sig);
339 debugmsg("forward signal %d", sig);
341 }
340 }
342
341
343 static void handlestopsignal(int sig)
342 static void handlestopsignal(int sig)
344 {
343 {
345 sigset_t unblockset, oldset;
344 sigset_t unblockset, oldset;
346 struct sigaction sa, oldsa;
345 struct sigaction sa, oldsa;
347 if (sigemptyset(&unblockset) < 0)
346 if (sigemptyset(&unblockset) < 0)
348 goto error;
347 goto error;
349 if (sigaddset(&unblockset, sig) < 0)
348 if (sigaddset(&unblockset, sig) < 0)
350 goto error;
349 goto error;
351 memset(&sa, 0, sizeof(sa));
350 memset(&sa, 0, sizeof(sa));
352 sa.sa_handler = SIG_DFL;
351 sa.sa_handler = SIG_DFL;
353 sa.sa_flags = SA_RESTART;
352 sa.sa_flags = SA_RESTART;
354 if (sigemptyset(&sa.sa_mask) < 0)
353 if (sigemptyset(&sa.sa_mask) < 0)
355 goto error;
354 goto error;
356
355
357 forwardsignal(sig);
356 forwardsignal(sig);
358 if (raise(sig) < 0) /* resend to self */
357 if (raise(sig) < 0) /* resend to self */
359 goto error;
358 goto error;
360 if (sigaction(sig, &sa, &oldsa) < 0)
359 if (sigaction(sig, &sa, &oldsa) < 0)
361 goto error;
360 goto error;
362 if (sigprocmask(SIG_UNBLOCK, &unblockset, &oldset) < 0)
361 if (sigprocmask(SIG_UNBLOCK, &unblockset, &oldset) < 0)
363 goto error;
362 goto error;
364 /* resent signal will be handled before sigprocmask() returns */
363 /* resent signal will be handled before sigprocmask() returns */
365 if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0)
364 if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0)
366 goto error;
365 goto error;
367 if (sigaction(sig, &oldsa, NULL) < 0)
366 if (sigaction(sig, &oldsa, NULL) < 0)
368 goto error;
367 goto error;
369 return;
368 return;
370
369
371 error:
370 error:
372 abortmsgerrno("failed to handle stop signal");
371 abortmsgerrno("failed to handle stop signal");
373 }
372 }
374
373
375 static void setupsignalhandler(pid_t pid)
374 static void setupsignalhandler(pid_t pid)
376 {
375 {
377 if (pid <= 0)
376 if (pid <= 0)
378 return;
377 return;
379 peerpid = pid;
378 peerpid = pid;
380
379
381 struct sigaction sa;
380 struct sigaction sa;
382 memset(&sa, 0, sizeof(sa));
381 memset(&sa, 0, sizeof(sa));
383 sa.sa_handler = forwardsignal;
382 sa.sa_handler = forwardsignal;
384 sa.sa_flags = SA_RESTART;
383 sa.sa_flags = SA_RESTART;
385 if (sigemptyset(&sa.sa_mask) < 0)
384 if (sigemptyset(&sa.sa_mask) < 0)
386 goto error;
385 goto error;
387
386
388 if (sigaction(SIGHUP, &sa, NULL) < 0)
387 if (sigaction(SIGHUP, &sa, NULL) < 0)
389 goto error;
388 goto error;
390 if (sigaction(SIGINT, &sa, NULL) < 0)
389 if (sigaction(SIGINT, &sa, NULL) < 0)
391 goto error;
390 goto error;
392
391
393 /* terminate frontend by double SIGTERM in case of server freeze */
392 /* terminate frontend by double SIGTERM in case of server freeze */
394 sa.sa_flags |= SA_RESETHAND;
393 sa.sa_flags |= SA_RESETHAND;
395 if (sigaction(SIGTERM, &sa, NULL) < 0)
394 if (sigaction(SIGTERM, &sa, NULL) < 0)
396 goto error;
395 goto error;
397
396
398 /* propagate job control requests to worker */
397 /* propagate job control requests to worker */
399 sa.sa_handler = forwardsignal;
398 sa.sa_handler = forwardsignal;
400 sa.sa_flags = SA_RESTART;
399 sa.sa_flags = SA_RESTART;
401 if (sigaction(SIGCONT, &sa, NULL) < 0)
400 if (sigaction(SIGCONT, &sa, NULL) < 0)
402 goto error;
401 goto error;
403 sa.sa_handler = handlestopsignal;
402 sa.sa_handler = handlestopsignal;
404 sa.sa_flags = SA_RESTART;
403 sa.sa_flags = SA_RESTART;
405 if (sigaction(SIGTSTP, &sa, NULL) < 0)
404 if (sigaction(SIGTSTP, &sa, NULL) < 0)
406 goto error;
405 goto error;
407
406
408 return;
407 return;
409
408
410 error:
409 error:
411 abortmsgerrno("failed to set up signal handlers");
410 abortmsgerrno("failed to set up signal handlers");
412 }
411 }
413
412
414 /* This implementation is based on hgext/pager.py (pre 369741ef7253) */
413 /* This implementation is based on hgext/pager.py (pre 369741ef7253) */
415 static void setuppager(hgclient_t *hgc, const char *const args[],
414 static void setuppager(hgclient_t *hgc, const char *const args[],
416 size_t argsize)
415 size_t argsize)
417 {
416 {
418 const char *pagercmd = hgc_getpager(hgc, args, argsize);
417 const char *pagercmd = hgc_getpager(hgc, args, argsize);
419 if (!pagercmd)
418 if (!pagercmd)
420 return;
419 return;
421
420
422 int pipefds[2];
421 int pipefds[2];
423 if (pipe(pipefds) < 0)
422 if (pipe(pipefds) < 0)
424 return;
423 return;
425 pid_t pid = fork();
424 pid_t pid = fork();
426 if (pid < 0)
425 if (pid < 0)
427 goto error;
426 goto error;
428 if (pid == 0) {
427 if (pid == 0) {
429 close(pipefds[0]);
428 close(pipefds[0]);
430 if (dup2(pipefds[1], fileno(stdout)) < 0)
429 if (dup2(pipefds[1], fileno(stdout)) < 0)
431 goto error;
430 goto error;
432 if (isatty(fileno(stderr))) {
431 if (isatty(fileno(stderr))) {
433 if (dup2(pipefds[1], fileno(stderr)) < 0)
432 if (dup2(pipefds[1], fileno(stderr)) < 0)
434 goto error;
433 goto error;
435 }
434 }
436 close(pipefds[1]);
435 close(pipefds[1]);
437 hgc_attachio(hgc); /* reattach to pager */
436 hgc_attachio(hgc); /* reattach to pager */
438 return;
437 return;
439 } else {
438 } else {
440 dup2(pipefds[0], fileno(stdin));
439 dup2(pipefds[0], fileno(stdin));
441 close(pipefds[0]);
440 close(pipefds[0]);
442 close(pipefds[1]);
441 close(pipefds[1]);
443
442
444 int r = execlp("/bin/sh", "/bin/sh", "-c", pagercmd, NULL);
443 int r = execlp("/bin/sh", "/bin/sh", "-c", pagercmd, NULL);
445 if (r < 0) {
444 if (r < 0) {
446 abortmsgerrno("cannot start pager '%s'", pagercmd);
445 abortmsgerrno("cannot start pager '%s'", pagercmd);
447 }
446 }
448 return;
447 return;
449 }
448 }
450
449
451 error:
450 error:
452 close(pipefds[0]);
451 close(pipefds[0]);
453 close(pipefds[1]);
452 close(pipefds[1]);
454 abortmsgerrno("failed to prepare pager");
453 abortmsgerrno("failed to prepare pager");
455 }
454 }
456
455
457 /* Run instructions sent from the server like unlink and set redirect path
456 /* Run instructions sent from the server like unlink and set redirect path
458 * Return 1 if reconnect is needed, otherwise 0 */
457 * Return 1 if reconnect is needed, otherwise 0 */
459 static int runinstructions(struct cmdserveropts *opts, const char **insts)
458 static int runinstructions(struct cmdserveropts *opts, const char **insts)
460 {
459 {
461 int needreconnect = 0;
460 int needreconnect = 0;
462 if (!insts)
461 if (!insts)
463 return needreconnect;
462 return needreconnect;
464
463
465 assert(insts);
464 assert(insts);
466 opts->redirectsockname[0] = '\0';
465 opts->redirectsockname[0] = '\0';
467 const char **pinst;
466 const char **pinst;
468 for (pinst = insts; *pinst; pinst++) {
467 for (pinst = insts; *pinst; pinst++) {
469 debugmsg("instruction: %s", *pinst);
468 debugmsg("instruction: %s", *pinst);
470 if (strncmp(*pinst, "unlink ", 7) == 0) {
469 if (strncmp(*pinst, "unlink ", 7) == 0) {
471 unlink(*pinst + 7);
470 unlink(*pinst + 7);
472 } else if (strncmp(*pinst, "redirect ", 9) == 0) {
471 } else if (strncmp(*pinst, "redirect ", 9) == 0) {
473 int r = snprintf(opts->redirectsockname,
472 int r = snprintf(opts->redirectsockname,
474 sizeof(opts->redirectsockname),
473 sizeof(opts->redirectsockname),
475 "%s", *pinst + 9);
474 "%s", *pinst + 9);
476 if (r < 0 || r >= (int)sizeof(opts->redirectsockname))
475 if (r < 0 || r >= (int)sizeof(opts->redirectsockname))
477 abortmsg("redirect path is too long (%d)", r);
476 abortmsg("redirect path is too long (%d)", r);
478 needreconnect = 1;
477 needreconnect = 1;
479 } else if (strncmp(*pinst, "exit ", 5) == 0) {
478 } else if (strncmp(*pinst, "exit ", 5) == 0) {
480 int n = 0;
479 int n = 0;
481 if (sscanf(*pinst + 5, "%d", &n) != 1)
480 if (sscanf(*pinst + 5, "%d", &n) != 1)
482 abortmsg("cannot read the exit code");
481 abortmsg("cannot read the exit code");
483 exit(n);
482 exit(n);
484 } else if (strcmp(*pinst, "reconnect") == 0) {
483 } else if (strcmp(*pinst, "reconnect") == 0) {
485 needreconnect = 1;
484 needreconnect = 1;
486 } else {
485 } else {
487 abortmsg("unknown instruction: %s", *pinst);
486 abortmsg("unknown instruction: %s", *pinst);
488 }
487 }
489 }
488 }
490 return needreconnect;
489 return needreconnect;
491 }
490 }
492
491
493 /*
492 /*
494 * Test whether the command is unsupported or not. This is not designed to
493 * Test whether the command is unsupported or not. This is not designed to
495 * cover all cases. But it's fast, does not depend on the server and does
494 * cover all cases. But it's fast, does not depend on the server and does
496 * not return false positives.
495 * not return false positives.
497 */
496 */
498 static int isunsupported(int argc, const char *argv[])
497 static int isunsupported(int argc, const char *argv[])
499 {
498 {
500 enum {
499 enum {
501 SERVE = 1,
500 SERVE = 1,
502 DAEMON = 2,
501 DAEMON = 2,
503 SERVEDAEMON = SERVE | DAEMON,
502 SERVEDAEMON = SERVE | DAEMON,
504 TIME = 4,
503 TIME = 4,
505 };
504 };
506 unsigned int state = 0;
505 unsigned int state = 0;
507 int i;
506 int i;
508 for (i = 0; i < argc; ++i) {
507 for (i = 0; i < argc; ++i) {
509 if (strcmp(argv[i], "--") == 0)
508 if (strcmp(argv[i], "--") == 0)
510 break;
509 break;
511 if (i == 0 && strcmp("serve", argv[i]) == 0)
510 if (i == 0 && strcmp("serve", argv[i]) == 0)
512 state |= SERVE;
511 state |= SERVE;
513 else if (strcmp("-d", argv[i]) == 0 ||
512 else if (strcmp("-d", argv[i]) == 0 ||
514 strcmp("--daemon", argv[i]) == 0)
513 strcmp("--daemon", argv[i]) == 0)
515 state |= DAEMON;
514 state |= DAEMON;
516 else if (strcmp("--time", argv[i]) == 0)
515 else if (strcmp("--time", argv[i]) == 0)
517 state |= TIME;
516 state |= TIME;
518 }
517 }
519 return (state & TIME) == TIME ||
518 return (state & TIME) == TIME ||
520 (state & SERVEDAEMON) == SERVEDAEMON;
519 (state & SERVEDAEMON) == SERVEDAEMON;
521 }
520 }
522
521
523 static void execoriginalhg(const char *argv[])
522 static void execoriginalhg(const char *argv[])
524 {
523 {
525 debugmsg("execute original hg");
524 debugmsg("execute original hg");
526 if (execvp(gethgcmd(), (char **)argv) < 0)
525 if (execvp(gethgcmd(), (char **)argv) < 0)
527 abortmsgerrno("failed to exec original hg");
526 abortmsgerrno("failed to exec original hg");
528 }
527 }
529
528
530 int main(int argc, const char *argv[], const char *envp[])
529 int main(int argc, const char *argv[], const char *envp[])
531 {
530 {
532 if (getenv("CHGDEBUG"))
531 if (getenv("CHGDEBUG"))
533 enabledebugmsg();
532 enabledebugmsg();
534
533
535 if (!getenv("HGPLAIN") && isatty(fileno(stderr)))
534 if (!getenv("HGPLAIN") && isatty(fileno(stderr)))
536 enablecolor();
535 enablecolor();
537
536
538 if (getenv("CHGINTERNALMARK"))
537 if (getenv("CHGINTERNALMARK"))
539 abortmsg("chg started by chg detected.\n"
538 abortmsg("chg started by chg detected.\n"
540 "Please make sure ${HG:-hg} is not a symlink or "
539 "Please make sure ${HG:-hg} is not a symlink or "
541 "wrapper to chg. Alternatively, set $CHGHG to the "
540 "wrapper to chg. Alternatively, set $CHGHG to the "
542 "path of real hg.");
541 "path of real hg.");
543
542
544 if (isunsupported(argc - 1, argv + 1))
543 if (isunsupported(argc - 1, argv + 1))
545 execoriginalhg(argv);
544 execoriginalhg(argv);
546
545
547 struct cmdserveropts opts;
546 struct cmdserveropts opts;
548 initcmdserveropts(&opts);
547 initcmdserveropts(&opts);
549 setcmdserveropts(&opts);
548 setcmdserveropts(&opts);
550 setcmdserverargs(&opts, argc, argv);
549 setcmdserverargs(&opts, argc, argv);
551
550
552 if (argc == 2) {
551 if (argc == 2) {
553 if (strcmp(argv[1], "--kill-chg-daemon") == 0) {
552 if (strcmp(argv[1], "--kill-chg-daemon") == 0) {
554 killcmdserver(&opts);
553 killcmdserver(&opts);
555 return 0;
554 return 0;
556 }
555 }
557 }
556 }
558
557
559 hgclient_t *hgc;
558 hgclient_t *hgc;
560 size_t retry = 0;
559 size_t retry = 0;
561 while (1) {
560 while (1) {
562 hgc = connectcmdserver(&opts);
561 hgc = connectcmdserver(&opts);
563 if (!hgc)
562 if (!hgc)
564 abortmsg("cannot open hg client");
563 abortmsg("cannot open hg client");
565 hgc_setenv(hgc, envp);
564 hgc_setenv(hgc, envp);
566 const char **insts = hgc_validate(hgc, argv + 1, argc - 1);
565 const char **insts = hgc_validate(hgc, argv + 1, argc - 1);
567 int needreconnect = runinstructions(&opts, insts);
566 int needreconnect = runinstructions(&opts, insts);
568 free(insts);
567 free(insts);
569 if (!needreconnect)
568 if (!needreconnect)
570 break;
569 break;
571 hgc_close(hgc);
570 hgc_close(hgc);
572 if (++retry > 10)
571 if (++retry > 10)
573 abortmsg("too many redirections.\n"
572 abortmsg("too many redirections.\n"
574 "Please make sure %s is not a wrapper which "
573 "Please make sure %s is not a wrapper which "
575 "changes sensitive environment variables "
574 "changes sensitive environment variables "
576 "before executing hg. If you have to use a "
575 "before executing hg. If you have to use a "
577 "wrapper, wrap chg instead of hg.",
576 "wrapper, wrap chg instead of hg.",
578 gethgcmd());
577 gethgcmd());
579 }
578 }
580
579
581 setupsignalhandler(hgc_peerpid(hgc));
580 setupsignalhandler(hgc_peerpid(hgc));
582 setuppager(hgc, argv + 1, argc - 1);
581 setuppager(hgc, argv + 1, argc - 1);
583 int exitcode = hgc_runcommand(hgc, argv + 1, argc - 1);
582 int exitcode = hgc_runcommand(hgc, argv + 1, argc - 1);
584 hgc_close(hgc);
583 hgc_close(hgc);
585 freecmdserveropts(&opts);
584 freecmdserveropts(&opts);
586 return exitcode;
585 return exitcode;
587 }
586 }
General Comments 0
You need to be logged in to leave comments. Login now