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