82 debugmsg("initialize context buffer with size %zu", ctx->maxdatasize); |
82 debugmsg("initialize context buffer with size %zu", ctx->maxdatasize); |
83 } |
83 } |
84 |
84 |
85 static void enlargecontext(context_t *ctx, size_t newsize) |
85 static void enlargecontext(context_t *ctx, size_t newsize) |
86 { |
86 { |
87 if (newsize <= ctx->maxdatasize) |
87 if (newsize <= ctx->maxdatasize) { |
88 return; |
88 return; |
|
89 } |
89 |
90 |
90 newsize = defaultdatasize * |
91 newsize = defaultdatasize * |
91 ((newsize + defaultdatasize - 1) / defaultdatasize); |
92 ((newsize + defaultdatasize - 1) / defaultdatasize); |
92 ctx->data = reallocx(ctx->data, newsize); |
93 ctx->data = reallocx(ctx->data, newsize); |
93 ctx->maxdatasize = newsize; |
94 ctx->maxdatasize = newsize; |
115 exit(255); |
116 exit(255); |
116 } |
117 } |
117 |
118 |
118 uint32_t datasize_n; |
119 uint32_t datasize_n; |
119 rsize = recv(hgc->sockfd, &datasize_n, sizeof(datasize_n), 0); |
120 rsize = recv(hgc->sockfd, &datasize_n, sizeof(datasize_n), 0); |
120 if (rsize != sizeof(datasize_n)) |
121 if (rsize != sizeof(datasize_n)) { |
121 abortmsg("failed to read data size"); |
122 abortmsg("failed to read data size"); |
|
123 } |
122 |
124 |
123 /* datasize denotes the maximum size to write if input request */ |
125 /* datasize denotes the maximum size to write if input request */ |
124 hgc->ctx.datasize = ntohl(datasize_n); |
126 hgc->ctx.datasize = ntohl(datasize_n); |
125 enlargecontext(&hgc->ctx, hgc->ctx.datasize); |
127 enlargecontext(&hgc->ctx, hgc->ctx.datasize); |
126 |
128 |
127 if (isupper(hgc->ctx.ch) && hgc->ctx.ch != 'S') |
129 if (isupper(hgc->ctx.ch) && hgc->ctx.ch != 'S') { |
128 return; /* assumes input request */ |
130 return; /* assumes input request */ |
|
131 } |
129 |
132 |
130 size_t cursize = 0; |
133 size_t cursize = 0; |
131 while (cursize < hgc->ctx.datasize) { |
134 while (cursize < hgc->ctx.datasize) { |
132 rsize = recv(hgc->sockfd, hgc->ctx.data + cursize, |
135 rsize = recv(hgc->sockfd, hgc->ctx.data + cursize, |
133 hgc->ctx.datasize - cursize, 0); |
136 hgc->ctx.datasize - cursize, 0); |
134 if (rsize < 1) |
137 if (rsize < 1) { |
135 abortmsg("failed to read data block"); |
138 abortmsg("failed to read data block"); |
|
139 } |
136 cursize += rsize; |
140 cursize += rsize; |
137 } |
141 } |
138 } |
142 } |
139 |
143 |
140 static void sendall(int sockfd, const void *data, size_t datasize) |
144 static void sendall(int sockfd, const void *data, size_t datasize) |
141 { |
145 { |
142 const char *p = data; |
146 const char *p = data; |
143 const char *const endp = p + datasize; |
147 const char *const endp = p + datasize; |
144 while (p < endp) { |
148 while (p < endp) { |
145 ssize_t r = send(sockfd, p, endp - p, 0); |
149 ssize_t r = send(sockfd, p, endp - p, 0); |
146 if (r < 0) |
150 if (r < 0) { |
147 abortmsgerrno("cannot communicate"); |
151 abortmsgerrno("cannot communicate"); |
|
152 } |
148 p += r; |
153 p += r; |
149 } |
154 } |
150 } |
155 } |
151 |
156 |
152 /* Write lengh-data block to cmdserver */ |
157 /* Write lengh-data block to cmdserver */ |
184 enlargecontext(ctx, ctx->datasize + n); |
189 enlargecontext(ctx, ctx->datasize + n); |
185 memcpy(ctx->data + ctx->datasize, *it, n); |
190 memcpy(ctx->data + ctx->datasize, *it, n); |
186 ctx->datasize += n; |
191 ctx->datasize += n; |
187 } |
192 } |
188 |
193 |
189 if (ctx->datasize > 0) |
194 if (ctx->datasize > 0) { |
190 --ctx->datasize; /* strip last '\0' */ |
195 --ctx->datasize; /* strip last '\0' */ |
|
196 } |
191 } |
197 } |
192 |
198 |
193 /* Extract '\0'-separated list of args to new buffer, terminated by NULL */ |
199 /* Extract '\0'-separated list of args to new buffer, terminated by NULL */ |
194 static const char **unpackcmdargsnul(const context_t *ctx) |
200 static const char **unpackcmdargsnul(const context_t *ctx) |
195 { |
201 { |
223 |
230 |
224 /* Read single-line */ |
231 /* Read single-line */ |
225 static void handlereadlinerequest(hgclient_t *hgc) |
232 static void handlereadlinerequest(hgclient_t *hgc) |
226 { |
233 { |
227 context_t *ctx = &hgc->ctx; |
234 context_t *ctx = &hgc->ctx; |
228 if (!fgets(ctx->data, ctx->datasize, stdin)) |
235 if (!fgets(ctx->data, ctx->datasize, stdin)) { |
229 ctx->data[0] = '\0'; |
236 ctx->data[0] = '\0'; |
|
237 } |
230 ctx->datasize = strlen(ctx->data); |
238 ctx->datasize = strlen(ctx->data); |
231 writeblock(hgc); |
239 writeblock(hgc); |
232 } |
240 } |
233 |
241 |
234 /* Execute the requested command and write exit code */ |
242 /* Execute the requested command and write exit code */ |
237 context_t *ctx = &hgc->ctx; |
245 context_t *ctx = &hgc->ctx; |
238 enlargecontext(ctx, ctx->datasize + 1); |
246 enlargecontext(ctx, ctx->datasize + 1); |
239 ctx->data[ctx->datasize] = '\0'; /* terminate last string */ |
247 ctx->data[ctx->datasize] = '\0'; /* terminate last string */ |
240 |
248 |
241 const char **args = unpackcmdargsnul(ctx); |
249 const char **args = unpackcmdargsnul(ctx); |
242 if (!args[0] || !args[1] || !args[2]) |
250 if (!args[0] || !args[1] || !args[2]) { |
243 abortmsg("missing type or command or cwd in system request"); |
251 abortmsg("missing type or command or cwd in system request"); |
|
252 } |
244 if (strcmp(args[0], "system") == 0) { |
253 if (strcmp(args[0], "system") == 0) { |
245 debugmsg("run '%s' at '%s'", args[1], args[2]); |
254 debugmsg("run '%s' at '%s'", args[1], args[2]); |
246 int32_t r = runshellcmd(args[1], args + 3, args[2]); |
255 int32_t r = runshellcmd(args[1], args + 3, args[2]); |
247 free(args); |
256 free(args); |
248 |
257 |
250 memcpy(ctx->data, &r_n, sizeof(r_n)); |
259 memcpy(ctx->data, &r_n, sizeof(r_n)); |
251 ctx->datasize = sizeof(r_n); |
260 ctx->datasize = sizeof(r_n); |
252 writeblock(hgc); |
261 writeblock(hgc); |
253 } else if (strcmp(args[0], "pager") == 0) { |
262 } else if (strcmp(args[0], "pager") == 0) { |
254 setuppager(args[1], args + 3); |
263 setuppager(args[1], args + 3); |
255 if (hgc->capflags & CAP_ATTACHIO) |
264 if (hgc->capflags & CAP_ATTACHIO) { |
256 attachio(hgc); |
265 attachio(hgc); |
|
266 } |
257 /* unblock the server */ |
267 /* unblock the server */ |
258 static const char emptycmd[] = "\n"; |
268 static const char emptycmd[] = "\n"; |
259 sendall(hgc->sockfd, emptycmd, sizeof(emptycmd) - 1); |
269 sendall(hgc->sockfd, emptycmd, sizeof(emptycmd) - 1); |
260 } else { |
270 } else { |
261 abortmsg("unknown type in system request: %s", args[0]); |
271 abortmsg("unknown type in system request: %s", args[0]); |
294 break; |
304 break; |
295 case 'S': |
305 case 'S': |
296 handlesystemrequest(hgc); |
306 handlesystemrequest(hgc); |
297 break; |
307 break; |
298 default: |
308 default: |
299 if (isupper(ctx->ch)) |
309 if (isupper(ctx->ch)) { |
300 abortmsg("cannot handle response (ch = %c)", |
310 abortmsg("cannot handle response (ch = %c)", |
301 ctx->ch); |
311 ctx->ch); |
|
312 } |
302 } |
313 } |
303 } |
314 } |
304 } |
315 } |
305 |
316 |
306 static unsigned int parsecapabilities(const char *s, const char *e) |
317 static unsigned int parsecapabilities(const char *s, const char *e) |
307 { |
318 { |
308 unsigned int flags = 0; |
319 unsigned int flags = 0; |
309 while (s < e) { |
320 while (s < e) { |
310 const char *t = strchr(s, ' '); |
321 const char *t = strchr(s, ' '); |
311 if (!t || t > e) |
322 if (!t || t > e) { |
312 t = e; |
323 t = e; |
|
324 } |
313 const cappair_t *cap; |
325 const cappair_t *cap; |
314 for (cap = captable; cap->flag; ++cap) { |
326 for (cap = captable; cap->flag; ++cap) { |
315 size_t n = t - s; |
327 size_t n = t - s; |
316 if (strncmp(s, cap->name, n) == 0 && |
328 if (strncmp(s, cap->name, n) == 0 && |
317 strlen(cap->name) == n) { |
329 strlen(cap->name) == n) { |
344 |
356 |
345 const char *s = ctx->data; |
357 const char *s = ctx->data; |
346 const char *const dataend = ctx->data + ctx->datasize; |
358 const char *const dataend = ctx->data + ctx->datasize; |
347 while (s < dataend) { |
359 while (s < dataend) { |
348 const char *t = strchr(s, ':'); |
360 const char *t = strchr(s, ':'); |
349 if (!t || t[1] != ' ') |
361 if (!t || t[1] != ' ') { |
350 break; |
362 break; |
|
363 } |
351 const char *u = strchr(t + 2, '\n'); |
364 const char *u = strchr(t + 2, '\n'); |
352 if (!u) |
365 if (!u) { |
353 u = dataend; |
366 u = dataend; |
|
367 } |
354 if (strncmp(s, "capabilities:", t - s + 1) == 0) { |
368 if (strncmp(s, "capabilities:", t - s + 1) == 0) { |
355 hgc->capflags = parsecapabilities(t + 2, u); |
369 hgc->capflags = parsecapabilities(t + 2, u); |
356 } else if (strncmp(s, "pgid:", t - s + 1) == 0) { |
370 } else if (strncmp(s, "pgid:", t - s + 1) == 0) { |
357 hgc->pgid = strtol(t + 2, NULL, 10); |
371 hgc->pgid = strtol(t + 2, NULL, 10); |
358 } else if (strncmp(s, "pid:", t - s + 1) == 0) { |
372 } else if (strncmp(s, "pid:", t - s + 1) == 0) { |
365 |
379 |
366 static void updateprocname(hgclient_t *hgc) |
380 static void updateprocname(hgclient_t *hgc) |
367 { |
381 { |
368 int r = snprintf(hgc->ctx.data, hgc->ctx.maxdatasize, "chg[worker/%d]", |
382 int r = snprintf(hgc->ctx.data, hgc->ctx.maxdatasize, "chg[worker/%d]", |
369 (int)getpid()); |
383 (int)getpid()); |
370 if (r < 0 || (size_t)r >= hgc->ctx.maxdatasize) |
384 if (r < 0 || (size_t)r >= hgc->ctx.maxdatasize) { |
371 abortmsg("insufficient buffer to write procname (r = %d)", r); |
385 abortmsg("insufficient buffer to write procname (r = %d)", r); |
|
386 } |
372 hgc->ctx.datasize = (size_t)r; |
387 hgc->ctx.datasize = (size_t)r; |
373 writeblockrequest(hgc, "setprocname"); |
388 writeblockrequest(hgc, "setprocname"); |
374 } |
389 } |
375 |
390 |
376 static void attachio(hgclient_t *hgc) |
391 static void attachio(hgclient_t *hgc) |
378 debugmsg("request attachio"); |
393 debugmsg("request attachio"); |
379 static const char chcmd[] = "attachio\n"; |
394 static const char chcmd[] = "attachio\n"; |
380 sendall(hgc->sockfd, chcmd, sizeof(chcmd) - 1); |
395 sendall(hgc->sockfd, chcmd, sizeof(chcmd) - 1); |
381 readchannel(hgc); |
396 readchannel(hgc); |
382 context_t *ctx = &hgc->ctx; |
397 context_t *ctx = &hgc->ctx; |
383 if (ctx->ch != 'I') |
398 if (ctx->ch != 'I') { |
384 abortmsg("unexpected response for attachio (ch = %c)", ctx->ch); |
399 abortmsg("unexpected response for attachio (ch = %c)", ctx->ch); |
|
400 } |
385 |
401 |
386 static const int fds[3] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}; |
402 static const int fds[3] = {STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO}; |
387 struct msghdr msgh; |
403 struct msghdr msgh; |
388 memset(&msgh, 0, sizeof(msgh)); |
404 memset(&msgh, 0, sizeof(msgh)); |
389 struct iovec iov = {ctx->data, ctx->datasize}; /* dummy payload */ |
405 struct iovec iov = {ctx->data, ctx->datasize}; /* dummy payload */ |
397 cmsg->cmsg_type = SCM_RIGHTS; |
413 cmsg->cmsg_type = SCM_RIGHTS; |
398 cmsg->cmsg_len = CMSG_LEN(sizeof(fds)); |
414 cmsg->cmsg_len = CMSG_LEN(sizeof(fds)); |
399 memcpy(CMSG_DATA(cmsg), fds, sizeof(fds)); |
415 memcpy(CMSG_DATA(cmsg), fds, sizeof(fds)); |
400 msgh.msg_controllen = cmsg->cmsg_len; |
416 msgh.msg_controllen = cmsg->cmsg_len; |
401 ssize_t r = sendmsg(hgc->sockfd, &msgh, 0); |
417 ssize_t r = sendmsg(hgc->sockfd, &msgh, 0); |
402 if (r < 0) |
418 if (r < 0) { |
403 abortmsgerrno("sendmsg failed"); |
419 abortmsgerrno("sendmsg failed"); |
|
420 } |
404 |
421 |
405 handleresponse(hgc); |
422 handleresponse(hgc); |
406 int32_t n; |
423 int32_t n; |
407 if (ctx->datasize != sizeof(n)) |
424 if (ctx->datasize != sizeof(n)) { |
408 abortmsg("unexpected size of attachio result"); |
425 abortmsg("unexpected size of attachio result"); |
|
426 } |
409 memcpy(&n, ctx->data, sizeof(n)); |
427 memcpy(&n, ctx->data, sizeof(n)); |
410 n = ntohl(n); |
428 n = ntohl(n); |
411 if (n != sizeof(fds) / sizeof(fds[0])) |
429 if (n != sizeof(fds) / sizeof(fds[0])) { |
412 abortmsg("failed to send fds (n = %d)", n); |
430 abortmsg("failed to send fds (n = %d)", n); |
|
431 } |
413 } |
432 } |
414 |
433 |
415 static void chdirtocwd(hgclient_t *hgc) |
434 static void chdirtocwd(hgclient_t *hgc) |
416 { |
435 { |
417 if (!getcwd(hgc->ctx.data, hgc->ctx.maxdatasize)) |
436 if (!getcwd(hgc->ctx.data, hgc->ctx.maxdatasize)) { |
418 abortmsgerrno("failed to getcwd"); |
437 abortmsgerrno("failed to getcwd"); |
|
438 } |
419 hgc->ctx.datasize = strlen(hgc->ctx.data); |
439 hgc->ctx.datasize = strlen(hgc->ctx.data); |
420 writeblockrequest(hgc, "chdir"); |
440 writeblockrequest(hgc, "chdir"); |
421 } |
441 } |
422 |
442 |
423 static void forwardumask(hgclient_t *hgc) |
443 static void forwardumask(hgclient_t *hgc) |
438 * If no background server running, returns NULL. |
458 * If no background server running, returns NULL. |
439 */ |
459 */ |
440 hgclient_t *hgc_open(const char *sockname) |
460 hgclient_t *hgc_open(const char *sockname) |
441 { |
461 { |
442 int fd = socket(AF_UNIX, SOCK_STREAM, 0); |
462 int fd = socket(AF_UNIX, SOCK_STREAM, 0); |
443 if (fd < 0) |
463 if (fd < 0) { |
444 abortmsgerrno("cannot create socket"); |
464 abortmsgerrno("cannot create socket"); |
|
465 } |
445 |
466 |
446 /* don't keep fd on fork(), so that it can be closed when the parent |
467 /* don't keep fd on fork(), so that it can be closed when the parent |
447 * process get terminated. */ |
468 * process get terminated. */ |
448 fsetcloexec(fd); |
469 fsetcloexec(fd); |
449 |
470 |
454 int bakfd = -1; |
475 int bakfd = -1; |
455 const char *basename = sockname; |
476 const char *basename = sockname; |
456 { |
477 { |
457 const char *split = strrchr(sockname, '/'); |
478 const char *split = strrchr(sockname, '/'); |
458 if (split && split != sockname) { |
479 if (split && split != sockname) { |
459 if (split[1] == '\0') |
480 if (split[1] == '\0') { |
460 abortmsg("sockname cannot end with a slash"); |
481 abortmsg("sockname cannot end with a slash"); |
|
482 } |
461 size_t len = split - sockname; |
483 size_t len = split - sockname; |
462 char sockdir[len + 1]; |
484 char sockdir[len + 1]; |
463 memcpy(sockdir, sockname, len); |
485 memcpy(sockdir, sockname, len); |
464 sockdir[len] = '\0'; |
486 sockdir[len] = '\0'; |
465 |
487 |
466 bakfd = open(".", O_DIRECTORY); |
488 bakfd = open(".", O_DIRECTORY); |
467 if (bakfd == -1) |
489 if (bakfd == -1) { |
468 abortmsgerrno("cannot open cwd"); |
490 abortmsgerrno("cannot open cwd"); |
|
491 } |
469 |
492 |
470 int r = chdir(sockdir); |
493 int r = chdir(sockdir); |
471 if (r != 0) |
494 if (r != 0) { |
472 abortmsgerrno("cannot chdir %s", sockdir); |
495 abortmsgerrno("cannot chdir %s", sockdir); |
|
496 } |
473 |
497 |
474 basename = split + 1; |
498 basename = split + 1; |
475 } |
499 } |
476 } |
500 } |
477 if (strlen(basename) >= sizeof(addr.sun_path)) |
501 if (strlen(basename) >= sizeof(addr.sun_path)) { |
478 abortmsg("sockname is too long: %s", basename); |
502 abortmsg("sockname is too long: %s", basename); |
|
503 } |
479 strncpy(addr.sun_path, basename, sizeof(addr.sun_path)); |
504 strncpy(addr.sun_path, basename, sizeof(addr.sun_path)); |
480 addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; |
505 addr.sun_path[sizeof(addr.sun_path) - 1] = '\0'; |
481 |
506 |
482 /* real connect */ |
507 /* real connect */ |
483 int r = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); |
508 int r = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); |
484 if (r < 0) { |
509 if (r < 0) { |
485 if (errno != ENOENT && errno != ECONNREFUSED) |
510 if (errno != ENOENT && errno != ECONNREFUSED) { |
486 abortmsgerrno("cannot connect to %s", sockname); |
511 abortmsgerrno("cannot connect to %s", sockname); |
|
512 } |
487 } |
513 } |
488 if (bakfd != -1) { |
514 if (bakfd != -1) { |
489 fchdirx(bakfd); |
515 fchdirx(bakfd); |
490 close(bakfd); |
516 close(bakfd); |
491 } |
517 } |
499 memset(hgc, 0, sizeof(*hgc)); |
525 memset(hgc, 0, sizeof(*hgc)); |
500 hgc->sockfd = fd; |
526 hgc->sockfd = fd; |
501 initcontext(&hgc->ctx); |
527 initcontext(&hgc->ctx); |
502 |
528 |
503 readhello(hgc); |
529 readhello(hgc); |
504 if (!(hgc->capflags & CAP_RUNCOMMAND)) |
530 if (!(hgc->capflags & CAP_RUNCOMMAND)) { |
505 abortmsg("insufficient capability: runcommand"); |
531 abortmsg("insufficient capability: runcommand"); |
506 if (hgc->capflags & CAP_SETPROCNAME) |
532 } |
|
533 if (hgc->capflags & CAP_SETPROCNAME) { |
507 updateprocname(hgc); |
534 updateprocname(hgc); |
508 if (hgc->capflags & CAP_ATTACHIO) |
535 } |
|
536 if (hgc->capflags & CAP_ATTACHIO) { |
509 attachio(hgc); |
537 attachio(hgc); |
510 if (hgc->capflags & CAP_CHDIR) |
538 } |
|
539 if (hgc->capflags & CAP_CHDIR) { |
511 chdirtocwd(hgc); |
540 chdirtocwd(hgc); |
512 if (hgc->capflags & CAP_SETUMASK2) |
541 } |
|
542 if (hgc->capflags & CAP_SETUMASK2) { |
513 forwardumask(hgc); |
543 forwardumask(hgc); |
|
544 } |
514 |
545 |
515 return hgc; |
546 return hgc; |
516 } |
547 } |
517 |
548 |
518 /*! |
549 /*! |
553 */ |
584 */ |
554 const char **hgc_validate(hgclient_t *hgc, const char *const args[], |
585 const char **hgc_validate(hgclient_t *hgc, const char *const args[], |
555 size_t argsize) |
586 size_t argsize) |
556 { |
587 { |
557 assert(hgc); |
588 assert(hgc); |
558 if (!(hgc->capflags & CAP_VALIDATE)) |
589 if (!(hgc->capflags & CAP_VALIDATE)) { |
559 return NULL; |
590 return NULL; |
|
591 } |
560 |
592 |
561 packcmdargs(&hgc->ctx, args, argsize); |
593 packcmdargs(&hgc->ctx, args, argsize); |
562 writeblockrequest(hgc, "validate"); |
594 writeblockrequest(hgc, "validate"); |
563 handleresponse(hgc); |
595 handleresponse(hgc); |
564 |
596 |
565 /* the server returns '\0' if it can handle our request */ |
597 /* the server returns '\0' if it can handle our request */ |
566 if (hgc->ctx.datasize <= 1) |
598 if (hgc->ctx.datasize <= 1) { |
567 return NULL; |
599 return NULL; |
|
600 } |
568 |
601 |
569 /* make sure the buffer is '\0' terminated */ |
602 /* make sure the buffer is '\0' terminated */ |
570 enlargecontext(&hgc->ctx, hgc->ctx.datasize + 1); |
603 enlargecontext(&hgc->ctx, hgc->ctx.datasize + 1); |
571 hgc->ctx.data[hgc->ctx.datasize] = '\0'; |
604 hgc->ctx.data[hgc->ctx.datasize] = '\0'; |
572 return unpackcmdargsnul(&hgc->ctx); |
605 return unpackcmdargsnul(&hgc->ctx); |
611 * terminated by NULL. |
645 * terminated by NULL. |
612 */ |
646 */ |
613 void hgc_setenv(hgclient_t *hgc, const char *const envp[]) |
647 void hgc_setenv(hgclient_t *hgc, const char *const envp[]) |
614 { |
648 { |
615 assert(hgc && envp); |
649 assert(hgc && envp); |
616 if (!(hgc->capflags & CAP_SETENV)) |
650 if (!(hgc->capflags & CAP_SETENV)) { |
617 return; |
651 return; |
|
652 } |
618 packcmdargs(&hgc->ctx, envp, /*argsize*/ -1); |
653 packcmdargs(&hgc->ctx, envp, /*argsize*/ -1); |
619 writeblockrequest(hgc, "setenv"); |
654 writeblockrequest(hgc, "setenv"); |
620 } |
655 } |