]>
Commit | Line | Data |
---|---|---|
daa75206 JS |
1 | // systemtap remote execution |
2 | // Copyright (C) 2010 Red Hat Inc. | |
3 | // | |
4 | // This file is part of systemtap, and is free software. You can | |
5 | // redistribute it and/or modify it under the terms of the GNU General | |
6 | // Public License (GPL); either version 2, or (at your option) any | |
7 | // later version. | |
8 | ||
9 | extern "C" { | |
e96f2257 JS |
10 | #include <fcntl.h> |
11 | #include <sys/types.h> | |
12 | #include <sys/stat.h> | |
13 | #include <unistd.h> | |
daa75206 JS |
14 | } |
15 | ||
e96f2257 | 16 | #include <cstdio> |
bf9c0b28 | 17 | #include <iomanip> |
daa75206 JS |
18 | #include <stdexcept> |
19 | #include <sstream> | |
20 | #include <string> | |
21 | #include <vector> | |
22 | ||
23 | #include "buildrun.h" | |
24 | #include "remote.h" | |
25 | #include "util.h" | |
26 | ||
27 | using namespace std; | |
28 | ||
91fa953d JS |
29 | // Decode URIs as per RFC 3986, though not bothering to be strict |
30 | class uri_decoder { | |
31 | public: | |
32 | const string uri; | |
33 | string scheme, authority, path, query, fragment; | |
34 | bool has_authority, has_query, has_fragment; | |
35 | ||
36 | uri_decoder(const string& uri): | |
37 | uri(uri), has_authority(false), has_query(false), has_fragment(false) | |
38 | { | |
39 | const string re = | |
40 | "^([^:]+):(//[^/?#]*)?([^?#]*)(\\?[^#]*)?(#.*)?$"; | |
41 | ||
42 | vector<string> matches; | |
43 | if (regexp_match(uri, re, matches) != 0) | |
44 | throw runtime_error("string doesn't appear to be a URI: " + uri); | |
45 | ||
46 | scheme = matches[1]; | |
47 | ||
48 | if (!matches[2].empty()) | |
49 | { | |
50 | has_authority = true; | |
51 | authority = matches[2].substr(2); | |
52 | } | |
53 | ||
54 | path = matches[3]; | |
55 | ||
56 | if (!matches[4].empty()) | |
57 | { | |
58 | has_query = true; | |
59 | query = matches[4].substr(1); | |
60 | } | |
61 | ||
62 | if (!matches[5].empty()) | |
63 | { | |
64 | has_fragment = true; | |
65 | fragment = matches[5].substr(1); | |
66 | } | |
67 | } | |
68 | }; | |
69 | ||
70 | ||
daa75206 JS |
71 | // loopback target for running locally |
72 | class direct : public remote { | |
ebff2ed0 JS |
73 | private: |
74 | pid_t child; | |
75 | direct(systemtap_session& s): remote(s), child(0) {} | |
daa75206 | 76 | |
ebff2ed0 JS |
77 | int start() |
78 | { | |
5eea6ed1 | 79 | pid_t pid = stap_spawn (s->verbose, make_run_command (*s)); |
4c156a0e JS |
80 | if (pid <= 0) |
81 | return 1; | |
82 | child = pid; | |
83 | return 0; | |
ebff2ed0 JS |
84 | } |
85 | ||
86 | int finish() | |
87 | { | |
4c156a0e JS |
88 | if (child <= 0) |
89 | return 1; | |
ebff2ed0 | 90 | |
4c156a0e JS |
91 | int ret = stap_waitpid(s->verbose, child); |
92 | child = 0; | |
93 | return ret; | |
ebff2ed0 | 94 | } |
4f244435 JS |
95 | |
96 | public: | |
97 | friend class remote; | |
98 | ||
99 | virtual ~direct() {} | |
daa75206 JS |
100 | }; |
101 | ||
e96f2257 JS |
102 | |
103 | class stapsh : public remote { | |
104 | private: | |
105 | int interrupts_sent; | |
106 | int fdin, fdout; | |
107 | FILE *IN, *OUT; | |
108 | ||
109 | virtual void prepare_poll(vector<pollfd>& fds) | |
110 | { | |
111 | if (fdout >= 0 && OUT) | |
112 | { | |
113 | pollfd p = { fdout, POLLIN }; | |
114 | fds.push_back(p); | |
115 | } | |
116 | ||
117 | // need to send a signal? | |
118 | if (fdin >= 0 && IN && interrupts_sent < pending_interrupts) | |
119 | { | |
120 | pollfd p = { fdin, POLLOUT }; | |
121 | fds.push_back(p); | |
122 | } | |
123 | } | |
124 | ||
125 | virtual void handle_poll(vector<pollfd>& fds) | |
126 | { | |
127 | for (unsigned i=0; i < fds.size(); ++i) | |
128 | if (fds[i].revents) | |
129 | { | |
130 | if (fdout >= 0 && OUT && fds[i].fd == fdout) | |
131 | { | |
132 | if (fds[i].revents & POLLIN) | |
133 | { | |
134 | // XXX should we do line-buffering? | |
135 | char buf[4096]; | |
136 | size_t rc = fread(buf, 1, sizeof(buf), OUT); | |
137 | if (rc > 0) | |
138 | { | |
139 | cout.write(buf, rc); | |
140 | continue; | |
141 | } | |
142 | } | |
143 | close(); | |
144 | } | |
145 | ||
146 | // need to send a signal? | |
147 | if (fdin >= 0 && IN && fds[i].fd == fdin && | |
148 | interrupts_sent < pending_interrupts) | |
149 | { | |
150 | if (fds[i].revents & POLLOUT) | |
151 | { | |
7185074b | 152 | if (send_command("quit\n") == 0) |
e96f2257 JS |
153 | { |
154 | ++interrupts_sent; | |
155 | continue; | |
156 | } | |
157 | } | |
158 | close(); | |
159 | } | |
160 | } | |
161 | } | |
162 | ||
163 | int send_command(const string& cmd) | |
164 | { | |
165 | if (!IN) | |
166 | return 2; | |
167 | if (fputs(cmd.c_str(), IN) < 0 || | |
168 | fflush(IN) != 0) | |
169 | return 1; | |
170 | return 0; | |
171 | } | |
172 | ||
173 | int send_file(const string& filename, const string& dest) | |
174 | { | |
175 | int rc = 0; | |
176 | FILE* f = fopen(filename.c_str(), "r"); | |
177 | if (!f) | |
178 | return 1; | |
179 | ||
180 | struct stat fs; | |
181 | rc = fstat(fileno(f), &fs); | |
182 | if (!rc) | |
183 | { | |
184 | ostringstream cmd; | |
185 | cmd << "file " << fs.st_size << " " << dest << "\n"; | |
186 | rc = send_command(cmd.str()); | |
187 | } | |
188 | ||
189 | off_t i = 0; | |
190 | while (!rc && i < fs.st_size) | |
191 | { | |
192 | char buf[4096]; | |
193 | size_t r = sizeof(buf); | |
194 | if (fs.st_size - i < (off_t)r) | |
195 | r = fs.st_size - i; | |
196 | r = fread(buf, 1, r, f); | |
197 | if (r == 0) | |
198 | rc = 1; | |
199 | else | |
200 | { | |
201 | size_t w = fwrite(buf, 1, r, IN); | |
202 | if (w != r) | |
203 | rc = 1; | |
204 | else | |
205 | i += w; | |
206 | } | |
207 | } | |
208 | if (!rc) | |
209 | rc = fflush(IN); | |
210 | ||
211 | fclose(f); | |
212 | return rc; | |
213 | } | |
214 | ||
bf9c0b28 | 215 | static string qpencode(const string& str) |
e96f2257 | 216 | { |
bf9c0b28 JS |
217 | ostringstream o; |
218 | o << setfill('0') << hex; | |
219 | for (const char* s = str.c_str(); *s; ++s) | |
220 | if (*s >= 33 && *s <= 126 && *s != 61) | |
221 | o << *s; | |
222 | else | |
223 | o << '=' << setw(2) << (unsigned)(unsigned char) *s; | |
224 | return o.str(); | |
e96f2257 JS |
225 | } |
226 | ||
227 | protected: | |
228 | stapsh(systemtap_session& s) | |
229 | : remote(s), interrupts_sent(0), | |
230 | fdin(-1), fdout(-1), IN(0), OUT(0) | |
231 | {} | |
232 | ||
3fe776ae | 233 | virtual int prepare() |
e96f2257 JS |
234 | { |
235 | int rc = 0; | |
236 | ||
237 | string localmodule = s->tmpdir + "/" + s->module_name + ".ko"; | |
238 | string remotemodule = s->module_name + ".ko"; | |
239 | if ((rc = send_file(localmodule, remotemodule))) | |
240 | return rc; | |
241 | ||
599c80df JS |
242 | if (file_exists(localmodule + ".sgn") && |
243 | (rc = send_file(localmodule + ".sgn", remotemodule + ".sgn"))) | |
244 | return rc; | |
245 | ||
246 | if (!s->uprobes_path.empty()) | |
247 | { | |
248 | string remoteuprobes = basename(s->uprobes_path.c_str()); | |
249 | if ((rc = send_file(s->uprobes_path, remoteuprobes))) | |
250 | return rc; | |
251 | ||
252 | if (file_exists(s->uprobes_path + ".sgn") && | |
253 | (rc = send_file(s->uprobes_path + ".sgn", remoteuprobes + ".sgn"))) | |
254 | return rc; | |
255 | } | |
e96f2257 | 256 | |
3fe776ae JS |
257 | return rc; |
258 | } | |
259 | ||
260 | virtual int start() | |
261 | { | |
e96f2257 JS |
262 | // Send the staprun args |
263 | // NB: The remote is left to decide its own staprun path | |
bf9c0b28 | 264 | ostringstream run("run", ios::out | ios::ate); |
3fe776ae | 265 | vector<string> cmd = make_run_command(*s, s->module_name + ".ko"); |
e96f2257 | 266 | for (unsigned i = 1; i < cmd.size(); ++i) |
bf9c0b28 JS |
267 | run << ' ' << qpencode(cmd[i]); |
268 | run << '\n'; | |
e96f2257 | 269 | |
3fe776ae | 270 | int rc = send_command(run.str()); |
e96f2257 JS |
271 | |
272 | if (!rc) | |
273 | { | |
274 | long flags = fcntl(fdout, F_GETFL) | O_NONBLOCK; | |
275 | fcntl(fdout, F_SETFL, flags); | |
276 | } | |
277 | ||
278 | return rc; | |
279 | } | |
280 | ||
281 | void close() | |
282 | { | |
283 | if (IN) fclose(IN); | |
284 | if (OUT) fclose(OUT); | |
285 | IN = OUT = NULL; | |
286 | fdin = fdout = -1; | |
287 | } | |
288 | ||
289 | virtual int finish() | |
290 | { | |
291 | close(); | |
292 | return 0; | |
293 | } | |
294 | ||
295 | void set_child_fds(int in, int out) | |
296 | { | |
297 | if (fdin >= 0 || fdout >= 0 || IN || OUT) | |
298 | throw runtime_error("stapsh file descriptors already set!"); | |
299 | ||
300 | fdin = in; | |
301 | fdout = out; | |
302 | IN = fdopen(fdin, "w"); | |
303 | OUT = fdopen(fdout, "r"); | |
304 | if (!IN || !OUT) | |
305 | throw runtime_error("invalid file descriptors for stapsh!"); | |
306 | ||
307 | if (send_command("stap " VERSION "\n")) | |
308 | throw runtime_error("error sending hello to stapsh!"); | |
309 | ||
310 | char reply[1024]; | |
311 | if (!fgets(reply, sizeof(reply), OUT)) | |
312 | throw runtime_error("error receiving hello from stapsh!"); | |
313 | ||
314 | // stapsh VERSION MACHINE RELEASE | |
315 | vector<string> uname; | |
316 | tokenize(reply, uname, " \t\r\n"); | |
317 | if (uname.size() != 4 || uname[0] != "stapsh") | |
318 | throw runtime_error("failed to get uname from stapsh"); | |
319 | // XXX check VERSION compatibility | |
320 | ||
321 | this->s = s->clone(uname[2], uname[3]); | |
322 | } | |
323 | ||
324 | public: | |
325 | virtual ~stapsh() { close(); } | |
326 | }; | |
327 | ||
328 | ||
329 | class direct_stapsh : public stapsh { | |
330 | private: | |
331 | pid_t child; | |
332 | ||
333 | direct_stapsh(systemtap_session& s) | |
334 | : stapsh(s), child(0) | |
335 | { | |
336 | int in, out; | |
337 | vector<string> cmd; | |
338 | cmd.push_back(BINDIR "/stapsh"); | |
c76b4180 JS |
339 | if (s.perpass_verbose[4] > 1) |
340 | cmd.push_back("-v"); | |
341 | if (s.perpass_verbose[4] > 2) | |
342 | cmd.push_back("-v"); | |
e96f2257 JS |
343 | child = stap_spawn_piped(s.verbose, cmd, &in, &out); |
344 | if (child <= 0) | |
345 | throw runtime_error("error launching stapsh!"); | |
346 | ||
347 | try | |
348 | { | |
349 | set_child_fds(in, out); | |
350 | } | |
351 | catch (runtime_error&) | |
352 | { | |
353 | finish(); | |
354 | throw; | |
355 | } | |
356 | } | |
357 | ||
358 | virtual int finish() | |
359 | { | |
360 | int rc = stapsh::finish(); | |
361 | if (child <= 0) | |
362 | return rc; | |
363 | ||
364 | int rc2 = stap_waitpid(s->verbose, child); | |
365 | child = 0; | |
366 | return rc ?: rc2; | |
367 | } | |
368 | ||
369 | public: | |
370 | friend class remote; | |
371 | ||
372 | virtual ~direct_stapsh() {} | |
373 | }; | |
374 | ||
375 | ||
376 | #if 1 // stapsh-based ssh_remote | |
377 | class ssh_remote : public stapsh { | |
378 | private: | |
379 | pid_t child; | |
380 | ||
381 | ssh_remote(systemtap_session& s, const string& host) | |
382 | : stapsh(s), child(0) | |
383 | { | |
384 | init(host); | |
385 | } | |
386 | ||
387 | ssh_remote(systemtap_session& s, const uri_decoder& ud) | |
388 | : stapsh(s), child(0) | |
389 | { | |
390 | if (!ud.has_authority || ud.authority.empty()) | |
391 | throw runtime_error("ssh target requires a hostname"); | |
392 | if (!ud.path.empty() && ud.path != "/") | |
393 | throw runtime_error("ssh target URI doesn't support a /path"); | |
394 | if (ud.has_query) | |
395 | throw runtime_error("ssh target URI doesn't support a ?query"); | |
396 | if (ud.has_fragment) | |
397 | throw runtime_error("ssh target URI doesn't support a #fragment"); | |
398 | ||
399 | init(ud.authority); | |
400 | } | |
401 | ||
402 | void init(const string& host) | |
403 | { | |
404 | // mask signals while we spawn, so we can manually send even tty | |
405 | // signals *through* ssh rather than to ssh itself | |
406 | sigset_t mask, oldmask; | |
407 | sigemptyset (&mask); | |
408 | sigaddset (&mask, SIGHUP); | |
409 | sigaddset (&mask, SIGPIPE); | |
410 | sigaddset (&mask, SIGINT); | |
411 | sigaddset (&mask, SIGTERM); | |
412 | sigprocmask (SIG_BLOCK, &mask, &oldmask); | |
413 | ||
414 | int in, out; | |
415 | vector<string> cmd; | |
416 | cmd.push_back("ssh"); | |
417 | cmd.push_back("-q"); | |
418 | cmd.push_back(host); | |
419 | cmd.push_back("stapsh"); // NB: relies on remote $PATH | |
c76b4180 JS |
420 | if (s->perpass_verbose[4] > 1) |
421 | cmd.push_back("-v"); | |
422 | if (s->perpass_verbose[4] > 2) | |
423 | cmd.push_back("-v"); | |
e96f2257 JS |
424 | child = stap_spawn_piped(s->verbose, cmd, &in, &out); |
425 | sigprocmask (SIG_SETMASK, &oldmask, NULL); // back to normal signals | |
426 | if (child <= 0) | |
427 | throw runtime_error("error launching stapsh!"); | |
428 | ||
429 | try | |
430 | { | |
431 | set_child_fds(in, out); | |
432 | } | |
433 | catch (runtime_error&) | |
434 | { | |
435 | finish(); | |
436 | throw; | |
437 | } | |
438 | } | |
439 | ||
440 | int finish() | |
441 | { | |
442 | int rc = stapsh::finish(); | |
443 | if (child <= 0) | |
444 | return rc; | |
445 | ||
446 | int rc2 = stap_waitpid(s->verbose, child); | |
447 | child = 0; | |
448 | return rc ?: rc2; | |
449 | } | |
450 | ||
451 | public: | |
452 | friend class remote; | |
453 | ||
454 | virtual ~ssh_remote() { } | |
455 | }; | |
456 | #else // !stapsh-based ssh_remote | |
daa75206 | 457 | class ssh_remote : public remote { |
b2cbfd40 | 458 | // NB: ssh commands use a tty (-t) so signals are passed along to the remote |
daa75206 | 459 | private: |
20f90026 JS |
460 | vector<string> ssh_args, scp_args; |
461 | string ssh_control; | |
91fa953d | 462 | string host, tmpdir; |
ebff2ed0 JS |
463 | pid_t child; |
464 | ||
91fa953d JS |
465 | ssh_remote(systemtap_session& s, const string& host) |
466 | : remote(s), host(host), child(0) | |
467 | { | |
1544bdf9 | 468 | init(); |
91fa953d JS |
469 | } |
470 | ||
471 | ssh_remote(systemtap_session& s, const uri_decoder& ud) | |
472 | : remote(s), child(0) | |
473 | { | |
474 | if (!ud.has_authority || ud.authority.empty()) | |
475 | throw runtime_error("ssh target requires a hostname"); | |
476 | if (!ud.path.empty() && ud.path != "/") | |
477 | throw runtime_error("ssh target URI doesn't support a /path"); | |
478 | if (ud.has_query) | |
479 | throw runtime_error("ssh target URI doesn't support a ?query"); | |
480 | if (ud.has_fragment) | |
481 | throw runtime_error("ssh target URI doesn't support a #fragment"); | |
482 | ||
483 | host = ud.authority; | |
1544bdf9 JS |
484 | init(); |
485 | } | |
486 | ||
487 | void init() | |
488 | { | |
4c156a0e | 489 | open_control_master(); |
1544bdf9 JS |
490 | try |
491 | { | |
492 | get_uname(); | |
493 | } | |
494 | catch (runtime_error&) | |
495 | { | |
496 | close_control_master(); | |
497 | throw; | |
498 | } | |
91fa953d JS |
499 | } |
500 | ||
4c156a0e JS |
501 | void open_control_master() |
502 | { | |
503 | static unsigned index = 0; | |
504 | ||
505 | if (s->tmpdir.empty()) // sanity check, shouldn't happen | |
506 | throw runtime_error("No tmpdir available for ssh control master"); | |
507 | ||
508 | ssh_control = s->tmpdir + "/ssh_remote_control_" + lex_cast(++index); | |
20f90026 JS |
509 | |
510 | scp_args.clear(); | |
511 | scp_args.push_back("scp"); | |
512 | scp_args.push_back("-q"); | |
513 | scp_args.push_back("-o"); | |
514 | scp_args.push_back("ControlPath=" + ssh_control); | |
515 | ||
516 | ssh_args = scp_args; | |
517 | ssh_args[0] = "ssh"; | |
518 | ssh_args.push_back(host); | |
4c156a0e JS |
519 | |
520 | // NB: ssh -f will stay in the foreground until authentication is | |
521 | // complete and the control socket is created, so we know it's ready to | |
522 | // go when stap_system returns. | |
20f90026 JS |
523 | vector<string> cmd = ssh_args; |
524 | cmd.push_back("-f"); | |
525 | cmd.push_back("-N"); | |
526 | cmd.push_back("-M"); | |
4c156a0e JS |
527 | int rc = stap_system(s->verbose, cmd); |
528 | if (rc != 0) | |
529 | { | |
530 | ostringstream err; | |
531 | err << "failed to create an ssh control master for " << host | |
532 | << " : rc=" << rc; | |
533 | throw runtime_error(err.str()); | |
534 | } | |
535 | ||
536 | if (s->verbose>1) | |
537 | clog << "Created ssh control master at " | |
538 | << lex_cast_qstring(ssh_control) << endl; | |
539 | } | |
540 | ||
541 | void close_control_master() | |
542 | { | |
543 | if (ssh_control.empty()) | |
544 | return; | |
545 | ||
20f90026 JS |
546 | vector<string> cmd = ssh_args; |
547 | cmd.push_back("-O"); | |
548 | cmd.push_back("exit"); | |
2323da5e | 549 | int rc = stap_system(s->verbose, cmd, true, true); |
4c156a0e JS |
550 | if (rc != 0) |
551 | cerr << "failed to stop the ssh control master for " << host | |
552 | << " : rc=" << rc << endl; | |
553 | ||
554 | ssh_control.clear(); | |
20f90026 JS |
555 | scp_args.clear(); |
556 | ssh_args.clear(); | |
4c156a0e JS |
557 | } |
558 | ||
91fa953d | 559 | void get_uname() |
daa75206 JS |
560 | { |
561 | ostringstream out; | |
562 | vector<string> uname; | |
20f90026 JS |
563 | vector<string> cmd = ssh_args; |
564 | cmd.push_back("-t"); | |
565 | cmd.push_back("uname -rm"); | |
566 | int rc = stap_system_read(s->verbose, cmd, out); | |
daa75206 JS |
567 | if (rc == 0) |
568 | tokenize(out.str(), uname, " \t\r\n"); | |
569 | if (uname.size() != 2) | |
91fa953d | 570 | throw runtime_error("failed to get uname from " + host |
daa75206 | 571 | + " : rc=" + lex_cast(rc)); |
4c156a0e JS |
572 | const string& release = uname[0]; |
573 | const string& arch = uname[1]; | |
574 | // XXX need to deal with command-line vs. implied arch/release | |
575 | this->s = s->clone(arch, release); | |
daa75206 JS |
576 | } |
577 | ||
ebff2ed0 | 578 | int start() |
daa75206 JS |
579 | { |
580 | int rc; | |
ebff2ed0 JS |
581 | string localmodule = s->tmpdir + "/" + s->module_name + ".ko"; |
582 | string tmpmodule; | |
daa75206 JS |
583 | |
584 | // Make a remote tempdir. | |
585 | { | |
586 | ostringstream out; | |
587 | vector<string> vout; | |
20f90026 JS |
588 | vector<string> cmd = ssh_args; |
589 | cmd.push_back("-t"); | |
590 | cmd.push_back("mktemp -d -t stapXXXXXX"); | |
ebff2ed0 | 591 | rc = stap_system_read(s->verbose, cmd, out); |
daa75206 | 592 | if (rc == 0) |
b2cbfd40 | 593 | tokenize(out.str(), vout, "\r\n"); |
daa75206 JS |
594 | if (vout.size() != 1) |
595 | { | |
91fa953d | 596 | cerr << "failed to make a tempdir on " << host |
daa75206 JS |
597 | << " : rc=" << rc << endl; |
598 | return -1; | |
599 | } | |
600 | tmpdir = vout[0]; | |
ebff2ed0 | 601 | tmpmodule = tmpdir + "/" + s->module_name + ".ko"; |
daa75206 JS |
602 | } |
603 | ||
604 | // Transfer the module. XXX and uprobes.ko, sigs, etc. | |
605 | if (rc == 0) { | |
20f90026 JS |
606 | vector<string> cmd = scp_args; |
607 | cmd.push_back(localmodule); | |
608 | cmd.push_back(host + ":" + tmpmodule); | |
ebff2ed0 | 609 | rc = stap_system(s->verbose, cmd); |
daa75206 | 610 | if (rc != 0) |
91fa953d | 611 | cerr << "failed to copy the module to " << host |
daa75206 JS |
612 | << " : rc=" << rc << endl; |
613 | } | |
614 | ||
615 | // Run the module on the remote. | |
616 | if (rc == 0) { | |
20f90026 JS |
617 | vector<string> cmd = ssh_args; |
618 | cmd.push_back("-t"); | |
5eea6ed1 | 619 | cmd.push_back(cmdstr_join(make_run_command(*s, tmpmodule))); |
ebff2ed0 JS |
620 | pid_t pid = stap_spawn(s->verbose, cmd); |
621 | if (pid > 0) | |
4c156a0e JS |
622 | child = pid; |
623 | else | |
624 | { | |
625 | cerr << "failed to run the module on " << host | |
626 | << " : ret=" << pid << endl; | |
627 | rc = -1; | |
628 | } | |
daa75206 JS |
629 | } |
630 | ||
4c156a0e | 631 | return rc; |
ebff2ed0 | 632 | } |
daa75206 | 633 | |
ebff2ed0 JS |
634 | int finish() |
635 | { | |
4c156a0e | 636 | int rc = 0; |
ebff2ed0 | 637 | |
4c156a0e JS |
638 | if (child > 0) |
639 | { | |
640 | rc = stap_waitpid(s->verbose, child); | |
641 | child = 0; | |
642 | } | |
ebff2ed0 JS |
643 | |
644 | if (!tmpdir.empty()) | |
4c156a0e JS |
645 | { |
646 | // Remove the tempdir. | |
647 | // XXX need to make sure this runs even with e.g. CTRL-C exits | |
20f90026 JS |
648 | vector<string> cmd = ssh_args; |
649 | cmd.push_back("-t"); | |
650 | cmd.push_back("rm -r " + cmdstr_quoted(tmpdir)); | |
4c156a0e JS |
651 | int rc2 = stap_system(s->verbose, cmd); |
652 | if (rc2 != 0) | |
653 | cerr << "failed to delete the tempdir on " << host | |
654 | << " : rc=" << rc2 << endl; | |
655 | if (rc == 0) | |
656 | rc = rc2; | |
20f90026 | 657 | tmpdir.clear(); |
4c156a0e JS |
658 | } |
659 | ||
660 | close_control_master(); | |
661 | ||
662 | return rc; | |
daa75206 | 663 | } |
4f244435 JS |
664 | |
665 | public: | |
666 | friend class remote; | |
667 | ||
668 | virtual ~ssh_remote() | |
669 | { | |
670 | close_control_master(); | |
671 | } | |
daa75206 | 672 | }; |
e96f2257 | 673 | #endif |
daa75206 JS |
674 | |
675 | ||
676 | remote* | |
677 | remote::create(systemtap_session& s, const string& uri) | |
678 | { | |
679 | try | |
680 | { | |
681 | if (uri == "direct") | |
682 | return new direct(s); | |
e96f2257 JS |
683 | else if (uri == "stapsh") |
684 | return new direct_stapsh(s); | |
91fa953d JS |
685 | else if (uri.find(':') != string::npos) |
686 | { | |
687 | const uri_decoder ud(uri); | |
688 | if (ud.scheme == "ssh") | |
689 | return new ssh_remote(s, ud); | |
690 | else | |
691 | { | |
692 | ostringstream msg; | |
693 | msg << "unrecognized URI scheme '" << ud.scheme | |
694 | << "' in remote: " << uri; | |
695 | throw runtime_error(msg.str()); | |
696 | } | |
697 | } | |
daa75206 JS |
698 | else |
699 | // XXX assuming everything else is ssh for now... | |
700 | return new ssh_remote(s, uri); | |
701 | } | |
702 | catch (std::runtime_error& e) | |
703 | { | |
704 | cerr << e.what() << endl; | |
705 | return NULL; | |
706 | } | |
707 | } | |
708 | ||
4f244435 JS |
709 | int |
710 | remote::run(const vector<remote*>& remotes) | |
711 | { | |
712 | // NB: the first failure "wins" | |
713 | int ret = 0, rc = 0; | |
714 | ||
715 | for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i) | |
3fe776ae | 716 | { |
c76b4180 | 717 | remotes[i]->s->verbose = remotes[i]->s->perpass_verbose[4]; |
3fe776ae JS |
718 | rc = remotes[i]->prepare(); |
719 | if (rc) | |
720 | return rc; | |
721 | } | |
722 | ||
723 | for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i) | |
4f244435 JS |
724 | { |
725 | rc = remotes[i]->start(); | |
726 | if (!ret) | |
727 | ret = rc; | |
728 | } | |
729 | ||
e96f2257 JS |
730 | // mask signals while we're preparing to poll |
731 | sigset_t mask, oldmask; | |
732 | sigemptyset (&mask); | |
733 | sigaddset (&mask, SIGHUP); | |
734 | sigaddset (&mask, SIGPIPE); | |
735 | sigaddset (&mask, SIGINT); | |
736 | sigaddset (&mask, SIGTERM); | |
737 | sigprocmask (SIG_BLOCK, &mask, &oldmask); | |
738 | ||
739 | for (;;) // polling loop for remotes that have fds to watch | |
740 | { | |
741 | vector<pollfd> fds; | |
742 | for (unsigned i = 0; i < remotes.size(); ++i) | |
743 | remotes[i]->prepare_poll (fds); | |
744 | if (fds.empty()) | |
745 | break; | |
746 | ||
747 | rc = ppoll (&fds[0], fds.size(), NULL, &oldmask); | |
748 | if (rc < 0 && errno != EINTR) | |
749 | break; | |
750 | ||
751 | for (unsigned i = 0; i < remotes.size(); ++i) | |
752 | remotes[i]->handle_poll (fds); | |
753 | } | |
754 | ||
755 | sigprocmask (SIG_SETMASK, &oldmask, NULL); | |
756 | ||
4f244435 JS |
757 | for (unsigned i = 0; i < remotes.size(); ++i) |
758 | { | |
759 | rc = remotes[i]->finish(); | |
760 | if (!ret) | |
761 | ret = rc; | |
762 | } | |
763 | ||
764 | return ret; | |
765 | } | |
766 | ||
daa75206 JS |
767 | |
768 | /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */ |