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