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