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 -------------------------------------------------*/