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