##// END OF EJS Templates
chg: use color in debug/error messages conditionally...
Jun Wu -
r28787:ea86cdcd default
parent child Browse files
Show More
@@ -1,574 +1,577
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 UNIX_PATH_MAX
29 #define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)NULL)->sun_path))
29 #define UNIX_PATH_MAX (sizeof(((struct sockaddr_un *)NULL)->sun_path))
30 #endif
30 #endif
31
31
32 struct cmdserveropts {
32 struct cmdserveropts {
33 char sockname[UNIX_PATH_MAX];
33 char sockname[UNIX_PATH_MAX];
34 char redirectsockname[UNIX_PATH_MAX];
34 char redirectsockname[UNIX_PATH_MAX];
35 char lockfile[UNIX_PATH_MAX];
35 char lockfile[UNIX_PATH_MAX];
36 size_t argsize;
36 size_t argsize;
37 const char **args;
37 const char **args;
38 int lockfd;
38 int lockfd;
39 };
39 };
40
40
41 static void initcmdserveropts(struct cmdserveropts *opts) {
41 static void initcmdserveropts(struct cmdserveropts *opts) {
42 memset(opts, 0, sizeof(struct cmdserveropts));
42 memset(opts, 0, sizeof(struct cmdserveropts));
43 opts->lockfd = -1;
43 opts->lockfd = -1;
44 }
44 }
45
45
46 static void freecmdserveropts(struct cmdserveropts *opts) {
46 static void freecmdserveropts(struct cmdserveropts *opts) {
47 free(opts->args);
47 free(opts->args);
48 opts->args = NULL;
48 opts->args = NULL;
49 opts->argsize = 0;
49 opts->argsize = 0;
50 }
50 }
51
51
52 /*
52 /*
53 * Test if an argument is a sensitive flag that should be passed to the server.
53 * Test if an argument is a sensitive flag that should be passed to the server.
54 * Return 0 if not, otherwise the number of arguments starting from the current
54 * Return 0 if not, otherwise the number of arguments starting from the current
55 * one that should be passed to the server.
55 * one that should be passed to the server.
56 */
56 */
57 static size_t testsensitiveflag(const char *arg)
57 static size_t testsensitiveflag(const char *arg)
58 {
58 {
59 static const struct {
59 static const struct {
60 const char *name;
60 const char *name;
61 size_t narg;
61 size_t narg;
62 } flags[] = {
62 } flags[] = {
63 {"--config", 1},
63 {"--config", 1},
64 {"--cwd", 1},
64 {"--cwd", 1},
65 {"--repo", 1},
65 {"--repo", 1},
66 {"--repository", 1},
66 {"--repository", 1},
67 {"--traceback", 0},
67 {"--traceback", 0},
68 {"-R", 1},
68 {"-R", 1},
69 };
69 };
70 size_t i;
70 size_t i;
71 for (i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i) {
71 for (i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i) {
72 size_t len = strlen(flags[i].name);
72 size_t len = strlen(flags[i].name);
73 size_t narg = flags[i].narg;
73 size_t narg = flags[i].narg;
74 if (memcmp(arg, flags[i].name, len) == 0) {
74 if (memcmp(arg, flags[i].name, len) == 0) {
75 if (arg[len] == '\0') { /* --flag (value) */
75 if (arg[len] == '\0') { /* --flag (value) */
76 return narg + 1;
76 return narg + 1;
77 } else if (arg[len] == '=' && narg > 0) { /* --flag=value */
77 } else if (arg[len] == '=' && narg > 0) { /* --flag=value */
78 return 1;
78 return 1;
79 } else if (flags[i].name[1] != '-') { /* short flag */
79 } else if (flags[i].name[1] != '-') { /* short flag */
80 return 1;
80 return 1;
81 }
81 }
82 }
82 }
83 }
83 }
84 return 0;
84 return 0;
85 }
85 }
86
86
87 /*
87 /*
88 * Parse argv[] and put sensitive flags to opts->args
88 * Parse argv[] and put sensitive flags to opts->args
89 */
89 */
90 static void setcmdserverargs(struct cmdserveropts *opts,
90 static void setcmdserverargs(struct cmdserveropts *opts,
91 int argc, const char *argv[])
91 int argc, const char *argv[])
92 {
92 {
93 size_t i, step;
93 size_t i, step;
94 opts->argsize = 0;
94 opts->argsize = 0;
95 for (i = 0, step = 1; i < (size_t)argc; i += step, step = 1) {
95 for (i = 0, step = 1; i < (size_t)argc; i += step, step = 1) {
96 if (!argv[i])
96 if (!argv[i])
97 continue; /* pass clang-analyse */
97 continue; /* pass clang-analyse */
98 if (strcmp(argv[i], "--") == 0)
98 if (strcmp(argv[i], "--") == 0)
99 break;
99 break;
100 size_t n = testsensitiveflag(argv[i]);
100 size_t n = testsensitiveflag(argv[i]);
101 if (n == 0 || i + n > (size_t)argc)
101 if (n == 0 || i + n > (size_t)argc)
102 continue;
102 continue;
103 opts->args = reallocx(opts->args,
103 opts->args = reallocx(opts->args,
104 (n + opts->argsize) * sizeof(char *));
104 (n + opts->argsize) * sizeof(char *));
105 memcpy(opts->args + opts->argsize, argv + i,
105 memcpy(opts->args + opts->argsize, argv + i,
106 sizeof(char *) * n);
106 sizeof(char *) * n);
107 opts->argsize += n;
107 opts->argsize += n;
108 step = n;
108 step = n;
109 }
109 }
110 }
110 }
111
111
112 static void preparesockdir(const char *sockdir)
112 static void preparesockdir(const char *sockdir)
113 {
113 {
114 int r;
114 int r;
115 r = mkdir(sockdir, 0700);
115 r = mkdir(sockdir, 0700);
116 if (r < 0 && errno != EEXIST)
116 if (r < 0 && errno != EEXIST)
117 abortmsg("cannot create sockdir %s (errno = %d)",
117 abortmsg("cannot create sockdir %s (errno = %d)",
118 sockdir, errno);
118 sockdir, errno);
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 abortmsg("cannot stat %s (errno = %d)", sockdir, errno);
123 abortmsg("cannot stat %s (errno = %d)", sockdir, errno);
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[UNIX_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 const char *lockfmt = (envsockname) ? "%s.lock" : "%s/lock";
150 const char *lockfmt = (envsockname) ? "%s.lock" : "%s/lock";
151 r = snprintf(opts->sockname, sizeof(opts->sockname), sockfmt, basename);
151 r = snprintf(opts->sockname, sizeof(opts->sockname), sockfmt, basename);
152 if (r < 0 || (size_t)r >= sizeof(opts->sockname))
152 if (r < 0 || (size_t)r >= sizeof(opts->sockname))
153 abortmsg("too long TMPDIR or CHGSOCKNAME (r = %d)", r);
153 abortmsg("too long TMPDIR or CHGSOCKNAME (r = %d)", r);
154 r = snprintf(opts->lockfile, sizeof(opts->lockfile), lockfmt, basename);
154 r = snprintf(opts->lockfile, sizeof(opts->lockfile), lockfmt, basename);
155 if (r < 0 || (size_t)r >= sizeof(opts->lockfile))
155 if (r < 0 || (size_t)r >= sizeof(opts->lockfile))
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 /*
159 /*
160 * Acquire a file lock that indicates a client is trying to start and connect
160 * Acquire a file lock that indicates a client is trying to start and connect
161 * to a server, before executing a command. The lock is released upon exit or
161 * to a server, before executing a command. The lock is released upon exit or
162 * explicit unlock. Will block if the lock is held by another process.
162 * explicit unlock. Will block if the lock is held by another process.
163 */
163 */
164 static void lockcmdserver(struct cmdserveropts *opts)
164 static void lockcmdserver(struct cmdserveropts *opts)
165 {
165 {
166 if (opts->lockfd == -1) {
166 if (opts->lockfd == -1) {
167 opts->lockfd = open(opts->lockfile, O_RDWR | O_CREAT | O_NOFOLLOW, 0600);
167 opts->lockfd = open(opts->lockfile, O_RDWR | O_CREAT | O_NOFOLLOW, 0600);
168 if (opts->lockfd == -1)
168 if (opts->lockfd == -1)
169 abortmsg("cannot create lock file %s", opts->lockfile);
169 abortmsg("cannot create lock file %s", opts->lockfile);
170 }
170 }
171 int r = flock(opts->lockfd, LOCK_EX);
171 int r = flock(opts->lockfd, LOCK_EX);
172 if (r == -1)
172 if (r == -1)
173 abortmsg("cannot acquire lock");
173 abortmsg("cannot acquire lock");
174 }
174 }
175
175
176 /*
176 /*
177 * Release the file lock held by calling lockcmdserver. Will do nothing if
177 * Release the file lock held by calling lockcmdserver. Will do nothing if
178 * lockcmdserver is not called.
178 * lockcmdserver is not called.
179 */
179 */
180 static void unlockcmdserver(struct cmdserveropts *opts)
180 static void unlockcmdserver(struct cmdserveropts *opts)
181 {
181 {
182 if (opts->lockfd == -1)
182 if (opts->lockfd == -1)
183 return;
183 return;
184 flock(opts->lockfd, LOCK_UN);
184 flock(opts->lockfd, LOCK_UN);
185 close(opts->lockfd);
185 close(opts->lockfd);
186 opts->lockfd = -1;
186 opts->lockfd = -1;
187 }
187 }
188
188
189 static const char *gethgcmd(void)
189 static const char *gethgcmd(void)
190 {
190 {
191 static const char *hgcmd = NULL;
191 static const char *hgcmd = NULL;
192 if (!hgcmd) {
192 if (!hgcmd) {
193 hgcmd = getenv("CHGHG");
193 hgcmd = getenv("CHGHG");
194 if (!hgcmd || hgcmd[0] == '\0')
194 if (!hgcmd || hgcmd[0] == '\0')
195 hgcmd = getenv("HG");
195 hgcmd = getenv("HG");
196 if (!hgcmd || hgcmd[0] == '\0')
196 if (!hgcmd || hgcmd[0] == '\0')
197 #ifdef HGPATH
197 #ifdef HGPATH
198 hgcmd = (HGPATH);
198 hgcmd = (HGPATH);
199 #else
199 #else
200 hgcmd = "hg";
200 hgcmd = "hg";
201 #endif
201 #endif
202 }
202 }
203 return hgcmd;
203 return hgcmd;
204 }
204 }
205
205
206 static void execcmdserver(const struct cmdserveropts *opts)
206 static void execcmdserver(const struct cmdserveropts *opts)
207 {
207 {
208 const char *hgcmd = gethgcmd();
208 const char *hgcmd = gethgcmd();
209
209
210 const char *baseargv[] = {
210 const char *baseargv[] = {
211 hgcmd,
211 hgcmd,
212 "serve",
212 "serve",
213 "--cmdserver", "chgunix",
213 "--cmdserver", "chgunix",
214 "--address", opts->sockname,
214 "--address", opts->sockname,
215 "--daemon-postexec", "chdir:/",
215 "--daemon-postexec", "chdir:/",
216 "--config", "extensions.chgserver=",
216 "--config", "extensions.chgserver=",
217 };
217 };
218 size_t baseargvsize = sizeof(baseargv) / sizeof(baseargv[0]);
218 size_t baseargvsize = sizeof(baseargv) / sizeof(baseargv[0]);
219 size_t argsize = baseargvsize + opts->argsize + 1;
219 size_t argsize = baseargvsize + opts->argsize + 1;
220
220
221 const char **argv = mallocx(sizeof(char *) * argsize);
221 const char **argv = mallocx(sizeof(char *) * argsize);
222 memcpy(argv, baseargv, sizeof(baseargv));
222 memcpy(argv, baseargv, sizeof(baseargv));
223 memcpy(argv + baseargvsize, opts->args, sizeof(char *) * opts->argsize);
223 memcpy(argv + baseargvsize, opts->args, sizeof(char *) * opts->argsize);
224 argv[argsize - 1] = NULL;
224 argv[argsize - 1] = NULL;
225
225
226 if (putenv("CHGINTERNALMARK=") != 0)
226 if (putenv("CHGINTERNALMARK=") != 0)
227 abortmsg("failed to putenv (errno = %d)", errno);
227 abortmsg("failed to putenv (errno = %d)", errno);
228 if (execvp(hgcmd, (char **)argv) < 0)
228 if (execvp(hgcmd, (char **)argv) < 0)
229 abortmsg("failed to exec cmdserver (errno = %d)", errno);
229 abortmsg("failed to exec cmdserver (errno = %d)", errno);
230 free(argv);
230 free(argv);
231 }
231 }
232
232
233 /* Retry until we can connect to the server. Give up after some time. */
233 /* Retry until we can connect to the server. Give up after some time. */
234 static hgclient_t *retryconnectcmdserver(struct cmdserveropts *opts, pid_t pid)
234 static hgclient_t *retryconnectcmdserver(struct cmdserveropts *opts, pid_t pid)
235 {
235 {
236 static const struct timespec sleepreq = {0, 10 * 1000000};
236 static const struct timespec sleepreq = {0, 10 * 1000000};
237 int pst = 0;
237 int pst = 0;
238
238
239 debugmsg("try connect to %s repeatedly", opts->sockname);
239 debugmsg("try connect to %s repeatedly", opts->sockname);
240 for (unsigned int i = 0; i < 10 * 100; i++) {
240 for (unsigned int i = 0; i < 10 * 100; i++) {
241 hgclient_t *hgc = hgc_open(opts->sockname);
241 hgclient_t *hgc = hgc_open(opts->sockname);
242 if (hgc)
242 if (hgc)
243 return hgc;
243 return hgc;
244
244
245 if (pid > 0) {
245 if (pid > 0) {
246 /* collect zombie if child process fails to start */
246 /* collect zombie if child process fails to start */
247 int r = waitpid(pid, &pst, WNOHANG);
247 int r = waitpid(pid, &pst, WNOHANG);
248 if (r != 0)
248 if (r != 0)
249 goto cleanup;
249 goto cleanup;
250 }
250 }
251
251
252 nanosleep(&sleepreq, NULL);
252 nanosleep(&sleepreq, NULL);
253 }
253 }
254
254
255 abortmsg("timed out waiting for cmdserver %s", opts->sockname);
255 abortmsg("timed out waiting for cmdserver %s", opts->sockname);
256 return NULL;
256 return NULL;
257
257
258 cleanup:
258 cleanup:
259 if (WIFEXITED(pst)) {
259 if (WIFEXITED(pst)) {
260 debugmsg("cmdserver exited with status %d", WEXITSTATUS(pst));
260 debugmsg("cmdserver exited with status %d", WEXITSTATUS(pst));
261 exit(WEXITSTATUS(pst));
261 exit(WEXITSTATUS(pst));
262 } else if (WIFSIGNALED(pst)) {
262 } else if (WIFSIGNALED(pst)) {
263 abortmsg("cmdserver killed by signal %d", WTERMSIG(pst));
263 abortmsg("cmdserver killed by signal %d", WTERMSIG(pst));
264 } else {
264 } else {
265 abortmsg("error white waiting cmdserver");
265 abortmsg("error white waiting cmdserver");
266 }
266 }
267 return NULL;
267 return NULL;
268 }
268 }
269
269
270 /* Connect to a cmdserver. Will start a new server on demand. */
270 /* Connect to a cmdserver. Will start a new server on demand. */
271 static hgclient_t *connectcmdserver(struct cmdserveropts *opts)
271 static hgclient_t *connectcmdserver(struct cmdserveropts *opts)
272 {
272 {
273 const char *sockname = opts->redirectsockname[0] ?
273 const char *sockname = opts->redirectsockname[0] ?
274 opts->redirectsockname : opts->sockname;
274 opts->redirectsockname : opts->sockname;
275 debugmsg("try connect to %s", sockname);
275 debugmsg("try connect to %s", sockname);
276 hgclient_t *hgc = hgc_open(sockname);
276 hgclient_t *hgc = hgc_open(sockname);
277 if (hgc)
277 if (hgc)
278 return hgc;
278 return hgc;
279
279
280 lockcmdserver(opts);
280 lockcmdserver(opts);
281 hgc = hgc_open(sockname);
281 hgc = hgc_open(sockname);
282 if (hgc) {
282 if (hgc) {
283 unlockcmdserver(opts);
283 unlockcmdserver(opts);
284 debugmsg("cmdserver is started by another process");
284 debugmsg("cmdserver is started by another process");
285 return hgc;
285 return hgc;
286 }
286 }
287
287
288 /* prevent us from being connected to an outdated server: we were
288 /* prevent us from being connected to an outdated server: we were
289 * told by a server to redirect to opts->redirectsockname and that
289 * told by a server to redirect to opts->redirectsockname and that
290 * address does not work. we do not want to connect to the server
290 * address does not work. we do not want to connect to the server
291 * again because it will probably tell us the same thing. */
291 * again because it will probably tell us the same thing. */
292 if (sockname == opts->redirectsockname)
292 if (sockname == opts->redirectsockname)
293 unlink(opts->sockname);
293 unlink(opts->sockname);
294
294
295 debugmsg("start cmdserver at %s", opts->sockname);
295 debugmsg("start cmdserver at %s", opts->sockname);
296
296
297 pid_t pid = fork();
297 pid_t pid = fork();
298 if (pid < 0)
298 if (pid < 0)
299 abortmsg("failed to fork cmdserver process");
299 abortmsg("failed to fork cmdserver process");
300 if (pid == 0) {
300 if (pid == 0) {
301 /* do not leak lockfd to hg */
301 /* do not leak lockfd to hg */
302 close(opts->lockfd);
302 close(opts->lockfd);
303 execcmdserver(opts);
303 execcmdserver(opts);
304 } else {
304 } else {
305 hgc = retryconnectcmdserver(opts, pid);
305 hgc = retryconnectcmdserver(opts, pid);
306 }
306 }
307
307
308 unlockcmdserver(opts);
308 unlockcmdserver(opts);
309 return hgc;
309 return hgc;
310 }
310 }
311
311
312 static void killcmdserver(const struct cmdserveropts *opts)
312 static void killcmdserver(const struct cmdserveropts *opts)
313 {
313 {
314 /* resolve config hash */
314 /* resolve config hash */
315 char *resolvedpath = realpath(opts->sockname, NULL);
315 char *resolvedpath = realpath(opts->sockname, NULL);
316 if (resolvedpath) {
316 if (resolvedpath) {
317 unlink(resolvedpath);
317 unlink(resolvedpath);
318 free(resolvedpath);
318 free(resolvedpath);
319 }
319 }
320 }
320 }
321
321
322 static pid_t peerpid = 0;
322 static pid_t peerpid = 0;
323
323
324 static void forwardsignal(int sig)
324 static void forwardsignal(int sig)
325 {
325 {
326 assert(peerpid > 0);
326 assert(peerpid > 0);
327 if (kill(peerpid, sig) < 0)
327 if (kill(peerpid, sig) < 0)
328 abortmsg("cannot kill %d (errno = %d)", peerpid, errno);
328 abortmsg("cannot kill %d (errno = %d)", peerpid, errno);
329 debugmsg("forward signal %d", sig);
329 debugmsg("forward signal %d", sig);
330 }
330 }
331
331
332 static void handlestopsignal(int sig)
332 static void handlestopsignal(int sig)
333 {
333 {
334 sigset_t unblockset, oldset;
334 sigset_t unblockset, oldset;
335 struct sigaction sa, oldsa;
335 struct sigaction sa, oldsa;
336 if (sigemptyset(&unblockset) < 0)
336 if (sigemptyset(&unblockset) < 0)
337 goto error;
337 goto error;
338 if (sigaddset(&unblockset, sig) < 0)
338 if (sigaddset(&unblockset, sig) < 0)
339 goto error;
339 goto error;
340 memset(&sa, 0, sizeof(sa));
340 memset(&sa, 0, sizeof(sa));
341 sa.sa_handler = SIG_DFL;
341 sa.sa_handler = SIG_DFL;
342 sa.sa_flags = SA_RESTART;
342 sa.sa_flags = SA_RESTART;
343 if (sigemptyset(&sa.sa_mask) < 0)
343 if (sigemptyset(&sa.sa_mask) < 0)
344 goto error;
344 goto error;
345
345
346 forwardsignal(sig);
346 forwardsignal(sig);
347 if (raise(sig) < 0) /* resend to self */
347 if (raise(sig) < 0) /* resend to self */
348 goto error;
348 goto error;
349 if (sigaction(sig, &sa, &oldsa) < 0)
349 if (sigaction(sig, &sa, &oldsa) < 0)
350 goto error;
350 goto error;
351 if (sigprocmask(SIG_UNBLOCK, &unblockset, &oldset) < 0)
351 if (sigprocmask(SIG_UNBLOCK, &unblockset, &oldset) < 0)
352 goto error;
352 goto error;
353 /* resent signal will be handled before sigprocmask() returns */
353 /* resent signal will be handled before sigprocmask() returns */
354 if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0)
354 if (sigprocmask(SIG_SETMASK, &oldset, NULL) < 0)
355 goto error;
355 goto error;
356 if (sigaction(sig, &oldsa, NULL) < 0)
356 if (sigaction(sig, &oldsa, NULL) < 0)
357 goto error;
357 goto error;
358 return;
358 return;
359
359
360 error:
360 error:
361 abortmsg("failed to handle stop signal (errno = %d)", errno);
361 abortmsg("failed to handle stop signal (errno = %d)", errno);
362 }
362 }
363
363
364 static void setupsignalhandler(pid_t pid)
364 static void setupsignalhandler(pid_t pid)
365 {
365 {
366 if (pid <= 0)
366 if (pid <= 0)
367 return;
367 return;
368 peerpid = pid;
368 peerpid = pid;
369
369
370 struct sigaction sa;
370 struct sigaction sa;
371 memset(&sa, 0, sizeof(sa));
371 memset(&sa, 0, sizeof(sa));
372 sa.sa_handler = forwardsignal;
372 sa.sa_handler = forwardsignal;
373 sa.sa_flags = SA_RESTART;
373 sa.sa_flags = SA_RESTART;
374 if (sigemptyset(&sa.sa_mask) < 0)
374 if (sigemptyset(&sa.sa_mask) < 0)
375 goto error;
375 goto error;
376
376
377 if (sigaction(SIGHUP, &sa, NULL) < 0)
377 if (sigaction(SIGHUP, &sa, NULL) < 0)
378 goto error;
378 goto error;
379 if (sigaction(SIGINT, &sa, NULL) < 0)
379 if (sigaction(SIGINT, &sa, NULL) < 0)
380 goto error;
380 goto error;
381
381
382 /* terminate frontend by double SIGTERM in case of server freeze */
382 /* terminate frontend by double SIGTERM in case of server freeze */
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 /* propagate job control requests to worker */
387 /* propagate job control requests to worker */
388 sa.sa_handler = forwardsignal;
388 sa.sa_handler = forwardsignal;
389 sa.sa_flags = SA_RESTART;
389 sa.sa_flags = SA_RESTART;
390 if (sigaction(SIGCONT, &sa, NULL) < 0)
390 if (sigaction(SIGCONT, &sa, NULL) < 0)
391 goto error;
391 goto error;
392 sa.sa_handler = handlestopsignal;
392 sa.sa_handler = handlestopsignal;
393 sa.sa_flags = SA_RESTART;
393 sa.sa_flags = SA_RESTART;
394 if (sigaction(SIGTSTP, &sa, NULL) < 0)
394 if (sigaction(SIGTSTP, &sa, NULL) < 0)
395 goto error;
395 goto error;
396
396
397 return;
397 return;
398
398
399 error:
399 error:
400 abortmsg("failed to set up signal handlers (errno = %d)", errno);
400 abortmsg("failed to set up signal handlers (errno = %d)", errno);
401 }
401 }
402
402
403 /* This implementation is based on hgext/pager.py (pre 369741ef7253) */
403 /* This implementation is based on hgext/pager.py (pre 369741ef7253) */
404 static void setuppager(hgclient_t *hgc, const char *const args[],
404 static void setuppager(hgclient_t *hgc, const char *const args[],
405 size_t argsize)
405 size_t argsize)
406 {
406 {
407 const char *pagercmd = hgc_getpager(hgc, args, argsize);
407 const char *pagercmd = hgc_getpager(hgc, args, argsize);
408 if (!pagercmd)
408 if (!pagercmd)
409 return;
409 return;
410
410
411 int pipefds[2];
411 int pipefds[2];
412 if (pipe(pipefds) < 0)
412 if (pipe(pipefds) < 0)
413 return;
413 return;
414 pid_t pid = fork();
414 pid_t pid = fork();
415 if (pid < 0)
415 if (pid < 0)
416 goto error;
416 goto error;
417 if (pid == 0) {
417 if (pid == 0) {
418 close(pipefds[0]);
418 close(pipefds[0]);
419 if (dup2(pipefds[1], fileno(stdout)) < 0)
419 if (dup2(pipefds[1], fileno(stdout)) < 0)
420 goto error;
420 goto error;
421 if (isatty(fileno(stderr))) {
421 if (isatty(fileno(stderr))) {
422 if (dup2(pipefds[1], fileno(stderr)) < 0)
422 if (dup2(pipefds[1], fileno(stderr)) < 0)
423 goto error;
423 goto error;
424 }
424 }
425 close(pipefds[1]);
425 close(pipefds[1]);
426 hgc_attachio(hgc); /* reattach to pager */
426 hgc_attachio(hgc); /* reattach to pager */
427 return;
427 return;
428 } else {
428 } else {
429 dup2(pipefds[0], fileno(stdin));
429 dup2(pipefds[0], fileno(stdin));
430 close(pipefds[0]);
430 close(pipefds[0]);
431 close(pipefds[1]);
431 close(pipefds[1]);
432
432
433 int r = execlp("/bin/sh", "/bin/sh", "-c", pagercmd, NULL);
433 int r = execlp("/bin/sh", "/bin/sh", "-c", pagercmd, NULL);
434 if (r < 0) {
434 if (r < 0) {
435 abortmsg("cannot start pager '%s' (errno = %d)",
435 abortmsg("cannot start pager '%s' (errno = %d)",
436 pagercmd, errno);
436 pagercmd, errno);
437 }
437 }
438 return;
438 return;
439 }
439 }
440
440
441 error:
441 error:
442 close(pipefds[0]);
442 close(pipefds[0]);
443 close(pipefds[1]);
443 close(pipefds[1]);
444 abortmsg("failed to prepare pager (errno = %d)", errno);
444 abortmsg("failed to prepare pager (errno = %d)", errno);
445 }
445 }
446
446
447 /* Run instructions sent from the server like unlink and set redirect path
447 /* Run instructions sent from the server like unlink and set redirect path
448 * Return 1 if reconnect is needed, otherwise 0 */
448 * Return 1 if reconnect is needed, otherwise 0 */
449 static int runinstructions(struct cmdserveropts *opts, const char **insts)
449 static int runinstructions(struct cmdserveropts *opts, const char **insts)
450 {
450 {
451 int needreconnect = 0;
451 int needreconnect = 0;
452 if (!insts)
452 if (!insts)
453 return needreconnect;
453 return needreconnect;
454
454
455 assert(insts);
455 assert(insts);
456 opts->redirectsockname[0] = '\0';
456 opts->redirectsockname[0] = '\0';
457 const char **pinst;
457 const char **pinst;
458 for (pinst = insts; *pinst; pinst++) {
458 for (pinst = insts; *pinst; pinst++) {
459 debugmsg("instruction: %s", *pinst);
459 debugmsg("instruction: %s", *pinst);
460 if (strncmp(*pinst, "unlink ", 7) == 0) {
460 if (strncmp(*pinst, "unlink ", 7) == 0) {
461 unlink(*pinst + 7);
461 unlink(*pinst + 7);
462 } else if (strncmp(*pinst, "redirect ", 9) == 0) {
462 } else if (strncmp(*pinst, "redirect ", 9) == 0) {
463 int r = snprintf(opts->redirectsockname,
463 int r = snprintf(opts->redirectsockname,
464 sizeof(opts->redirectsockname),
464 sizeof(opts->redirectsockname),
465 "%s", *pinst + 9);
465 "%s", *pinst + 9);
466 if (r < 0 || r >= (int)sizeof(opts->redirectsockname))
466 if (r < 0 || r >= (int)sizeof(opts->redirectsockname))
467 abortmsg("redirect path is too long (%d)", r);
467 abortmsg("redirect path is too long (%d)", r);
468 needreconnect = 1;
468 needreconnect = 1;
469 } else if (strncmp(*pinst, "exit ", 5) == 0) {
469 } else if (strncmp(*pinst, "exit ", 5) == 0) {
470 int n = 0;
470 int n = 0;
471 if (sscanf(*pinst + 5, "%d", &n) != 1)
471 if (sscanf(*pinst + 5, "%d", &n) != 1)
472 abortmsg("cannot read the exit code");
472 abortmsg("cannot read the exit code");
473 exit(n);
473 exit(n);
474 } else if (strcmp(*pinst, "reconnect") == 0) {
474 } else if (strcmp(*pinst, "reconnect") == 0) {
475 needreconnect = 1;
475 needreconnect = 1;
476 } else {
476 } else {
477 abortmsg("unknown instruction: %s", *pinst);
477 abortmsg("unknown instruction: %s", *pinst);
478 }
478 }
479 }
479 }
480 return needreconnect;
480 return needreconnect;
481 }
481 }
482
482
483 /*
483 /*
484 * Test whether the command is unsupported or not. This is not designed to
484 * Test whether the command is unsupported or not. This is not designed to
485 * cover all cases. But it's fast, does not depend on the server and does
485 * cover all cases. But it's fast, does not depend on the server and does
486 * not return false positives.
486 * not return false positives.
487 */
487 */
488 static int isunsupported(int argc, const char *argv[])
488 static int isunsupported(int argc, const char *argv[])
489 {
489 {
490 enum {
490 enum {
491 SERVE = 1,
491 SERVE = 1,
492 DAEMON = 2,
492 DAEMON = 2,
493 SERVEDAEMON = SERVE | DAEMON,
493 SERVEDAEMON = SERVE | DAEMON,
494 TIME = 4,
494 TIME = 4,
495 };
495 };
496 unsigned int state = 0;
496 unsigned int state = 0;
497 int i;
497 int i;
498 for (i = 0; i < argc; ++i) {
498 for (i = 0; i < argc; ++i) {
499 if (strcmp(argv[i], "--") == 0)
499 if (strcmp(argv[i], "--") == 0)
500 break;
500 break;
501 if (i == 0 && strcmp("serve", argv[i]) == 0)
501 if (i == 0 && strcmp("serve", argv[i]) == 0)
502 state |= SERVE;
502 state |= SERVE;
503 else if (strcmp("-d", argv[i]) == 0 ||
503 else if (strcmp("-d", argv[i]) == 0 ||
504 strcmp("--daemon", argv[i]) == 0)
504 strcmp("--daemon", argv[i]) == 0)
505 state |= DAEMON;
505 state |= DAEMON;
506 else if (strcmp("--time", argv[i]) == 0)
506 else if (strcmp("--time", argv[i]) == 0)
507 state |= TIME;
507 state |= TIME;
508 }
508 }
509 return (state & TIME) == TIME ||
509 return (state & TIME) == TIME ||
510 (state & SERVEDAEMON) == SERVEDAEMON;
510 (state & SERVEDAEMON) == SERVEDAEMON;
511 }
511 }
512
512
513 static void execoriginalhg(const char *argv[])
513 static void execoriginalhg(const char *argv[])
514 {
514 {
515 debugmsg("execute original hg");
515 debugmsg("execute original hg");
516 if (execvp(gethgcmd(), (char **)argv) < 0)
516 if (execvp(gethgcmd(), (char **)argv) < 0)
517 abortmsg("failed to exec original hg (errno = %d)", errno);
517 abortmsg("failed to exec original hg (errno = %d)", errno);
518 }
518 }
519
519
520 int main(int argc, const char *argv[], const char *envp[])
520 int main(int argc, const char *argv[], const char *envp[])
521 {
521 {
522 if (getenv("CHGDEBUG"))
522 if (getenv("CHGDEBUG"))
523 enabledebugmsg();
523 enabledebugmsg();
524
524
525 if (!getenv("HGPLAIN") && isatty(fileno(stderr)))
526 enablecolor();
527
525 if (getenv("CHGINTERNALMARK"))
528 if (getenv("CHGINTERNALMARK"))
526 abortmsg("chg started by chg detected.\n"
529 abortmsg("chg started by chg detected.\n"
527 "Please make sure ${HG:-hg} is not a symlink or "
530 "Please make sure ${HG:-hg} is not a symlink or "
528 "wrapper to chg. Alternatively, set $CHGHG to the "
531 "wrapper to chg. Alternatively, set $CHGHG to the "
529 "path of real hg.");
532 "path of real hg.");
530
533
531 if (isunsupported(argc - 1, argv + 1))
534 if (isunsupported(argc - 1, argv + 1))
532 execoriginalhg(argv);
535 execoriginalhg(argv);
533
536
534 struct cmdserveropts opts;
537 struct cmdserveropts opts;
535 initcmdserveropts(&opts);
538 initcmdserveropts(&opts);
536 setcmdserveropts(&opts);
539 setcmdserveropts(&opts);
537 setcmdserverargs(&opts, argc, argv);
540 setcmdserverargs(&opts, argc, argv);
538
541
539 if (argc == 2) {
542 if (argc == 2) {
540 if (strcmp(argv[1], "--kill-chg-daemon") == 0) {
543 if (strcmp(argv[1], "--kill-chg-daemon") == 0) {
541 killcmdserver(&opts);
544 killcmdserver(&opts);
542 return 0;
545 return 0;
543 }
546 }
544 }
547 }
545
548
546 hgclient_t *hgc;
549 hgclient_t *hgc;
547 size_t retry = 0;
550 size_t retry = 0;
548 while (1) {
551 while (1) {
549 hgc = connectcmdserver(&opts);
552 hgc = connectcmdserver(&opts);
550 if (!hgc)
553 if (!hgc)
551 abortmsg("cannot open hg client");
554 abortmsg("cannot open hg client");
552 hgc_setenv(hgc, envp);
555 hgc_setenv(hgc, envp);
553 const char **insts = hgc_validate(hgc, argv + 1, argc - 1);
556 const char **insts = hgc_validate(hgc, argv + 1, argc - 1);
554 int needreconnect = runinstructions(&opts, insts);
557 int needreconnect = runinstructions(&opts, insts);
555 free(insts);
558 free(insts);
556 if (!needreconnect)
559 if (!needreconnect)
557 break;
560 break;
558 hgc_close(hgc);
561 hgc_close(hgc);
559 if (++retry > 10)
562 if (++retry > 10)
560 abortmsg("too many redirections.\n"
563 abortmsg("too many redirections.\n"
561 "Please make sure %s is not a wrapper which "
564 "Please make sure %s is not a wrapper which "
562 "changes sensitive environment variables "
565 "changes sensitive environment variables "
563 "before executing hg. If you have to use a "
566 "before executing hg. If you have to use a "
564 "wrapper, wrap chg instead of hg.",
567 "wrapper, wrap chg instead of hg.",
565 gethgcmd());
568 gethgcmd());
566 }
569 }
567
570
568 setupsignalhandler(hgc_peerpid(hgc));
571 setupsignalhandler(hgc_peerpid(hgc));
569 setuppager(hgc, argv + 1, argc - 1);
572 setuppager(hgc, argv + 1, argc - 1);
570 int exitcode = hgc_runcommand(hgc, argv + 1, argc - 1);
573 int exitcode = hgc_runcommand(hgc, argv + 1, argc - 1);
571 hgc_close(hgc);
574 hgc_close(hgc);
572 freecmdserveropts(&opts);
575 freecmdserveropts(&opts);
573 return exitcode;
576 return exitcode;
574 }
577 }
@@ -1,139 +1,157
1 /*
1 /*
2 * Utility functions
2 * Utility functions
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 <signal.h>
10 #include <signal.h>
11 #include <stdarg.h>
11 #include <stdarg.h>
12 #include <stdio.h>
12 #include <stdio.h>
13 #include <stdlib.h>
13 #include <stdlib.h>
14 #include <string.h>
14 #include <string.h>
15 #include <sys/types.h>
15 #include <sys/types.h>
16 #include <sys/wait.h>
16 #include <sys/wait.h>
17 #include <unistd.h>
17 #include <unistd.h>
18
18
19 #include "util.h"
19 #include "util.h"
20
20
21 static int colorenabled = 0;
22
23 static inline void fsetcolor(FILE *fp, const char *code)
24 {
25 if (!colorenabled)
26 return;
27 fprintf(fp, "\033[%sm", code);
28 }
29
21 void abortmsg(const char *fmt, ...)
30 void abortmsg(const char *fmt, ...)
22 {
31 {
23 va_list args;
32 va_list args;
24 va_start(args, fmt);
33 va_start(args, fmt);
25 fputs("\033[1;31mchg: abort: ", stderr);
34 fsetcolor(stderr, "1;31");
35 fputs("chg: abort: ", stderr);
26 vfprintf(stderr, fmt, args);
36 vfprintf(stderr, fmt, args);
27 fputs("\033[m\n", stderr);
37 fsetcolor(stderr, "");
38 fputc('\n', stderr);
28 va_end(args);
39 va_end(args);
29
40
30 exit(255);
41 exit(255);
31 }
42 }
32
43
33 static int debugmsgenabled = 0;
44 static int debugmsgenabled = 0;
34
45
46 void enablecolor(void)
47 {
48 colorenabled = 1;
49 }
50
35 void enabledebugmsg(void)
51 void enabledebugmsg(void)
36 {
52 {
37 debugmsgenabled = 1;
53 debugmsgenabled = 1;
38 }
54 }
39
55
40 void debugmsg(const char *fmt, ...)
56 void debugmsg(const char *fmt, ...)
41 {
57 {
42 if (!debugmsgenabled)
58 if (!debugmsgenabled)
43 return;
59 return;
44
60
45 va_list args;
61 va_list args;
46 va_start(args, fmt);
62 va_start(args, fmt);
47 fputs("\033[1;30mchg: debug: ", stderr);
63 fsetcolor(stderr, "1;30");
64 fputs("chg: debug: ", stderr);
48 vfprintf(stderr, fmt, args);
65 vfprintf(stderr, fmt, args);
49 fputs("\033[m\n", stderr);
66 fsetcolor(stderr, "");
67 fputc('\n', stderr);
50 va_end(args);
68 va_end(args);
51 }
69 }
52
70
53 void *mallocx(size_t size)
71 void *mallocx(size_t size)
54 {
72 {
55 void *result = malloc(size);
73 void *result = malloc(size);
56 if (!result)
74 if (!result)
57 abortmsg("failed to malloc");
75 abortmsg("failed to malloc");
58 return result;
76 return result;
59 }
77 }
60
78
61 void *reallocx(void *ptr, size_t size)
79 void *reallocx(void *ptr, size_t size)
62 {
80 {
63 void *result = realloc(ptr, size);
81 void *result = realloc(ptr, size);
64 if (!result)
82 if (!result)
65 abortmsg("failed to realloc");
83 abortmsg("failed to realloc");
66 return result;
84 return result;
67 }
85 }
68
86
69 /*
87 /*
70 * Execute a shell command in mostly the same manner as system(), with the
88 * Execute a shell command in mostly the same manner as system(), with the
71 * give environment variables, after chdir to the given cwd. Returns a status
89 * give environment variables, after chdir to the given cwd. Returns a status
72 * code compatible with the Python subprocess module.
90 * code compatible with the Python subprocess module.
73 */
91 */
74 int runshellcmd(const char *cmd, const char *envp[], const char *cwd)
92 int runshellcmd(const char *cmd, const char *envp[], const char *cwd)
75 {
93 {
76 enum { F_SIGINT = 1, F_SIGQUIT = 2, F_SIGMASK = 4, F_WAITPID = 8 };
94 enum { F_SIGINT = 1, F_SIGQUIT = 2, F_SIGMASK = 4, F_WAITPID = 8 };
77 unsigned int doneflags = 0;
95 unsigned int doneflags = 0;
78 int status = 0;
96 int status = 0;
79 struct sigaction newsa, oldsaint, oldsaquit;
97 struct sigaction newsa, oldsaint, oldsaquit;
80 sigset_t oldmask;
98 sigset_t oldmask;
81
99
82 /* block or mask signals just as system() does */
100 /* block or mask signals just as system() does */
83 memset(&newsa, 0, sizeof(newsa));
101 memset(&newsa, 0, sizeof(newsa));
84 newsa.sa_handler = SIG_IGN;
102 newsa.sa_handler = SIG_IGN;
85 newsa.sa_flags = 0;
103 newsa.sa_flags = 0;
86 if (sigemptyset(&newsa.sa_mask) < 0)
104 if (sigemptyset(&newsa.sa_mask) < 0)
87 goto done;
105 goto done;
88 if (sigaction(SIGINT, &newsa, &oldsaint) < 0)
106 if (sigaction(SIGINT, &newsa, &oldsaint) < 0)
89 goto done;
107 goto done;
90 doneflags |= F_SIGINT;
108 doneflags |= F_SIGINT;
91 if (sigaction(SIGQUIT, &newsa, &oldsaquit) < 0)
109 if (sigaction(SIGQUIT, &newsa, &oldsaquit) < 0)
92 goto done;
110 goto done;
93 doneflags |= F_SIGQUIT;
111 doneflags |= F_SIGQUIT;
94
112
95 if (sigaddset(&newsa.sa_mask, SIGCHLD) < 0)
113 if (sigaddset(&newsa.sa_mask, SIGCHLD) < 0)
96 goto done;
114 goto done;
97 if (sigprocmask(SIG_BLOCK, &newsa.sa_mask, &oldmask) < 0)
115 if (sigprocmask(SIG_BLOCK, &newsa.sa_mask, &oldmask) < 0)
98 goto done;
116 goto done;
99 doneflags |= F_SIGMASK;
117 doneflags |= F_SIGMASK;
100
118
101 pid_t pid = fork();
119 pid_t pid = fork();
102 if (pid < 0)
120 if (pid < 0)
103 goto done;
121 goto done;
104 if (pid == 0) {
122 if (pid == 0) {
105 sigaction(SIGINT, &oldsaint, NULL);
123 sigaction(SIGINT, &oldsaint, NULL);
106 sigaction(SIGQUIT, &oldsaquit, NULL);
124 sigaction(SIGQUIT, &oldsaquit, NULL);
107 sigprocmask(SIG_SETMASK, &oldmask, NULL);
125 sigprocmask(SIG_SETMASK, &oldmask, NULL);
108 if (cwd && chdir(cwd) < 0)
126 if (cwd && chdir(cwd) < 0)
109 _exit(127);
127 _exit(127);
110 const char *argv[] = {"sh", "-c", cmd, NULL};
128 const char *argv[] = {"sh", "-c", cmd, NULL};
111 if (envp) {
129 if (envp) {
112 execve("/bin/sh", (char **)argv, (char **)envp);
130 execve("/bin/sh", (char **)argv, (char **)envp);
113 } else {
131 } else {
114 execv("/bin/sh", (char **)argv);
132 execv("/bin/sh", (char **)argv);
115 }
133 }
116 _exit(127);
134 _exit(127);
117 } else {
135 } else {
118 if (waitpid(pid, &status, 0) < 0)
136 if (waitpid(pid, &status, 0) < 0)
119 goto done;
137 goto done;
120 doneflags |= F_WAITPID;
138 doneflags |= F_WAITPID;
121 }
139 }
122
140
123 done:
141 done:
124 if (doneflags & F_SIGINT)
142 if (doneflags & F_SIGINT)
125 sigaction(SIGINT, &oldsaint, NULL);
143 sigaction(SIGINT, &oldsaint, NULL);
126 if (doneflags & F_SIGQUIT)
144 if (doneflags & F_SIGQUIT)
127 sigaction(SIGQUIT, &oldsaquit, NULL);
145 sigaction(SIGQUIT, &oldsaquit, NULL);
128 if (doneflags & F_SIGMASK)
146 if (doneflags & F_SIGMASK)
129 sigprocmask(SIG_SETMASK, &oldmask, NULL);
147 sigprocmask(SIG_SETMASK, &oldmask, NULL);
130
148
131 /* no way to report other errors, use 127 (= shell termination) */
149 /* no way to report other errors, use 127 (= shell termination) */
132 if (!(doneflags & F_WAITPID))
150 if (!(doneflags & F_WAITPID))
133 return 127;
151 return 127;
134 if (WIFEXITED(status))
152 if (WIFEXITED(status))
135 return WEXITSTATUS(status);
153 return WEXITSTATUS(status);
136 if (WIFSIGNALED(status))
154 if (WIFSIGNALED(status))
137 return -WTERMSIG(status);
155 return -WTERMSIG(status);
138 return 127;
156 return 127;
139 }
157 }
@@ -1,29 +1,30
1 /*
1 /*
2 * Utility functions
2 * Utility functions
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 #ifndef UTIL_H_
10 #ifndef UTIL_H_
11 #define UTIL_H_
11 #define UTIL_H_
12
12
13 #ifdef __GNUC__
13 #ifdef __GNUC__
14 #define PRINTF_FORMAT_ __attribute__((format(printf, 1, 2)))
14 #define PRINTF_FORMAT_ __attribute__((format(printf, 1, 2)))
15 #else
15 #else
16 #define PRINTF_FORMAT_
16 #define PRINTF_FORMAT_
17 #endif
17 #endif
18
18
19 void abortmsg(const char *fmt, ...) PRINTF_FORMAT_;
19 void abortmsg(const char *fmt, ...) PRINTF_FORMAT_;
20
20
21 void enablecolor(void);
21 void enabledebugmsg(void);
22 void enabledebugmsg(void);
22 void debugmsg(const char *fmt, ...) PRINTF_FORMAT_;
23 void debugmsg(const char *fmt, ...) PRINTF_FORMAT_;
23
24
24 void *mallocx(size_t size);
25 void *mallocx(size_t size);
25 void *reallocx(void *ptr, size_t size);
26 void *reallocx(void *ptr, size_t size);
26
27
27 int runshellcmd(const char *cmd, const char *envp[], const char *cwd);
28 int runshellcmd(const char *cmd, const char *envp[], const char *cwd);
28
29
29 #endif /* UTIL_H_ */
30 #endif /* UTIL_H_ */
General Comments 0
You need to be logged in to leave comments. Login now