##// END OF EJS Templates
chg: check snprintf result strictly...
Jun Wu -
r30756:1f9684fe default
parent child Browse files
Show More
@@ -1,618 +1,620
1 /*
1 /*
2 * A command server client that uses Unix domain socket
2 * A command server client that uses Unix domain socket
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 <arpa/inet.h> /* for ntohl(), htonl() */
10 #include <arpa/inet.h> /* for ntohl(), htonl() */
11 #include <assert.h>
11 #include <assert.h>
12 #include <ctype.h>
12 #include <ctype.h>
13 #include <errno.h>
13 #include <errno.h>
14 #include <fcntl.h>
14 #include <fcntl.h>
15 #include <signal.h>
15 #include <signal.h>
16 #include <stdint.h>
16 #include <stdint.h>
17 #include <stdio.h>
17 #include <stdio.h>
18 #include <stdlib.h>
18 #include <stdlib.h>
19 #include <string.h>
19 #include <string.h>
20 #include <sys/socket.h>
20 #include <sys/socket.h>
21 #include <sys/stat.h>
21 #include <sys/stat.h>
22 #include <sys/un.h>
22 #include <sys/un.h>
23 #include <unistd.h>
23 #include <unistd.h>
24
24
25 #include "hgclient.h"
25 #include "hgclient.h"
26 #include "procutil.h"
26 #include "procutil.h"
27 #include "util.h"
27 #include "util.h"
28
28
29 enum {
29 enum {
30 CAP_GETENCODING = 0x0001,
30 CAP_GETENCODING = 0x0001,
31 CAP_RUNCOMMAND = 0x0002,
31 CAP_RUNCOMMAND = 0x0002,
32 /* cHg extension: */
32 /* cHg extension: */
33 CAP_ATTACHIO = 0x0100,
33 CAP_ATTACHIO = 0x0100,
34 CAP_CHDIR = 0x0200,
34 CAP_CHDIR = 0x0200,
35 CAP_SETENV = 0x0800,
35 CAP_SETENV = 0x0800,
36 CAP_SETUMASK = 0x1000,
36 CAP_SETUMASK = 0x1000,
37 CAP_VALIDATE = 0x2000,
37 CAP_VALIDATE = 0x2000,
38 CAP_SETPROCNAME = 0x4000,
38 CAP_SETPROCNAME = 0x4000,
39 };
39 };
40
40
41 typedef struct {
41 typedef struct {
42 const char *name;
42 const char *name;
43 unsigned int flag;
43 unsigned int flag;
44 } cappair_t;
44 } cappair_t;
45
45
46 static const cappair_t captable[] = {
46 static const cappair_t captable[] = {
47 {"getencoding", CAP_GETENCODING},
47 {"getencoding", CAP_GETENCODING},
48 {"runcommand", CAP_RUNCOMMAND},
48 {"runcommand", CAP_RUNCOMMAND},
49 {"attachio", CAP_ATTACHIO},
49 {"attachio", CAP_ATTACHIO},
50 {"chdir", CAP_CHDIR},
50 {"chdir", CAP_CHDIR},
51 {"setenv", CAP_SETENV},
51 {"setenv", CAP_SETENV},
52 {"setumask", CAP_SETUMASK},
52 {"setumask", CAP_SETUMASK},
53 {"validate", CAP_VALIDATE},
53 {"validate", CAP_VALIDATE},
54 {"setprocname", CAP_SETPROCNAME},
54 {"setprocname", CAP_SETPROCNAME},
55 {NULL, 0}, /* terminator */
55 {NULL, 0}, /* terminator */
56 };
56 };
57
57
58 typedef struct {
58 typedef struct {
59 char ch;
59 char ch;
60 char *data;
60 char *data;
61 size_t maxdatasize;
61 size_t maxdatasize;
62 size_t datasize;
62 size_t datasize;
63 } context_t;
63 } context_t;
64
64
65 struct hgclient_tag_ {
65 struct hgclient_tag_ {
66 int sockfd;
66 int sockfd;
67 pid_t pgid;
67 pid_t pgid;
68 pid_t pid;
68 pid_t pid;
69 context_t ctx;
69 context_t ctx;
70 unsigned int capflags;
70 unsigned int capflags;
71 };
71 };
72
72
73 static const size_t defaultdatasize = 4096;
73 static const size_t defaultdatasize = 4096;
74
74
75 static void attachio(hgclient_t *hgc);
75 static void attachio(hgclient_t *hgc);
76
76
77 static void initcontext(context_t *ctx)
77 static void initcontext(context_t *ctx)
78 {
78 {
79 ctx->ch = '\0';
79 ctx->ch = '\0';
80 ctx->data = malloc(defaultdatasize);
80 ctx->data = malloc(defaultdatasize);
81 ctx->maxdatasize = (ctx->data) ? defaultdatasize : 0;
81 ctx->maxdatasize = (ctx->data) ? defaultdatasize : 0;
82 ctx->datasize = 0;
82 ctx->datasize = 0;
83 debugmsg("initialize context buffer with size %zu", ctx->maxdatasize);
83 debugmsg("initialize context buffer with size %zu", ctx->maxdatasize);
84 }
84 }
85
85
86 static void enlargecontext(context_t *ctx, size_t newsize)
86 static void enlargecontext(context_t *ctx, size_t newsize)
87 {
87 {
88 if (newsize <= ctx->maxdatasize)
88 if (newsize <= ctx->maxdatasize)
89 return;
89 return;
90
90
91 newsize = defaultdatasize
91 newsize = defaultdatasize
92 * ((newsize + defaultdatasize - 1) / defaultdatasize);
92 * ((newsize + defaultdatasize - 1) / defaultdatasize);
93 ctx->data = reallocx(ctx->data, newsize);
93 ctx->data = reallocx(ctx->data, newsize);
94 ctx->maxdatasize = newsize;
94 ctx->maxdatasize = newsize;
95 debugmsg("enlarge context buffer to %zu", ctx->maxdatasize);
95 debugmsg("enlarge context buffer to %zu", ctx->maxdatasize);
96 }
96 }
97
97
98 static void freecontext(context_t *ctx)
98 static void freecontext(context_t *ctx)
99 {
99 {
100 debugmsg("free context buffer");
100 debugmsg("free context buffer");
101 free(ctx->data);
101 free(ctx->data);
102 ctx->data = NULL;
102 ctx->data = NULL;
103 ctx->maxdatasize = 0;
103 ctx->maxdatasize = 0;
104 ctx->datasize = 0;
104 ctx->datasize = 0;
105 }
105 }
106
106
107 /* Read channeled response from cmdserver */
107 /* Read channeled response from cmdserver */
108 static void readchannel(hgclient_t *hgc)
108 static void readchannel(hgclient_t *hgc)
109 {
109 {
110 assert(hgc);
110 assert(hgc);
111
111
112 ssize_t rsize = recv(hgc->sockfd, &hgc->ctx.ch, sizeof(hgc->ctx.ch), 0);
112 ssize_t rsize = recv(hgc->sockfd, &hgc->ctx.ch, sizeof(hgc->ctx.ch), 0);
113 if (rsize != sizeof(hgc->ctx.ch)) {
113 if (rsize != sizeof(hgc->ctx.ch)) {
114 /* server would have exception and traceback would be printed */
114 /* server would have exception and traceback would be printed */
115 debugmsg("failed to read channel");
115 debugmsg("failed to read channel");
116 exit(255);
116 exit(255);
117 }
117 }
118
118
119 uint32_t datasize_n;
119 uint32_t datasize_n;
120 rsize = recv(hgc->sockfd, &datasize_n, sizeof(datasize_n), 0);
120 rsize = recv(hgc->sockfd, &datasize_n, sizeof(datasize_n), 0);
121 if (rsize != sizeof(datasize_n))
121 if (rsize != sizeof(datasize_n))
122 abortmsg("failed to read data size");
122 abortmsg("failed to read data size");
123
123
124 /* datasize denotes the maximum size to write if input request */
124 /* datasize denotes the maximum size to write if input request */
125 hgc->ctx.datasize = ntohl(datasize_n);
125 hgc->ctx.datasize = ntohl(datasize_n);
126 enlargecontext(&hgc->ctx, hgc->ctx.datasize);
126 enlargecontext(&hgc->ctx, hgc->ctx.datasize);
127
127
128 if (isupper(hgc->ctx.ch) && hgc->ctx.ch != 'S')
128 if (isupper(hgc->ctx.ch) && hgc->ctx.ch != 'S')
129 return; /* assumes input request */
129 return; /* assumes input request */
130
130
131 size_t cursize = 0;
131 size_t cursize = 0;
132 while (cursize < hgc->ctx.datasize) {
132 while (cursize < hgc->ctx.datasize) {
133 rsize = recv(hgc->sockfd, hgc->ctx.data + cursize,
133 rsize = recv(hgc->sockfd, hgc->ctx.data + cursize,
134 hgc->ctx.datasize - cursize, 0);
134 hgc->ctx.datasize - cursize, 0);
135 if (rsize < 1)
135 if (rsize < 1)
136 abortmsg("failed to read data block");
136 abortmsg("failed to read data block");
137 cursize += rsize;
137 cursize += rsize;
138 }
138 }
139 }
139 }
140
140
141 static void sendall(int sockfd, const void *data, size_t datasize)
141 static void sendall(int sockfd, const void *data, size_t datasize)
142 {
142 {
143 const char *p = data;
143 const char *p = data;
144 const char *const endp = p + datasize;
144 const char *const endp = p + datasize;
145 while (p < endp) {
145 while (p < endp) {
146 ssize_t r = send(sockfd, p, endp - p, 0);
146 ssize_t r = send(sockfd, p, endp - p, 0);
147 if (r < 0)
147 if (r < 0)
148 abortmsgerrno("cannot communicate");
148 abortmsgerrno("cannot communicate");
149 p += r;
149 p += r;
150 }
150 }
151 }
151 }
152
152
153 /* Write lengh-data block to cmdserver */
153 /* Write lengh-data block to cmdserver */
154 static void writeblock(const hgclient_t *hgc)
154 static void writeblock(const hgclient_t *hgc)
155 {
155 {
156 assert(hgc);
156 assert(hgc);
157
157
158 const uint32_t datasize_n = htonl(hgc->ctx.datasize);
158 const uint32_t datasize_n = htonl(hgc->ctx.datasize);
159 sendall(hgc->sockfd, &datasize_n, sizeof(datasize_n));
159 sendall(hgc->sockfd, &datasize_n, sizeof(datasize_n));
160
160
161 sendall(hgc->sockfd, hgc->ctx.data, hgc->ctx.datasize);
161 sendall(hgc->sockfd, hgc->ctx.data, hgc->ctx.datasize);
162 }
162 }
163
163
164 static void writeblockrequest(const hgclient_t *hgc, const char *chcmd)
164 static void writeblockrequest(const hgclient_t *hgc, const char *chcmd)
165 {
165 {
166 debugmsg("request %s, block size %zu", chcmd, hgc->ctx.datasize);
166 debugmsg("request %s, block size %zu", chcmd, hgc->ctx.datasize);
167
167
168 char buf[strlen(chcmd) + 1];
168 char buf[strlen(chcmd) + 1];
169 memcpy(buf, chcmd, sizeof(buf) - 1);
169 memcpy(buf, chcmd, sizeof(buf) - 1);
170 buf[sizeof(buf) - 1] = '\n';
170 buf[sizeof(buf) - 1] = '\n';
171 sendall(hgc->sockfd, buf, sizeof(buf));
171 sendall(hgc->sockfd, buf, sizeof(buf));
172
172
173 writeblock(hgc);
173 writeblock(hgc);
174 }
174 }
175
175
176 /* Build '\0'-separated list of args. argsize < 0 denotes that args are
176 /* Build '\0'-separated list of args. argsize < 0 denotes that args are
177 * terminated by NULL. */
177 * terminated by NULL. */
178 static void packcmdargs(context_t *ctx, const char *const args[],
178 static void packcmdargs(context_t *ctx, const char *const args[],
179 ssize_t argsize)
179 ssize_t argsize)
180 {
180 {
181 ctx->datasize = 0;
181 ctx->datasize = 0;
182 const char *const *const end = (argsize >= 0) ? args + argsize : NULL;
182 const char *const *const end = (argsize >= 0) ? args + argsize : NULL;
183 for (const char *const *it = args; it != end && *it; ++it) {
183 for (const char *const *it = args; it != end && *it; ++it) {
184 const size_t n = strlen(*it) + 1; /* include '\0' */
184 const size_t n = strlen(*it) + 1; /* include '\0' */
185 enlargecontext(ctx, ctx->datasize + n);
185 enlargecontext(ctx, ctx->datasize + n);
186 memcpy(ctx->data + ctx->datasize, *it, n);
186 memcpy(ctx->data + ctx->datasize, *it, n);
187 ctx->datasize += n;
187 ctx->datasize += n;
188 }
188 }
189
189
190 if (ctx->datasize > 0)
190 if (ctx->datasize > 0)
191 --ctx->datasize; /* strip last '\0' */
191 --ctx->datasize; /* strip last '\0' */
192 }
192 }
193
193
194 /* Extract '\0'-separated list of args to new buffer, terminated by NULL */
194 /* Extract '\0'-separated list of args to new buffer, terminated by NULL */
195 static const char **unpackcmdargsnul(const context_t *ctx)
195 static const char **unpackcmdargsnul(const context_t *ctx)
196 {
196 {
197 const char **args = NULL;
197 const char **args = NULL;
198 size_t nargs = 0, maxnargs = 0;
198 size_t nargs = 0, maxnargs = 0;
199 const char *s = ctx->data;
199 const char *s = ctx->data;
200 const char *e = ctx->data + ctx->datasize;
200 const char *e = ctx->data + ctx->datasize;
201 for (;;) {
201 for (;;) {
202 if (nargs + 1 >= maxnargs) { /* including last NULL */
202 if (nargs + 1 >= maxnargs) { /* including last NULL */
203 maxnargs += 256;
203 maxnargs += 256;
204 args = reallocx(args, maxnargs * sizeof(args[0]));
204 args = reallocx(args, maxnargs * sizeof(args[0]));
205 }
205 }
206 args[nargs] = s;
206 args[nargs] = s;
207 nargs++;
207 nargs++;
208 s = memchr(s, '\0', e - s);
208 s = memchr(s, '\0', e - s);
209 if (!s)
209 if (!s)
210 break;
210 break;
211 s++;
211 s++;
212 }
212 }
213 args[nargs] = NULL;
213 args[nargs] = NULL;
214 return args;
214 return args;
215 }
215 }
216
216
217 static void handlereadrequest(hgclient_t *hgc)
217 static void handlereadrequest(hgclient_t *hgc)
218 {
218 {
219 context_t *ctx = &hgc->ctx;
219 context_t *ctx = &hgc->ctx;
220 size_t r = fread(ctx->data, sizeof(ctx->data[0]), ctx->datasize, stdin);
220 size_t r = fread(ctx->data, sizeof(ctx->data[0]), ctx->datasize, stdin);
221 ctx->datasize = r;
221 ctx->datasize = r;
222 writeblock(hgc);
222 writeblock(hgc);
223 }
223 }
224
224
225 /* Read single-line */
225 /* Read single-line */
226 static void handlereadlinerequest(hgclient_t *hgc)
226 static void handlereadlinerequest(hgclient_t *hgc)
227 {
227 {
228 context_t *ctx = &hgc->ctx;
228 context_t *ctx = &hgc->ctx;
229 if (!fgets(ctx->data, ctx->datasize, stdin))
229 if (!fgets(ctx->data, ctx->datasize, stdin))
230 ctx->data[0] = '\0';
230 ctx->data[0] = '\0';
231 ctx->datasize = strlen(ctx->data);
231 ctx->datasize = strlen(ctx->data);
232 writeblock(hgc);
232 writeblock(hgc);
233 }
233 }
234
234
235 /* Execute the requested command and write exit code */
235 /* Execute the requested command and write exit code */
236 static void handlesystemrequest(hgclient_t *hgc)
236 static void handlesystemrequest(hgclient_t *hgc)
237 {
237 {
238 context_t *ctx = &hgc->ctx;
238 context_t *ctx = &hgc->ctx;
239 enlargecontext(ctx, ctx->datasize + 1);
239 enlargecontext(ctx, ctx->datasize + 1);
240 ctx->data[ctx->datasize] = '\0'; /* terminate last string */
240 ctx->data[ctx->datasize] = '\0'; /* terminate last string */
241
241
242 const char **args = unpackcmdargsnul(ctx);
242 const char **args = unpackcmdargsnul(ctx);
243 if (!args[0] || !args[1] || !args[2])
243 if (!args[0] || !args[1] || !args[2])
244 abortmsg("missing type or command or cwd in system request");
244 abortmsg("missing type or command or cwd in system request");
245 if (strcmp(args[0], "system") == 0) {
245 if (strcmp(args[0], "system") == 0) {
246 debugmsg("run '%s' at '%s'", args[1], args[2]);
246 debugmsg("run '%s' at '%s'", args[1], args[2]);
247 int32_t r = runshellcmd(args[1], args + 3, args[2]);
247 int32_t r = runshellcmd(args[1], args + 3, args[2]);
248 free(args);
248 free(args);
249
249
250 uint32_t r_n = htonl(r);
250 uint32_t r_n = htonl(r);
251 memcpy(ctx->data, &r_n, sizeof(r_n));
251 memcpy(ctx->data, &r_n, sizeof(r_n));
252 ctx->datasize = sizeof(r_n);
252 ctx->datasize = sizeof(r_n);
253 writeblock(hgc);
253 writeblock(hgc);
254 } else if (strcmp(args[0], "pager") == 0) {
254 } else if (strcmp(args[0], "pager") == 0) {
255 setuppager(args[1]);
255 setuppager(args[1]);
256 if (hgc->capflags & CAP_ATTACHIO)
256 if (hgc->capflags & CAP_ATTACHIO)
257 attachio(hgc);
257 attachio(hgc);
258 /* unblock the server */
258 /* unblock the server */
259 static const char emptycmd[] = "\n";
259 static const char emptycmd[] = "\n";
260 sendall(hgc->sockfd, emptycmd, sizeof(emptycmd) - 1);
260 sendall(hgc->sockfd, emptycmd, sizeof(emptycmd) - 1);
261 } else {
261 } else {
262 abortmsg("unknown type in system request: %s", args[0]);
262 abortmsg("unknown type in system request: %s", args[0]);
263 }
263 }
264 }
264 }
265
265
266 /* Read response of command execution until receiving 'r'-esult */
266 /* Read response of command execution until receiving 'r'-esult */
267 static void handleresponse(hgclient_t *hgc)
267 static void handleresponse(hgclient_t *hgc)
268 {
268 {
269 for (;;) {
269 for (;;) {
270 readchannel(hgc);
270 readchannel(hgc);
271 context_t *ctx = &hgc->ctx;
271 context_t *ctx = &hgc->ctx;
272 debugmsg("response read from channel %c, size %zu",
272 debugmsg("response read from channel %c, size %zu",
273 ctx->ch, ctx->datasize);
273 ctx->ch, ctx->datasize);
274 switch (ctx->ch) {
274 switch (ctx->ch) {
275 case 'o':
275 case 'o':
276 fwrite(ctx->data, sizeof(ctx->data[0]), ctx->datasize,
276 fwrite(ctx->data, sizeof(ctx->data[0]), ctx->datasize,
277 stdout);
277 stdout);
278 break;
278 break;
279 case 'e':
279 case 'e':
280 fwrite(ctx->data, sizeof(ctx->data[0]), ctx->datasize,
280 fwrite(ctx->data, sizeof(ctx->data[0]), ctx->datasize,
281 stderr);
281 stderr);
282 break;
282 break;
283 case 'd':
283 case 'd':
284 /* assumes last char is '\n' */
284 /* assumes last char is '\n' */
285 ctx->data[ctx->datasize - 1] = '\0';
285 ctx->data[ctx->datasize - 1] = '\0';
286 debugmsg("server: %s", ctx->data);
286 debugmsg("server: %s", ctx->data);
287 break;
287 break;
288 case 'r':
288 case 'r':
289 return;
289 return;
290 case 'I':
290 case 'I':
291 handlereadrequest(hgc);
291 handlereadrequest(hgc);
292 break;
292 break;
293 case 'L':
293 case 'L':
294 handlereadlinerequest(hgc);
294 handlereadlinerequest(hgc);
295 break;
295 break;
296 case 'S':
296 case 'S':
297 handlesystemrequest(hgc);
297 handlesystemrequest(hgc);
298 break;
298 break;
299 default:
299 default:
300 if (isupper(ctx->ch))
300 if (isupper(ctx->ch))
301 abortmsg("cannot handle response (ch = %c)",
301 abortmsg("cannot handle response (ch = %c)",
302 ctx->ch);
302 ctx->ch);
303 }
303 }
304 }
304 }
305 }
305 }
306
306
307 static unsigned int parsecapabilities(const char *s, const char *e)
307 static unsigned int parsecapabilities(const char *s, const char *e)
308 {
308 {
309 unsigned int flags = 0;
309 unsigned int flags = 0;
310 while (s < e) {
310 while (s < e) {
311 const char *t = strchr(s, ' ');
311 const char *t = strchr(s, ' ');
312 if (!t || t > e)
312 if (!t || t > e)
313 t = e;
313 t = e;
314 const cappair_t *cap;
314 const cappair_t *cap;
315 for (cap = captable; cap->flag; ++cap) {
315 for (cap = captable; cap->flag; ++cap) {
316 size_t n = t - s;
316 size_t n = t - s;
317 if (strncmp(s, cap->name, n) == 0 &&
317 if (strncmp(s, cap->name, n) == 0 &&
318 strlen(cap->name) == n) {
318 strlen(cap->name) == n) {
319 flags |= cap->flag;
319 flags |= cap->flag;
320 break;
320 break;
321 }
321 }
322 }
322 }
323 s = t + 1;
323 s = t + 1;
324 }
324 }
325 return flags;
325 return flags;
326 }
326 }
327
327
328 static void readhello(hgclient_t *hgc)
328 static void readhello(hgclient_t *hgc)
329 {
329 {
330 readchannel(hgc);
330 readchannel(hgc);
331 context_t *ctx = &hgc->ctx;
331 context_t *ctx = &hgc->ctx;
332 if (ctx->ch != 'o') {
332 if (ctx->ch != 'o') {
333 char ch = ctx->ch;
333 char ch = ctx->ch;
334 if (ch == 'e') {
334 if (ch == 'e') {
335 /* write early error and will exit */
335 /* write early error and will exit */
336 fwrite(ctx->data, sizeof(ctx->data[0]), ctx->datasize,
336 fwrite(ctx->data, sizeof(ctx->data[0]), ctx->datasize,
337 stderr);
337 stderr);
338 handleresponse(hgc);
338 handleresponse(hgc);
339 }
339 }
340 abortmsg("unexpected channel of hello message (ch = %c)", ch);
340 abortmsg("unexpected channel of hello message (ch = %c)", ch);
341 }
341 }
342 enlargecontext(ctx, ctx->datasize + 1);
342 enlargecontext(ctx, ctx->datasize + 1);
343 ctx->data[ctx->datasize] = '\0';
343 ctx->data[ctx->datasize] = '\0';
344 debugmsg("hello received: %s (size = %zu)", ctx->data, ctx->datasize);
344 debugmsg("hello received: %s (size = %zu)", ctx->data, ctx->datasize);
345
345
346 const char *s = ctx->data;
346 const char *s = ctx->data;
347 const char *const dataend = ctx->data + ctx->datasize;
347 const char *const dataend = ctx->data + ctx->datasize;
348 while (s < dataend) {
348 while (s < dataend) {
349 const char *t = strchr(s, ':');
349 const char *t = strchr(s, ':');
350 if (!t || t[1] != ' ')
350 if (!t || t[1] != ' ')
351 break;
351 break;
352 const char *u = strchr(t + 2, '\n');
352 const char *u = strchr(t + 2, '\n');
353 if (!u)
353 if (!u)
354 u = dataend;
354 u = dataend;
355 if (strncmp(s, "capabilities:", t - s + 1) == 0) {
355 if (strncmp(s, "capabilities:", t - s + 1) == 0) {
356 hgc->capflags = parsecapabilities(t + 2, u);
356 hgc->capflags = parsecapabilities(t + 2, u);
357 } else if (strncmp(s, "pgid:", t - s + 1) == 0) {
357 } else if (strncmp(s, "pgid:", t - s + 1) == 0) {
358 hgc->pgid = strtol(t + 2, NULL, 10);
358 hgc->pgid = strtol(t + 2, NULL, 10);
359 } else if (strncmp(s, "pid:", t - s + 1) == 0) {
359 } else if (strncmp(s, "pid:", t - s + 1) == 0) {
360 hgc->pid = strtol(t + 2, NULL, 10);
360 hgc->pid = strtol(t + 2, NULL, 10);
361 }
361 }
362 s = u + 1;
362 s = u + 1;
363 }
363 }
364 debugmsg("capflags=0x%04x, pid=%d", hgc->capflags, hgc->pid);
364 debugmsg("capflags=0x%04x, pid=%d", hgc->capflags, hgc->pid);
365 }
365 }
366
366
367 static void updateprocname(hgclient_t *hgc)
367 static void updateprocname(hgclient_t *hgc)
368 {
368 {
369 size_t n = (size_t)snprintf(hgc->ctx.data, hgc->ctx.maxdatasize,
369 int r = snprintf(hgc->ctx.data, hgc->ctx.maxdatasize,
370 "chg[worker/%d]", (int)getpid());
370 "chg[worker/%d]", (int)getpid());
371 hgc->ctx.datasize = n;
371 if (r < 0 || (size_t)r >= hgc->ctx.maxdatasize)
372 abortmsg("insufficient buffer to write procname (r = %d)", r);
373 hgc->ctx.datasize = (size_t)r;
372 writeblockrequest(hgc, "setprocname");
374 writeblockrequest(hgc, "setprocname");
373 }
375 }
374
376
375 static void attachio(hgclient_t *hgc)
377 static void attachio(hgclient_t *hgc)
376 {
378 {
377 debugmsg("request attachio");
379 debugmsg("request attachio");
378 static const char chcmd[] = "attachio\n";
380 static const char chcmd[] = "attachio\n";
379 sendall(hgc->sockfd, chcmd, sizeof(chcmd) - 1);
381 sendall(hgc->sockfd, chcmd, sizeof(chcmd) - 1);
380 readchannel(hgc);
382 readchannel(hgc);
381 context_t *ctx = &hgc->ctx;
383 context_t *ctx = &hgc->ctx;
382 if (ctx->ch != 'I')
384 if (ctx->ch != 'I')
383 abortmsg("unexpected response for attachio (ch = %c)", ctx->ch);
385 abortmsg("unexpected response for attachio (ch = %c)", ctx->ch);
384
386
385 static const int fds[3] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
387 static const int fds[3] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO};
386 struct msghdr msgh;
388 struct msghdr msgh;
387 memset(&msgh, 0, sizeof(msgh));
389 memset(&msgh, 0, sizeof(msgh));
388 struct iovec iov = {ctx->data, ctx->datasize}; /* dummy payload */
390 struct iovec iov = {ctx->data, ctx->datasize}; /* dummy payload */
389 msgh.msg_iov = &iov;
391 msgh.msg_iov = &iov;
390 msgh.msg_iovlen = 1;
392 msgh.msg_iovlen = 1;
391 char fdbuf[CMSG_SPACE(sizeof(fds))];
393 char fdbuf[CMSG_SPACE(sizeof(fds))];
392 msgh.msg_control = fdbuf;
394 msgh.msg_control = fdbuf;
393 msgh.msg_controllen = sizeof(fdbuf);
395 msgh.msg_controllen = sizeof(fdbuf);
394 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh);
396 struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msgh);
395 cmsg->cmsg_level = SOL_SOCKET;
397 cmsg->cmsg_level = SOL_SOCKET;
396 cmsg->cmsg_type = SCM_RIGHTS;
398 cmsg->cmsg_type = SCM_RIGHTS;
397 cmsg->cmsg_len = CMSG_LEN(sizeof(fds));
399 cmsg->cmsg_len = CMSG_LEN(sizeof(fds));
398 memcpy(CMSG_DATA(cmsg), fds, sizeof(fds));
400 memcpy(CMSG_DATA(cmsg), fds, sizeof(fds));
399 msgh.msg_controllen = cmsg->cmsg_len;
401 msgh.msg_controllen = cmsg->cmsg_len;
400 ssize_t r = sendmsg(hgc->sockfd, &msgh, 0);
402 ssize_t r = sendmsg(hgc->sockfd, &msgh, 0);
401 if (r < 0)
403 if (r < 0)
402 abortmsgerrno("sendmsg failed");
404 abortmsgerrno("sendmsg failed");
403
405
404 handleresponse(hgc);
406 handleresponse(hgc);
405 int32_t n;
407 int32_t n;
406 if (ctx->datasize != sizeof(n))
408 if (ctx->datasize != sizeof(n))
407 abortmsg("unexpected size of attachio result");
409 abortmsg("unexpected size of attachio result");
408 memcpy(&n, ctx->data, sizeof(n));
410 memcpy(&n, ctx->data, sizeof(n));
409 n = ntohl(n);
411 n = ntohl(n);
410 if (n != sizeof(fds) / sizeof(fds[0]))
412 if (n != sizeof(fds) / sizeof(fds[0]))
411 abortmsg("failed to send fds (n = %d)", n);
413 abortmsg("failed to send fds (n = %d)", n);
412 }
414 }
413
415
414 static void chdirtocwd(hgclient_t *hgc)
416 static void chdirtocwd(hgclient_t *hgc)
415 {
417 {
416 if (!getcwd(hgc->ctx.data, hgc->ctx.maxdatasize))
418 if (!getcwd(hgc->ctx.data, hgc->ctx.maxdatasize))
417 abortmsgerrno("failed to getcwd");
419 abortmsgerrno("failed to getcwd");
418 hgc->ctx.datasize = strlen(hgc->ctx.data);
420 hgc->ctx.datasize = strlen(hgc->ctx.data);
419 writeblockrequest(hgc, "chdir");
421 writeblockrequest(hgc, "chdir");
420 }
422 }
421
423
422 static void forwardumask(hgclient_t *hgc)
424 static void forwardumask(hgclient_t *hgc)
423 {
425 {
424 mode_t mask = umask(0);
426 mode_t mask = umask(0);
425 umask(mask);
427 umask(mask);
426
428
427 static const char command[] = "setumask\n";
429 static const char command[] = "setumask\n";
428 sendall(hgc->sockfd, command, sizeof(command) - 1);
430 sendall(hgc->sockfd, command, sizeof(command) - 1);
429 uint32_t data = htonl(mask);
431 uint32_t data = htonl(mask);
430 sendall(hgc->sockfd, &data, sizeof(data));
432 sendall(hgc->sockfd, &data, sizeof(data));
431 }
433 }
432
434
433 /*!
435 /*!
434 * Open connection to per-user cmdserver
436 * Open connection to per-user cmdserver
435 *
437 *
436 * If no background server running, returns NULL.
438 * If no background server running, returns NULL.
437 */
439 */
438 hgclient_t *hgc_open(const char *sockname)
440 hgclient_t *hgc_open(const char *sockname)
439 {
441 {
440 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
442 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
441 if (fd < 0)
443 if (fd < 0)
442 abortmsgerrno("cannot create socket");
444 abortmsgerrno("cannot create socket");
443
445
444 /* don't keep fd on fork(), so that it can be closed when the parent
446 /* don't keep fd on fork(), so that it can be closed when the parent
445 * process get terminated. */
447 * process get terminated. */
446 fsetcloexec(fd);
448 fsetcloexec(fd);
447
449
448 struct sockaddr_un addr;
450 struct sockaddr_un addr;
449 addr.sun_family = AF_UNIX;
451 addr.sun_family = AF_UNIX;
450
452
451 /* use chdir to workaround small sizeof(sun_path) */
453 /* use chdir to workaround small sizeof(sun_path) */
452 int bakfd = -1;
454 int bakfd = -1;
453 const char *basename = sockname;
455 const char *basename = sockname;
454 {
456 {
455 const char *split = strrchr(sockname, '/');
457 const char *split = strrchr(sockname, '/');
456 if (split && split != sockname) {
458 if (split && split != sockname) {
457 if (split[1] == '\0')
459 if (split[1] == '\0')
458 abortmsg("sockname cannot end with a slash");
460 abortmsg("sockname cannot end with a slash");
459 size_t len = split - sockname;
461 size_t len = split - sockname;
460 char sockdir[len + 1];
462 char sockdir[len + 1];
461 memcpy(sockdir, sockname, len);
463 memcpy(sockdir, sockname, len);
462 sockdir[len] = '\0';
464 sockdir[len] = '\0';
463
465
464 bakfd = open(".", O_DIRECTORY);
466 bakfd = open(".", O_DIRECTORY);
465 if (bakfd == -1)
467 if (bakfd == -1)
466 abortmsgerrno("cannot open cwd");
468 abortmsgerrno("cannot open cwd");
467
469
468 int r = chdir(sockdir);
470 int r = chdir(sockdir);
469 if (r != 0)
471 if (r != 0)
470 abortmsgerrno("cannot chdir %s", sockdir);
472 abortmsgerrno("cannot chdir %s", sockdir);
471
473
472 basename = split + 1;
474 basename = split + 1;
473 }
475 }
474 }
476 }
475 if (strlen(basename) >= sizeof(addr.sun_path))
477 if (strlen(basename) >= sizeof(addr.sun_path))
476 abortmsg("sockname is too long: %s", basename);
478 abortmsg("sockname is too long: %s", basename);
477 strncpy(addr.sun_path, basename, sizeof(addr.sun_path));
479 strncpy(addr.sun_path, basename, sizeof(addr.sun_path));
478 addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
480 addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
479
481
480 /* real connect */
482 /* real connect */
481 int r = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
483 int r = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
482 if (r < 0) {
484 if (r < 0) {
483 if (errno != ENOENT && errno != ECONNREFUSED)
485 if (errno != ENOENT && errno != ECONNREFUSED)
484 abortmsgerrno("cannot connect to %s", sockname);
486 abortmsgerrno("cannot connect to %s", sockname);
485 }
487 }
486 if (bakfd != -1) {
488 if (bakfd != -1) {
487 fchdirx(bakfd);
489 fchdirx(bakfd);
488 close(bakfd);
490 close(bakfd);
489 }
491 }
490 if (r < 0) {
492 if (r < 0) {
491 close(fd);
493 close(fd);
492 return NULL;
494 return NULL;
493 }
495 }
494 debugmsg("connected to %s", addr.sun_path);
496 debugmsg("connected to %s", addr.sun_path);
495
497
496 hgclient_t *hgc = mallocx(sizeof(hgclient_t));
498 hgclient_t *hgc = mallocx(sizeof(hgclient_t));
497 memset(hgc, 0, sizeof(*hgc));
499 memset(hgc, 0, sizeof(*hgc));
498 hgc->sockfd = fd;
500 hgc->sockfd = fd;
499 initcontext(&hgc->ctx);
501 initcontext(&hgc->ctx);
500
502
501 readhello(hgc);
503 readhello(hgc);
502 if (!(hgc->capflags & CAP_RUNCOMMAND))
504 if (!(hgc->capflags & CAP_RUNCOMMAND))
503 abortmsg("insufficient capability: runcommand");
505 abortmsg("insufficient capability: runcommand");
504 if (hgc->capflags & CAP_SETPROCNAME)
506 if (hgc->capflags & CAP_SETPROCNAME)
505 updateprocname(hgc);
507 updateprocname(hgc);
506 if (hgc->capflags & CAP_ATTACHIO)
508 if (hgc->capflags & CAP_ATTACHIO)
507 attachio(hgc);
509 attachio(hgc);
508 if (hgc->capflags & CAP_CHDIR)
510 if (hgc->capflags & CAP_CHDIR)
509 chdirtocwd(hgc);
511 chdirtocwd(hgc);
510 if (hgc->capflags & CAP_SETUMASK)
512 if (hgc->capflags & CAP_SETUMASK)
511 forwardumask(hgc);
513 forwardumask(hgc);
512
514
513 return hgc;
515 return hgc;
514 }
516 }
515
517
516 /*!
518 /*!
517 * Close connection and free allocated memory
519 * Close connection and free allocated memory
518 */
520 */
519 void hgc_close(hgclient_t *hgc)
521 void hgc_close(hgclient_t *hgc)
520 {
522 {
521 assert(hgc);
523 assert(hgc);
522 freecontext(&hgc->ctx);
524 freecontext(&hgc->ctx);
523 close(hgc->sockfd);
525 close(hgc->sockfd);
524 free(hgc);
526 free(hgc);
525 }
527 }
526
528
527 pid_t hgc_peerpgid(const hgclient_t *hgc)
529 pid_t hgc_peerpgid(const hgclient_t *hgc)
528 {
530 {
529 assert(hgc);
531 assert(hgc);
530 return hgc->pgid;
532 return hgc->pgid;
531 }
533 }
532
534
533 pid_t hgc_peerpid(const hgclient_t *hgc)
535 pid_t hgc_peerpid(const hgclient_t *hgc)
534 {
536 {
535 assert(hgc);
537 assert(hgc);
536 return hgc->pid;
538 return hgc->pid;
537 }
539 }
538
540
539 /*!
541 /*!
540 * Send command line arguments to let the server load the repo config and check
542 * Send command line arguments to let the server load the repo config and check
541 * whether it can process our request directly or not.
543 * whether it can process our request directly or not.
542 * Make sure hgc_setenv is called before calling this.
544 * Make sure hgc_setenv is called before calling this.
543 *
545 *
544 * @return - NULL, the server believes it can handle our request, or does not
546 * @return - NULL, the server believes it can handle our request, or does not
545 * support "validate" command.
547 * support "validate" command.
546 * - a list of strings, the server probably cannot handle our request
548 * - a list of strings, the server probably cannot handle our request
547 * and it sent instructions telling us what to do next. See
549 * and it sent instructions telling us what to do next. See
548 * chgserver.py for possible instruction formats.
550 * chgserver.py for possible instruction formats.
549 * the list should be freed by the caller.
551 * the list should be freed by the caller.
550 * the last string is guaranteed to be NULL.
552 * the last string is guaranteed to be NULL.
551 */
553 */
552 const char **hgc_validate(hgclient_t *hgc, const char *const args[],
554 const char **hgc_validate(hgclient_t *hgc, const char *const args[],
553 size_t argsize)
555 size_t argsize)
554 {
556 {
555 assert(hgc);
557 assert(hgc);
556 if (!(hgc->capflags & CAP_VALIDATE))
558 if (!(hgc->capflags & CAP_VALIDATE))
557 return NULL;
559 return NULL;
558
560
559 packcmdargs(&hgc->ctx, args, argsize);
561 packcmdargs(&hgc->ctx, args, argsize);
560 writeblockrequest(hgc, "validate");
562 writeblockrequest(hgc, "validate");
561 handleresponse(hgc);
563 handleresponse(hgc);
562
564
563 /* the server returns '\0' if it can handle our request */
565 /* the server returns '\0' if it can handle our request */
564 if (hgc->ctx.datasize <= 1)
566 if (hgc->ctx.datasize <= 1)
565 return NULL;
567 return NULL;
566
568
567 /* make sure the buffer is '\0' terminated */
569 /* make sure the buffer is '\0' terminated */
568 enlargecontext(&hgc->ctx, hgc->ctx.datasize + 1);
570 enlargecontext(&hgc->ctx, hgc->ctx.datasize + 1);
569 hgc->ctx.data[hgc->ctx.datasize] = '\0';
571 hgc->ctx.data[hgc->ctx.datasize] = '\0';
570 return unpackcmdargsnul(&hgc->ctx);
572 return unpackcmdargsnul(&hgc->ctx);
571 }
573 }
572
574
573 /*!
575 /*!
574 * Execute the specified Mercurial command
576 * Execute the specified Mercurial command
575 *
577 *
576 * @return result code
578 * @return result code
577 */
579 */
578 int hgc_runcommand(hgclient_t *hgc, const char *const args[], size_t argsize)
580 int hgc_runcommand(hgclient_t *hgc, const char *const args[], size_t argsize)
579 {
581 {
580 assert(hgc);
582 assert(hgc);
581
583
582 packcmdargs(&hgc->ctx, args, argsize);
584 packcmdargs(&hgc->ctx, args, argsize);
583 writeblockrequest(hgc, "runcommand");
585 writeblockrequest(hgc, "runcommand");
584 handleresponse(hgc);
586 handleresponse(hgc);
585
587
586 int32_t exitcode_n;
588 int32_t exitcode_n;
587 if (hgc->ctx.datasize != sizeof(exitcode_n)) {
589 if (hgc->ctx.datasize != sizeof(exitcode_n)) {
588 abortmsg("unexpected size of exitcode");
590 abortmsg("unexpected size of exitcode");
589 }
591 }
590 memcpy(&exitcode_n, hgc->ctx.data, sizeof(exitcode_n));
592 memcpy(&exitcode_n, hgc->ctx.data, sizeof(exitcode_n));
591 return ntohl(exitcode_n);
593 return ntohl(exitcode_n);
592 }
594 }
593
595
594 /*!
596 /*!
595 * (Re-)send client's stdio channels so that the server can access to tty
597 * (Re-)send client's stdio channels so that the server can access to tty
596 */
598 */
597 void hgc_attachio(hgclient_t *hgc)
599 void hgc_attachio(hgclient_t *hgc)
598 {
600 {
599 assert(hgc);
601 assert(hgc);
600 if (!(hgc->capflags & CAP_ATTACHIO))
602 if (!(hgc->capflags & CAP_ATTACHIO))
601 return;
603 return;
602 attachio(hgc);
604 attachio(hgc);
603 }
605 }
604
606
605 /*!
607 /*!
606 * Update server's environment variables
608 * Update server's environment variables
607 *
609 *
608 * @param envp list of environment variables in "NAME=VALUE" format,
610 * @param envp list of environment variables in "NAME=VALUE" format,
609 * terminated by NULL.
611 * terminated by NULL.
610 */
612 */
611 void hgc_setenv(hgclient_t *hgc, const char *const envp[])
613 void hgc_setenv(hgclient_t *hgc, const char *const envp[])
612 {
614 {
613 assert(hgc && envp);
615 assert(hgc && envp);
614 if (!(hgc->capflags & CAP_SETENV))
616 if (!(hgc->capflags & CAP_SETENV))
615 return;
617 return;
616 packcmdargs(&hgc->ctx, envp, /*argsize*/ -1);
618 packcmdargs(&hgc->ctx, envp, /*argsize*/ -1);
617 writeblockrequest(hgc, "setenv");
619 writeblockrequest(hgc, "setenv");
618 }
620 }
General Comments 0
You need to be logged in to leave comments. Login now