##// END OF EJS Templates
chg: support long socket path...
Jun Wu -
r30677:c80c16a8 default
parent child Browse files
Show More
@@ -1,638 +1,638
1 /*
1 /*
2 * A fast client for Mercurial command server
2 * A fast client for Mercurial command server
3 *
3 *
4 * Copyright (c) 2011 Yuya Nishihara <yuya@tcha.org>
4 * Copyright (c) 2011 Yuya Nishihara <yuya@tcha.org>
5 *
5 *
6 * This software may be used and distributed according to the terms of the
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.
7 * GNU General Public License version 2 or any later version.
8 */
8 */
9
9
10 #include <assert.h>
10 #include <assert.h>
11 #include <errno.h>
11 #include <errno.h>
12 #include <fcntl.h>
12 #include <fcntl.h>
13 #include <signal.h>
13 #include <signal.h>
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/file.h>
18 #include <sys/stat.h>
18 #include <sys/stat.h>
19 #include <sys/types.h>
19 #include <sys/types.h>
20 #include <sys/un.h>
20 #include <sys/un.h>
21 #include <sys/wait.h>
21 #include <sys/wait.h>
22 #include <time.h>
22 #include <time.h>
23 #include <unistd.h>
23 #include <unistd.h>
24
24
25 #include "hgclient.h"
25 #include "hgclient.h"
26 #include "util.h"
26 #include "util.h"
27
27
28 #ifndef UNIX_PATH_MAX
28 #ifndef PATH_MAX
29 #define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)NULL)->sun_path))
29 #define PATH_MAX 4096
30 #endif
30 #endif
31
31
32 struct cmdserveropts {
32 struct cmdserveropts {
33 char sockname[UNIX_PATH_MAX];
33 char sockname[PATH_MAX];
34 char initsockname[UNIX_PATH_MAX];
34 char initsockname[PATH_MAX];
35 char redirectsockname[UNIX_PATH_MAX];
35 char redirectsockname[PATH_MAX];
36 size_t argsize;
36 size_t argsize;
37 const char **args;
37 const char **args;
38 };
38 };
39
39
40 static void initcmdserveropts(struct cmdserveropts *opts) {
40 static void initcmdserveropts(struct cmdserveropts *opts) {
41 memset(opts, 0, sizeof(struct cmdserveropts));
41 memset(opts, 0, sizeof(struct cmdserveropts));
42 }
42 }
43
43
44 static void freecmdserveropts(struct cmdserveropts *opts) {
44 static void freecmdserveropts(struct cmdserveropts *opts) {
45 free(opts->args);
45 free(opts->args);
46 opts->args = NULL;
46 opts->args = NULL;
47 opts->argsize = 0;
47 opts->argsize = 0;
48 }
48 }
49
49
50 /*
50 /*
51 * Test if an argument is a sensitive flag that should be passed to the server.
51 * Test if an argument is a sensitive flag that should be passed to the server.
52 * Return 0 if not, otherwise the number of arguments starting from the current
52 * Return 0 if not, otherwise the number of arguments starting from the current
53 * one that should be passed to the server.
53 * one that should be passed to the server.
54 */
54 */
55 static size_t testsensitiveflag(const char *arg)
55 static size_t testsensitiveflag(const char *arg)
56 {
56 {
57 static const struct {
57 static const struct {
58 const char *name;
58 const char *name;
59 size_t narg;
59 size_t narg;
60 } flags[] = {
60 } flags[] = {
61 {"--config", 1},
61 {"--config", 1},
62 {"--cwd", 1},
62 {"--cwd", 1},
63 {"--repo", 1},
63 {"--repo", 1},
64 {"--repository", 1},
64 {"--repository", 1},
65 {"--traceback", 0},
65 {"--traceback", 0},
66 {"-R", 1},
66 {"-R", 1},
67 };
67 };
68 size_t i;
68 size_t i;
69 for (i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i) {
69 for (i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i) {
70 size_t len = strlen(flags[i].name);
70 size_t len = strlen(flags[i].name);
71 size_t narg = flags[i].narg;
71 size_t narg = flags[i].narg;
72 if (memcmp(arg, flags[i].name, len) == 0) {
72 if (memcmp(arg, flags[i].name, len) == 0) {
73 if (arg[len] == '\0') {
73 if (arg[len] == '\0') {
74 /* --flag (value) */
74 /* --flag (value) */
75 return narg + 1;
75 return narg + 1;
76 } else if (arg[len] == '=' && narg > 0) {
76 } else if (arg[len] == '=' && narg > 0) {
77 /* --flag=value */
77 /* --flag=value */
78 return 1;
78 return 1;
79 } else if (flags[i].name[1] != '-') {
79 } else if (flags[i].name[1] != '-') {
80 /* short flag */
80 /* short flag */
81 return 1;
81 return 1;
82 }
82 }
83 }
83 }
84 }
84 }
85 return 0;
85 return 0;
86 }
86 }
87
87
88 /*
88 /*
89 * Parse argv[] and put sensitive flags to opts->args
89 * Parse argv[] and put sensitive flags to opts->args
90 */
90 */
91 static void setcmdserverargs(struct cmdserveropts *opts,
91 static void setcmdserverargs(struct cmdserveropts *opts,
92 int argc, const char *argv[])
92 int argc, const char *argv[])
93 {
93 {
94 size_t i, step;
94 size_t i, step;
95 opts->argsize = 0;
95 opts->argsize = 0;
96 for (i = 0, step = 1; i < (size_t)argc; i += step, step = 1) {
96 for (i = 0, step = 1; i < (size_t)argc; i += step, step = 1) {
97 if (!argv[i])
97 if (!argv[i])
98 continue; /* pass clang-analyse */
98 continue; /* pass clang-analyse */
99 if (strcmp(argv[i], "--") == 0)
99 if (strcmp(argv[i], "--") == 0)
100 break;
100 break;
101 size_t n = testsensitiveflag(argv[i]);
101 size_t n = testsensitiveflag(argv[i]);
102 if (n == 0 || i + n > (size_t)argc)
102 if (n == 0 || i + n > (size_t)argc)
103 continue;
103 continue;
104 opts->args = reallocx(opts->args,
104 opts->args = reallocx(opts->args,
105 (n + opts->argsize) * sizeof(char *));
105 (n + opts->argsize) * sizeof(char *));
106 memcpy(opts->args + opts->argsize, argv + i,
106 memcpy(opts->args + opts->argsize, argv + i,
107 sizeof(char *) * n);
107 sizeof(char *) * n);
108 opts->argsize += n;
108 opts->argsize += n;
109 step = n;
109 step = n;
110 }
110 }
111 }
111 }
112
112
113 static void preparesockdir(const char *sockdir)
113 static void preparesockdir(const char *sockdir)
114 {
114 {
115 int r;
115 int r;
116 r = mkdir(sockdir, 0700);
116 r = mkdir(sockdir, 0700);
117 if (r < 0 && errno != EEXIST)
117 if (r < 0 && errno != EEXIST)
118 abortmsgerrno("cannot create sockdir %s", sockdir);
118 abortmsgerrno("cannot create sockdir %s", sockdir);
119
119
120 struct stat st;
120 struct stat st;
121 r = lstat(sockdir, &st);
121 r = lstat(sockdir, &st);
122 if (r < 0)
122 if (r < 0)
123 abortmsgerrno("cannot stat %s", sockdir);
123 abortmsgerrno("cannot stat %s", sockdir);
124 if (!S_ISDIR(st.st_mode))
124 if (!S_ISDIR(st.st_mode))
125 abortmsg("cannot create sockdir %s (file exists)", sockdir);
125 abortmsg("cannot create sockdir %s (file exists)", sockdir);
126 if (st.st_uid != geteuid() || st.st_mode & 0077)
126 if (st.st_uid != geteuid() || st.st_mode & 0077)
127 abortmsg("insecure sockdir %s", sockdir);
127 abortmsg("insecure sockdir %s", sockdir);
128 }
128 }
129
129
130 static void setcmdserveropts(struct cmdserveropts *opts)
130 static void setcmdserveropts(struct cmdserveropts *opts)
131 {
131 {
132 int r;
132 int r;
133 char sockdir[UNIX_PATH_MAX];
133 char sockdir[PATH_MAX];
134 const char *envsockname = getenv("CHGSOCKNAME");
134 const char *envsockname = getenv("CHGSOCKNAME");
135 if (!envsockname) {
135 if (!envsockname) {
136 /* by default, put socket file in secure directory
136 /* by default, put socket file in secure directory
137 * (permission of socket file may be ignored on some Unices) */
137 * (permission of socket file may be ignored on some Unices) */
138 const char *tmpdir = getenv("TMPDIR");
138 const char *tmpdir = getenv("TMPDIR");
139 if (!tmpdir)
139 if (!tmpdir)
140 tmpdir = "/tmp";
140 tmpdir = "/tmp";
141 r = snprintf(sockdir, sizeof(sockdir), "%s/chg%d",
141 r = snprintf(sockdir, sizeof(sockdir), "%s/chg%d",
142 tmpdir, geteuid());
142 tmpdir, geteuid());
143 if (r < 0 || (size_t)r >= sizeof(sockdir))
143 if (r < 0 || (size_t)r >= sizeof(sockdir))
144 abortmsg("too long TMPDIR (r = %d)", r);
144 abortmsg("too long TMPDIR (r = %d)", r);
145 preparesockdir(sockdir);
145 preparesockdir(sockdir);
146 }
146 }
147
147
148 const char *basename = (envsockname) ? envsockname : sockdir;
148 const char *basename = (envsockname) ? envsockname : sockdir;
149 const char *sockfmt = (envsockname) ? "%s" : "%s/server";
149 const char *sockfmt = (envsockname) ? "%s" : "%s/server";
150 r = snprintf(opts->sockname, sizeof(opts->sockname), sockfmt, basename);
150 r = snprintf(opts->sockname, sizeof(opts->sockname), sockfmt, basename);
151 if (r < 0 || (size_t)r >= sizeof(opts->sockname))
151 if (r < 0 || (size_t)r >= sizeof(opts->sockname))
152 abortmsg("too long TMPDIR or CHGSOCKNAME (r = %d)", r);
152 abortmsg("too long TMPDIR or CHGSOCKNAME (r = %d)", r);
153 r = snprintf(opts->initsockname, sizeof(opts->initsockname),
153 r = snprintf(opts->initsockname, sizeof(opts->initsockname),
154 "%s.%u", opts->sockname, (unsigned)getpid());
154 "%s.%u", opts->sockname, (unsigned)getpid());
155 if (r < 0 || (size_t)r >= sizeof(opts->initsockname))
155 if (r < 0 || (size_t)r >= sizeof(opts->initsockname))
156 abortmsg("too long TMPDIR or CHGSOCKNAME (r = %d)", r);
156 abortmsg("too long TMPDIR or CHGSOCKNAME (r = %d)", r);
157 }
157 }
158
158
159 static const char *gethgcmd(void)
159 static const char *gethgcmd(void)
160 {
160 {
161 static const char *hgcmd = NULL;
161 static const char *hgcmd = NULL;
162 if (!hgcmd) {
162 if (!hgcmd) {
163 hgcmd = getenv("CHGHG");
163 hgcmd = getenv("CHGHG");
164 if (!hgcmd || hgcmd[0] == '\0')
164 if (!hgcmd || hgcmd[0] == '\0')
165 hgcmd = getenv("HG");
165 hgcmd = getenv("HG");
166 if (!hgcmd || hgcmd[0] == '\0')
166 if (!hgcmd || hgcmd[0] == '\0')
167 #ifdef HGPATH
167 #ifdef HGPATH
168 hgcmd = (HGPATH);
168 hgcmd = (HGPATH);
169 #else
169 #else
170 hgcmd = "hg";
170 hgcmd = "hg";
171 #endif
171 #endif
172 }
172 }
173 return hgcmd;
173 return hgcmd;
174 }
174 }
175
175
176 static void execcmdserver(const struct cmdserveropts *opts)
176 static void execcmdserver(const struct cmdserveropts *opts)
177 {
177 {
178 const char *hgcmd = gethgcmd();
178 const char *hgcmd = gethgcmd();
179
179
180 const char *baseargv[] = {
180 const char *baseargv[] = {
181 hgcmd,
181 hgcmd,
182 "serve",
182 "serve",
183 "--cmdserver", "chgunix",
183 "--cmdserver", "chgunix",
184 "--address", opts->initsockname,
184 "--address", opts->initsockname,
185 "--daemon-postexec", "chdir:/",
185 "--daemon-postexec", "chdir:/",
186 };
186 };
187 size_t baseargvsize = sizeof(baseargv) / sizeof(baseargv[0]);
187 size_t baseargvsize = sizeof(baseargv) / sizeof(baseargv[0]);
188 size_t argsize = baseargvsize + opts->argsize + 1;
188 size_t argsize = baseargvsize + opts->argsize + 1;
189
189
190 const char **argv = mallocx(sizeof(char *) * argsize);
190 const char **argv = mallocx(sizeof(char *) * argsize);
191 memcpy(argv, baseargv, sizeof(baseargv));
191 memcpy(argv, baseargv, sizeof(baseargv));
192 memcpy(argv + baseargvsize, opts->args, sizeof(char *) * opts->argsize);
192 memcpy(argv + baseargvsize, opts->args, sizeof(char *) * opts->argsize);
193 argv[argsize - 1] = NULL;
193 argv[argsize - 1] = NULL;
194
194
195 if (putenv("CHGINTERNALMARK=") != 0)
195 if (putenv("CHGINTERNALMARK=") != 0)
196 abortmsgerrno("failed to putenv");
196 abortmsgerrno("failed to putenv");
197 if (execvp(hgcmd, (char **)argv) < 0)
197 if (execvp(hgcmd, (char **)argv) < 0)
198 abortmsgerrno("failed to exec cmdserver");
198 abortmsgerrno("failed to exec cmdserver");
199 free(argv);
199 free(argv);
200 }
200 }
201
201
202 /* Retry until we can connect to the server. Give up after some time. */
202 /* Retry until we can connect to the server. Give up after some time. */
203 static hgclient_t *retryconnectcmdserver(struct cmdserveropts *opts, pid_t pid)
203 static hgclient_t *retryconnectcmdserver(struct cmdserveropts *opts, pid_t pid)
204 {
204 {
205 static const struct timespec sleepreq = {0, 10 * 1000000};
205 static const struct timespec sleepreq = {0, 10 * 1000000};
206 int pst = 0;
206 int pst = 0;
207
207
208 debugmsg("try connect to %s repeatedly", opts->initsockname);
208 debugmsg("try connect to %s repeatedly", opts->initsockname);
209
209
210 unsigned int timeoutsec = 60; /* default: 60 seconds */
210 unsigned int timeoutsec = 60; /* default: 60 seconds */
211 const char *timeoutenv = getenv("CHGTIMEOUT");
211 const char *timeoutenv = getenv("CHGTIMEOUT");
212 if (timeoutenv)
212 if (timeoutenv)
213 sscanf(timeoutenv, "%u", &timeoutsec);
213 sscanf(timeoutenv, "%u", &timeoutsec);
214
214
215 for (unsigned int i = 0; !timeoutsec || i < timeoutsec * 100; i++) {
215 for (unsigned int i = 0; !timeoutsec || i < timeoutsec * 100; i++) {
216 hgclient_t *hgc = hgc_open(opts->initsockname);
216 hgclient_t *hgc = hgc_open(opts->initsockname);
217 if (hgc) {
217 if (hgc) {
218 debugmsg("rename %s to %s", opts->initsockname,
218 debugmsg("rename %s to %s", opts->initsockname,
219 opts->sockname);
219 opts->sockname);
220 int r = rename(opts->initsockname, opts->sockname);
220 int r = rename(opts->initsockname, opts->sockname);
221 if (r != 0)
221 if (r != 0)
222 abortmsgerrno("cannot rename");
222 abortmsgerrno("cannot rename");
223 return hgc;
223 return hgc;
224 }
224 }
225
225
226 if (pid > 0) {
226 if (pid > 0) {
227 /* collect zombie if child process fails to start */
227 /* collect zombie if child process fails to start */
228 int r = waitpid(pid, &pst, WNOHANG);
228 int r = waitpid(pid, &pst, WNOHANG);
229 if (r != 0)
229 if (r != 0)
230 goto cleanup;
230 goto cleanup;
231 }
231 }
232
232
233 nanosleep(&sleepreq, NULL);
233 nanosleep(&sleepreq, NULL);
234 }
234 }
235
235
236 abortmsg("timed out waiting for cmdserver %s", opts->initsockname);
236 abortmsg("timed out waiting for cmdserver %s", opts->initsockname);
237 return NULL;
237 return NULL;
238
238
239 cleanup:
239 cleanup:
240 if (WIFEXITED(pst)) {
240 if (WIFEXITED(pst)) {
241 if (WEXITSTATUS(pst) == 0)
241 if (WEXITSTATUS(pst) == 0)
242 abortmsg("could not connect to cmdserver "
242 abortmsg("could not connect to cmdserver "
243 "(exited with status 0)");
243 "(exited with status 0)");
244 debugmsg("cmdserver exited with status %d", WEXITSTATUS(pst));
244 debugmsg("cmdserver exited with status %d", WEXITSTATUS(pst));
245 exit(WEXITSTATUS(pst));
245 exit(WEXITSTATUS(pst));
246 } else if (WIFSIGNALED(pst)) {
246 } else if (WIFSIGNALED(pst)) {
247 abortmsg("cmdserver killed by signal %d", WTERMSIG(pst));
247 abortmsg("cmdserver killed by signal %d", WTERMSIG(pst));
248 } else {
248 } else {
249 abortmsg("error while waiting for cmdserver");
249 abortmsg("error while waiting for cmdserver");
250 }
250 }
251 return NULL;
251 return NULL;
252 }
252 }
253
253
254 /* Connect to a cmdserver. Will start a new server on demand. */
254 /* Connect to a cmdserver. Will start a new server on demand. */
255 static hgclient_t *connectcmdserver(struct cmdserveropts *opts)
255 static hgclient_t *connectcmdserver(struct cmdserveropts *opts)
256 {
256 {
257 const char *sockname = opts->redirectsockname[0] ?
257 const char *sockname = opts->redirectsockname[0] ?
258 opts->redirectsockname : opts->sockname;
258 opts->redirectsockname : opts->sockname;
259 debugmsg("try connect to %s", sockname);
259 debugmsg("try connect to %s", sockname);
260 hgclient_t *hgc = hgc_open(sockname);
260 hgclient_t *hgc = hgc_open(sockname);
261 if (hgc)
261 if (hgc)
262 return hgc;
262 return hgc;
263
263
264 /* prevent us from being connected to an outdated server: we were
264 /* prevent us from being connected to an outdated server: we were
265 * told by a server to redirect to opts->redirectsockname and that
265 * told by a server to redirect to opts->redirectsockname and that
266 * address does not work. we do not want to connect to the server
266 * address does not work. we do not want to connect to the server
267 * again because it will probably tell us the same thing. */
267 * again because it will probably tell us the same thing. */
268 if (sockname == opts->redirectsockname)
268 if (sockname == opts->redirectsockname)
269 unlink(opts->sockname);
269 unlink(opts->sockname);
270
270
271 debugmsg("start cmdserver at %s", opts->initsockname);
271 debugmsg("start cmdserver at %s", opts->initsockname);
272
272
273 pid_t pid = fork();
273 pid_t pid = fork();
274 if (pid < 0)
274 if (pid < 0)
275 abortmsg("failed to fork cmdserver process");
275 abortmsg("failed to fork cmdserver process");
276 if (pid == 0) {
276 if (pid == 0) {
277 execcmdserver(opts);
277 execcmdserver(opts);
278 } else {
278 } else {
279 hgc = retryconnectcmdserver(opts, pid);
279 hgc = retryconnectcmdserver(opts, pid);
280 }
280 }
281
281
282 return hgc;
282 return hgc;
283 }
283 }
284
284
285 static void killcmdserver(const struct cmdserveropts *opts)
285 static void killcmdserver(const struct cmdserveropts *opts)
286 {
286 {
287 /* resolve config hash */
287 /* resolve config hash */
288 char *resolvedpath = realpath(opts->sockname, NULL);
288 char *resolvedpath = realpath(opts->sockname, NULL);
289 if (resolvedpath) {
289 if (resolvedpath) {
290 unlink(resolvedpath);
290 unlink(resolvedpath);
291 free(resolvedpath);
291 free(resolvedpath);
292 }
292 }
293 }
293 }
294
294
295 static pid_t pagerpid = 0;
295 static pid_t pagerpid = 0;
296 static pid_t peerpgid = 0;
296 static pid_t peerpgid = 0;
297 static pid_t peerpid = 0;
297 static pid_t peerpid = 0;
298
298
299 static void forwardsignal(int sig)
299 static void forwardsignal(int sig)
300 {
300 {
301 assert(peerpid > 0);
301 assert(peerpid > 0);
302 if (kill(peerpid, sig) < 0)
302 if (kill(peerpid, sig) < 0)
303 abortmsgerrno("cannot kill %d", peerpid);
303 abortmsgerrno("cannot kill %d", peerpid);
304 debugmsg("forward signal %d", sig);
304 debugmsg("forward signal %d", sig);
305 }
305 }
306
306
307 static void forwardsignaltogroup(int sig)
307 static void forwardsignaltogroup(int sig)
308 {
308 {
309 /* prefer kill(-pgid, sig), fallback to pid if pgid is invalid */
309 /* prefer kill(-pgid, sig), fallback to pid if pgid is invalid */
310 pid_t killpid = peerpgid > 1 ? -peerpgid : peerpid;
310 pid_t killpid = peerpgid > 1 ? -peerpgid : peerpid;
311 if (kill(killpid, sig) < 0)
311 if (kill(killpid, sig) < 0)
312 abortmsgerrno("cannot kill %d", killpid);
312 abortmsgerrno("cannot kill %d", killpid);
313 debugmsg("forward signal %d to %d", sig, killpid);
313 debugmsg("forward signal %d to %d", sig, killpid);
314 }
314 }
315
315
316 static void handlestopsignal(int sig)
316 static void handlestopsignal(int sig)
317 {
317 {
318 sigset_t unblockset, oldset;
318 sigset_t unblockset, oldset;
319 struct sigaction sa, oldsa;
319 struct sigaction sa, oldsa;
320 if (sigemptyset(&unblockset) < 0)
320 if (sigemptyset(&unblockset) < 0)
321 goto error;
321 goto error;
322 if (sigaddset(&unblockset, sig) < 0)
322 if (sigaddset(&unblockset, sig) < 0)
323 goto error;
323 goto error;
324 memset(&sa, 0, sizeof(sa));
324 memset(&sa, 0, sizeof(sa));
325 sa.sa_handler = SIG_DFL;
325 sa.sa_handler = SIG_DFL;
326 sa.sa_flags = SA_RESTART;
326 sa.sa_flags = SA_RESTART;
327 if (sigemptyset(&sa.sa_mask) < 0)
327 if (sigemptyset(&sa.sa_mask) < 0)
328 goto error;
328 goto error;
329
329
330 forwardsignal(sig);
330 forwardsignal(sig);
331 if (raise(sig) < 0) /* resend to self */
331 if (raise(sig) < 0) /* resend to self */
332 goto error;
332 goto error;
333 if (sigaction(sig, &sa, &oldsa) < 0)
333 if (sigaction(sig, &sa, &oldsa) < 0)
334 goto error;
334 goto error;
335 if (sigprocmask(SIG_UNBLOCK, &unblockset, &oldset) < 0)
335 if (sigprocmask(SIG_UNBLOCK, &unblockset, &oldset) < 0)
336 goto error;
336 goto error;
337 /* resent signal will be handled before sigprocmask() returns */
337 /* resent signal will be handled before sigprocmask() returns */
338 if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0)
338 if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0)
339 goto error;
339 goto error;
340 if (sigaction(sig, &oldsa, NULL) < 0)
340 if (sigaction(sig, &oldsa, NULL) < 0)
341 goto error;
341 goto error;
342 return;
342 return;
343
343
344 error:
344 error:
345 abortmsgerrno("failed to handle stop signal");
345 abortmsgerrno("failed to handle stop signal");
346 }
346 }
347
347
348 static void handlechildsignal(int sig UNUSED_)
348 static void handlechildsignal(int sig UNUSED_)
349 {
349 {
350 if (peerpid == 0 || pagerpid == 0)
350 if (peerpid == 0 || pagerpid == 0)
351 return;
351 return;
352 /* if pager exits, notify the server with SIGPIPE immediately.
352 /* if pager exits, notify the server with SIGPIPE immediately.
353 * otherwise the server won't get SIGPIPE if it does not write
353 * otherwise the server won't get SIGPIPE if it does not write
354 * anything. (issue5278) */
354 * anything. (issue5278) */
355 if (waitpid(pagerpid, NULL, WNOHANG) == pagerpid)
355 if (waitpid(pagerpid, NULL, WNOHANG) == pagerpid)
356 kill(peerpid, SIGPIPE);
356 kill(peerpid, SIGPIPE);
357 }
357 }
358
358
359 static void setupsignalhandler(const hgclient_t *hgc)
359 static void setupsignalhandler(const hgclient_t *hgc)
360 {
360 {
361 pid_t pid = hgc_peerpid(hgc);
361 pid_t pid = hgc_peerpid(hgc);
362 if (pid <= 0)
362 if (pid <= 0)
363 return;
363 return;
364 peerpid = pid;
364 peerpid = pid;
365
365
366 pid_t pgid = hgc_peerpgid(hgc);
366 pid_t pgid = hgc_peerpgid(hgc);
367 peerpgid = (pgid <= 1 ? 0 : pgid);
367 peerpgid = (pgid <= 1 ? 0 : pgid);
368
368
369 struct sigaction sa;
369 struct sigaction sa;
370 memset(&sa, 0, sizeof(sa));
370 memset(&sa, 0, sizeof(sa));
371 sa.sa_handler = forwardsignaltogroup;
371 sa.sa_handler = forwardsignaltogroup;
372 sa.sa_flags = SA_RESTART;
372 sa.sa_flags = SA_RESTART;
373 if (sigemptyset(&sa.sa_mask) < 0)
373 if (sigemptyset(&sa.sa_mask) < 0)
374 goto error;
374 goto error;
375
375
376 if (sigaction(SIGHUP, &sa, NULL) < 0)
376 if (sigaction(SIGHUP, &sa, NULL) < 0)
377 goto error;
377 goto error;
378 if (sigaction(SIGINT, &sa, NULL) < 0)
378 if (sigaction(SIGINT, &sa, NULL) < 0)
379 goto error;
379 goto error;
380
380
381 /* terminate frontend by double SIGTERM in case of server freeze */
381 /* terminate frontend by double SIGTERM in case of server freeze */
382 sa.sa_handler = forwardsignal;
382 sa.sa_handler = forwardsignal;
383 sa.sa_flags |= SA_RESETHAND;
383 sa.sa_flags |= SA_RESETHAND;
384 if (sigaction(SIGTERM, &sa, NULL) < 0)
384 if (sigaction(SIGTERM, &sa, NULL) < 0)
385 goto error;
385 goto error;
386
386
387 /* notify the worker about window resize events */
387 /* notify the worker about window resize events */
388 sa.sa_flags = SA_RESTART;
388 sa.sa_flags = SA_RESTART;
389 if (sigaction(SIGWINCH, &sa, NULL) < 0)
389 if (sigaction(SIGWINCH, &sa, NULL) < 0)
390 goto error;
390 goto error;
391 /* propagate job control requests to worker */
391 /* propagate job control requests to worker */
392 sa.sa_handler = forwardsignal;
392 sa.sa_handler = forwardsignal;
393 sa.sa_flags = SA_RESTART;
393 sa.sa_flags = SA_RESTART;
394 if (sigaction(SIGCONT, &sa, NULL) < 0)
394 if (sigaction(SIGCONT, &sa, NULL) < 0)
395 goto error;
395 goto error;
396 sa.sa_handler = handlestopsignal;
396 sa.sa_handler = handlestopsignal;
397 sa.sa_flags = SA_RESTART;
397 sa.sa_flags = SA_RESTART;
398 if (sigaction(SIGTSTP, &sa, NULL) < 0)
398 if (sigaction(SIGTSTP, &sa, NULL) < 0)
399 goto error;
399 goto error;
400 /* get notified when pager exits */
400 /* get notified when pager exits */
401 sa.sa_handler = handlechildsignal;
401 sa.sa_handler = handlechildsignal;
402 sa.sa_flags = SA_RESTART;
402 sa.sa_flags = SA_RESTART;
403 if (sigaction(SIGCHLD, &sa, NULL) < 0)
403 if (sigaction(SIGCHLD, &sa, NULL) < 0)
404 goto error;
404 goto error;
405
405
406 return;
406 return;
407
407
408 error:
408 error:
409 abortmsgerrno("failed to set up signal handlers");
409 abortmsgerrno("failed to set up signal handlers");
410 }
410 }
411
411
412 static void restoresignalhandler()
412 static void restoresignalhandler()
413 {
413 {
414 struct sigaction sa;
414 struct sigaction sa;
415 memset(&sa, 0, sizeof(sa));
415 memset(&sa, 0, sizeof(sa));
416 sa.sa_handler = SIG_DFL;
416 sa.sa_handler = SIG_DFL;
417 sa.sa_flags = SA_RESTART;
417 sa.sa_flags = SA_RESTART;
418 if (sigemptyset(&sa.sa_mask) < 0)
418 if (sigemptyset(&sa.sa_mask) < 0)
419 goto error;
419 goto error;
420
420
421 if (sigaction(SIGHUP, &sa, NULL) < 0)
421 if (sigaction(SIGHUP, &sa, NULL) < 0)
422 goto error;
422 goto error;
423 if (sigaction(SIGTERM, &sa, NULL) < 0)
423 if (sigaction(SIGTERM, &sa, NULL) < 0)
424 goto error;
424 goto error;
425 if (sigaction(SIGWINCH, &sa, NULL) < 0)
425 if (sigaction(SIGWINCH, &sa, NULL) < 0)
426 goto error;
426 goto error;
427 if (sigaction(SIGCONT, &sa, NULL) < 0)
427 if (sigaction(SIGCONT, &sa, NULL) < 0)
428 goto error;
428 goto error;
429 if (sigaction(SIGTSTP, &sa, NULL) < 0)
429 if (sigaction(SIGTSTP, &sa, NULL) < 0)
430 goto error;
430 goto error;
431 if (sigaction(SIGCHLD, &sa, NULL) < 0)
431 if (sigaction(SIGCHLD, &sa, NULL) < 0)
432 goto error;
432 goto error;
433
433
434 /* ignore Ctrl+C while shutting down to make pager exits cleanly */
434 /* ignore Ctrl+C while shutting down to make pager exits cleanly */
435 sa.sa_handler = SIG_IGN;
435 sa.sa_handler = SIG_IGN;
436 if (sigaction(SIGINT, &sa, NULL) < 0)
436 if (sigaction(SIGINT, &sa, NULL) < 0)
437 goto error;
437 goto error;
438
438
439 peerpid = 0;
439 peerpid = 0;
440 return;
440 return;
441
441
442 error:
442 error:
443 abortmsgerrno("failed to restore signal handlers");
443 abortmsgerrno("failed to restore signal handlers");
444 }
444 }
445
445
446 /* This implementation is based on hgext/pager.py (post 369741ef7253)
446 /* This implementation is based on hgext/pager.py (post 369741ef7253)
447 * Return 0 if pager is not started, or pid of the pager */
447 * Return 0 if pager is not started, or pid of the pager */
448 static pid_t setuppager(hgclient_t *hgc, const char *const args[],
448 static pid_t setuppager(hgclient_t *hgc, const char *const args[],
449 size_t argsize)
449 size_t argsize)
450 {
450 {
451 const char *pagercmd = hgc_getpager(hgc, args, argsize);
451 const char *pagercmd = hgc_getpager(hgc, args, argsize);
452 if (!pagercmd)
452 if (!pagercmd)
453 return 0;
453 return 0;
454
454
455 int pipefds[2];
455 int pipefds[2];
456 if (pipe(pipefds) < 0)
456 if (pipe(pipefds) < 0)
457 return 0;
457 return 0;
458 pid_t pid = fork();
458 pid_t pid = fork();
459 if (pid < 0)
459 if (pid < 0)
460 goto error;
460 goto error;
461 if (pid > 0) {
461 if (pid > 0) {
462 close(pipefds[0]);
462 close(pipefds[0]);
463 if (dup2(pipefds[1], fileno(stdout)) < 0)
463 if (dup2(pipefds[1], fileno(stdout)) < 0)
464 goto error;
464 goto error;
465 if (isatty(fileno(stderr))) {
465 if (isatty(fileno(stderr))) {
466 if (dup2(pipefds[1], fileno(stderr)) < 0)
466 if (dup2(pipefds[1], fileno(stderr)) < 0)
467 goto error;
467 goto error;
468 }
468 }
469 close(pipefds[1]);
469 close(pipefds[1]);
470 hgc_attachio(hgc); /* reattach to pager */
470 hgc_attachio(hgc); /* reattach to pager */
471 return pid;
471 return pid;
472 } else {
472 } else {
473 dup2(pipefds[0], fileno(stdin));
473 dup2(pipefds[0], fileno(stdin));
474 close(pipefds[0]);
474 close(pipefds[0]);
475 close(pipefds[1]);
475 close(pipefds[1]);
476
476
477 int r = execlp("/bin/sh", "/bin/sh", "-c", pagercmd, NULL);
477 int r = execlp("/bin/sh", "/bin/sh", "-c", pagercmd, NULL);
478 if (r < 0) {
478 if (r < 0) {
479 abortmsgerrno("cannot start pager '%s'", pagercmd);
479 abortmsgerrno("cannot start pager '%s'", pagercmd);
480 }
480 }
481 return 0;
481 return 0;
482 }
482 }
483
483
484 error:
484 error:
485 close(pipefds[0]);
485 close(pipefds[0]);
486 close(pipefds[1]);
486 close(pipefds[1]);
487 abortmsgerrno("failed to prepare pager");
487 abortmsgerrno("failed to prepare pager");
488 return 0;
488 return 0;
489 }
489 }
490
490
491 static void waitpager(pid_t pid)
491 static void waitpager(pid_t pid)
492 {
492 {
493 /* close output streams to notify the pager its input ends */
493 /* close output streams to notify the pager its input ends */
494 fclose(stdout);
494 fclose(stdout);
495 fclose(stderr);
495 fclose(stderr);
496 while (1) {
496 while (1) {
497 pid_t ret = waitpid(pid, NULL, 0);
497 pid_t ret = waitpid(pid, NULL, 0);
498 if (ret == -1 && errno == EINTR)
498 if (ret == -1 && errno == EINTR)
499 continue;
499 continue;
500 break;
500 break;
501 }
501 }
502 }
502 }
503
503
504 /* Run instructions sent from the server like unlink and set redirect path
504 /* Run instructions sent from the server like unlink and set redirect path
505 * Return 1 if reconnect is needed, otherwise 0 */
505 * Return 1 if reconnect is needed, otherwise 0 */
506 static int runinstructions(struct cmdserveropts *opts, const char **insts)
506 static int runinstructions(struct cmdserveropts *opts, const char **insts)
507 {
507 {
508 int needreconnect = 0;
508 int needreconnect = 0;
509 if (!insts)
509 if (!insts)
510 return needreconnect;
510 return needreconnect;
511
511
512 assert(insts);
512 assert(insts);
513 opts->redirectsockname[0] = '\0';
513 opts->redirectsockname[0] = '\0';
514 const char **pinst;
514 const char **pinst;
515 for (pinst = insts; *pinst; pinst++) {
515 for (pinst = insts; *pinst; pinst++) {
516 debugmsg("instruction: %s", *pinst);
516 debugmsg("instruction: %s", *pinst);
517 if (strncmp(*pinst, "unlink ", 7) == 0) {
517 if (strncmp(*pinst, "unlink ", 7) == 0) {
518 unlink(*pinst + 7);
518 unlink(*pinst + 7);
519 } else if (strncmp(*pinst, "redirect ", 9) == 0) {
519 } else if (strncmp(*pinst, "redirect ", 9) == 0) {
520 int r = snprintf(opts->redirectsockname,
520 int r = snprintf(opts->redirectsockname,
521 sizeof(opts->redirectsockname),
521 sizeof(opts->redirectsockname),
522 "%s", *pinst + 9);
522 "%s", *pinst + 9);
523 if (r < 0 || r >= (int)sizeof(opts->redirectsockname))
523 if (r < 0 || r >= (int)sizeof(opts->redirectsockname))
524 abortmsg("redirect path is too long (%d)", r);
524 abortmsg("redirect path is too long (%d)", r);
525 needreconnect = 1;
525 needreconnect = 1;
526 } else if (strncmp(*pinst, "exit ", 5) == 0) {
526 } else if (strncmp(*pinst, "exit ", 5) == 0) {
527 int n = 0;
527 int n = 0;
528 if (sscanf(*pinst + 5, "%d", &n) != 1)
528 if (sscanf(*pinst + 5, "%d", &n) != 1)
529 abortmsg("cannot read the exit code");
529 abortmsg("cannot read the exit code");
530 exit(n);
530 exit(n);
531 } else if (strcmp(*pinst, "reconnect") == 0) {
531 } else if (strcmp(*pinst, "reconnect") == 0) {
532 needreconnect = 1;
532 needreconnect = 1;
533 } else {
533 } else {
534 abortmsg("unknown instruction: %s", *pinst);
534 abortmsg("unknown instruction: %s", *pinst);
535 }
535 }
536 }
536 }
537 return needreconnect;
537 return needreconnect;
538 }
538 }
539
539
540 /*
540 /*
541 * Test whether the command is unsupported or not. This is not designed to
541 * Test whether the command is unsupported or not. This is not designed to
542 * cover all cases. But it's fast, does not depend on the server and does
542 * cover all cases. But it's fast, does not depend on the server and does
543 * not return false positives.
543 * not return false positives.
544 */
544 */
545 static int isunsupported(int argc, const char *argv[])
545 static int isunsupported(int argc, const char *argv[])
546 {
546 {
547 enum {
547 enum {
548 SERVE = 1,
548 SERVE = 1,
549 DAEMON = 2,
549 DAEMON = 2,
550 SERVEDAEMON = SERVE | DAEMON,
550 SERVEDAEMON = SERVE | DAEMON,
551 TIME = 4,
551 TIME = 4,
552 };
552 };
553 unsigned int state = 0;
553 unsigned int state = 0;
554 int i;
554 int i;
555 for (i = 0; i < argc; ++i) {
555 for (i = 0; i < argc; ++i) {
556 if (strcmp(argv[i], "--") == 0)
556 if (strcmp(argv[i], "--") == 0)
557 break;
557 break;
558 if (i == 0 && strcmp("serve", argv[i]) == 0)
558 if (i == 0 && strcmp("serve", argv[i]) == 0)
559 state |= SERVE;
559 state |= SERVE;
560 else if (strcmp("-d", argv[i]) == 0 ||
560 else if (strcmp("-d", argv[i]) == 0 ||
561 strcmp("--daemon", argv[i]) == 0)
561 strcmp("--daemon", argv[i]) == 0)
562 state |= DAEMON;
562 state |= DAEMON;
563 else if (strcmp("--time", argv[i]) == 0)
563 else if (strcmp("--time", argv[i]) == 0)
564 state |= TIME;
564 state |= TIME;
565 }
565 }
566 return (state & TIME) == TIME ||
566 return (state & TIME) == TIME ||
567 (state & SERVEDAEMON) == SERVEDAEMON;
567 (state & SERVEDAEMON) == SERVEDAEMON;
568 }
568 }
569
569
570 static void execoriginalhg(const char *argv[])
570 static void execoriginalhg(const char *argv[])
571 {
571 {
572 debugmsg("execute original hg");
572 debugmsg("execute original hg");
573 if (execvp(gethgcmd(), (char **)argv) < 0)
573 if (execvp(gethgcmd(), (char **)argv) < 0)
574 abortmsgerrno("failed to exec original hg");
574 abortmsgerrno("failed to exec original hg");
575 }
575 }
576
576
577 int main(int argc, const char *argv[], const char *envp[])
577 int main(int argc, const char *argv[], const char *envp[])
578 {
578 {
579 if (getenv("CHGDEBUG"))
579 if (getenv("CHGDEBUG"))
580 enabledebugmsg();
580 enabledebugmsg();
581
581
582 if (!getenv("HGPLAIN") && isatty(fileno(stderr)))
582 if (!getenv("HGPLAIN") && isatty(fileno(stderr)))
583 enablecolor();
583 enablecolor();
584
584
585 if (getenv("CHGINTERNALMARK"))
585 if (getenv("CHGINTERNALMARK"))
586 abortmsg("chg started by chg detected.\n"
586 abortmsg("chg started by chg detected.\n"
587 "Please make sure ${HG:-hg} is not a symlink or "
587 "Please make sure ${HG:-hg} is not a symlink or "
588 "wrapper to chg. Alternatively, set $CHGHG to the "
588 "wrapper to chg. Alternatively, set $CHGHG to the "
589 "path of real hg.");
589 "path of real hg.");
590
590
591 if (isunsupported(argc - 1, argv + 1))
591 if (isunsupported(argc - 1, argv + 1))
592 execoriginalhg(argv);
592 execoriginalhg(argv);
593
593
594 struct cmdserveropts opts;
594 struct cmdserveropts opts;
595 initcmdserveropts(&opts);
595 initcmdserveropts(&opts);
596 setcmdserveropts(&opts);
596 setcmdserveropts(&opts);
597 setcmdserverargs(&opts, argc, argv);
597 setcmdserverargs(&opts, argc, argv);
598
598
599 if (argc == 2) {
599 if (argc == 2) {
600 if (strcmp(argv[1], "--kill-chg-daemon") == 0) {
600 if (strcmp(argv[1], "--kill-chg-daemon") == 0) {
601 killcmdserver(&opts);
601 killcmdserver(&opts);
602 return 0;
602 return 0;
603 }
603 }
604 }
604 }
605
605
606 hgclient_t *hgc;
606 hgclient_t *hgc;
607 size_t retry = 0;
607 size_t retry = 0;
608 while (1) {
608 while (1) {
609 hgc = connectcmdserver(&opts);
609 hgc = connectcmdserver(&opts);
610 if (!hgc)
610 if (!hgc)
611 abortmsg("cannot open hg client");
611 abortmsg("cannot open hg client");
612 hgc_setenv(hgc, envp);
612 hgc_setenv(hgc, envp);
613 const char **insts = hgc_validate(hgc, argv + 1, argc - 1);
613 const char **insts = hgc_validate(hgc, argv + 1, argc - 1);
614 int needreconnect = runinstructions(&opts, insts);
614 int needreconnect = runinstructions(&opts, insts);
615 free(insts);
615 free(insts);
616 if (!needreconnect)
616 if (!needreconnect)
617 break;
617 break;
618 hgc_close(hgc);
618 hgc_close(hgc);
619 if (++retry > 10)
619 if (++retry > 10)
620 abortmsg("too many redirections.\n"
620 abortmsg("too many redirections.\n"
621 "Please make sure %s is not a wrapper which "
621 "Please make sure %s is not a wrapper which "
622 "changes sensitive environment variables "
622 "changes sensitive environment variables "
623 "before executing hg. If you have to use a "
623 "before executing hg. If you have to use a "
624 "wrapper, wrap chg instead of hg.",
624 "wrapper, wrap chg instead of hg.",
625 gethgcmd());
625 gethgcmd());
626 }
626 }
627
627
628 setupsignalhandler(hgc);
628 setupsignalhandler(hgc);
629 pagerpid = setuppager(hgc, argv + 1, argc - 1);
629 pagerpid = setuppager(hgc, argv + 1, argc - 1);
630 int exitcode = hgc_runcommand(hgc, argv + 1, argc - 1);
630 int exitcode = hgc_runcommand(hgc, argv + 1, argc - 1);
631 restoresignalhandler();
631 restoresignalhandler();
632 hgc_close(hgc);
632 hgc_close(hgc);
633 freecmdserveropts(&opts);
633 freecmdserveropts(&opts);
634 if (pagerpid)
634 if (pagerpid)
635 waitpager(pagerpid);
635 waitpager(pagerpid);
636
636
637 return exitcode;
637 return exitcode;
638 }
638 }
@@ -1,86 +1,100
1 #require chg
1 #require chg
2
2
3 $ cp $HGRCPATH $HGRCPATH.orig
3 $ cp $HGRCPATH $HGRCPATH.orig
4
4
5 init repo
5 init repo
6
6
7 $ chg init foo
7 $ chg init foo
8 $ cd foo
8 $ cd foo
9
9
10 ill-formed config
10 ill-formed config
11
11
12 $ chg status
12 $ chg status
13 $ echo '=brokenconfig' >> $HGRCPATH
13 $ echo '=brokenconfig' >> $HGRCPATH
14 $ chg status
14 $ chg status
15 hg: parse error at * (glob)
15 hg: parse error at * (glob)
16 [255]
16 [255]
17
17
18 $ cp $HGRCPATH.orig $HGRCPATH
18 $ cp $HGRCPATH.orig $HGRCPATH
19
20 long socket path
21
22 $ sockpath=$TESTTMP/this/path/should/be/longer/than/one-hundred-and-seven/characters/where/107/is/the/typical/size/limit/of/unix-domain-socket
23 $ mkdir -p $sockpath
24 $ bakchgsockname=$CHGSOCKNAME
25 $ CHGSOCKNAME=$sockpath/server
26 $ export CHGSOCKNAME
27 $ chg root
28 $TESTTMP/foo
29 $ rm -rf $sockpath
30 $ CHGSOCKNAME=$bakchgsockname
31 $ export CHGSOCKNAME
32
19 $ cd ..
33 $ cd ..
20
34
21 server lifecycle
35 server lifecycle
22 ----------------
36 ----------------
23
37
24 chg server should be restarted on code change, and old server will shut down
38 chg server should be restarted on code change, and old server will shut down
25 automatically. In this test, we use the following time parameters:
39 automatically. In this test, we use the following time parameters:
26
40
27 - "sleep 1" to make mtime different
41 - "sleep 1" to make mtime different
28 - "sleep 2" to notice mtime change (polling interval is 1 sec)
42 - "sleep 2" to notice mtime change (polling interval is 1 sec)
29
43
30 set up repository with an extension:
44 set up repository with an extension:
31
45
32 $ chg init extreload
46 $ chg init extreload
33 $ cd extreload
47 $ cd extreload
34 $ touch dummyext.py
48 $ touch dummyext.py
35 $ cat <<EOF >> .hg/hgrc
49 $ cat <<EOF >> .hg/hgrc
36 > [extensions]
50 > [extensions]
37 > dummyext = dummyext.py
51 > dummyext = dummyext.py
38 > EOF
52 > EOF
39
53
40 isolate socket directory for stable result:
54 isolate socket directory for stable result:
41
55
42 $ OLDCHGSOCKNAME=$CHGSOCKNAME
56 $ OLDCHGSOCKNAME=$CHGSOCKNAME
43 $ mkdir chgsock
57 $ mkdir chgsock
44 $ CHGSOCKNAME=`pwd`/chgsock/server
58 $ CHGSOCKNAME=`pwd`/chgsock/server
45
59
46 warm up server:
60 warm up server:
47
61
48 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
62 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
49 chg: debug: start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
63 chg: debug: start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
50
64
51 new server should be started if extension modified:
65 new server should be started if extension modified:
52
66
53 $ sleep 1
67 $ sleep 1
54 $ touch dummyext.py
68 $ touch dummyext.py
55 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
69 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
56 chg: debug: instruction: unlink $TESTTMP/extreload/chgsock/server-* (glob)
70 chg: debug: instruction: unlink $TESTTMP/extreload/chgsock/server-* (glob)
57 chg: debug: instruction: reconnect
71 chg: debug: instruction: reconnect
58 chg: debug: start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
72 chg: debug: start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
59
73
60 old server will shut down, while new server should still be reachable:
74 old server will shut down, while new server should still be reachable:
61
75
62 $ sleep 2
76 $ sleep 2
63 $ CHGDEBUG= chg log 2>&1 | (egrep 'instruction|start' || true)
77 $ CHGDEBUG= chg log 2>&1 | (egrep 'instruction|start' || true)
64
78
65 socket file should never be unlinked by old server:
79 socket file should never be unlinked by old server:
66 (simulates unowned socket by updating mtime, which makes sure server exits
80 (simulates unowned socket by updating mtime, which makes sure server exits
67 at polling cycle)
81 at polling cycle)
68
82
69 $ ls chgsock/server-*
83 $ ls chgsock/server-*
70 chgsock/server-* (glob)
84 chgsock/server-* (glob)
71 $ touch chgsock/server-*
85 $ touch chgsock/server-*
72 $ sleep 2
86 $ sleep 2
73 $ ls chgsock/server-*
87 $ ls chgsock/server-*
74 chgsock/server-* (glob)
88 chgsock/server-* (glob)
75
89
76 since no server is reachable from socket file, new server should be started:
90 since no server is reachable from socket file, new server should be started:
77 (this test makes sure that old server shut down automatically)
91 (this test makes sure that old server shut down automatically)
78
92
79 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
93 $ CHGDEBUG= chg log 2>&1 | egrep 'instruction|start'
80 chg: debug: start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
94 chg: debug: start cmdserver at $TESTTMP/extreload/chgsock/server.* (glob)
81
95
82 shut down servers and restore environment:
96 shut down servers and restore environment:
83
97
84 $ rm -R chgsock
98 $ rm -R chgsock
85 $ CHGSOCKNAME=$OLDCHGSOCKNAME
99 $ CHGSOCKNAME=$OLDCHGSOCKNAME
86 $ cd ..
100 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now