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