chiark / gitweb /
Merge some changes from 1.0.4. Very odd.
[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[0] && 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   /* --- Object if stdin is a terminal --- */
542
543   if (isatty(STDIN_FILENO)) {
544     die(EXIT_FAILURE,
545         "don't use the `--remote' option unless "
546         "you know what you're doing.");
547   }
548
549   /* --- Read packets from the remote host --- */
550
551   for (;;) {
552     int t = pkrecv(&r);
553     switch (t) {
554       case -1:
555         swdie(&r, 1, "error reading packet: %s", strerror(errno));
556         break;
557       case PKTYPE_EOF:
558         goto done;
559       case PKTYPE_ARGS:
560         if (argv)
561           free(argv);
562         argv = snarfargv(r.buf, r.sz);
563         break;
564       case PKTYPE_ENV:
565         if (env)
566           free(env);
567         env = snarfargv(r.buf, r.sz);
568         break;
569       case PKTYPE_DIR:
570         if (dir)
571           free(dir);
572         r.buf[r.sz] = 0;
573         dir = xstrdup(r.buf);
574         break;
575       case PKTYPE_GO:
576         goto done;
577       case PKTYPE_DATA:
578       case PKTYPE_STATUS:
579         swdie(&r, 1, "internal error: unexpected packet");
580         break;
581     }
582   }
583
584   /* --- Sort out any missing arguments --- */
585
586 done:
587   if (!argv)
588     argv = &dummy;
589   if (!env)
590     env = &dummy;
591   if (dir)
592     chdir(dir);
593
594   /* --- Run the command --- */
595
596   TRY
597     remote(&r, cmd, argv, env);
598   CATCH switch (exc_type) {
599     case EXC_NOMEM: {
600       static char msg[] = "\nsw [remote]: not enough memory\n";
601       pksend(&r, PKTYPE_DATA, msg, sizeof(msg) - 1);
602       swexit(&r, 1);
603     } break;
604     default:
605       swdie(&r, 1, "uncaught exception, type = %lx", exc_type);
606   } END_TRY;
607 }
608
609 /*----- Starting remote jobs ----------------------------------------------*/
610
611 /* --- @sigchld@ --- *
612  *
613  * Arguments:   @int sig@ = the signal number
614  *
615  * Returns:     ---
616  *
617  * Use:         Catches @SIGCHLD@ and reaps any children that have lost.
618  */
619
620 static void sigchld(int sig)
621 {
622   int e = errno;
623 #ifdef DEBUG_SIGCHLD
624   int status;
625   while (waitpid(-1, &status, WNOHANG) > 0) {
626     if (WIFEXITED(status)) {
627       fprintf(stderr, "reap child with exit status %i\n",
628               WEXITSTATUS(status));
629     } else if (WIFSIGNALED(status)) {
630       fprintf(stderr, "reap child killed by signal %s\n",
631               strsignal(WTERMSIG(status)));
632     } else
633       fprintf(stderr, "reaped bizarre child which is still alive\n");
634   }
635 #else
636   while (waitpid(-1, 0, WNOHANG) > 0)
637     ;
638 #endif
639   errno = e;
640 }
641
642 /* --- @swrsh@ --- *
643  *
644  * Arguments:   @sw_remote *r@ = remote process block to look after
645  *              @const char *host@ = host to run on (0 for this one)
646  *              @const char *cmd@ = remote command to run
647  *              @char *argv[]@ = arguments to pass on
648  *
649  * Returns:     Zero if it worked, nonzero if not.
650  *
651  * Use:         Runs a command on a remote host.  The argument array is
652  *              mangled to come out OK at the far end.  The environment and
653  *              current directory are also passed along, and pop out the
654  *              other end unmolested.
655  */
656
657 int swrsh(sw_remote *r, const char *host, const char *cmd, char *argv[])
658 {
659   int sk[2];
660   pid_t kid;
661
662   /* --- Get a socket pair for communicating with the other end --- */
663
664   if (socketpair(PF_UNIX, SOCK_STREAM, 0, sk))
665     goto tidy_0;
666
667   /* --- Set up a signal handler --- */
668
669   if (!handler) {
670     struct sigaction sa;
671     sa.sa_handler = sigchld;
672     sa.sa_flags = SA_NOCLDSTOP;
673 #ifdef SA_RESTART
674     sa.sa_flags |= SA_RESTART;
675 #endif
676     sigemptyset(&sa.sa_mask);
677     sigaction(SIGCHLD, &sa, 0);
678     handler = 1;
679   }
680
681   /* --- Fork off a child to cope with stuff --- */
682
683   kid = fork();
684   if (kid < 0)
685     goto tidy_1;
686
687   /* --- Handle the child process --- *
688    *
689    * If this is a local job, then just loop around inside to handle the
690    * `remote' command.  Otherwise crank up `rsh' and pass the command over to
691    * a remote copy of myself.
692    *
693    * (Why do I need a separate process for local jobs?  I don't really, but
694    * it makes everything much simpler when running multiple jobs at the same
695    * time.)
696    */
697
698   if (kid == 0) {
699     close(sk[0]);
700
701     /* --- Child end of a local job --- */
702
703     signal(SIGINT, SIG_DFL);
704     signal(SIGQUIT, SIG_DFL);
705
706     if (!host) {
707       r->fdin = r->fdout = sk[1];
708       remote(r, cmd, argv, environ);
709     }
710
711     /* --- Local child end of a remote job --- */
712
713     else {
714       const char *rsh;
715       dup2(sk[1], 0);
716       dup2(sk[1], 1);
717       dup2(sk[1], 2);
718       if (sk[1] > 2)
719         close(sk[1]);
720       rsh = getenv("SW_RSH");
721       if (!rsh)
722         rsh = RSH;
723       execlp(rsh, rsh, host, PATH_SW, "--remote", cmd, (char *)0);
724     }
725
726     /* --- I don't expect either to come back --- */
727
728     _exit(1);
729   }
730
731   /* --- Local sort out of what to do --- *
732    *
733    * Either way, I've now got a socket tied to something which speaks my
734    * communication protocol.  However, if this is a local job, then I can get
735    * going right away; otherwise, I've got to transmit various bits of
736    * information over the protocol.
737    */
738
739   r->fdin = r->fdout = sk[0];
740   close(sk[1]);
741
742   if (host) {
743     char buf[PKMAX];
744     if (!getcwd(buf, sizeof(buf)))
745       goto tidy_1;
746     sendargv(r, PKTYPE_ARGS, argv);
747     sendargv(r, PKTYPE_ENV, environ);
748     pksend(r, PKTYPE_DIR, buf, strlen(buf) + 1);
749     pksend(r, PKTYPE_GO, 0, 0);
750   }
751
752   /* --- Ready to rock'n'roll --- */
753
754   r->sz = 0;
755   return (0);
756
757   /* --- Tidy up if it failed --- */
758
759 tidy_1:
760   close(sk[0]);
761   close(sk[1]);
762 tidy_0:
763   return (-1);
764 }
765
766 /*----- Subcommands -------------------------------------------------------*/
767
768 /* --- @swrsh_rsh@ --- */
769
770 void rsw_rsh(sw_remote *r, char *argv[], char *env[])
771 {
772   pid_t kid, k;
773   int pfd[2];
774   int status;
775
776   /* --- Create a pipe --- */
777
778   if (pipe(pfd))
779     swdie(r, 1, "couldn't create pipe: %s", strerror(errno));
780
781   /* --- Start the child process up --- */
782
783   kid = fork();
784   if (kid < 0)
785     swdie(r, 1, "fork failed: %s", strerror(errno));
786   else if (kid == 0) {
787     int fd;
788
789     /* --- Use my new environment --- */
790
791     environ = env; /* Yuk. */
792
793     /* --- Fiddle with pipe file descriptors --- */
794
795     close(pfd[0]);
796     dup2(pfd[1], 1);
797     dup2(pfd[1], 2);
798     if (pfd[1] > 2)
799       close(pfd[1]);
800
801     /* --- Make sure it doesn't get any input --- */
802
803     close(0);
804     fd = open("/dev/null", O_RDONLY);
805     if (fd != 0) {
806       dup2(fd, 0);
807       close(fd);
808     }
809
810     /* --- Run the program --- */
811
812     execvp(argv[0], argv);
813     die(1, "couldn't exec `%s': %s", argv[0], strerror(errno));
814   }
815
816   /* --- Read the data from the pipe until it closes --- */
817
818   close(pfd[1]);
819   for (;;) {
820     ssize_t n = read(pfd[0], r->buf, sizeof(r->buf));
821     if (n < 0)
822       swdie(r, 1, "read error: %s", strerror(errno));
823     else if (!n)
824       break;
825     else
826       pksend(r, PKTYPE_DATA, r->buf, n);
827   }
828   close(pfd[0]);
829
830   /* --- Finally, reap the exit status and pass it on --- */
831
832   for (;;) {
833     k = wait(&status);
834     if (k == kid)
835       break;
836     if (k < 0)
837       swdie(r, 1, "error reaping child: %s", strerror(errno));
838   }
839   swwait(r, status);
840 }
841
842 /* --- @sw_rsh@ --- */
843
844 int sw_rsh(int argc, char *argv[])
845 {
846   sw_remote r;
847   char *h;
848   int status = 1;
849
850   /* --- Check the arguments --- */
851
852   if (argc < 3)
853     die(1, "Usage: rsh HOST|ARCH COMMAND [ARGS...]");
854
855   /* --- Translate architecture names into hostnames --- */
856
857   if (strcmp(argv[1], "-") == 0)
858     h = 0;
859   else {
860     archent *a = arch_lookup(argv[1], 0);
861     if (!a)
862       h = argv[1];
863     else if (a->flags & archFlag_home)
864       h = 0;
865     else
866       h = a->host;
867   }
868
869   /* --- Start the remote process --- */
870
871   if (swrsh(&r, h, "rsh", argv + 2))
872     die(1, "remote shell failed: %s", strerror(errno));
873
874   /* --- Cope with packets from the remote process --- */
875
876   fflush(stdout);
877   for (;;) {
878     int t = pkrecv(&r);
879     switch (t) {
880       case -1:
881         die(1, "error reading packet: %s", strerror(errno));
882       case PKTYPE_DATA:
883         write(STDOUT_FILENO, r.buf, r.sz);
884         break;
885       case PKTYPE_STATUS:
886         r.buf[r.sz] = 0;
887         if (r.sz > 1) {
888           status = 127;
889           moan("command exited due to signal: %s", r.buf);
890         } else {
891           status = r.buf[0];
892             moan("command exited with status %i", r.buf[0]);
893         }
894         goto done;
895       case PKTYPE_EOF:
896         moan("command exited unexpectedly");
897         goto done;
898       default:
899         die(1, "unexpected packet type");
900     }
901   }
902
903   /* --- Finished --- */
904
905 done:
906   close(r.fdin);
907   return (status);
908 }
909
910 /*----- That's all, folks -------------------------------------------------*/