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