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