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