##// END OF EJS Templates
chg: hold a lock file before connected to server...
Jun Wu -
r28196:87de4a22 default
parent child Browse files
Show More
@@ -14,6 +14,7 b''
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 b' 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 b' 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 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)
@@ -189,7 +204,7 b' 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", 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 */
@@ -208,28 +223,20 b' 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 b' 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->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)) {
@@ -251,26 +255,32 b' 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 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) {
@@ -279,8 +289,11 b' static void startcmdserver(const struct '
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)
@@ -448,11 +461,7 b' int main(int argc, const char *argv[], c'
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
General Comments 0
You need to be logged in to leave comments. Login now