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