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