]>
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 | { | |
152 | ostringstream cmd; | |
153 | cmd << "signal " << SIGINT << "\n"; | |
154 | if (send_command(cmd.str()) == 0) | |
155 | { | |
156 | ++interrupts_sent; | |
157 | continue; | |
158 | } | |
159 | } | |
160 | close(); | |
161 | } | |
162 | } | |
163 | } | |
164 | ||
165 | int send_command(const string& cmd) | |
166 | { | |
167 | if (!IN) | |
168 | return 2; | |
169 | if (fputs(cmd.c_str(), IN) < 0 || | |
170 | fflush(IN) != 0) | |
171 | return 1; | |
172 | return 0; | |
173 | } | |
174 | ||
175 | int send_file(const string& filename, const string& dest) | |
176 | { | |
177 | int rc = 0; | |
178 | FILE* f = fopen(filename.c_str(), "r"); | |
179 | if (!f) | |
180 | return 1; | |
181 | ||
182 | struct stat fs; | |
183 | rc = fstat(fileno(f), &fs); | |
184 | if (!rc) | |
185 | { | |
186 | ostringstream cmd; | |
187 | cmd << "file " << fs.st_size << " " << dest << "\n"; | |
188 | rc = send_command(cmd.str()); | |
189 | } | |
190 | ||
191 | off_t i = 0; | |
192 | while (!rc && i < fs.st_size) | |
193 | { | |
194 | char buf[4096]; | |
195 | size_t r = sizeof(buf); | |
196 | if (fs.st_size - i < (off_t)r) | |
197 | r = fs.st_size - i; | |
198 | r = fread(buf, 1, r, f); | |
199 | if (r == 0) | |
200 | rc = 1; | |
201 | else | |
202 | { | |
203 | size_t w = fwrite(buf, 1, r, IN); | |
204 | if (w != r) | |
205 | rc = 1; | |
206 | else | |
207 | i += w; | |
208 | } | |
209 | } | |
210 | if (!rc) | |
211 | rc = fflush(IN); | |
212 | ||
213 | fclose(f); | |
214 | return rc; | |
215 | } | |
216 | ||
bf9c0b28 | 217 | static string qpencode(const string& str) |
e96f2257 | 218 | { |
bf9c0b28 JS |
219 | ostringstream o; |
220 | o << setfill('0') << hex; | |
221 | for (const char* s = str.c_str(); *s; ++s) | |
222 | if (*s >= 33 && *s <= 126 && *s != 61) | |
223 | o << *s; | |
224 | else | |
225 | o << '=' << setw(2) << (unsigned)(unsigned char) *s; | |
226 | return o.str(); | |
e96f2257 JS |
227 | } |
228 | ||
229 | protected: | |
230 | stapsh(systemtap_session& s) | |
231 | : remote(s), interrupts_sent(0), | |
232 | fdin(-1), fdout(-1), IN(0), OUT(0) | |
233 | {} | |
234 | ||
3fe776ae | 235 | virtual int prepare() |
e96f2257 JS |
236 | { |
237 | int rc = 0; | |
238 | ||
239 | string localmodule = s->tmpdir + "/" + s->module_name + ".ko"; | |
240 | string remotemodule = s->module_name + ".ko"; | |
241 | if ((rc = send_file(localmodule, remotemodule))) | |
242 | return rc; | |
243 | ||
244 | // XXX upload module.sig [and uprobes, uprobes.sig] | |
245 | ||
3fe776ae JS |
246 | return rc; |
247 | } | |
248 | ||
249 | virtual int start() | |
250 | { | |
e96f2257 JS |
251 | // Send the staprun args |
252 | // NB: The remote is left to decide its own staprun path | |
bf9c0b28 | 253 | ostringstream run("run", ios::out | ios::ate); |
3fe776ae | 254 | vector<string> cmd = make_run_command(*s, s->module_name + ".ko"); |
e96f2257 | 255 | for (unsigned i = 1; i < cmd.size(); ++i) |
bf9c0b28 JS |
256 | run << ' ' << qpencode(cmd[i]); |
257 | run << '\n'; | |
e96f2257 | 258 | |
3fe776ae | 259 | int rc = send_command(run.str()); |
e96f2257 JS |
260 | |
261 | if (!rc) | |
262 | { | |
263 | long flags = fcntl(fdout, F_GETFL) | O_NONBLOCK; | |
264 | fcntl(fdout, F_SETFL, flags); | |
265 | } | |
266 | ||
267 | return rc; | |
268 | } | |
269 | ||
270 | void close() | |
271 | { | |
272 | if (IN) fclose(IN); | |
273 | if (OUT) fclose(OUT); | |
274 | IN = OUT = NULL; | |
275 | fdin = fdout = -1; | |
276 | } | |
277 | ||
278 | virtual int finish() | |
279 | { | |
280 | close(); | |
281 | return 0; | |
282 | } | |
283 | ||
284 | void set_child_fds(int in, int out) | |
285 | { | |
286 | if (fdin >= 0 || fdout >= 0 || IN || OUT) | |
287 | throw runtime_error("stapsh file descriptors already set!"); | |
288 | ||
289 | fdin = in; | |
290 | fdout = out; | |
291 | IN = fdopen(fdin, "w"); | |
292 | OUT = fdopen(fdout, "r"); | |
293 | if (!IN || !OUT) | |
294 | throw runtime_error("invalid file descriptors for stapsh!"); | |
295 | ||
296 | if (send_command("stap " VERSION "\n")) | |
297 | throw runtime_error("error sending hello to stapsh!"); | |
298 | ||
299 | char reply[1024]; | |
300 | if (!fgets(reply, sizeof(reply), OUT)) | |
301 | throw runtime_error("error receiving hello from stapsh!"); | |
302 | ||
303 | // stapsh VERSION MACHINE RELEASE | |
304 | vector<string> uname; | |
305 | tokenize(reply, uname, " \t\r\n"); | |
306 | if (uname.size() != 4 || uname[0] != "stapsh") | |
307 | throw runtime_error("failed to get uname from stapsh"); | |
308 | // XXX check VERSION compatibility | |
309 | ||
310 | this->s = s->clone(uname[2], uname[3]); | |
311 | } | |
312 | ||
313 | public: | |
314 | virtual ~stapsh() { close(); } | |
315 | }; | |
316 | ||
317 | ||
318 | class direct_stapsh : public stapsh { | |
319 | private: | |
320 | pid_t child; | |
321 | ||
322 | direct_stapsh(systemtap_session& s) | |
323 | : stapsh(s), child(0) | |
324 | { | |
325 | int in, out; | |
326 | vector<string> cmd; | |
327 | cmd.push_back(BINDIR "/stapsh"); | |
328 | child = stap_spawn_piped(s.verbose, cmd, &in, &out); | |
329 | if (child <= 0) | |
330 | throw runtime_error("error launching stapsh!"); | |
331 | ||
332 | try | |
333 | { | |
334 | set_child_fds(in, out); | |
335 | } | |
336 | catch (runtime_error&) | |
337 | { | |
338 | finish(); | |
339 | throw; | |
340 | } | |
341 | } | |
342 | ||
343 | virtual int finish() | |
344 | { | |
345 | int rc = stapsh::finish(); | |
346 | if (child <= 0) | |
347 | return rc; | |
348 | ||
349 | int rc2 = stap_waitpid(s->verbose, child); | |
350 | child = 0; | |
351 | return rc ?: rc2; | |
352 | } | |
353 | ||
354 | public: | |
355 | friend class remote; | |
356 | ||
357 | virtual ~direct_stapsh() {} | |
358 | }; | |
359 | ||
360 | ||
361 | #if 1 // stapsh-based ssh_remote | |
362 | class ssh_remote : public stapsh { | |
363 | private: | |
364 | pid_t child; | |
365 | ||
366 | ssh_remote(systemtap_session& s, const string& host) | |
367 | : stapsh(s), child(0) | |
368 | { | |
369 | init(host); | |
370 | } | |
371 | ||
372 | ssh_remote(systemtap_session& s, const uri_decoder& ud) | |
373 | : stapsh(s), child(0) | |
374 | { | |
375 | if (!ud.has_authority || ud.authority.empty()) | |
376 | throw runtime_error("ssh target requires a hostname"); | |
377 | if (!ud.path.empty() && ud.path != "/") | |
378 | throw runtime_error("ssh target URI doesn't support a /path"); | |
379 | if (ud.has_query) | |
380 | throw runtime_error("ssh target URI doesn't support a ?query"); | |
381 | if (ud.has_fragment) | |
382 | throw runtime_error("ssh target URI doesn't support a #fragment"); | |
383 | ||
384 | init(ud.authority); | |
385 | } | |
386 | ||
387 | void init(const string& host) | |
388 | { | |
389 | // mask signals while we spawn, so we can manually send even tty | |
390 | // signals *through* ssh rather than to ssh itself | |
391 | sigset_t mask, oldmask; | |
392 | sigemptyset (&mask); | |
393 | sigaddset (&mask, SIGHUP); | |
394 | sigaddset (&mask, SIGPIPE); | |
395 | sigaddset (&mask, SIGINT); | |
396 | sigaddset (&mask, SIGTERM); | |
397 | sigprocmask (SIG_BLOCK, &mask, &oldmask); | |
398 | ||
399 | int in, out; | |
400 | vector<string> cmd; | |
401 | cmd.push_back("ssh"); | |
402 | cmd.push_back("-q"); | |
403 | cmd.push_back(host); | |
404 | cmd.push_back("stapsh"); // NB: relies on remote $PATH | |
405 | child = stap_spawn_piped(s->verbose, cmd, &in, &out); | |
406 | sigprocmask (SIG_SETMASK, &oldmask, NULL); // back to normal signals | |
407 | if (child <= 0) | |
408 | throw runtime_error("error launching stapsh!"); | |
409 | ||
410 | try | |
411 | { | |
412 | set_child_fds(in, out); | |
413 | } | |
414 | catch (runtime_error&) | |
415 | { | |
416 | finish(); | |
417 | throw; | |
418 | } | |
419 | } | |
420 | ||
421 | int finish() | |
422 | { | |
423 | int rc = stapsh::finish(); | |
424 | if (child <= 0) | |
425 | return rc; | |
426 | ||
427 | int rc2 = stap_waitpid(s->verbose, child); | |
428 | child = 0; | |
429 | return rc ?: rc2; | |
430 | } | |
431 | ||
432 | public: | |
433 | friend class remote; | |
434 | ||
435 | virtual ~ssh_remote() { } | |
436 | }; | |
437 | #else // !stapsh-based ssh_remote | |
daa75206 | 438 | class ssh_remote : public remote { |
b2cbfd40 | 439 | // NB: ssh commands use a tty (-t) so signals are passed along to the remote |
daa75206 | 440 | private: |
20f90026 JS |
441 | vector<string> ssh_args, scp_args; |
442 | string ssh_control; | |
91fa953d | 443 | string host, tmpdir; |
ebff2ed0 JS |
444 | pid_t child; |
445 | ||
91fa953d JS |
446 | ssh_remote(systemtap_session& s, const string& host) |
447 | : remote(s), host(host), child(0) | |
448 | { | |
1544bdf9 | 449 | init(); |
91fa953d JS |
450 | } |
451 | ||
452 | ssh_remote(systemtap_session& s, const uri_decoder& ud) | |
453 | : remote(s), child(0) | |
454 | { | |
455 | if (!ud.has_authority || ud.authority.empty()) | |
456 | throw runtime_error("ssh target requires a hostname"); | |
457 | if (!ud.path.empty() && ud.path != "/") | |
458 | throw runtime_error("ssh target URI doesn't support a /path"); | |
459 | if (ud.has_query) | |
460 | throw runtime_error("ssh target URI doesn't support a ?query"); | |
461 | if (ud.has_fragment) | |
462 | throw runtime_error("ssh target URI doesn't support a #fragment"); | |
463 | ||
464 | host = ud.authority; | |
1544bdf9 JS |
465 | init(); |
466 | } | |
467 | ||
468 | void init() | |
469 | { | |
4c156a0e | 470 | open_control_master(); |
1544bdf9 JS |
471 | try |
472 | { | |
473 | get_uname(); | |
474 | } | |
475 | catch (runtime_error&) | |
476 | { | |
477 | close_control_master(); | |
478 | throw; | |
479 | } | |
91fa953d JS |
480 | } |
481 | ||
4c156a0e JS |
482 | void open_control_master() |
483 | { | |
484 | static unsigned index = 0; | |
485 | ||
486 | if (s->tmpdir.empty()) // sanity check, shouldn't happen | |
487 | throw runtime_error("No tmpdir available for ssh control master"); | |
488 | ||
489 | ssh_control = s->tmpdir + "/ssh_remote_control_" + lex_cast(++index); | |
20f90026 JS |
490 | |
491 | scp_args.clear(); | |
492 | scp_args.push_back("scp"); | |
493 | scp_args.push_back("-q"); | |
494 | scp_args.push_back("-o"); | |
495 | scp_args.push_back("ControlPath=" + ssh_control); | |
496 | ||
497 | ssh_args = scp_args; | |
498 | ssh_args[0] = "ssh"; | |
499 | ssh_args.push_back(host); | |
4c156a0e JS |
500 | |
501 | // NB: ssh -f will stay in the foreground until authentication is | |
502 | // complete and the control socket is created, so we know it's ready to | |
503 | // go when stap_system returns. | |
20f90026 JS |
504 | vector<string> cmd = ssh_args; |
505 | cmd.push_back("-f"); | |
506 | cmd.push_back("-N"); | |
507 | cmd.push_back("-M"); | |
4c156a0e JS |
508 | int rc = stap_system(s->verbose, cmd); |
509 | if (rc != 0) | |
510 | { | |
511 | ostringstream err; | |
512 | err << "failed to create an ssh control master for " << host | |
513 | << " : rc=" << rc; | |
514 | throw runtime_error(err.str()); | |
515 | } | |
516 | ||
517 | if (s->verbose>1) | |
518 | clog << "Created ssh control master at " | |
519 | << lex_cast_qstring(ssh_control) << endl; | |
520 | } | |
521 | ||
522 | void close_control_master() | |
523 | { | |
524 | if (ssh_control.empty()) | |
525 | return; | |
526 | ||
20f90026 JS |
527 | vector<string> cmd = ssh_args; |
528 | cmd.push_back("-O"); | |
529 | cmd.push_back("exit"); | |
2323da5e | 530 | int rc = stap_system(s->verbose, cmd, true, true); |
4c156a0e JS |
531 | if (rc != 0) |
532 | cerr << "failed to stop the ssh control master for " << host | |
533 | << " : rc=" << rc << endl; | |
534 | ||
535 | ssh_control.clear(); | |
20f90026 JS |
536 | scp_args.clear(); |
537 | ssh_args.clear(); | |
4c156a0e JS |
538 | } |
539 | ||
91fa953d | 540 | void get_uname() |
daa75206 JS |
541 | { |
542 | ostringstream out; | |
543 | vector<string> uname; | |
20f90026 JS |
544 | vector<string> cmd = ssh_args; |
545 | cmd.push_back("-t"); | |
546 | cmd.push_back("uname -rm"); | |
547 | int rc = stap_system_read(s->verbose, cmd, out); | |
daa75206 JS |
548 | if (rc == 0) |
549 | tokenize(out.str(), uname, " \t\r\n"); | |
550 | if (uname.size() != 2) | |
91fa953d | 551 | throw runtime_error("failed to get uname from " + host |
daa75206 | 552 | + " : rc=" + lex_cast(rc)); |
4c156a0e JS |
553 | const string& release = uname[0]; |
554 | const string& arch = uname[1]; | |
555 | // XXX need to deal with command-line vs. implied arch/release | |
556 | this->s = s->clone(arch, release); | |
daa75206 JS |
557 | } |
558 | ||
ebff2ed0 | 559 | int start() |
daa75206 JS |
560 | { |
561 | int rc; | |
ebff2ed0 JS |
562 | string localmodule = s->tmpdir + "/" + s->module_name + ".ko"; |
563 | string tmpmodule; | |
daa75206 JS |
564 | |
565 | // Make a remote tempdir. | |
566 | { | |
567 | ostringstream out; | |
568 | vector<string> vout; | |
20f90026 JS |
569 | vector<string> cmd = ssh_args; |
570 | cmd.push_back("-t"); | |
571 | cmd.push_back("mktemp -d -t stapXXXXXX"); | |
ebff2ed0 | 572 | rc = stap_system_read(s->verbose, cmd, out); |
daa75206 | 573 | if (rc == 0) |
b2cbfd40 | 574 | tokenize(out.str(), vout, "\r\n"); |
daa75206 JS |
575 | if (vout.size() != 1) |
576 | { | |
91fa953d | 577 | cerr << "failed to make a tempdir on " << host |
daa75206 JS |
578 | << " : rc=" << rc << endl; |
579 | return -1; | |
580 | } | |
581 | tmpdir = vout[0]; | |
ebff2ed0 | 582 | tmpmodule = tmpdir + "/" + s->module_name + ".ko"; |
daa75206 JS |
583 | } |
584 | ||
585 | // Transfer the module. XXX and uprobes.ko, sigs, etc. | |
586 | if (rc == 0) { | |
20f90026 JS |
587 | vector<string> cmd = scp_args; |
588 | cmd.push_back(localmodule); | |
589 | cmd.push_back(host + ":" + tmpmodule); | |
ebff2ed0 | 590 | rc = stap_system(s->verbose, cmd); |
daa75206 | 591 | if (rc != 0) |
91fa953d | 592 | cerr << "failed to copy the module to " << host |
daa75206 JS |
593 | << " : rc=" << rc << endl; |
594 | } | |
595 | ||
596 | // Run the module on the remote. | |
597 | if (rc == 0) { | |
20f90026 JS |
598 | vector<string> cmd = ssh_args; |
599 | cmd.push_back("-t"); | |
5eea6ed1 | 600 | cmd.push_back(cmdstr_join(make_run_command(*s, tmpmodule))); |
ebff2ed0 JS |
601 | pid_t pid = stap_spawn(s->verbose, cmd); |
602 | if (pid > 0) | |
4c156a0e JS |
603 | child = pid; |
604 | else | |
605 | { | |
606 | cerr << "failed to run the module on " << host | |
607 | << " : ret=" << pid << endl; | |
608 | rc = -1; | |
609 | } | |
daa75206 JS |
610 | } |
611 | ||
4c156a0e | 612 | return rc; |
ebff2ed0 | 613 | } |
daa75206 | 614 | |
ebff2ed0 JS |
615 | int finish() |
616 | { | |
4c156a0e | 617 | int rc = 0; |
ebff2ed0 | 618 | |
4c156a0e JS |
619 | if (child > 0) |
620 | { | |
621 | rc = stap_waitpid(s->verbose, child); | |
622 | child = 0; | |
623 | } | |
ebff2ed0 JS |
624 | |
625 | if (!tmpdir.empty()) | |
4c156a0e JS |
626 | { |
627 | // Remove the tempdir. | |
628 | // XXX need to make sure this runs even with e.g. CTRL-C exits | |
20f90026 JS |
629 | vector<string> cmd = ssh_args; |
630 | cmd.push_back("-t"); | |
631 | cmd.push_back("rm -r " + cmdstr_quoted(tmpdir)); | |
4c156a0e JS |
632 | int rc2 = stap_system(s->verbose, cmd); |
633 | if (rc2 != 0) | |
634 | cerr << "failed to delete the tempdir on " << host | |
635 | << " : rc=" << rc2 << endl; | |
636 | if (rc == 0) | |
637 | rc = rc2; | |
20f90026 | 638 | tmpdir.clear(); |
4c156a0e JS |
639 | } |
640 | ||
641 | close_control_master(); | |
642 | ||
643 | return rc; | |
daa75206 | 644 | } |
4f244435 JS |
645 | |
646 | public: | |
647 | friend class remote; | |
648 | ||
649 | virtual ~ssh_remote() | |
650 | { | |
651 | close_control_master(); | |
652 | } | |
daa75206 | 653 | }; |
e96f2257 | 654 | #endif |
daa75206 JS |
655 | |
656 | ||
657 | remote* | |
658 | remote::create(systemtap_session& s, const string& uri) | |
659 | { | |
660 | try | |
661 | { | |
662 | if (uri == "direct") | |
663 | return new direct(s); | |
e96f2257 JS |
664 | else if (uri == "stapsh") |
665 | return new direct_stapsh(s); | |
91fa953d JS |
666 | else if (uri.find(':') != string::npos) |
667 | { | |
668 | const uri_decoder ud(uri); | |
669 | if (ud.scheme == "ssh") | |
670 | return new ssh_remote(s, ud); | |
671 | else | |
672 | { | |
673 | ostringstream msg; | |
674 | msg << "unrecognized URI scheme '" << ud.scheme | |
675 | << "' in remote: " << uri; | |
676 | throw runtime_error(msg.str()); | |
677 | } | |
678 | } | |
daa75206 JS |
679 | else |
680 | // XXX assuming everything else is ssh for now... | |
681 | return new ssh_remote(s, uri); | |
682 | } | |
683 | catch (std::runtime_error& e) | |
684 | { | |
685 | cerr << e.what() << endl; | |
686 | return NULL; | |
687 | } | |
688 | } | |
689 | ||
4f244435 JS |
690 | int |
691 | remote::run(const vector<remote*>& remotes) | |
692 | { | |
693 | // NB: the first failure "wins" | |
694 | int ret = 0, rc = 0; | |
695 | ||
696 | for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i) | |
3fe776ae JS |
697 | { |
698 | rc = remotes[i]->prepare(); | |
699 | if (rc) | |
700 | return rc; | |
701 | } | |
702 | ||
703 | for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i) | |
4f244435 JS |
704 | { |
705 | rc = remotes[i]->start(); | |
706 | if (!ret) | |
707 | ret = rc; | |
708 | } | |
709 | ||
e96f2257 JS |
710 | // mask signals while we're preparing to poll |
711 | sigset_t mask, oldmask; | |
712 | sigemptyset (&mask); | |
713 | sigaddset (&mask, SIGHUP); | |
714 | sigaddset (&mask, SIGPIPE); | |
715 | sigaddset (&mask, SIGINT); | |
716 | sigaddset (&mask, SIGTERM); | |
717 | sigprocmask (SIG_BLOCK, &mask, &oldmask); | |
718 | ||
719 | for (;;) // polling loop for remotes that have fds to watch | |
720 | { | |
721 | vector<pollfd> fds; | |
722 | for (unsigned i = 0; i < remotes.size(); ++i) | |
723 | remotes[i]->prepare_poll (fds); | |
724 | if (fds.empty()) | |
725 | break; | |
726 | ||
727 | rc = ppoll (&fds[0], fds.size(), NULL, &oldmask); | |
728 | if (rc < 0 && errno != EINTR) | |
729 | break; | |
730 | ||
731 | for (unsigned i = 0; i < remotes.size(); ++i) | |
732 | remotes[i]->handle_poll (fds); | |
733 | } | |
734 | ||
735 | sigprocmask (SIG_SETMASK, &oldmask, NULL); | |
736 | ||
4f244435 JS |
737 | for (unsigned i = 0; i < remotes.size(); ++i) |
738 | { | |
739 | rc = remotes[i]->finish(); | |
740 | if (!ret) | |
741 | ret = rc; | |
742 | } | |
743 | ||
744 | return ret; | |
745 | } | |
746 | ||
daa75206 JS |
747 | |
748 | /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */ |