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