author | Jun Wu <quark@fb.com> |
Mon, 22 Feb 2016 16:59:08 +0000 | |
changeset 28194 | 7623ba92af72 |
parent 28167 | 66f6dad20c19 |
child 28196 | 87de4a22e8c2 |
permissions | -rw-r--r-- |
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, |
|
28194
7623ba92af72
serve: rename --daemon-pipefds to --daemon-postexec (BC)
Jun Wu <quark@fb.com>
parents:
28167
diff
changeset
|
192 |
"--daemon-postexec", opts->lockfile, |
28060 | 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 |
} |