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