##// END OF EJS Templates
chg: extract gethgcmd logic to a function...
Jun Wu -
r28237:a3d73e06 default
parent child Browse files
Show More
@@ -1,475 +1,484
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 lockfile[UNIX_PATH_MAX];
34 char lockfile[UNIX_PATH_MAX];
35 char pidfile[UNIX_PATH_MAX];
35 char pidfile[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') { /* --flag (value) */
76 return narg + 1;
76 return narg + 1;
77 } else if (arg[len] == '=' && narg > 0) { /* --flag=value */
77 } else if (arg[len] == '=' && narg > 0) { /* --flag=value */
78 return 1;
78 return 1;
79 } else if (flags[i].name[1] != '-') { /* short flag */
79 } else if (flags[i].name[1] != '-') { /* short flag */
80 return 1;
80 return 1;
81 }
81 }
82 }
82 }
83 }
83 }
84 return 0;
84 return 0;
85 }
85 }
86
86
87 /*
87 /*
88 * Parse argv[] and put sensitive flags to opts->args
88 * Parse argv[] and put sensitive flags to opts->args
89 */
89 */
90 static void setcmdserverargs(struct cmdserveropts *opts,
90 static void setcmdserverargs(struct cmdserveropts *opts,
91 int argc, const char *argv[])
91 int argc, const char *argv[])
92 {
92 {
93 size_t i, step;
93 size_t i, step;
94 opts->argsize = 0;
94 opts->argsize = 0;
95 for (i = 0, step = 1; i < (size_t)argc; i += step, step = 1) {
95 for (i = 0, step = 1; i < (size_t)argc; i += step, step = 1) {
96 if (!argv[i])
96 if (!argv[i])
97 continue; /* pass clang-analyse */
97 continue; /* pass clang-analyse */
98 if (strcmp(argv[i], "--") == 0)
98 if (strcmp(argv[i], "--") == 0)
99 break;
99 break;
100 size_t n = testsensitiveflag(argv[i]);
100 size_t n = testsensitiveflag(argv[i]);
101 if (n == 0 || i + n > (size_t)argc)
101 if (n == 0 || i + n > (size_t)argc)
102 continue;
102 continue;
103 opts->args = reallocx(opts->args,
103 opts->args = reallocx(opts->args,
104 (n + opts->argsize) * sizeof(char *));
104 (n + opts->argsize) * sizeof(char *));
105 memcpy(opts->args + opts->argsize, argv + i,
105 memcpy(opts->args + opts->argsize, argv + i,
106 sizeof(char *) * n);
106 sizeof(char *) * n);
107 opts->argsize += n;
107 opts->argsize += n;
108 step = n;
108 step = n;
109 }
109 }
110 }
110 }
111
111
112 static void preparesockdir(const char *sockdir)
112 static void preparesockdir(const char *sockdir)
113 {
113 {
114 int r;
114 int r;
115 r = mkdir(sockdir, 0700);
115 r = mkdir(sockdir, 0700);
116 if (r < 0 && errno != EEXIST)
116 if (r < 0 && errno != EEXIST)
117 abortmsg("cannot create sockdir %s (errno = %d)",
117 abortmsg("cannot create sockdir %s (errno = %d)",
118 sockdir, errno);
118 sockdir, errno);
119
119
120 struct stat st;
120 struct stat st;
121 r = lstat(sockdir, &st);
121 r = lstat(sockdir, &st);
122 if (r < 0)
122 if (r < 0)
123 abortmsg("cannot stat %s (errno = %d)", sockdir, errno);
123 abortmsg("cannot stat %s (errno = %d)", sockdir, errno);
124 if (!S_ISDIR(st.st_mode))
124 if (!S_ISDIR(st.st_mode))
125 abortmsg("cannot create sockdir %s (file exists)", sockdir);
125 abortmsg("cannot create sockdir %s (file exists)", sockdir);
126 if (st.st_uid != geteuid() || st.st_mode & 0077)
126 if (st.st_uid != geteuid() || st.st_mode & 0077)
127 abortmsg("insecure sockdir %s", sockdir);
127 abortmsg("insecure sockdir %s", sockdir);
128 }
128 }
129
129
130 static void setcmdserveropts(struct cmdserveropts *opts)
130 static void setcmdserveropts(struct cmdserveropts *opts)
131 {
131 {
132 int r;
132 int r;
133 char sockdir[UNIX_PATH_MAX];
133 char sockdir[UNIX_PATH_MAX];
134 const char *envsockname = getenv("CHGSOCKNAME");
134 const char *envsockname = getenv("CHGSOCKNAME");
135 if (!envsockname) {
135 if (!envsockname) {
136 /* by default, put socket file in secure directory
136 /* by default, put socket file in secure directory
137 * (permission of socket file may be ignored on some Unices) */
137 * (permission of socket file may be ignored on some Unices) */
138 const char *tmpdir = getenv("TMPDIR");
138 const char *tmpdir = getenv("TMPDIR");
139 if (!tmpdir)
139 if (!tmpdir)
140 tmpdir = "/tmp";
140 tmpdir = "/tmp";
141 r = snprintf(sockdir, sizeof(sockdir), "%s/chg%d",
141 r = snprintf(sockdir, sizeof(sockdir), "%s/chg%d",
142 tmpdir, geteuid());
142 tmpdir, geteuid());
143 if (r < 0 || (size_t)r >= sizeof(sockdir))
143 if (r < 0 || (size_t)r >= sizeof(sockdir))
144 abortmsg("too long TMPDIR (r = %d)", r);
144 abortmsg("too long TMPDIR (r = %d)", r);
145 preparesockdir(sockdir);
145 preparesockdir(sockdir);
146 }
146 }
147
147
148 const char *basename = (envsockname) ? envsockname : sockdir;
148 const char *basename = (envsockname) ? envsockname : sockdir;
149 const char *sockfmt = (envsockname) ? "%s" : "%s/server";
149 const char *sockfmt = (envsockname) ? "%s" : "%s/server";
150 const char *lockfmt = (envsockname) ? "%s.lock" : "%s/lock";
150 const char *lockfmt = (envsockname) ? "%s.lock" : "%s/lock";
151 const char *pidfmt = (envsockname) ? "%s.pid" : "%s/pid";
151 const char *pidfmt = (envsockname) ? "%s.pid" : "%s/pid";
152 r = snprintf(opts->sockname, sizeof(opts->sockname), sockfmt, basename);
152 r = snprintf(opts->sockname, sizeof(opts->sockname), sockfmt, basename);
153 if (r < 0 || (size_t)r >= sizeof(opts->sockname))
153 if (r < 0 || (size_t)r >= sizeof(opts->sockname))
154 abortmsg("too long TMPDIR or CHGSOCKNAME (r = %d)", r);
154 abortmsg("too long TMPDIR or CHGSOCKNAME (r = %d)", r);
155 r = snprintf(opts->lockfile, sizeof(opts->lockfile), lockfmt, basename);
155 r = snprintf(opts->lockfile, sizeof(opts->lockfile), lockfmt, basename);
156 if (r < 0 || (size_t)r >= sizeof(opts->lockfile))
156 if (r < 0 || (size_t)r >= sizeof(opts->lockfile))
157 abortmsg("too long TMPDIR or CHGSOCKNAME (r = %d)", r);
157 abortmsg("too long TMPDIR or CHGSOCKNAME (r = %d)", r);
158 r = snprintf(opts->pidfile, sizeof(opts->pidfile), pidfmt, basename);
158 r = snprintf(opts->pidfile, sizeof(opts->pidfile), pidfmt, basename);
159 if (r < 0 || (size_t)r >= sizeof(opts->pidfile))
159 if (r < 0 || (size_t)r >= sizeof(opts->pidfile))
160 abortmsg("too long TMPDIR or CHGSOCKNAME (r = %d)", r);
160 abortmsg("too long TMPDIR or CHGSOCKNAME (r = %d)", r);
161 }
161 }
162
162
163 /*
163 /*
164 * Acquire a file lock that indicates a client is trying to start and connect
164 * Acquire a file lock that indicates a client is trying to start and connect
165 * to a server, before executing a command. The lock is released upon exit or
165 * to a server, before executing a command. The lock is released upon exit or
166 * explicit unlock. Will block if the lock is held by another process.
166 * explicit unlock. Will block if the lock is held by another process.
167 */
167 */
168 static void lockcmdserver(struct cmdserveropts *opts)
168 static void lockcmdserver(struct cmdserveropts *opts)
169 {
169 {
170 if (opts->lockfd == -1) {
170 if (opts->lockfd == -1) {
171 opts->lockfd = open(opts->lockfile, O_RDWR | O_CREAT | O_NOFOLLOW, 0600);
171 opts->lockfd = open(opts->lockfile, O_RDWR | O_CREAT | O_NOFOLLOW, 0600);
172 if (opts->lockfd == -1)
172 if (opts->lockfd == -1)
173 abortmsg("cannot create lock file %s", opts->lockfile);
173 abortmsg("cannot create lock file %s", opts->lockfile);
174 }
174 }
175 int r = flock(opts->lockfd, LOCK_EX);
175 int r = flock(opts->lockfd, LOCK_EX);
176 if (r == -1)
176 if (r == -1)
177 abortmsg("cannot acquire lock");
177 abortmsg("cannot acquire lock");
178 }
178 }
179
179
180 /*
180 /*
181 * 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
182 * lockcmdserver is not called.
182 * lockcmdserver is not called.
183 */
183 */
184 static void unlockcmdserver(struct cmdserveropts *opts)
184 static void unlockcmdserver(struct cmdserveropts *opts)
185 {
185 {
186 if (opts->lockfd == -1)
186 if (opts->lockfd == -1)
187 return;
187 return;
188 flock(opts->lockfd, LOCK_UN);
188 flock(opts->lockfd, LOCK_UN);
189 close(opts->lockfd);
189 close(opts->lockfd);
190 opts->lockfd = -1;
190 opts->lockfd = -1;
191 }
191 }
192
192
193 static const char *gethgcmd(void)
194 {
195 static const char *hgcmd = NULL;
196 if (!hgcmd) {
197 hgcmd = getenv("CHGHG");
198 if (!hgcmd || hgcmd[0] == '\0')
199 hgcmd = getenv("HG");
200 if (!hgcmd || hgcmd[0] == '\0')
201 hgcmd = "hg";
202 }
203 return hgcmd;
204 }
205
193 static void execcmdserver(const struct cmdserveropts *opts)
206 static void execcmdserver(const struct cmdserveropts *opts)
194 {
207 {
195 const char *hgcmd = getenv("CHGHG");
208 const char *hgcmd = gethgcmd();
196 if (!hgcmd || hgcmd[0] == '\0')
197 hgcmd = getenv("HG");
198 if (!hgcmd || hgcmd[0] == '\0')
199 hgcmd = "hg";
200
209
201 const char *baseargv[] = {
210 const char *baseargv[] = {
202 hgcmd,
211 hgcmd,
203 "serve",
212 "serve",
204 "--cwd", "/",
213 "--cwd", "/",
205 "--cmdserver", "chgunix",
214 "--cmdserver", "chgunix",
206 "--address", opts->sockname,
215 "--address", opts->sockname,
207 "--daemon-postexec", "none",
216 "--daemon-postexec", "none",
208 "--pid-file", opts->pidfile,
217 "--pid-file", opts->pidfile,
209 "--config", "extensions.chgserver=",
218 "--config", "extensions.chgserver=",
210 /* wrap root ui so that it can be disabled/enabled by config */
219 /* wrap root ui so that it can be disabled/enabled by config */
211 "--config", "progress.assume-tty=1",
220 "--config", "progress.assume-tty=1",
212 };
221 };
213 size_t baseargvsize = sizeof(baseargv) / sizeof(baseargv[0]);
222 size_t baseargvsize = sizeof(baseargv) / sizeof(baseargv[0]);
214 size_t argsize = baseargvsize + opts->argsize + 1;
223 size_t argsize = baseargvsize + opts->argsize + 1;
215
224
216 const char **argv = mallocx(sizeof(char *) * argsize);
225 const char **argv = mallocx(sizeof(char *) * argsize);
217 memcpy(argv, baseargv, sizeof(baseargv));
226 memcpy(argv, baseargv, sizeof(baseargv));
218 memcpy(argv + baseargvsize, opts->args, sizeof(char *) * opts->argsize);
227 memcpy(argv + baseargvsize, opts->args, sizeof(char *) * opts->argsize);
219 argv[argsize - 1] = NULL;
228 argv[argsize - 1] = NULL;
220
229
221 if (execvp(hgcmd, (char **)argv) < 0)
230 if (execvp(hgcmd, (char **)argv) < 0)
222 abortmsg("failed to exec cmdserver (errno = %d)", errno);
231 abortmsg("failed to exec cmdserver (errno = %d)", errno);
223 free(argv);
232 free(argv);
224 }
233 }
225
234
226 /* 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. */
227 static hgclient_t *retryconnectcmdserver(struct cmdserveropts *opts, pid_t pid)
236 static hgclient_t *retryconnectcmdserver(struct cmdserveropts *opts, pid_t pid)
228 {
237 {
229 static const struct timespec sleepreq = {0, 10 * 1000000};
238 static const struct timespec sleepreq = {0, 10 * 1000000};
230 int pst = 0;
239 int pst = 0;
231
240
232 for (unsigned int i = 0; i < 10 * 100; i++) {
241 for (unsigned int i = 0; i < 10 * 100; i++) {
233 hgclient_t *hgc = hgc_open(opts->sockname);
242 hgclient_t *hgc = hgc_open(opts->sockname);
234 if (hgc)
243 if (hgc)
235 return hgc;
244 return hgc;
236
245
237 if (pid > 0) {
246 if (pid > 0) {
238 /* collect zombie if child process fails to start */
247 /* collect zombie if child process fails to start */
239 int r = waitpid(pid, &pst, WNOHANG);
248 int r = waitpid(pid, &pst, WNOHANG);
240 if (r != 0)
249 if (r != 0)
241 goto cleanup;
250 goto cleanup;
242 }
251 }
243
252
244 nanosleep(&sleepreq, NULL);
253 nanosleep(&sleepreq, NULL);
245 }
254 }
246
255
247 abortmsg("timed out waiting for cmdserver %s", opts->sockname);
256 abortmsg("timed out waiting for cmdserver %s", opts->sockname);
248 return NULL;
257 return NULL;
249
258
250 cleanup:
259 cleanup:
251 if (WIFEXITED(pst)) {
260 if (WIFEXITED(pst)) {
252 abortmsg("cmdserver exited with status %d", WEXITSTATUS(pst));
261 abortmsg("cmdserver exited with status %d", WEXITSTATUS(pst));
253 } else if (WIFSIGNALED(pst)) {
262 } else if (WIFSIGNALED(pst)) {
254 abortmsg("cmdserver killed by signal %d", WTERMSIG(pst));
263 abortmsg("cmdserver killed by signal %d", WTERMSIG(pst));
255 } else {
264 } else {
256 abortmsg("error white waiting cmdserver");
265 abortmsg("error white waiting cmdserver");
257 }
266 }
258 return NULL;
267 return NULL;
259 }
268 }
260
269
261 /* Connect to a cmdserver. Will start a new server on demand. */
270 /* Connect to a cmdserver. Will start a new server on demand. */
262 static hgclient_t *connectcmdserver(struct cmdserveropts *opts)
271 static hgclient_t *connectcmdserver(struct cmdserveropts *opts)
263 {
272 {
264 hgclient_t *hgc = hgc_open(opts->sockname);
273 hgclient_t *hgc = hgc_open(opts->sockname);
265 if (hgc)
274 if (hgc)
266 return hgc;
275 return hgc;
267
276
268 lockcmdserver(opts);
277 lockcmdserver(opts);
269 hgc = hgc_open(opts->sockname);
278 hgc = hgc_open(opts->sockname);
270 if (hgc) {
279 if (hgc) {
271 unlockcmdserver(opts);
280 unlockcmdserver(opts);
272 debugmsg("cmdserver is started by another process");
281 debugmsg("cmdserver is started by another process");
273 return hgc;
282 return hgc;
274 }
283 }
275
284
276 debugmsg("start cmdserver at %s", opts->sockname);
285 debugmsg("start cmdserver at %s", opts->sockname);
277
286
278 pid_t pid = fork();
287 pid_t pid = fork();
279 if (pid < 0)
288 if (pid < 0)
280 abortmsg("failed to fork cmdserver process");
289 abortmsg("failed to fork cmdserver process");
281 if (pid == 0) {
290 if (pid == 0) {
282 /* do not leak lockfd to hg */
291 /* do not leak lockfd to hg */
283 close(opts->lockfd);
292 close(opts->lockfd);
284 /* bypass uisetup() of pager extension */
293 /* bypass uisetup() of pager extension */
285 int nullfd = open("/dev/null", O_WRONLY);
294 int nullfd = open("/dev/null", O_WRONLY);
286 if (nullfd >= 0) {
295 if (nullfd >= 0) {
287 dup2(nullfd, fileno(stdout));
296 dup2(nullfd, fileno(stdout));
288 close(nullfd);
297 close(nullfd);
289 }
298 }
290 execcmdserver(opts);
299 execcmdserver(opts);
291 } else {
300 } else {
292 hgc = retryconnectcmdserver(opts, pid);
301 hgc = retryconnectcmdserver(opts, pid);
293 }
302 }
294
303
295 unlockcmdserver(opts);
304 unlockcmdserver(opts);
296 return hgc;
305 return hgc;
297 }
306 }
298
307
299 static void killcmdserver(const struct cmdserveropts *opts, int sig)
308 static void killcmdserver(const struct cmdserveropts *opts, int sig)
300 {
309 {
301 FILE *fp = fopen(opts->pidfile, "r");
310 FILE *fp = fopen(opts->pidfile, "r");
302 if (!fp)
311 if (!fp)
303 abortmsg("cannot open %s (errno = %d)", opts->pidfile, errno);
312 abortmsg("cannot open %s (errno = %d)", opts->pidfile, errno);
304 int pid = 0;
313 int pid = 0;
305 int n = fscanf(fp, "%d", &pid);
314 int n = fscanf(fp, "%d", &pid);
306 fclose(fp);
315 fclose(fp);
307 if (n != 1 || pid <= 0)
316 if (n != 1 || pid <= 0)
308 abortmsg("cannot read pid from %s", opts->pidfile);
317 abortmsg("cannot read pid from %s", opts->pidfile);
309
318
310 if (kill((pid_t)pid, sig) < 0) {
319 if (kill((pid_t)pid, sig) < 0) {
311 if (errno == ESRCH)
320 if (errno == ESRCH)
312 return;
321 return;
313 abortmsg("cannot kill %d (errno = %d)", pid, errno);
322 abortmsg("cannot kill %d (errno = %d)", pid, errno);
314 }
323 }
315 }
324 }
316
325
317 static pid_t peerpid = 0;
326 static pid_t peerpid = 0;
318
327
319 static void forwardsignal(int sig)
328 static void forwardsignal(int sig)
320 {
329 {
321 assert(peerpid > 0);
330 assert(peerpid > 0);
322 if (kill(peerpid, sig) < 0)
331 if (kill(peerpid, sig) < 0)
323 abortmsg("cannot kill %d (errno = %d)", peerpid, errno);
332 abortmsg("cannot kill %d (errno = %d)", peerpid, errno);
324 debugmsg("forward signal %d", sig);
333 debugmsg("forward signal %d", sig);
325 }
334 }
326
335
327 static void handlestopsignal(int sig)
336 static void handlestopsignal(int sig)
328 {
337 {
329 sigset_t unblockset, oldset;
338 sigset_t unblockset, oldset;
330 struct sigaction sa, oldsa;
339 struct sigaction sa, oldsa;
331 if (sigemptyset(&unblockset) < 0)
340 if (sigemptyset(&unblockset) < 0)
332 goto error;
341 goto error;
333 if (sigaddset(&unblockset, sig) < 0)
342 if (sigaddset(&unblockset, sig) < 0)
334 goto error;
343 goto error;
335 memset(&sa, 0, sizeof(sa));
344 memset(&sa, 0, sizeof(sa));
336 sa.sa_handler = SIG_DFL;
345 sa.sa_handler = SIG_DFL;
337 sa.sa_flags = SA_RESTART;
346 sa.sa_flags = SA_RESTART;
338 if (sigemptyset(&sa.sa_mask) < 0)
347 if (sigemptyset(&sa.sa_mask) < 0)
339 goto error;
348 goto error;
340
349
341 forwardsignal(sig);
350 forwardsignal(sig);
342 if (raise(sig) < 0) /* resend to self */
351 if (raise(sig) < 0) /* resend to self */
343 goto error;
352 goto error;
344 if (sigaction(sig, &sa, &oldsa) < 0)
353 if (sigaction(sig, &sa, &oldsa) < 0)
345 goto error;
354 goto error;
346 if (sigprocmask(SIG_UNBLOCK, &unblockset, &oldset) < 0)
355 if (sigprocmask(SIG_UNBLOCK, &unblockset, &oldset) < 0)
347 goto error;
356 goto error;
348 /* resent signal will be handled before sigprocmask() returns */
357 /* resent signal will be handled before sigprocmask() returns */
349 if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0)
358 if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0)
350 goto error;
359 goto error;
351 if (sigaction(sig, &oldsa, NULL) < 0)
360 if (sigaction(sig, &oldsa, NULL) < 0)
352 goto error;
361 goto error;
353 return;
362 return;
354
363
355 error:
364 error:
356 abortmsg("failed to handle stop signal (errno = %d)", errno);
365 abortmsg("failed to handle stop signal (errno = %d)", errno);
357 }
366 }
358
367
359 static void setupsignalhandler(pid_t pid)
368 static void setupsignalhandler(pid_t pid)
360 {
369 {
361 if (pid <= 0)
370 if (pid <= 0)
362 return;
371 return;
363 peerpid = pid;
372 peerpid = pid;
364
373
365 struct sigaction sa;
374 struct sigaction sa;
366 memset(&sa, 0, sizeof(sa));
375 memset(&sa, 0, sizeof(sa));
367 sa.sa_handler = forwardsignal;
376 sa.sa_handler = forwardsignal;
368 sa.sa_flags = SA_RESTART;
377 sa.sa_flags = SA_RESTART;
369 if (sigemptyset(&sa.sa_mask) < 0)
378 if (sigemptyset(&sa.sa_mask) < 0)
370 goto error;
379 goto error;
371
380
372 if (sigaction(SIGHUP, &sa, NULL) < 0)
381 if (sigaction(SIGHUP, &sa, NULL) < 0)
373 goto error;
382 goto error;
374 if (sigaction(SIGINT, &sa, NULL) < 0)
383 if (sigaction(SIGINT, &sa, NULL) < 0)
375 goto error;
384 goto error;
376
385
377 /* terminate frontend by double SIGTERM in case of server freeze */
386 /* terminate frontend by double SIGTERM in case of server freeze */
378 sa.sa_flags |= SA_RESETHAND;
387 sa.sa_flags |= SA_RESETHAND;
379 if (sigaction(SIGTERM, &sa, NULL) < 0)
388 if (sigaction(SIGTERM, &sa, NULL) < 0)
380 goto error;
389 goto error;
381
390
382 /* propagate job control requests to worker */
391 /* propagate job control requests to worker */
383 sa.sa_handler = forwardsignal;
392 sa.sa_handler = forwardsignal;
384 sa.sa_flags = SA_RESTART;
393 sa.sa_flags = SA_RESTART;
385 if (sigaction(SIGCONT, &sa, NULL) < 0)
394 if (sigaction(SIGCONT, &sa, NULL) < 0)
386 goto error;
395 goto error;
387 sa.sa_handler = handlestopsignal;
396 sa.sa_handler = handlestopsignal;
388 sa.sa_flags = SA_RESTART;
397 sa.sa_flags = SA_RESTART;
389 if (sigaction(SIGTSTP, &sa, NULL) < 0)
398 if (sigaction(SIGTSTP, &sa, NULL) < 0)
390 goto error;
399 goto error;
391
400
392 return;
401 return;
393
402
394 error:
403 error:
395 abortmsg("failed to set up signal handlers (errno = %d)", errno);
404 abortmsg("failed to set up signal handlers (errno = %d)", errno);
396 }
405 }
397
406
398 /* This implementation is based on hgext/pager.py (pre 369741ef7253) */
407 /* This implementation is based on hgext/pager.py (pre 369741ef7253) */
399 static void setuppager(hgclient_t *hgc, const char *const args[],
408 static void setuppager(hgclient_t *hgc, const char *const args[],
400 size_t argsize)
409 size_t argsize)
401 {
410 {
402 const char *pagercmd = hgc_getpager(hgc, args, argsize);
411 const char *pagercmd = hgc_getpager(hgc, args, argsize);
403 if (!pagercmd)
412 if (!pagercmd)
404 return;
413 return;
405
414
406 int pipefds[2];
415 int pipefds[2];
407 if (pipe(pipefds) < 0)
416 if (pipe(pipefds) < 0)
408 return;
417 return;
409 pid_t pid = fork();
418 pid_t pid = fork();
410 if (pid < 0)
419 if (pid < 0)
411 goto error;
420 goto error;
412 if (pid == 0) {
421 if (pid == 0) {
413 close(pipefds[0]);
422 close(pipefds[0]);
414 if (dup2(pipefds[1], fileno(stdout)) < 0)
423 if (dup2(pipefds[1], fileno(stdout)) < 0)
415 goto error;
424 goto error;
416 if (isatty(fileno(stderr))) {
425 if (isatty(fileno(stderr))) {
417 if (dup2(pipefds[1], fileno(stderr)) < 0)
426 if (dup2(pipefds[1], fileno(stderr)) < 0)
418 goto error;
427 goto error;
419 }
428 }
420 close(pipefds[1]);
429 close(pipefds[1]);
421 hgc_attachio(hgc); /* reattach to pager */
430 hgc_attachio(hgc); /* reattach to pager */
422 return;
431 return;
423 } else {
432 } else {
424 dup2(pipefds[0], fileno(stdin));
433 dup2(pipefds[0], fileno(stdin));
425 close(pipefds[0]);
434 close(pipefds[0]);
426 close(pipefds[1]);
435 close(pipefds[1]);
427
436
428 int r = execlp("/bin/sh", "/bin/sh", "-c", pagercmd, NULL);
437 int r = execlp("/bin/sh", "/bin/sh", "-c", pagercmd, NULL);
429 if (r < 0) {
438 if (r < 0) {
430 abortmsg("cannot start pager '%s' (errno = %d)",
439 abortmsg("cannot start pager '%s' (errno = %d)",
431 pagercmd, errno);
440 pagercmd, errno);
432 }
441 }
433 return;
442 return;
434 }
443 }
435
444
436 error:
445 error:
437 close(pipefds[0]);
446 close(pipefds[0]);
438 close(pipefds[1]);
447 close(pipefds[1]);
439 abortmsg("failed to prepare pager (errno = %d)", errno);
448 abortmsg("failed to prepare pager (errno = %d)", errno);
440 }
449 }
441
450
442 int main(int argc, const char *argv[], const char *envp[])
451 int main(int argc, const char *argv[], const char *envp[])
443 {
452 {
444 if (getenv("CHGDEBUG"))
453 if (getenv("CHGDEBUG"))
445 enabledebugmsg();
454 enabledebugmsg();
446
455
447 struct cmdserveropts opts;
456 struct cmdserveropts opts;
448 initcmdserveropts(&opts);
457 initcmdserveropts(&opts);
449 setcmdserveropts(&opts);
458 setcmdserveropts(&opts);
450 setcmdserverargs(&opts, argc, argv);
459 setcmdserverargs(&opts, argc, argv);
451
460
452 if (argc == 2) {
461 if (argc == 2) {
453 int sig = 0;
462 int sig = 0;
454 if (strcmp(argv[1], "--kill-chg-daemon") == 0)
463 if (strcmp(argv[1], "--kill-chg-daemon") == 0)
455 sig = SIGTERM;
464 sig = SIGTERM;
456 if (strcmp(argv[1], "--reload-chg-daemon") == 0)
465 if (strcmp(argv[1], "--reload-chg-daemon") == 0)
457 sig = SIGHUP;
466 sig = SIGHUP;
458 if (sig > 0) {
467 if (sig > 0) {
459 killcmdserver(&opts, sig);
468 killcmdserver(&opts, sig);
460 return 0;
469 return 0;
461 }
470 }
462 }
471 }
463
472
464 hgclient_t *hgc = connectcmdserver(&opts);
473 hgclient_t *hgc = connectcmdserver(&opts);
465 if (!hgc)
474 if (!hgc)
466 abortmsg("cannot open hg client");
475 abortmsg("cannot open hg client");
467
476
468 setupsignalhandler(hgc_peerpid(hgc));
477 setupsignalhandler(hgc_peerpid(hgc));
469 hgc_setenv(hgc, envp);
478 hgc_setenv(hgc, envp);
470 setuppager(hgc, argv + 1, argc - 1);
479 setuppager(hgc, argv + 1, argc - 1);
471 int exitcode = hgc_runcommand(hgc, argv + 1, argc - 1);
480 int exitcode = hgc_runcommand(hgc, argv + 1, argc - 1);
472 hgc_close(hgc);
481 hgc_close(hgc);
473 freecmdserveropts(&opts);
482 freecmdserveropts(&opts);
474 return exitcode;
483 return exitcode;
475 }
484 }
General Comments 0
You need to be logged in to leave comments. Login now