Show More
@@ -14,6 +14,7 | |||||
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> | |
@@ -34,10 +35,12 struct cmdserveropts { | |||||
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) { | |
@@ -158,21 +161,33 static void setcmdserveropts(struct cmds | |||||
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 |
|
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) | |
@@ -189,7 +204,7 static void execcmdserver(const struct c | |||||
189 | "--cwd", "/", |
|
204 | "--cwd", "/", | |
190 | "--cmdserver", "chgunix", |
|
205 | "--cmdserver", "chgunix", | |
191 | "--address", opts->sockname, |
|
206 | "--address", opts->sockname, | |
192 |
"--daemon-postexec", |
|
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 */ | |
@@ -208,28 +223,20 static void execcmdserver(const struct c | |||||
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 | } | |
@@ -237,13 +244,10 static void waitcmdserver(const struct c | |||||
237 | nanosleep(&sleepreq, NULL); |
|
244 | nanosleep(&sleepreq, NULL); | |
238 | } |
|
245 | } | |
239 |
|
246 | |||
240 |
abortmsg("timed out waiting for cmdserver %s", opts-> |
|
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)) { | |
@@ -251,26 +255,32 cleanup: | |||||
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 |
|
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 |
|
|
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) { | |
@@ -279,8 +289,11 static void startcmdserver(const struct | |||||
279 | } |
|
289 | } | |
280 | execcmdserver(opts); |
|
290 | execcmdserver(opts); | |
281 | } else { |
|
291 | } else { | |
282 |
|
|
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) | |
@@ -448,11 +461,7 int main(int argc, const char *argv[], c | |||||
448 | } |
|
461 | } | |
449 | } |
|
462 | } | |
450 |
|
463 | |||
451 |
hgclient_t *hgc = |
|
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 |
General Comments 0
You need to be logged in to leave comments.
Login now