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