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