Fix typo in comment.
[sw-tools] / src / sw_rsh.c
CommitLineData
3315e8b3 1/* -*-c-*-
2 *
f5c398f3 3 * $Id: sw_rsh.c,v 1.7 1999/09/24 13:16:22 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 $
f5c398f3 32 * Revision 1.7 1999/09/24 13:16:22 mdw
33 * Fix typo in comment.
34 *
0df5f2b4 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 *
95e672af 39 * Revision 1.5 1999/06/24 16:02:22 mdw
40 * Fix signal handling some more.
41 *
63124806 42 * Revision 1.4 1999/06/24 15:51:17 mdw
43 * Fix signal handlers so they don't corrupt `errno'.
44 *
f192155f 45 * Revision 1.3 1999/06/18 18:58:54 mdw
46 * Signal handling fixes.
47 *
fb28c8b9 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.
3315e8b3 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>
fb28c8b9 83#include <mLib/bits.h>
3315e8b3 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
3315e8b3 99#define PKHEADSZ 3
100
3315e8b3 101/*----- Static variables --------------------------------------------------*/
102
103static int handler = 0;
104static 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
f5c398f3 118 * arbitrarily large and is divided into small enough chunks.
3315e8b3 119 * Otherwise it's an error to send a packet that's too big.
120 */
121
122int pksend(sw_remote *r, int type, const void *p, size_t sz)
123{
0df5f2b4 124 octet h[PKHEADSZ];
3315e8b3 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
0df5f2b4 137 h[2] = type;
3315e8b3 138 do {
139
140 /* --- Set up the packet header --- */
141
142 chunk = (sz > PKMAX ? PKMAX : sz);
0df5f2b4 143 STORE16(h, chunk);
3315e8b3 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
187int pkrecv(sw_remote *r)
188{
0df5f2b4 189 octet h[PKHEADSZ];
3315e8b3 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
0df5f2b4 227 if (h[2] >= PKTYPE_BOGUS) {
3315e8b3 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
0df5f2b4 238 sz = LOAD16(h);
3315e8b3 239 r->sz = sz;
240 if (!sz)
0df5f2b4 241 return (h[2]);
3315e8b3 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
0df5f2b4 262 return (h[2]);
3315e8b3 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
277void 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
295void 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
320void 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
342void 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
361void 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
381void 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
413static 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
469static 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
497static 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 }
520done_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 }
541done_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
560void 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
607done:
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
641static void sigchld(int sig)
642{
63124806 643 int e = errno;
3315e8b3 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
63124806 660 errno = e;
3315e8b3 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
678int 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;
f192155f 693 sa.sa_flags = SA_NOCLDSTOP;
694#ifdef SA_RESTART
695 sa.sa_flags |= SA_RESTART;
696#endif
3315e8b3 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
95e672af 724 signal(SIGINT, SIG_DFL);
725 signal(SIGQUIT, SIG_DFL);
726
3315e8b3 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
780tidy_1:
781 close(sk[0]);
782 close(sk[1]);
783tidy_0:
784 return (-1);
785}
786
787/*----- Subcommands -------------------------------------------------------*/
788
789/* --- @swrsh_rsh@ --- */
790
791void 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
865int 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
926done:
927 close(r.fdin);
928 return (status);
929}
930
931/*----- That's all, folks -------------------------------------------------*/