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