3 * $Id: sw_rsh.c,v 1.7 1999/09/24 13:16:22 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.7 1999/09/24 13:16:22 mdw
33 * Fix typo in comment.
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.)
39 * Revision 1.5 1999/06/24 16:02:22 mdw
40 * Fix signal handling some more.
42 * Revision 1.4 1999/06/24 15:51:17 mdw
43 * Fix signal handlers so they don't corrupt `errno'.
45 * Revision 1.3 1999/06/18 18:58:54 mdw
46 * Signal handling fixes.
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
53 * Revision 1.1.1.1 1999/06/02 16:53:34 mdw
58 /*----- Header files ------------------------------------------------------*/
70 #include <sys/types.h>
75 #include <sys/socket.h>
79 extern char **environ;
82 #include <mLib/alloc.h>
83 #include <mLib/bits.h>
84 #include <mLib/dstr.h>
86 #include <mLib/mdwopt.h>
87 #include <mLib/quis.h>
88 #include <mLib/report.h>
97 /*----- Data structures ---------------------------------------------------*/
101 /*----- Static variables --------------------------------------------------*/
103 static int handler = 0;
104 static rcmd *rcmds = RCMD_LINK;
106 /*----- Packet interface --------------------------------------------------*/
108 /* --- @pksend@ --- *
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
115 * Returns: Zero if it worked, nonzero otherwise.
117 * Use: Sends a data packet. If the type is `data', then `sz' may be
118 * arbitrarily large and is divided into small enough chunks.
119 * Otherwise it's an error to send a packet that's too big.
122 int pksend(sw_remote *r, int type, const void *p, size_t sz)
128 /* --- Sort out error conditions --- */
130 if (sz > PKMAX && type != PKTYPE_DATA) {
135 /* --- Main output loop --- */
140 /* --- Set up the packet header --- */
142 chunk = (sz > PKMAX ? PKMAX : sz);
145 /* --- Write the packet header --- */
148 if (write(r->fdout, &h, PKHEADSZ) < PKHEADSZ) {
154 /* --- Write the payload, if there is one --- *
156 * Maybe the OS won't want to bite it all off in one go.
160 ssize_t n = write(r->fdout, q, chunk);
161 if (n < 0 && errno != EINTR)
176 /* --- @pkrecv@ --- *
178 * Arguments: @sw_remote *r@ = remote block
180 * Returns: Packet type received, or @-1@ for an error.
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.
187 int pkrecv(sw_remote *r)
194 /* --- Read the packet header --- */
199 n = read(r->fdin, p, sz);
200 if (n < 0 && errno != EINTR)
210 /* --- Hack for error messages --- *
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.
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.
222 * This is certainly better than the behaviour otherwise, which is an
223 * @E2BIG@ message reported when the packet size is really ASCII
227 if (h[2] >= PKTYPE_BOGUS) {
228 memcpy(r->buf, &h, PKHEADSZ);
229 n = read(r->fdin, r->buf + PKHEADSZ, sizeof(r->buf) - PKHEADSZ);
232 r->sz = n + PKHEADSZ;
233 return (PKTYPE_DATA);
236 /* --- Sort out what's going on --- */
247 /* --- Read the packet payload --- */
251 n = read(r->fdin, p, sz);
252 if (n < 0 && errno != EINTR)
265 /*----- Error reporting and exit statuses --------------------------------*/
267 /* --- @swexit@ --- *
269 * Arguments: @sw_remote *r@ = remote context
270 * @int status@ = exit status to return
274 * Use: Reports the exit status via packet protocol and quits.
277 void swexit(sw_remote *r, int status)
279 unsigned char s = status;
280 pksend(r, PKTYPE_STATUS, &s, 1);
284 /* --- @swsignal@ --- *
286 * Arguments: @sw_remote *r@ = remote context
287 * @int sig@ = signal ocurrence to return
291 * Use: Reports a signalled-to-death status via packet protocol and
295 void swsignal(sw_remote *r, int sig)
297 #if defined(HAVE_STRSIGNAL)
298 char *s = strsignal(sig);
299 #elif defined(HAVE__SYS_SIGLIST)
300 char *s = _sys_siglist[sig];
303 sprintf(s, "signal %i", sig);
306 pksend(r, PKTYPE_STATUS, s, strlen(s) + 1);
310 /* --- @swwait@ --- *
312 * Arguments: @sw_remote *r@ = remote context
313 * @int status@ = status answer from @wait@(2)
317 * Use: Reports a child's demise appropriately, and quits.
320 void swwait(sw_remote *r, int status)
322 if (WIFEXITED(status))
323 swexit(r, WEXITSTATUS(status));
324 else if (WIFSIGNALED(status))
325 swsignal(r, WTERMSIG(status));
330 /* --- @swvprintf@ --- *
332 * Arguments: @sw_remote *r@ = remote context
333 * @const char *format@ = format string
334 * @va_list ap@ = things to format
338 * Use: Writes a string to the remote end. This is the low-level bit
342 void swvprintf(sw_remote *r, const char *format, va_list ap)
345 dstr_vputf(&d, format, ap);
346 pksend(r, PKTYPE_DATA, d.buf, d.len);
350 /* --- @swprintf@ --- *
352 * Arguments: @sw_remote *r@ = remote context
353 * @const char *format@ = format string
354 * @...@ = other arguments
358 * Use: Writes a string to the remote end.
361 void swprintf(sw_remote *r, const char *format, ...)
364 va_start(ap, format);
365 swvprintf(r, format, ap);
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
378 * Use: Reports a message and quits.
381 void swdie(sw_remote *r, int status, const char *format, ...)
386 va_start(ap, format);
387 dstr_putf(&d, "%s [remote]: ", QUIS);
388 dstr_vputf(&d, format, ap);
392 pksend(r, PKTYPE_DATA, d.buf, d.len);
397 /*----- Command handling and dispatch -------------------------------------*/
399 /* --- @remote@ --- *
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
406 * Returns: Doesn't. Reports an exit status through packet protocol and
409 * Use: Dispatches a remote command. At this point, the two code
410 * paths for local and remote invokation have joined again.
413 static void remote(sw_remote *r, const char *cmd, char *argv[], char *env[])
415 struct rcmd *p, *chosen = 0;
416 size_t sz = strlen(cmd);
418 /* --- Make sure that I can get the exit status of children --- */
420 signal(SIGCHLD, SIG_DFL);
422 /* --- Fix up the environment --- */
428 if (env != environ) {
430 env_import(&t, environ);
432 env_put(&t, "SW_ARCH", ARCH);
433 env_file(&t, DATADIR "/sw-env");
434 env = env_export(&t);
437 /* --- Dispatch to the command handler --- */
439 for (p = rcmds; p; p = p->next) {
440 if (strncmp(cmd, p->name, sz) == 0) {
441 if (p->name[sz] == 0) {
445 swdie(r, 1, "ambiguous remote command name `%s'", cmd);
451 swdie(r, 1, "unknown remote command name `%s'", cmd);
452 chosen->rcmd(r, argv, env);
455 /*----- Remote invocation -------------------------------------------------*/
457 /* --- @sendargv@ --- *
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
463 * Returns: Zero if OK, nonzero if it failed.
465 * Use: Sends something @argv@-shaped; i.e., an array of strings
466 * terminated by a null pointer.
469 static int sendargv(sw_remote *r, int type, char *v[])
476 d.len++; /* Make the null `real' */
479 e = pksend(r, type, d.buf, d.len);
484 /* --- @snarfargv@ --- *
486 * Arguments: @const char *buf@ = pointer to buffer
487 * @size_t sz@ = size of buffer
489 * Returns: Pointer to argument array (allocated with @malloc@).
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.
497 static char **snarfargv(const char *buf, size_t sz)
499 /* --- Initial setup --- */
506 /* --- Pass one: count the number of arguments --- */
522 /* --- Allocate memory for everything --- */
524 v = xmalloc((c + 1) * sizeof(char *) + sz + 1);
525 q = (char *)(v + c + 1);
528 /* --- Pass two: set up the arrays --- */
550 /* --- @swrsh_remote@ --- *
552 * Arguments: @const char *cmd@ = the command to perform
554 * Returns: Doesn't. Reports an exit status through packet protocol and
557 * Use: Handles the remote end of a remote job invokation.
560 void swrsh_remote(const char *cmd)
563 static char *dummy = 0;
570 /* --- Read packets from the remote host --- */
576 swdie(&r, 1, "error reading packet: %s", strerror(errno));
583 argv = snarfargv(r.buf, r.sz);
588 env = snarfargv(r.buf, r.sz);
594 dir = xstrdup(r.buf);
600 swdie(&r, 1, "internal error: unexpected packet");
605 /* --- Sort out any missing arguments --- */
615 /* --- Run the command --- */
618 remote(&r, cmd, argv, env);
619 CATCH switch (exc_type) {
621 static char msg[] = "\nsw [remote]: not enough memory\n";
622 pksend(&r, PKTYPE_DATA, msg, sizeof(msg) - 1);
626 swdie(&r, 1, "uncaught exception, type = %lx", exc_type);
630 /*----- Starting remote jobs ----------------------------------------------*/
632 /* --- @sigchld@ --- *
634 * Arguments: @int sig@ = the signal number
638 * Use: Catches @SIGCHLD@ and reaps any children that have lost.
641 static void sigchld(int sig)
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)));
654 fprintf(stderr, "reaped bizarre child which is still alive\n");
657 while (waitpid(-1, 0, WNOHANG) > 0)
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
670 * Returns: Zero if it worked, nonzero if not.
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.
678 int swrsh(sw_remote *r, const char *host, const char *cmd, char *argv[])
683 /* --- Get a socket pair for communicating with the other end --- */
685 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sk))
688 /* --- Set up a signal handler --- */
692 sa.sa_handler = sigchld;
693 sa.sa_flags = SA_NOCLDSTOP;
695 sa.sa_flags |= SA_RESTART;
697 sigemptyset(&sa.sa_mask);
698 sigaction(SIGCHLD, &sa, 0);
702 /* --- Fork off a child to cope with stuff --- */
708 /* --- Handle the child process --- *
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.
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
722 /* --- Child end of a local job --- */
724 signal(SIGINT, SIG_DFL);
725 signal(SIGQUIT, SIG_DFL);
728 r->fdin = r->fdout = sk[1];
729 remote(r, cmd, argv, environ);
732 /* --- Local child end of a remote job --- */
741 rsh = getenv("SW_RSH");
744 execlp(rsh, rsh, host, PATH_SW, "--remote", cmd, (char *)0);
747 /* --- I don't expect either to come back --- */
752 /* --- Local sort out of what to do --- *
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.
760 r->fdin = r->fdout = sk[0];
765 if (!getcwd(buf, sizeof(buf)))
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);
773 /* --- Ready to rock'n'roll --- */
778 /* --- Tidy up if it failed --- */
787 /*----- Subcommands -------------------------------------------------------*/
789 /* --- @swrsh_rsh@ --- */
791 void rsw_rsh(sw_remote *r, char *argv[], char *env[])
797 /* --- Create a pipe --- */
800 swdie(r, 1, "couldn't create pipe: %s", strerror(errno));
802 /* --- Start the child process up --- */
806 swdie(r, 1, "fork failed: %s", strerror(errno));
810 /* --- Use my new environment --- */
812 environ = env; /* Yuk. */
814 /* --- Fiddle with pipe file descriptors --- */
822 /* --- Make sure it doesn't get any input --- */
825 fd = open("/dev/null", O_RDONLY);
831 /* --- Run the program --- */
833 execvp(argv[0], argv);
834 die(1, "couldn't exec `%s': %s", argv[0], strerror(errno));
837 /* --- Read the data from the pipe until it closes --- */
841 ssize_t n = read(pfd[0], r->buf, sizeof(r->buf));
843 swdie(r, 1, "read error: %s", strerror(errno));
847 pksend(r, PKTYPE_DATA, r->buf, n);
851 /* --- Finally, reap the exit status and pass it on --- */
858 swdie(r, 1, "error reaping child: %s", strerror(errno));
863 /* --- @sw_rsh@ --- */
865 int sw_rsh(int argc, char *argv[])
871 /* --- Check the arguments --- */
874 die(1, "Usage: rsh HOST|ARCH COMMAND [ARGS...]");
876 /* --- Translate architecture names into hostnames --- */
878 if (strcmp(argv[1], "-") == 0)
881 archent *a = arch_lookup(argv[1], 0);
884 else if (a->flags & archFlag_home)
890 /* --- Start the remote process --- */
892 if (swrsh(&r, h, "rsh", argv + 2))
893 die(1, "remote shell failed: %s", strerror(errno));
895 /* --- Cope with packets from the remote process --- */
902 die(1, "error reading packet: %s", strerror(errno));
904 write(STDOUT_FILENO, r.buf, r.sz);
910 moan("command exited due to signal: %s", r.buf);
913 moan("command exited with status %i", r.buf[0]);
917 moan("command exited unexpectedly");
920 die(1, "unexpected packet type");
924 /* --- Finished --- */
931 /*----- That's all, folks -------------------------------------------------*/