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