Show More
@@ -14,6 +14,7 | |||
|
14 | 14 | #include <stdio.h> |
|
15 | 15 | #include <stdlib.h> |
|
16 | 16 | #include <string.h> |
|
17 | #include <sys/file.h> | |
|
17 | 18 | #include <sys/stat.h> |
|
18 | 19 | #include <sys/types.h> |
|
19 | 20 | #include <sys/un.h> |
@@ -34,10 +35,12 struct cmdserveropts { | |||
|
34 | 35 | char pidfile[UNIX_PATH_MAX]; |
|
35 | 36 | size_t argsize; |
|
36 | 37 | const char **args; |
|
38 | int lockfd; | |
|
37 | 39 | }; |
|
38 | 40 | |
|
39 | 41 | static void initcmdserveropts(struct cmdserveropts *opts) { |
|
40 | 42 | memset(opts, 0, sizeof(struct cmdserveropts)); |
|
43 | opts->lockfd = -1; | |
|
41 | 44 | } |
|
42 | 45 | |
|
43 | 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 | |
|
162 | * lock file will be deleted by server. (0: success, -1: lock exists) | |
|
164 | * Acquire a file lock that indicates a client is trying to start and connect | |
|
165 | * to a server, before executing a command. The lock is released upon exit or | |
|
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; | |
|
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; | |
|
170 | if (opts->lockfd == -1) { | |
|
171 | opts->lockfd = open(opts->lockfile, O_RDWR | O_CREAT | O_NOFOLLOW, 0600); | |
|
172 | if (opts->lockfd == -1) | |
|
173 | abortmsg("cannot create lock file %s", opts->lockfile); | |
|
174 | } | |
|
175 | int r = flock(opts->lockfd, LOCK_EX); | |
|
176 | if (r == -1) | |
|
177 | abortmsg("cannot acquire lock"); | |
|
178 | } | |
|
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 | 193 | static void execcmdserver(const struct cmdserveropts *opts) |
@@ -189,7 +204,7 static void execcmdserver(const struct c | |||
|
189 | 204 | "--cwd", "/", |
|
190 | 205 | "--cmdserver", "chgunix", |
|
191 | 206 | "--address", opts->sockname, |
|
192 |
"--daemon-postexec", |
|
|
207 | "--daemon-postexec", "none", | |
|
193 | 208 | "--pid-file", opts->pidfile, |
|
194 | 209 | "--config", "extensions.chgserver=", |
|
195 | 210 | /* wrap root ui so that it can be disabled/enabled by config */ |
@@ -208,28 +223,20 static void execcmdserver(const struct c | |||
|
208 | 223 | free(argv); |
|
209 | 224 | } |
|
210 | 225 | |
|
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) | |
|
226 | /* Retry until we can connect to the server. Give up after some time. */ | |
|
227 | static hgclient_t *retryconnectcmdserver(struct cmdserveropts *opts, pid_t pid) | |
|
216 | 228 | { |
|
217 | 229 | static const struct timespec sleepreq = {0, 10 * 1000000}; |
|
218 | 230 | int pst = 0; |
|
219 | 231 | |
|
220 | 232 | 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; | |
|
233 | hgclient_t *hgc = hgc_open(opts->sockname); | |
|
234 | if (hgc) | |
|
235 | return hgc; | |
|
229 | 236 | |
|
230 | 237 | if (pid > 0) { |
|
231 | 238 | /* collect zombie if child process fails to start */ |
|
232 | r = waitpid(pid, &pst, WNOHANG); | |
|
239 | int r = waitpid(pid, &pst, WNOHANG); | |
|
233 | 240 | if (r != 0) |
|
234 | 241 | goto cleanup; |
|
235 | 242 | } |
@@ -237,13 +244,10 static void waitcmdserver(const struct c | |||
|
237 | 244 | nanosleep(&sleepreq, NULL); |
|
238 | 245 | } |
|
239 | 246 | |
|
240 |
abortmsg("timed out waiting for cmdserver %s", opts-> |
|
|
241 | return; | |
|
247 | abortmsg("timed out waiting for cmdserver %s", opts->sockname); | |
|
248 | return NULL; | |
|
242 | 249 | |
|
243 | 250 | cleanup: |
|
244 | if (pid > 0) | |
|
245 | /* lockfile should be made by this process */ | |
|
246 | unlink(opts->lockfile); | |
|
247 | 251 | if (WIFEXITED(pst)) { |
|
248 | 252 | abortmsg("cmdserver exited with status %d", WEXITSTATUS(pst)); |
|
249 | 253 | } else if (WIFSIGNALED(pst)) { |
@@ -251,26 +255,32 cleanup: | |||
|
251 | 255 | } else { |
|
252 | 256 | abortmsg("error white waiting cmdserver"); |
|
253 | 257 | } |
|
258 | return NULL; | |
|
254 | 259 | } |
|
255 | 260 | |
|
256 | /* Spawn new background cmdserver */ | |
|
257 |
static |
|
|
261 | /* Connect to a cmdserver. Will start a new server on demand. */ | |
|
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 |
|
|
|
262 | debugmsg("lock file exists, waiting..."); | |
|
263 | waitcmdserver(opts, 0); | |
|
264 | return; | |
|
268 | lockcmdserver(opts); | |
|
269 | hgc = hgc_open(opts->sockname); | |
|
270 | if (hgc) { | |
|
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 */ | |
|
268 | unlink(opts->sockname); | |
|
276 | debugmsg("start cmdserver at %s", opts->sockname); | |
|
269 | 277 | |
|
270 | 278 | pid_t pid = fork(); |
|
271 | 279 | if (pid < 0) |
|
272 | 280 | abortmsg("failed to fork cmdserver process"); |
|
273 | 281 | if (pid == 0) { |
|
282 | /* do not leak lockfd to hg */ | |
|
283 | close(opts->lockfd); | |
|
274 | 284 | /* bypass uisetup() of pager extension */ |
|
275 | 285 | int nullfd = open("/dev/null", O_WRONLY); |
|
276 | 286 | if (nullfd >= 0) { |
@@ -279,8 +289,11 static void startcmdserver(const struct | |||
|
279 | 289 | } |
|
280 | 290 | execcmdserver(opts); |
|
281 | 291 | } else { |
|
282 |
|
|
|
292 | hgc = retryconnectcmdserver(opts, pid); | |
|
283 | 293 | } |
|
294 | ||
|
295 | unlockcmdserver(opts); | |
|
296 | return hgc; | |
|
284 | 297 | } |
|
285 | 298 | |
|
286 | 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 = |
|
|
452 | if (!hgc) { | |
|
453 | startcmdserver(&opts); | |
|
454 | hgc = hgc_open(opts.sockname); | |
|
455 | } | |
|
464 | hgclient_t *hgc = connectcmdserver(&opts); | |
|
456 | 465 | if (!hgc) |
|
457 | 466 | abortmsg("cannot open hg client"); |
|
458 | 467 |
General Comments 0
You need to be logged in to leave comments.
Login now