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