chiark / gitweb /
Initial revision
[sw-tools] / src / sw_rsh.c
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
77 typedef unsigned char octet;
78
79 #define PKHEADSZ 3
80
81 typedef struct pkhead {
82   octet len[2];
83   octet type;
84 } pkhead;
85
86 /*----- Static variables --------------------------------------------------*/
87
88 static int handler = 0;
89 static 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
107 int 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
173 int 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
263 void 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
281 void 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
306 void 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
328 void 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
347 void 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
367 void 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
399 static 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
455 static 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
483 static 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   }
506 done_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   }
527 done_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
546 void 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
593 done:
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
627 static 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
662 int 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
758 tidy_1:
759   close(sk[0]);
760   close(sk[1]);
761 tidy_0:
762   return (-1);
763 }
764
765 /*----- Subcommands -------------------------------------------------------*/
766
767 /* --- @swrsh_rsh@ --- */
768
769 void 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
843 int 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
904 done:
905   close(r.fdin);
906   return (status);
907 }
908
909 /*----- That's all, folks -------------------------------------------------*/