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