3 * $Id: sw_rsh.c,v 1.6 1999/09/24 13:15:57 mdw Exp $
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of sw-tools.
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.
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.
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.
29 /*----- Revision history --------------------------------------------------*
32 * Revision 1.6 1999/09/24 13:15:57 mdw
33 * Remove unnecessary assumptions about structure layouts. (The `pkhead'
34 * structure is no more.)
36 * Revision 1.5 1999/06/24 16:02:22 mdw
37 * Fix signal handling some more.
39 * Revision 1.4 1999/06/24 15:51:17 mdw
40 * Fix signal handlers so they don't corrupt `errno'.
42 * Revision 1.3 1999/06/18 18:58:54 mdw
43 * Signal handling fixes.
45 * Revision 1.2 1999/06/02 17:03:29 mdw
46 * Fix use of `octet' now that mLib includes `bits.h' (as of version 1.3.5
47 * release). Also use the mLib load and store macros rather than doing it
50 * Revision 1.1.1.1 1999/06/02 16:53:34 mdw
55 /*----- Header files ------------------------------------------------------*/
67 #include <sys/types.h>
72 #include <sys/socket.h>
76 extern char **environ
;
79 #include <mLib/alloc.h>
80 #include <mLib/bits.h>
81 #include <mLib/dstr.h>
83 #include <mLib/mdwopt.h>
84 #include <mLib/quis.h>
85 #include <mLib/report.h>
94 /*----- Data structures ---------------------------------------------------*/
98 /*----- Static variables --------------------------------------------------*/
100 static int handler
= 0;
101 static rcmd
*rcmds
= RCMD_LINK
;
103 /*----- Packet interface --------------------------------------------------*/
105 /* --- @pksend@ --- *
107 * Arguments: @sw_remote@ = pointer to the remote block
108 * @int type@ = packet type to send
109 * @const void *p@ = pointer to packet data
110 * @size_t sz@ = size of data to send
112 * Returns: Zero if it worked, nonzero otherwise.
114 * Use: Sends a data packet. If the type is `data', then `sz' may be
115 * arbitrarily large and is divided into small eenough chunks.
116 * Otherwise it's an error to send a packet that's too big.
119 int pksend(sw_remote
*r
, int type
, const void *p
, size_t sz
)
125 /* --- Sort out error conditions --- */
127 if (sz
> PKMAX
&& type
!= PKTYPE_DATA
) {
132 /* --- Main output loop --- */
137 /* --- Set up the packet header --- */
139 chunk
= (sz
> PKMAX ? PKMAX
: sz
);
142 /* --- Write the packet header --- */
145 if (write(r
->fdout
, &h
, PKHEADSZ
) < PKHEADSZ
) {
151 /* --- Write the payload, if there is one --- *
153 * Maybe the OS won't want to bite it all off in one go.
157 ssize_t n
= write(r
->fdout
, q
, chunk
);
158 if (n
< 0 && errno
!= EINTR
)
173 /* --- @pkrecv@ --- *
175 * Arguments: @sw_remote *r@ = remote block
177 * Returns: Packet type received, or @-1@ for an error.
179 * Use: Receives a packet from the remote host. The packet data is
180 * placed in the block's buffer, the block's packet length is
181 * diddled appropriately.
184 int pkrecv(sw_remote
*r
)
191 /* --- Read the packet header --- */
196 n
= read(r
->fdin
, p
, sz
);
197 if (n
< 0 && errno
!= EINTR
)
207 /* --- Hack for error messages --- *
209 * If it doesn't look like a valid packet, read a `chunk' and pretend it's
210 * data. This isn't too bad, because all the packet types are control
211 * codes, and are unlikely to be in a textual message.
213 * Normally what happens here is that something sitting before the `sw'
214 * program fails, reports a plain textual error, and exits. Grabbing the
215 * `last gasp' like this, then, traps that error message and allows
216 * something to report it. The rest ought to be completely silent, so I
217 * get an `unexpected eof' and then drop everything.
219 * This is certainly better than the behaviour otherwise, which is an
220 * @E2BIG@ message reported when the packet size is really ASCII
224 if (h
[2] >= PKTYPE_BOGUS
) {
225 memcpy(r
->buf
, &h
, PKHEADSZ
);
226 n
= read(r
->fdin
, r
->buf
+ PKHEADSZ
, sizeof(r
->buf
) - PKHEADSZ
);
229 r
->sz
= n
+ PKHEADSZ
;
230 return (PKTYPE_DATA
);
233 /* --- Sort out what's going on --- */
244 /* --- Read the packet payload --- */
248 n
= read(r
->fdin
, p
, sz
);
249 if (n
< 0 && errno
!= EINTR
)
262 /*----- Error reporting and exit statuses --------------------------------*/
264 /* --- @swexit@ --- *
266 * Arguments: @sw_remote *r@ = remote context
267 * @int status@ = exit status to return
271 * Use: Reports the exit status via packet protocol and quits.
274 void swexit(sw_remote
*r
, int status
)
276 unsigned char s
= status
;
277 pksend(r
, PKTYPE_STATUS
, &s
, 1);
281 /* --- @swsignal@ --- *
283 * Arguments: @sw_remote *r@ = remote context
284 * @int sig@ = signal ocurrence to return
288 * Use: Reports a signalled-to-death status via packet protocol and
292 void swsignal(sw_remote
*r
, int sig
)
294 #if defined(HAVE_STRSIGNAL)
295 char *s
= strsignal(sig
);
296 #elif defined(HAVE__SYS_SIGLIST)
297 char *s
= _sys_siglist
[sig
];
300 sprintf(s
, "signal %i", sig
);
303 pksend(r
, PKTYPE_STATUS
, s
, strlen(s
) + 1);
307 /* --- @swwait@ --- *
309 * Arguments: @sw_remote *r@ = remote context
310 * @int status@ = status answer from @wait@(2)
314 * Use: Reports a child's demise appropriately, and quits.
317 void swwait(sw_remote
*r
, int status
)
319 if (WIFEXITED(status
))
320 swexit(r
, WEXITSTATUS(status
));
321 else if (WIFSIGNALED(status
))
322 swsignal(r
, WTERMSIG(status
));
327 /* --- @swvprintf@ --- *
329 * Arguments: @sw_remote *r@ = remote context
330 * @const char *format@ = format string
331 * @va_list ap@ = things to format
335 * Use: Writes a string to the remote end. This is the low-level bit
339 void swvprintf(sw_remote
*r
, const char *format
, va_list ap
)
342 dstr_vputf(&d
, format
, ap
);
343 pksend(r
, PKTYPE_DATA
, d
.buf
, d
.len
);
347 /* --- @swprintf@ --- *
349 * Arguments: @sw_remote *r@ = remote context
350 * @const char *format@ = format string
351 * @...@ = other arguments
355 * Use: Writes a string to the remote end.
358 void swprintf(sw_remote
*r
, const char *format
, ...)
361 va_start(ap
, format
);
362 swvprintf(r
, format
, ap
);
368 * Arguments: @sw_remote *r@ = remote context
369 * @int status@ = exit status to report
370 * @const char *format@ = format string to fill in
371 * @...@ = other arguments
375 * Use: Reports a message and quits.
378 void swdie(sw_remote
*r
, int status
, const char *format
, ...)
383 va_start(ap
, format
);
384 dstr_putf(&d
, "%s [remote]: ", QUIS
);
385 dstr_vputf(&d
, format
, ap
);
389 pksend(r
, PKTYPE_DATA
, d
.buf
, d
.len
);
394 /*----- Command handling and dispatch -------------------------------------*/
396 /* --- @remote@ --- *
398 * Arguments: @sw_remote *r@ = pointer to remote block
399 * @const char *cmd@ = command to run
400 * @char *argv[]@ = argument array
401 * @char *env[]@ = environment variables
403 * Returns: Doesn't. Reports an exit status through packet protocol and
406 * Use: Dispatches a remote command. At this point, the two code
407 * paths for local and remote invokation have joined again.
410 static void remote(sw_remote
*r
, const char *cmd
, char *argv
[], char *env
[])
412 struct rcmd
*p
, *chosen
= 0;
413 size_t sz
= strlen(cmd
);
415 /* --- Make sure that I can get the exit status of children --- */
417 signal(SIGCHLD
, SIG_DFL
);
419 /* --- Fix up the environment --- */
425 if (env
!= environ
) {
427 env_import(&t
, environ
);
429 env_put(&t
, "SW_ARCH", ARCH
);
430 env_file(&t
, DATADIR
"/sw-env");
431 env
= env_export(&t
);
434 /* --- Dispatch to the command handler --- */
436 for (p
= rcmds
; p
; p
= p
->next
) {
437 if (strncmp(cmd
, p
->name
, sz
) == 0) {
438 if (p
->name
[sz
] == 0) {
442 swdie(r
, 1, "ambiguous remote command name `%s'", cmd
);
448 swdie(r
, 1, "unknown remote command name `%s'", cmd
);
449 chosen
->rcmd(r
, argv
, env
);
452 /*----- Remote invocation -------------------------------------------------*/
454 /* --- @sendargv@ --- *
456 * Arguments: @sw_remote *r@ = pointer to the remote context
457 * @int type@ = packet type to send with
458 * @char *v[]@ = pointer to the array to send
460 * Returns: Zero if OK, nonzero if it failed.
462 * Use: Sends something @argv@-shaped; i.e., an array of strings
463 * terminated by a null pointer.
466 static int sendargv(sw_remote
*r
, int type
, char *v
[])
473 d
.len
++; /* Make the null `real' */
476 e
= pksend(r
, type
, d
.buf
, d
.len
);
481 /* --- @snarfargv@ --- *
483 * Arguments: @const char *buf@ = pointer to buffer
484 * @size_t sz@ = size of buffer
486 * Returns: Pointer to argument array (allocated with @malloc@).
488 * Use: Snarfs the null-terminated strings in the buffer and returns
489 * an array of them. The whole lot, strings and array, is
490 * returned in one big chunk allocated from the heap. Note that
491 * this means it's OK to throw the initial buffer away.
494 static char **snarfargv(const char *buf
, size_t sz
)
496 /* --- Initial setup --- */
503 /* --- Pass one: count the number of arguments --- */
519 /* --- Allocate memory for everything --- */
521 v
= xmalloc((c
+ 1) * sizeof(char *) + sz
+ 1);
522 q
= (char *)(v
+ c
+ 1);
525 /* --- Pass two: set up the arrays --- */
547 /* --- @swrsh_remote@ --- *
549 * Arguments: @const char *cmd@ = the command to perform
551 * Returns: Doesn't. Reports an exit status through packet protocol and
554 * Use: Handles the remote end of a remote job invokation.
557 void swrsh_remote(const char *cmd
)
560 static char *dummy
= 0;
567 /* --- Read packets from the remote host --- */
573 swdie(&r
, 1, "error reading packet: %s", strerror(errno
));
580 argv
= snarfargv(r
.buf
, r
.sz
);
585 env
= snarfargv(r
.buf
, r
.sz
);
591 dir
= xstrdup(r
.buf
);
597 swdie(&r
, 1, "internal error: unexpected packet");
602 /* --- Sort out any missing arguments --- */
612 /* --- Run the command --- */
615 remote(&r
, cmd
, argv
, env
);
616 CATCH
switch (exc_type
) {
618 static char msg
[] = "\nsw [remote]: not enough memory\n";
619 pksend(&r
, PKTYPE_DATA
, msg
, sizeof(msg
) - 1);
623 swdie(&r
, 1, "uncaught exception, type = %lx", exc_type
);
627 /*----- Starting remote jobs ----------------------------------------------*/
629 /* --- @sigchld@ --- *
631 * Arguments: @int sig@ = the signal number
635 * Use: Catches @SIGCHLD@ and reaps any children that have lost.
638 static void sigchld(int sig
)
643 while (waitpid(-1, &status
, WNOHANG
) > 0) {
644 if (WIFEXITED(status
)) {
645 fprintf(stderr
, "reap child with exit status %i\n",
646 WEXITSTATUS(status
));
647 } else if (WIFSIGNALED(status
)) {
648 fprintf(stderr
, "reap child killed by signal %s\n",
649 strsignal(WTERMSIG(status
)));
651 fprintf(stderr
, "reaped bizarre child which is still alive\n");
654 while (waitpid(-1, 0, WNOHANG
) > 0)
662 * Arguments: @sw_remote *r@ = remote process block to look after
663 * @const char *host@ = host to run on (0 for this one)
664 * @const char *cmd@ = remote command to run
665 * @char *argv[]@ = arguments to pass on
667 * Returns: Zero if it worked, nonzero if not.
669 * Use: Runs a command on a remote host. The argument array is
670 * mangled to come out OK at the far end. The environment and
671 * current directory are also passed along, and pop out the
672 * other end unmolested.
675 int swrsh(sw_remote
*r
, const char *host
, const char *cmd
, char *argv
[])
680 /* --- Get a socket pair for communicating with the other end --- */
682 if (socketpair(PF_UNIX
, SOCK_STREAM
, 0, sk
))
685 /* --- Set up a signal handler --- */
689 sa
.sa_handler
= sigchld
;
690 sa
.sa_flags
= SA_NOCLDSTOP
;
692 sa
.sa_flags
|= SA_RESTART
;
694 sigemptyset(&sa
.sa_mask
);
695 sigaction(SIGCHLD
, &sa
, 0);
699 /* --- Fork off a child to cope with stuff --- */
705 /* --- Handle the child process --- *
707 * If this is a local job, then just loop around inside to handle the
708 * `remote' command. Otherwise crank up `rsh' and pass the command over to
709 * a remote copy of myself.
711 * (Why do I need a separate process for local jobs? I don't really, but
712 * it makes everything much simpler when running multiple jobs at the same
719 /* --- Child end of a local job --- */
721 signal(SIGINT
, SIG_DFL
);
722 signal(SIGQUIT
, SIG_DFL
);
725 r
->fdin
= r
->fdout
= sk
[1];
726 remote(r
, cmd
, argv
, environ
);
729 /* --- Local child end of a remote job --- */
738 rsh
= getenv("SW_RSH");
741 execlp(rsh
, rsh
, host
, PATH_SW
, "--remote", cmd
, (char *)0);
744 /* --- I don't expect either to come back --- */
749 /* --- Local sort out of what to do --- *
751 * Either way, I've now got a socket tied to something which speaks my
752 * communication protocol. However, if this is a local job, then I can get
753 * going right away; otherwise, I've got to transmit various bits of
754 * information over the protocol.
757 r
->fdin
= r
->fdout
= sk
[0];
762 if (!getcwd(buf
, sizeof(buf
)))
764 sendargv(r
, PKTYPE_ARGS
, argv
);
765 sendargv(r
, PKTYPE_ENV
, environ
);
766 pksend(r
, PKTYPE_DIR
, buf
, strlen(buf
) + 1);
767 pksend(r
, PKTYPE_GO
, 0, 0);
770 /* --- Ready to rock'n'roll --- */
775 /* --- Tidy up if it failed --- */
784 /*----- Subcommands -------------------------------------------------------*/
786 /* --- @swrsh_rsh@ --- */
788 void rsw_rsh(sw_remote
*r
, char *argv
[], char *env
[])
794 /* --- Create a pipe --- */
797 swdie(r
, 1, "couldn't create pipe: %s", strerror(errno
));
799 /* --- Start the child process up --- */
803 swdie(r
, 1, "fork failed: %s", strerror(errno
));
807 /* --- Use my new environment --- */
809 environ
= env
; /* Yuk. */
811 /* --- Fiddle with pipe file descriptors --- */
819 /* --- Make sure it doesn't get any input --- */
822 fd
= open("/dev/null", O_RDONLY
);
828 /* --- Run the program --- */
830 execvp(argv
[0], argv
);
831 die(1, "couldn't exec `%s': %s", argv
[0], strerror(errno
));
834 /* --- Read the data from the pipe until it closes --- */
838 ssize_t n
= read(pfd
[0], r
->buf
, sizeof(r
->buf
));
840 swdie(r
, 1, "read error: %s", strerror(errno
));
844 pksend(r
, PKTYPE_DATA
, r
->buf
, n
);
848 /* --- Finally, reap the exit status and pass it on --- */
855 swdie(r
, 1, "error reaping child: %s", strerror(errno
));
860 /* --- @sw_rsh@ --- */
862 int sw_rsh(int argc
, char *argv
[])
868 /* --- Check the arguments --- */
871 die(1, "Usage: rsh HOST|ARCH COMMAND [ARGS...]");
873 /* --- Translate architecture names into hostnames --- */
875 if (strcmp(argv
[1], "-") == 0)
878 archent
*a
= arch_lookup(argv
[1], 0);
881 else if (a
->flags
& archFlag_home
)
887 /* --- Start the remote process --- */
889 if (swrsh(&r
, h
, "rsh", argv
+ 2))
890 die(1, "remote shell failed: %s", strerror(errno
));
892 /* --- Cope with packets from the remote process --- */
899 die(1, "error reading packet: %s", strerror(errno
));
901 write(STDOUT_FILENO
, r
.buf
, r
.sz
);
907 moan("command exited due to signal: %s", r
.buf
);
910 moan("command exited with status %i", r
.buf
[0]);
914 moan("command exited unexpectedly");
917 die(1, "unexpected packet type");
921 /* --- Finished --- */
928 /*----- That's all, folks -------------------------------------------------*/