Initial revision
[sw-tools] / src / sw_rsh.c
CommitLineData
3315e8b3 1/* -*-c-*-
2 *
3 * $Id: sw_rsh.c,v 1.1 1999/06/02 16:53:34 mdw Exp $
4 *
5 * Run remote commands
6 *
7 * (c) 1999 EBI
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of sw-tools.
13 *
14 * sw-tools is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * sw-tools is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with sw-tools; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29/*----- Revision history --------------------------------------------------*
30 *
31 * $Log: sw_rsh.c,v $
32 * Revision 1.1 1999/06/02 16:53:34 mdw
33 * Initial revision
34 *
35 */
36
37/*----- Header files ------------------------------------------------------*/
38
39#include "config.h"
40
41#include <ctype.h>
42#include <errno.h>
43#include <signal.h>
44#include <stdarg.h>
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48
49#include <sys/types.h>
50#include <sys/time.h>
51#include <fcntl.h>
52#include <unistd.h>
53
54#include <sys/socket.h>
55#include <sys/wait.h>
56
57#ifndef DECL_ENVIRON
58 extern char **environ;
59#endif
60
61#include <mLib/alloc.h>
62#include <mLib/dstr.h>
63#include <mLib/exc.h>
64#include <mLib/mdwopt.h>
65#include <mLib/quis.h>
66#include <mLib/report.h>
67#include <mLib/sym.h>
68
69#define RCMD_LINK 0
70#include "sw_arch.h"
71#include "sw_build.h"
72#include "sw_env.h"
73#include "sw_rsh.h"
74
75/*----- Data structures ---------------------------------------------------*/
76
77typedef unsigned char octet;
78
79#define PKHEADSZ 3
80
81typedef struct pkhead {
82 octet len[2];
83 octet type;
84} pkhead;
85
86/*----- Static variables --------------------------------------------------*/
87
88static int handler = 0;
89static rcmd *rcmds = RCMD_LINK;
90
91/*----- Packet interface --------------------------------------------------*/
92
93/* --- @pksend@ --- *
94 *
95 * Arguments: @sw_remote@ = pointer to the remote block
96 * @int type@ = packet type to send
97 * @const void *p@ = pointer to packet data
98 * @size_t sz@ = size of data to send
99 *
100 * Returns: Zero if it worked, nonzero otherwise.
101 *
102 * Use: Sends a data packet. If the type is `data', then `sz' may be
103 * arbitrarily large and is divided into small eenough chunks.
104 * Otherwise it's an error to send a packet that's too big.
105 */
106
107int pksend(sw_remote *r, int type, const void *p, size_t sz)
108{
109 pkhead h;
110 const char *q = p;
111 size_t chunk;
112
113 /* --- Sort out error conditions --- */
114
115 if (sz > PKMAX && type != PKTYPE_DATA) {
116 errno = E2BIG;
117 return (-1);
118 }
119
120 /* --- Main output loop --- */
121
122 h.type = type;
123 do {
124
125 /* --- Set up the packet header --- */
126
127 chunk = (sz > PKMAX ? PKMAX : sz);
128 h.len[0] = chunk & 0xff;
129 h.len[1] = (chunk >> 8) & 0xff;
130
131 /* --- Write the packet header --- */
132
133 try_again:
134 if (write(r->fdout, &h, PKHEADSZ) < PKHEADSZ) {
135 if (errno == EINTR)
136 goto try_again;
137 return (-1);
138 }
139
140 /* --- Write the payload, if there is one --- *
141 *
142 * Maybe the OS won't want to bite it all off in one go.
143 */
144
145 while (chunk) {
146 ssize_t n = write(r->fdout, q, chunk);
147 if (n < 0 && errno != EINTR)
148 return (-1);
149 if (n > 0) {
150 q += n;
151 chunk -= n;
152 sz -= n;
153 }
154 }
155 } while (sz);
156
157 /* --- Done --- */
158
159 return (0);
160}
161
162/* --- @pkrecv@ --- *
163 *
164 * Arguments: @sw_remote *r@ = remote block
165 *
166 * Returns: Packet type received, or @-1@ for an error.
167 *
168 * Use: Receives a packet from the remote host. The packet data is
169 * placed in the block's buffer, the block's packet length is
170 * diddled appropriately.
171 */
172
173int pkrecv(sw_remote *r)
174{
175 pkhead h;
176 size_t sz;
177 char *p;
178 ssize_t n;
179
180 /* --- Read the packet header --- */
181
182 sz = PKHEADSZ;
183 p = (char *)&h;
184 while (sz) {
185 n = read(r->fdin, p, sz);
186 if (n < 0 && errno != EINTR)
187 return (-1);
188 if (n == 0)
189 return (PKTYPE_EOF);
190 if (n > 0) {
191 p += n;
192 sz -= n;
193 }
194 }
195
196 /* --- Hack for error messages --- *
197 *
198 * If it doesn't look like a valid packet, read a `chunk' and pretend it's
199 * data. This isn't too bad, because all the packet types are control
200 * codes, and are unlikely to be in a textual message.
201 *
202 * Normally what happens here is that something sitting before the `sw'
203 * program fails, reports a plain textual error, and exits. Grabbing the
204 * `last gasp' like this, then, traps that error message and allows
205 * something to report it. The rest ought to be completely silent, so I
206 * get an `unexpected eof' and then drop everything.
207 *
208 * This is certainly better than the behaviour otherwise, which is an
209 * @E2BIG@ message reported when the packet size is really ASCII
210 * characters.
211 */
212
213 if (h.type >= PKTYPE_BOGUS) {
214 memcpy(r->buf, &h, PKHEADSZ);
215 n = read(r->fdin, r->buf + PKHEADSZ, sizeof(r->buf) - PKHEADSZ);
216 if (n < 0)
217 return (-1);
218 r->sz = n + PKHEADSZ;
219 return (PKTYPE_DATA);
220 }
221
222 /* --- Sort out what's going on --- */
223
224 sz = h.len[0] | (h.len[1] << 8);
225 r->sz = sz;
226 if (!sz)
227 return (h.type);
228 if (sz > PKMAX) {
229 errno = E2BIG;
230 return (-1);
231 }
232
233 /* --- Read the packet payload --- */
234
235 p = r->buf;
236 while (sz) {
237 n = read(r->fdin, p, sz);
238 if (n < 0 && errno != EINTR)
239 return (-1);
240 if (n == 0)
241 return (PKTYPE_EOF);
242 if (n > 0) {
243 p += n;
244 sz -= n;
245 }
246 }
247
248 return (h.type);
249}
250
251/*----- Error reporting and exit statuses --------------------------------*/
252
253/* --- @swexit@ --- *
254 *
255 * Arguments: @sw_remote *r@ = remote context
256 * @int status@ = exit status to return
257 *
258 * Returns: Doesn't.
259 *
260 * Use: Reports the exit status via packet protocol and quits.
261 */
262
263void swexit(sw_remote *r, int status)
264{
265 unsigned char s = status;
266 pksend(r, PKTYPE_STATUS, &s, 1);
267 _exit(status);
268}
269
270/* --- @swsignal@ --- *
271 *
272 * Arguments: @sw_remote *r@ = remote context
273 * @int sig@ = signal ocurrence to return
274 *
275 * Returns: Doesn't.
276 *
277 * Use: Reports a signalled-to-death status via packet protocol and
278 * quits.
279 */
280
281void swsignal(sw_remote *r, int sig)
282{
283#if defined(HAVE_STRSIGNAL)
284 char *s = strsignal(sig);
285#elif defined(HAVE__SYS_SIGLIST)
286 char *s = _sys_siglist[sig];
287#else
288 char s[16];
289 sprintf(s, "signal %i", sig);
290#endif
291
292 pksend(r, PKTYPE_STATUS, s, strlen(s) + 1);
293 _exit(127);
294}
295
296/* --- @swwait@ --- *
297 *
298 * Arguments: @sw_remote *r@ = remote context
299 * @int status@ = status answer from @wait@(2)
300 *
301 * Returns: Doesn't.
302 *
303 * Use: Reports a child's demise appropriately, and quits.
304 */
305
306void swwait(sw_remote *r, int status)
307{
308 if (WIFEXITED(status))
309 swexit(r, WEXITSTATUS(status));
310 else if (WIFSIGNALED(status))
311 swsignal(r, WTERMSIG(status));
312 else
313 swexit(r, 126);
314}
315
316/* --- @swvprintf@ --- *
317 *
318 * Arguments: @sw_remote *r@ = remote context
319 * @const char *format@ = format string
320 * @va_list ap@ = things to format
321 *
322 * Returns: ---
323 *
324 * Use: Writes a string to the remote end. This is the low-level bit
325 * of @swprintf@.
326 */
327
328void swvprintf(sw_remote *r, const char *format, va_list ap)
329{
330 dstr d = DSTR_INIT;
331 dstr_vputf(&d, format, ap);
332 pksend(r, PKTYPE_DATA, d.buf, d.len);
333 dstr_destroy(&d);
334}
335
336/* --- @swprintf@ --- *
337 *
338 * Arguments: @sw_remote *r@ = remote context
339 * @const char *format@ = format string
340 * @...@ = other arguments
341 *
342 * Returns: ---
343 *
344 * Use: Writes a string to the remote end.
345 */
346
347void swprintf(sw_remote *r, const char *format, ...)
348{
349 va_list ap;
350 va_start(ap, format);
351 swvprintf(r, format, ap);
352 va_end(ap);
353}
354
355/* --- @swdie@ --- *
356 *
357 * Arguments: @sw_remote *r@ = remote context
358 * @int status@ = exit status to report
359 * @const char *format@ = format string to fill in
360 * @...@ = other arguments
361 *
362 * Returns: Doesn't.
363 *
364 * Use: Reports a message and quits.
365 */
366
367void swdie(sw_remote *r, int status, const char *format, ...)
368{
369 va_list ap;
370 dstr d = DSTR_INIT;
371
372 va_start(ap, format);
373 dstr_putf(&d, "%s [remote]: ", QUIS);
374 dstr_vputf(&d, format, ap);
375 dstr_putc(&d, '\n');
376 dstr_putz(&d);
377 va_end(ap);
378 pksend(r, PKTYPE_DATA, d.buf, d.len);
379 dstr_destroy(&d);
380 swexit(r, status);
381}
382
383/*----- Command handling and dispatch -------------------------------------*/
384
385/* --- @remote@ --- *
386 *
387 * Arguments: @sw_remote *r@ = pointer to remote block
388 * @const char *cmd@ = command to run
389 * @char *argv[]@ = argument array
390 * @char *env[]@ = environment variables
391 *
392 * Returns: Doesn't. Reports an exit status through packet protocol and
393 * quits.
394 *
395 * Use: Dispatches a remote command. At this point, the two code
396 * paths for local and remote invokation have joined again.
397 */
398
399static void remote(sw_remote *r, const char *cmd, char *argv[], char *env[])
400{
401 struct rcmd *p, *chosen = 0;
402 size_t sz = strlen(cmd);
403
404 /* --- Make sure that I can get the exit status of children --- */
405
406 signal(SIGCHLD, SIG_DFL);
407
408 /* --- Fix up the environment --- */
409
410 {
411 sym_table t;
412 sym_create(&t);
413 env_import(&t, env);
414 if (env != environ) {
415 free(env);
416 env_import(&t, environ);
417 }
418 env_put(&t, "SW_ARCH", ARCH);
419 env_file(&t, DATADIR "/sw-env");
420 env = env_export(&t);
421 }
422
423 /* --- Dispatch to the command handler --- */
424
425 for (p = rcmds; p; p = p->next) {
426 if (strncmp(cmd, p->name, sz) == 0) {
427 if (p->name[sz] == 0) {
428 chosen = p;
429 break;
430 } else if (chosen)
431 swdie(r, 1, "ambiguous remote command name `%s'", cmd);
432 else
433 chosen = p;
434 }
435 }
436 if (!chosen)
437 swdie(r, 1, "unknown remote command name `%s'", cmd);
438 chosen->rcmd(r, argv, env);
439}
440
441/*----- Remote invocation -------------------------------------------------*/
442
443/* --- @sendargv@ --- *
444 *
445 * Arguments: @sw_remote *r@ = pointer to the remote context
446 * @int type@ = packet type to send with
447 * @char *v[]@ = pointer to the array to send
448 *
449 * Returns: Zero if OK, nonzero if it failed.
450 *
451 * Use: Sends something @argv@-shaped; i.e., an array of strings
452 * terminated by a null pointer.
453 */
454
455static int sendargv(sw_remote *r, int type, char *v[])
456{
457 dstr d = DSTR_INIT;
458 int e;
459
460 while (*v) {
461 dstr_puts(&d, *v);
462 d.len++; /* Make the null `real' */
463 v++;
464 }
465 e = pksend(r, type, d.buf, d.len);
466 dstr_destroy(&d);
467 return (e);
468}
469
470/* --- @snarfargv@ --- *
471 *
472 * Arguments: @const char *buf@ = pointer to buffer
473 * @size_t sz@ = size of buffer
474 *
475 * Returns: Pointer to argument array (allocated with @malloc@).
476 *
477 * Use: Snarfs the null-terminated strings in the buffer and returns
478 * an array of them. The whole lot, strings and array, is
479 * returned in one big chunk allocated from the heap. Note that
480 * this means it's OK to throw the initial buffer away.
481 */
482
483static char **snarfargv(const char *buf, size_t sz)
484{
485 /* --- Initial setup --- */
486
487 const char *p, *lim;
488 char *q;
489 size_t c;
490 char **v, **w;
491
492 /* --- Pass one: count the number of arguments --- */
493
494 c = 0;
495 p = buf;
496 lim = p + sz;
497 while (p < lim) {
498 c++;
499 while (*p) {
500 p++;
501 if (p >= lim)
502 goto done_pass1;
503 }
504 p++;
505 }
506done_pass1:
507
508 /* --- Allocate memory for everything --- */
509
510 v = xmalloc((c + 1) * sizeof(char *) + sz + 1);
511 q = (char *)(v + c + 1);
512 memcpy(q, buf, sz);
513
514 /* --- Pass two: set up the arrays --- */
515
516 lim = q + sz;
517 w = v;
518 while (q < lim) {
519 *w++ = q;
520 while (*q) {
521 q++;
522 if (q >= lim)
523 goto done_pass2;
524 }
525 q++;
526 }
527done_pass2:
528 *w++ = 0;
529 *q++ = 0;
530
531 /* --- Done --- */
532
533 return (v);
534}
535
536/* --- @swrsh_remote@ --- *
537 *
538 * Arguments: @const char *cmd@ = the command to perform
539 *
540 * Returns: Doesn't. Reports an exit status through packet protocol and
541 * quits.
542 *
543 * Use: Handles the remote end of a remote job invokation.
544 */
545
546void swrsh_remote(const char *cmd)
547{
548 sw_remote r;
549 static char *dummy = 0;
550 char **argv = 0;
551 char **env = 0;
552 char *dir = 0;
553 r.fdin = 0;
554 r.fdout = 1;
555
556 /* --- Read packets from the remote host --- */
557
558 for (;;) {
559 int t = pkrecv(&r);
560 switch (t) {
561 case -1:
562 swdie(&r, 1, "error reading packet: %s", strerror(errno));
563 break;
564 case PKTYPE_EOF:
565 goto done;
566 case PKTYPE_ARGS:
567 if (argv)
568 free(argv);
569 argv = snarfargv(r.buf, r.sz);
570 break;
571 case PKTYPE_ENV:
572 if (env)
573 free(env);
574 env = snarfargv(r.buf, r.sz);
575 break;
576 case PKTYPE_DIR:
577 if (dir)
578 free(dir);
579 r.buf[r.sz] = 0;
580 dir = xstrdup(r.buf);
581 break;
582 case PKTYPE_GO:
583 goto done;
584 case PKTYPE_DATA:
585 case PKTYPE_STATUS:
586 swdie(&r, 1, "internal error: unexpected packet");
587 break;
588 }
589 }
590
591 /* --- Sort out any missing arguments --- */
592
593done:
594 if (!argv)
595 argv = &dummy;
596 if (!env)
597 env = &dummy;
598 if (dir)
599 chdir(dir);
600
601 /* --- Run the command --- */
602
603 TRY
604 remote(&r, cmd, argv, env);
605 CATCH switch (exc_type) {
606 case EXC_NOMEM: {
607 static char msg[] = "\nsw [remote]: not enough memory\n";
608 pksend(&r, PKTYPE_DATA, msg, sizeof(msg) - 1);
609 swexit(&r, 1);
610 } break;
611 default:
612 swdie(&r, 1, "uncaught exception, type = %lx", exc_type);
613 } END_TRY;
614}
615
616/*----- Starting remote jobs ----------------------------------------------*/
617
618/* --- @sigchld@ --- *
619 *
620 * Arguments: @int sig@ = the signal number
621 *
622 * Returns: ---
623 *
624 * Use: Catches @SIGCHLD@ and reaps any children that have lost.
625 */
626
627static void sigchld(int sig)
628{
629#ifdef DEBUG_SIGCHLD
630 int status;
631 while (waitpid(-1, &status, WNOHANG) > 0) {
632 if (WIFEXITED(status)) {
633 fprintf(stderr, "reap child with exit status %i\n",
634 WEXITSTATUS(status));
635 } else if (WIFSIGNALED(status)) {
636 fprintf(stderr, "reap child killed by signal %s\n",
637 strsignal(WTERMSIG(status)));
638 } else
639 fprintf(stderr, "reaped bizarre child which is still alive\n");
640 }
641#else
642 while (waitpid(-1, 0, WNOHANG) > 0)
643 ;
644#endif
645}
646
647/* --- @swrsh@ --- *
648 *
649 * Arguments: @sw_remote *r@ = remote process block to look after
650 * @const char *host@ = host to run on (0 for this one)
651 * @const char *cmd@ = remote command to run
652 * @char *argv[]@ = arguments to pass on
653 *
654 * Returns: Zero if it worked, nonzero if not.
655 *
656 * Use: Runs a command on a remote host. The argument array is
657 * mangled to come out OK at the far end. The environment and
658 * current directory are also passed along, and pop out the
659 * other end unmolested.
660 */
661
662int swrsh(sw_remote *r, const char *host, const char *cmd, char *argv[])
663{
664 int sk[2];
665 pid_t kid;
666
667 /* --- Get a socket pair for communicating with the other end --- */
668
669 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sk))
670 goto tidy_0;
671
672 /* --- Set up a signal handler --- */
673
674 if (!handler) {
675 struct sigaction sa;
676 sa.sa_handler = sigchld;
677 sa.sa_flags = 0;
678 sigemptyset(&sa.sa_mask);
679 sigaction(SIGCHLD, &sa, 0);
680 handler = 1;
681 }
682
683 /* --- Fork off a child to cope with stuff --- */
684
685 kid = fork();
686 if (kid < 0)
687 goto tidy_1;
688
689 /* --- Handle the child process --- *
690 *
691 * If this is a local job, then just loop around inside to handle the
692 * `remote' command. Otherwise crank up `rsh' and pass the command over to
693 * a remote copy of myself.
694 *
695 * (Why do I need a separate process for local jobs? I don't really, but
696 * it makes everything much simpler when running multiple jobs at the same
697 * time.)
698 */
699
700 if (kid == 0) {
701 close(sk[0]);
702
703 /* --- Child end of a local job --- */
704
705 if (!host) {
706 r->fdin = r->fdout = sk[1];
707 remote(r, cmd, argv, environ);
708 }
709
710 /* --- Local child end of a remote job --- */
711
712 else {
713 const char *rsh;
714 dup2(sk[1], 0);
715 dup2(sk[1], 1);
716 dup2(sk[1], 2);
717 if (sk[1] > 2)
718 close(sk[1]);
719 rsh = getenv("SW_RSH");
720 if (!rsh)
721 rsh = RSH;
722 execlp(rsh, rsh, host, PATH_SW, "--remote", cmd, (char *)0);
723 }
724
725 /* --- I don't expect either to come back --- */
726
727 _exit(1);
728 }
729
730 /* --- Local sort out of what to do --- *
731 *
732 * Either way, I've now got a socket tied to something which speaks my
733 * communication protocol. However, if this is a local job, then I can get
734 * going right away; otherwise, I've got to transmit various bits of
735 * information over the protocol.
736 */
737
738 r->fdin = r->fdout = sk[0];
739 close(sk[1]);
740
741 if (host) {
742 char buf[PKMAX];
743 if (!getcwd(buf, sizeof(buf)))
744 goto tidy_1;
745 sendargv(r, PKTYPE_ARGS, argv);
746 sendargv(r, PKTYPE_ENV, environ);
747 pksend(r, PKTYPE_DIR, buf, strlen(buf) + 1);
748 pksend(r, PKTYPE_GO, 0, 0);
749 }
750
751 /* --- Ready to rock'n'roll --- */
752
753 r->sz = 0;
754 return (0);
755
756 /* --- Tidy up if it failed --- */
757
758tidy_1:
759 close(sk[0]);
760 close(sk[1]);
761tidy_0:
762 return (-1);
763}
764
765/*----- Subcommands -------------------------------------------------------*/
766
767/* --- @swrsh_rsh@ --- */
768
769void rsw_rsh(sw_remote *r, char *argv[], char *env[])
770{
771 pid_t kid, k;
772 int pfd[2];
773 int status;
774
775 /* --- Create a pipe --- */
776
777 if (pipe(pfd))
778 swdie(r, 1, "couldn't create pipe: %s", strerror(errno));
779
780 /* --- Start the child process up --- */
781
782 kid = fork();
783 if (kid < 0)
784 swdie(r, 1, "fork failed: %s", strerror(errno));
785 else if (kid == 0) {
786 int fd;
787
788 /* --- Use my new environment --- */
789
790 environ = env; /* Yuk. */
791
792 /* --- Fiddle with pipe file descriptors --- */
793
794 close(pfd[0]);
795 dup2(pfd[1], 1);
796 dup2(pfd[1], 2);
797 if (pfd[1] > 2)
798 close(pfd[1]);
799
800 /* --- Make sure it doesn't get any input --- */
801
802 close(0);
803 fd = open("/dev/null", O_RDONLY);
804 if (fd != 0) {
805 dup2(fd, 0);
806 close(fd);
807 }
808
809 /* --- Run the program --- */
810
811 execvp(argv[0], argv);
812 die(1, "couldn't exec `%s': %s", argv[0], strerror(errno));
813 }
814
815 /* --- Read the data from the pipe until it closes --- */
816
817 close(pfd[1]);
818 for (;;) {
819 ssize_t n = read(pfd[0], r->buf, sizeof(r->buf));
820 if (n < 0)
821 swdie(r, 1, "read error: %s", strerror(errno));
822 else if (!n)
823 break;
824 else
825 pksend(r, PKTYPE_DATA, r->buf, n);
826 }
827 close(pfd[0]);
828
829 /* --- Finally, reap the exit status and pass it on --- */
830
831 for (;;) {
832 k = wait(&status);
833 if (k == kid)
834 break;
835 if (k < 0)
836 swdie(r, 1, "error reaping child: %s", strerror(errno));
837 }
838 swwait(r, status);
839}
840
841/* --- @sw_rsh@ --- */
842
843int sw_rsh(int argc, char *argv[])
844{
845 sw_remote r;
846 char *h;
847 int status = 1;
848
849 /* --- Check the arguments --- */
850
851 if (argc < 3)
852 die(1, "Usage: rsh HOST|ARCH COMMAND [ARGS...]");
853
854 /* --- Translate architecture names into hostnames --- */
855
856 if (strcmp(argv[1], "-") == 0)
857 h = 0;
858 else {
859 archent *a = arch_lookup(argv[1], 0);
860 if (!a)
861 h = argv[1];
862 else if (a->flags & archFlag_home)
863 h = 0;
864 else
865 h = a->host;
866 }
867
868 /* --- Start the remote process --- */
869
870 if (swrsh(&r, h, "rsh", argv + 2))
871 die(1, "remote shell failed: %s", strerror(errno));
872
873 /* --- Cope with packets from the remote process --- */
874
875 fflush(stdout);
876 for (;;) {
877 int t = pkrecv(&r);
878 switch (t) {
879 case -1:
880 die(1, "error reading packet: %s", strerror(errno));
881 case PKTYPE_DATA:
882 write(STDOUT_FILENO, r.buf, r.sz);
883 break;
884 case PKTYPE_STATUS:
885 r.buf[r.sz] = 0;
886 if (r.sz > 1) {
887 status = 127;
888 moan("command exited due to signal: %s", r.buf);
889 } else {
890 status = r.buf[0];
891 moan("command exited with status %i", r.buf[0]);
892 }
893 goto done;
894 case PKTYPE_EOF:
895 moan("command exited unexpectedly");
896 goto done;
897 default:
898 die(1, "unexpected packet type");
899 }
900 }
901
902 /* --- Finished --- */
903
904done:
905 close(r.fdin);
906 return (status);
907}
908
909/*----- That's all, folks -------------------------------------------------*/