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