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