]> sourceware.org Git - systemtap.git/blame - remote.cxx
Added news for pr12729
[systemtap.git] / remote.cxx
CommitLineData
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
e04a4b41
JS
9#include "config.h"
10
daa75206 11extern "C" {
e96f2257 12#include <fcntl.h>
e04a4b41 13#include <poll.h>
e96f2257
JS
14#include <sys/types.h>
15#include <sys/stat.h>
16#include <unistd.h>
daa75206
JS
17}
18
e96f2257 19#include <cstdio>
bf9c0b28 20#include <iomanip>
8d1a21b3 21#include <memory>
daa75206
JS
22#include <stdexcept>
23#include <sstream>
24#include <string>
25#include <vector>
26
27#include "buildrun.h"
28#include "remote.h"
29#include "util.h"
30
31using namespace std;
32
91fa953d
JS
33// Decode URIs as per RFC 3986, though not bothering to be strict
34class uri_decoder {
35 public:
36 const string uri;
37 string scheme, authority, path, query, fragment;
38 bool has_authority, has_query, has_fragment;
39
40 uri_decoder(const string& uri):
41 uri(uri), has_authority(false), has_query(false), has_fragment(false)
42 {
43 const string re =
44 "^([^:]+):(//[^/?#]*)?([^?#]*)(\\?[^#]*)?(#.*)?$";
45
46 vector<string> matches;
47 if (regexp_match(uri, re, matches) != 0)
58a834b1 48 throw runtime_error(_F("string doesn't appear to be a URI: %s", uri.c_str()));
91fa953d
JS
49
50 scheme = matches[1];
51
52 if (!matches[2].empty())
53 {
54 has_authority = true;
55 authority = matches[2].substr(2);
56 }
57
58 path = matches[3];
59
60 if (!matches[4].empty())
61 {
62 has_query = true;
63 query = matches[4].substr(1);
64 }
65
66 if (!matches[5].empty())
67 {
68 has_fragment = true;
69 fragment = matches[5].substr(1);
70 }
71 }
72};
73
74
daa75206
JS
75// loopback target for running locally
76class direct : public remote {
ebff2ed0
JS
77 private:
78 pid_t child;
5def5d2a 79 vector<string> args;
ebff2ed0 80 direct(systemtap_session& s): remote(s), child(0) {}
daa75206 81
ebff2ed0
JS
82 int start()
83 {
5def5d2a
LB
84 args = make_run_command(*s);
85 pid_t pid = stap_spawn (s->verbose, args);
4c156a0e
JS
86 if (pid <= 0)
87 return 1;
88 child = pid;
89 return 0;
ebff2ed0
JS
90 }
91
92 int finish()
93 {
4c156a0e
JS
94 if (child <= 0)
95 return 1;
ebff2ed0 96
4c156a0e 97 int ret = stap_waitpid(s->verbose, child);
5def5d2a
LB
98 if(ret)
99 clog << _F("Warning: %s exited with status: %d", args.front().c_str(), ret) << endl;
4c156a0e
JS
100 child = 0;
101 return ret;
ebff2ed0 102 }
4f244435
JS
103
104 public:
105 friend class remote;
106
10a5bce5 107 virtual ~direct() { finish(); }
daa75206
JS
108};
109
e96f2257
JS
110
111class stapsh : public remote {
112 private:
113 int interrupts_sent;
114 int fdin, fdout;
115 FILE *IN, *OUT;
116
117 virtual void prepare_poll(vector<pollfd>& fds)
118 {
119 if (fdout >= 0 && OUT)
120 {
822a6a3d 121 pollfd p = { fdout, POLLIN, 0 };
e96f2257
JS
122 fds.push_back(p);
123 }
124
125 // need to send a signal?
126 if (fdin >= 0 && IN && interrupts_sent < pending_interrupts)
127 {
822a6a3d 128 pollfd p = { fdin, POLLOUT, 0 };
e96f2257
JS
129 fds.push_back(p);
130 }
131 }
132
133 virtual void handle_poll(vector<pollfd>& fds)
134 {
135 for (unsigned i=0; i < fds.size(); ++i)
136 if (fds[i].revents)
137 {
138 if (fdout >= 0 && OUT && fds[i].fd == fdout)
139 {
140 if (fds[i].revents & POLLIN)
141 {
e96f2257 142 char buf[4096];
756cfd49 143 if (!prefix.empty())
e96f2257 144 {
756cfd49
JS
145 // If we have a line prefix, then read lines one at a
146 // time and copy out with the prefix.
147 errno = 0;
148 while (fgets(buf, sizeof(buf), OUT))
149 cout << prefix << buf;
150 if (errno == EAGAIN)
151 continue;
152 }
153 else
154 {
155 // Otherwise read an entire block of data at once.
156 size_t rc = fread(buf, 1, sizeof(buf), OUT);
157 if (rc > 0)
158 {
159 // NB: The buf could contain binary data,
160 // including \0, so write as a block instead of
161 // the usual <<string.
162 cout.write(buf, rc);
163 continue;
164 }
e96f2257
JS
165 }
166 }
167 close();
168 }
169
170 // need to send a signal?
171 if (fdin >= 0 && IN && fds[i].fd == fdin &&
172 interrupts_sent < pending_interrupts)
173 {
174 if (fds[i].revents & POLLOUT)
175 {
7185074b 176 if (send_command("quit\n") == 0)
e96f2257
JS
177 {
178 ++interrupts_sent;
179 continue;
180 }
181 }
182 close();
183 }
184 }
185 }
186
35df6b43
JS
187 string get_reply()
188 {
189 char reply[4096];
190 if (!fgets(reply, sizeof(reply), OUT))
191 reply[0] = '\0';
192 return reply;
193 }
194
e96f2257
JS
195 int send_command(const string& cmd)
196 {
197 if (!IN)
198 return 2;
199 if (fputs(cmd.c_str(), IN) < 0 ||
200 fflush(IN) != 0)
201 return 1;
202 return 0;
203 }
204
205 int send_file(const string& filename, const string& dest)
206 {
207 int rc = 0;
208 FILE* f = fopen(filename.c_str(), "r");
209 if (!f)
210 return 1;
211
212 struct stat fs;
213 rc = fstat(fileno(f), &fs);
214 if (!rc)
215 {
216 ostringstream cmd;
217 cmd << "file " << fs.st_size << " " << dest << "\n";
218 rc = send_command(cmd.str());
219 }
220
221 off_t i = 0;
222 while (!rc && i < fs.st_size)
223 {
224 char buf[4096];
225 size_t r = sizeof(buf);
226 if (fs.st_size - i < (off_t)r)
227 r = fs.st_size - i;
228 r = fread(buf, 1, r, f);
229 if (r == 0)
230 rc = 1;
231 else
232 {
233 size_t w = fwrite(buf, 1, r, IN);
234 if (w != r)
235 rc = 1;
236 else
237 i += w;
238 }
239 }
240 if (!rc)
241 rc = fflush(IN);
242
243 fclose(f);
35df6b43
JS
244
245 if (!rc)
246 {
247 string reply = get_reply();
248 if (reply != "OK\n")
249 {
250 rc = 1;
251 if (s->verbose > 1)
252 {
253 if (reply.empty())
f3f6443c 254 clog << _("stapsh file ERROR: no reply") << endl;
35df6b43 255 else
f3f6443c 256 clog << _F("stapsh file replied %s", reply.c_str());
35df6b43
JS
257 }
258 }
259 }
260
e96f2257
JS
261 return rc;
262 }
263
bf9c0b28 264 static string qpencode(const string& str)
e96f2257 265 {
bf9c0b28
JS
266 ostringstream o;
267 o << setfill('0') << hex;
268 for (const char* s = str.c_str(); *s; ++s)
269 if (*s >= 33 && *s <= 126 && *s != 61)
270 o << *s;
271 else
272 o << '=' << setw(2) << (unsigned)(unsigned char) *s;
273 return o.str();
e96f2257
JS
274 }
275
276 protected:
277 stapsh(systemtap_session& s)
278 : remote(s), interrupts_sent(0),
279 fdin(-1), fdout(-1), IN(0), OUT(0)
280 {}
281
3fe776ae 282 virtual int prepare()
e96f2257
JS
283 {
284 int rc = 0;
285
286 string localmodule = s->tmpdir + "/" + s->module_name + ".ko";
287 string remotemodule = s->module_name + ".ko";
288 if ((rc = send_file(localmodule, remotemodule)))
289 return rc;
290
599c80df
JS
291 if (file_exists(localmodule + ".sgn") &&
292 (rc = send_file(localmodule + ".sgn", remotemodule + ".sgn")))
293 return rc;
294
295 if (!s->uprobes_path.empty())
296 {
297 string remoteuprobes = basename(s->uprobes_path.c_str());
298 if ((rc = send_file(s->uprobes_path, remoteuprobes)))
299 return rc;
300
301 if (file_exists(s->uprobes_path + ".sgn") &&
302 (rc = send_file(s->uprobes_path + ".sgn", remoteuprobes + ".sgn")))
303 return rc;
304 }
e96f2257 305
3fe776ae
JS
306 return rc;
307 }
308
309 virtual int start()
310 {
e96f2257
JS
311 // Send the staprun args
312 // NB: The remote is left to decide its own staprun path
bf9c0b28 313 ostringstream run("run", ios::out | ios::ate);
3fe776ae 314 vector<string> cmd = make_run_command(*s, s->module_name + ".ko");
e96f2257 315 for (unsigned i = 1; i < cmd.size(); ++i)
bf9c0b28
JS
316 run << ' ' << qpencode(cmd[i]);
317 run << '\n';
e96f2257 318
3fe776ae 319 int rc = send_command(run.str());
e96f2257 320
35df6b43
JS
321 if (!rc)
322 {
323 string reply = get_reply();
324 if (reply != "OK\n")
325 {
326 rc = 1;
327 if (s->verbose > 1)
328 {
329 if (reply.empty())
f3f6443c 330 clog << _("stapsh run ERROR: no reply") << endl;
35df6b43 331 else
f3f6443c 332 clog << _F("stapsh run replied %s", reply.c_str());
35df6b43
JS
333 }
334 }
335 }
336
e96f2257
JS
337 if (!rc)
338 {
339 long flags = fcntl(fdout, F_GETFL) | O_NONBLOCK;
340 fcntl(fdout, F_SETFL, flags);
341 }
342
343 return rc;
344 }
345
346 void close()
347 {
348 if (IN) fclose(IN);
349 if (OUT) fclose(OUT);
350 IN = OUT = NULL;
351 fdin = fdout = -1;
352 }
353
354 virtual int finish()
355 {
356 close();
357 return 0;
358 }
359
360 void set_child_fds(int in, int out)
361 {
362 if (fdin >= 0 || fdout >= 0 || IN || OUT)
8d1a21b3 363 throw runtime_error(_("stapsh file descriptors already set"));
e96f2257
JS
364
365 fdin = in;
366 fdout = out;
367 IN = fdopen(fdin, "w");
368 OUT = fdopen(fdout, "r");
369 if (!IN || !OUT)
8d1a21b3 370 throw runtime_error(_("invalid file descriptors for stapsh"));
e96f2257
JS
371
372 if (send_command("stap " VERSION "\n"))
8d1a21b3 373 throw runtime_error(_("error sending hello to stapsh"));
e96f2257 374
35df6b43
JS
375 string reply = get_reply();
376 if (reply.empty())
8d1a21b3 377 throw runtime_error(_("error receiving hello from stapsh"));
e96f2257
JS
378
379 // stapsh VERSION MACHINE RELEASE
380 vector<string> uname;
381 tokenize(reply, uname, " \t\r\n");
382 if (uname.size() != 4 || uname[0] != "stapsh")
f3f6443c 383 throw runtime_error(_("failed to get uname from stapsh"));
e96f2257
JS
384 // XXX check VERSION compatibility
385
386 this->s = s->clone(uname[2], uname[3]);
387 }
388
389 public:
390 virtual ~stapsh() { close(); }
391};
392
393
88e08f53
JS
394// direct_stapsh is meant only for testing, as a way to exercise the stapsh
395// mechanisms without requiring test machines to have actual remote access.
e96f2257
JS
396class direct_stapsh : public stapsh {
397 private:
398 pid_t child;
399
400 direct_stapsh(systemtap_session& s)
401 : stapsh(s), child(0)
402 {
8d1a21b3
JS
403 int in, out;
404 vector<string> cmd;
405 cmd.push_back(BINDIR "/stapsh");
406 if (s.perpass_verbose[4] > 1)
407 cmd.push_back("-v");
408 if (s.perpass_verbose[4] > 2)
409 cmd.push_back("-v");
410
88e08f53
JS
411 // mask signals while we spawn, so we can simulate manual signals to
412 // the "remote" target, as we must for the real ssh_remote case.
a69b4b55
JS
413 {
414 stap_sigmasker masked;
415 child = stap_spawn_piped(s.verbose, cmd, &in, &out);
416 }
8d1a21b3 417
e96f2257 418 if (child <= 0)
8d1a21b3 419 throw runtime_error(_("error launching stapsh"));
e96f2257
JS
420
421 try
422 {
423 set_child_fds(in, out);
424 }
425 catch (runtime_error&)
426 {
427 finish();
428 throw;
429 }
430 }
431
432 virtual int finish()
433 {
434 int rc = stapsh::finish();
435 if (child <= 0)
436 return rc;
437
438 int rc2 = stap_waitpid(s->verbose, child);
439 child = 0;
440 return rc ?: rc2;
441 }
442
443 public:
444 friend class remote;
445
10a5bce5 446 virtual ~direct_stapsh() { finish(); }
e96f2257
JS
447};
448
449
8d1a21b3 450// stapsh-based ssh_remote
e96f2257
JS
451class ssh_remote : public stapsh {
452 private:
453 pid_t child;
454
8d1a21b3 455 ssh_remote(systemtap_session& s): stapsh(s), child(0) {}
e96f2257 456
eacb2780 457 int connect(const string& host, const string& port)
e96f2257 458 {
8d1a21b3
JS
459 int rc = 0;
460 int in, out;
461 vector<string> cmd;
462 cmd.push_back("ssh");
463 cmd.push_back("-q");
464 cmd.push_back(host);
eacb2780
JS
465 if (!port.empty())
466 {
467 cmd.push_back("-p");
468 cmd.push_back(port);
469 }
8d1a21b3
JS
470
471 // This is crafted so that we get a silent failure with status 127 if
472 // the command is not found. The combination of -P and $cmd ensures
473 // that we pull the command out of the PATH, not aliases or such.
75daad84 474 string stapsh_cmd = "cmd=`type -P stapsh || exit 127` && exec \"$cmd\"";
8d1a21b3 475 if (s->perpass_verbose[4] > 1)
75daad84 476 stapsh_cmd.append(" -v");
8d1a21b3 477 if (s->perpass_verbose[4] > 2)
75daad84
JS
478 stapsh_cmd.append(" -v");
479 // NB: We need to explicitly choose bash, as $SHELL may be weird...
480 cmd.push_back("/bin/bash -c '" + stapsh_cmd + "'");
e96f2257 481
e96f2257
JS
482 // mask signals while we spawn, so we can manually send even tty
483 // signals *through* ssh rather than to ssh itself
a69b4b55
JS
484 {
485 stap_sigmasker masked;
486 child = stap_spawn_piped(s->verbose, cmd, &in, &out);
487 }
8d1a21b3 488
e96f2257 489 if (child <= 0)
8d1a21b3 490 throw runtime_error(_("error launching stapsh"));
e96f2257
JS
491
492 try
493 {
494 set_child_fds(in, out);
495 }
496 catch (runtime_error&)
497 {
c00db46e
JS
498 rc = finish();
499
500 // ssh itself signals errors with 255
501 if (rc == 255)
502 throw runtime_error(_("error establishing ssh connection"));
503
8d1a21b3
JS
504 // If rc == 127, that's command-not-found, so we let ::create()
505 // try again in legacy mode. But only do this if there's a single
506 // remote, as the old code didn't handle ttys well with multiple
507 // remotes. Otherwise, throw up again. *barf*
8d1a21b3
JS
508 if (rc != 127 || s->remote_uris.size() > 1)
509 throw;
e96f2257 510 }
8d1a21b3
JS
511
512 return rc;
e96f2257
JS
513 }
514
515 int finish()
516 {
517 int rc = stapsh::finish();
518 if (child <= 0)
519 return rc;
520
521 int rc2 = stap_waitpid(s->verbose, child);
522 child = 0;
523 return rc ?: rc2;
524 }
525
526 public:
8d1a21b3
JS
527
528 static remote* create(systemtap_session& s, const string& host);
529 static remote* create(systemtap_session& s, const uri_decoder& ud);
e96f2257 530
10a5bce5 531 virtual ~ssh_remote() { finish(); }
e96f2257 532};
8d1a21b3
JS
533
534
535// ssh connection without stapsh, for legacy stap installations
536// NB: ssh commands use a tty (-t) so signals are passed along to the remote.
537// It does this by putting the local tty in raw mode, so it only works for tty
538// signals, and only for a single remote at a time.
539class ssh_legacy_remote : public remote {
daa75206 540 private:
20f90026
JS
541 vector<string> ssh_args, scp_args;
542 string ssh_control;
eacb2780 543 string host, port, tmpdir;
ebff2ed0
JS
544 pid_t child;
545
eacb2780
JS
546 ssh_legacy_remote(systemtap_session& s, const string& host, const string& port)
547 : remote(s), host(host), port(port), child(0)
1544bdf9 548 {
4c156a0e 549 open_control_master();
1544bdf9
JS
550 try
551 {
552 get_uname();
553 }
554 catch (runtime_error&)
555 {
556 close_control_master();
557 throw;
558 }
91fa953d
JS
559 }
560
4c156a0e
JS
561 void open_control_master()
562 {
563 static unsigned index = 0;
564
565 if (s->tmpdir.empty()) // sanity check, shouldn't happen
58a834b1 566 throw runtime_error(_("No tmpdir available for ssh control master"));
4c156a0e
JS
567
568 ssh_control = s->tmpdir + "/ssh_remote_control_" + lex_cast(++index);
20f90026
JS
569
570 scp_args.clear();
571 scp_args.push_back("scp");
572 scp_args.push_back("-q");
573 scp_args.push_back("-o");
574 scp_args.push_back("ControlPath=" + ssh_control);
575
576 ssh_args = scp_args;
577 ssh_args[0] = "ssh";
578 ssh_args.push_back(host);
4c156a0e 579
eacb2780
JS
580 if (!port.empty())
581 {
582 scp_args.push_back("-P");
583 scp_args.push_back(port);
584 ssh_args.push_back("-p");
585 ssh_args.push_back(port);
586 }
587
4c156a0e
JS
588 // NB: ssh -f will stay in the foreground until authentication is
589 // complete and the control socket is created, so we know it's ready to
590 // go when stap_system returns.
20f90026
JS
591 vector<string> cmd = ssh_args;
592 cmd.push_back("-f");
593 cmd.push_back("-N");
594 cmd.push_back("-M");
4c156a0e
JS
595 int rc = stap_system(s->verbose, cmd);
596 if (rc != 0)
8d1a21b3
JS
597 throw runtime_error(_F("failed to create an ssh control master for %s : rc= %d",
598 host.c_str(), rc));
4c156a0e
JS
599
600 if (s->verbose>1)
58a834b1
LB
601 clog << _F("Created ssh control master at %s",
602 lex_cast_qstring(ssh_control).c_str()) << endl;
4c156a0e
JS
603 }
604
605 void close_control_master()
606 {
607 if (ssh_control.empty())
608 return;
609
20f90026
JS
610 vector<string> cmd = ssh_args;
611 cmd.push_back("-O");
612 cmd.push_back("exit");
2323da5e 613 int rc = stap_system(s->verbose, cmd, true, true);
4c156a0e 614 if (rc != 0)
58a834b1
LB
615 cerr << _F("failed to stop the ssh control master for %s : rc=%d",
616 host.c_str(), rc) << endl;
4c156a0e
JS
617
618 ssh_control.clear();
20f90026
JS
619 scp_args.clear();
620 ssh_args.clear();
4c156a0e
JS
621 }
622
91fa953d 623 void get_uname()
daa75206
JS
624 {
625 ostringstream out;
626 vector<string> uname;
20f90026
JS
627 vector<string> cmd = ssh_args;
628 cmd.push_back("-t");
629 cmd.push_back("uname -rm");
630 int rc = stap_system_read(s->verbose, cmd, out);
daa75206
JS
631 if (rc == 0)
632 tokenize(out.str(), uname, " \t\r\n");
633 if (uname.size() != 2)
58a834b1 634 throw runtime_error(_F("failed to get uname from %s : rc= %d", host.c_str(), rc));
4c156a0e
JS
635 const string& release = uname[0];
636 const string& arch = uname[1];
637 // XXX need to deal with command-line vs. implied arch/release
638 this->s = s->clone(arch, release);
daa75206
JS
639 }
640
ebff2ed0 641 int start()
daa75206
JS
642 {
643 int rc;
ebff2ed0
JS
644 string localmodule = s->tmpdir + "/" + s->module_name + ".ko";
645 string tmpmodule;
daa75206
JS
646
647 // Make a remote tempdir.
648 {
649 ostringstream out;
650 vector<string> vout;
20f90026
JS
651 vector<string> cmd = ssh_args;
652 cmd.push_back("-t");
653 cmd.push_back("mktemp -d -t stapXXXXXX");
ebff2ed0 654 rc = stap_system_read(s->verbose, cmd, out);
daa75206 655 if (rc == 0)
b2cbfd40 656 tokenize(out.str(), vout, "\r\n");
daa75206
JS
657 if (vout.size() != 1)
658 {
58a834b1
LB
659 cerr << _F("failed to make a tempdir on %s : rc=%d",
660 host.c_str(), rc) << endl;
daa75206
JS
661 return -1;
662 }
663 tmpdir = vout[0];
ebff2ed0 664 tmpmodule = tmpdir + "/" + s->module_name + ".ko";
daa75206
JS
665 }
666
667 // Transfer the module. XXX and uprobes.ko, sigs, etc.
668 if (rc == 0) {
20f90026
JS
669 vector<string> cmd = scp_args;
670 cmd.push_back(localmodule);
671 cmd.push_back(host + ":" + tmpmodule);
ebff2ed0 672 rc = stap_system(s->verbose, cmd);
daa75206 673 if (rc != 0)
58a834b1
LB
674 cerr << _F("failed to copy the module to %s : rc=%d",
675 host.c_str(), rc) << endl;
daa75206
JS
676 }
677
678 // Run the module on the remote.
679 if (rc == 0) {
20f90026
JS
680 vector<string> cmd = ssh_args;
681 cmd.push_back("-t");
5eea6ed1 682 cmd.push_back(cmdstr_join(make_run_command(*s, tmpmodule)));
ebff2ed0
JS
683 pid_t pid = stap_spawn(s->verbose, cmd);
684 if (pid > 0)
4c156a0e
JS
685 child = pid;
686 else
687 {
58a834b1
LB
688 cerr << _F("failed to run the module on %s : ret=%d",
689 host.c_str(), pid) << endl;
4c156a0e
JS
690 rc = -1;
691 }
daa75206
JS
692 }
693
4c156a0e 694 return rc;
ebff2ed0 695 }
daa75206 696
ebff2ed0
JS
697 int finish()
698 {
4c156a0e 699 int rc = 0;
ebff2ed0 700
4c156a0e
JS
701 if (child > 0)
702 {
703 rc = stap_waitpid(s->verbose, child);
704 child = 0;
705 }
ebff2ed0
JS
706
707 if (!tmpdir.empty())
4c156a0e
JS
708 {
709 // Remove the tempdir.
710 // XXX need to make sure this runs even with e.g. CTRL-C exits
20f90026
JS
711 vector<string> cmd = ssh_args;
712 cmd.push_back("-t");
713 cmd.push_back("rm -r " + cmdstr_quoted(tmpdir));
4c156a0e
JS
714 int rc2 = stap_system(s->verbose, cmd);
715 if (rc2 != 0)
58a834b1
LB
716 cerr << _F("failed to delete the tempdir on %s : rc=%d",
717 host.c_str(), rc2) << endl;
4c156a0e
JS
718 if (rc == 0)
719 rc = rc2;
20f90026 720 tmpdir.clear();
4c156a0e
JS
721 }
722
723 close_control_master();
724
725 return rc;
daa75206 726 }
4f244435
JS
727
728 public:
8d1a21b3 729 friend class ssh_remote;
4f244435 730
8d1a21b3 731 virtual ~ssh_legacy_remote()
4f244435
JS
732 {
733 close_control_master();
734 }
daa75206 735};
8d1a21b3
JS
736
737
738// Try to establish a stapsh connection to the remote, but fallback
739// to the older mechanism if the command is not found.
740remote*
eacb2780 741ssh_remote::create(systemtap_session& s, const string& target)
8d1a21b3 742{
eacb2780
JS
743 string port, host = target;
744 size_t i = host.find(':');
745 if (i != string::npos)
746 {
747 port = host.substr(i + 1);
748 host.erase(i);
749 }
750
8d1a21b3 751 auto_ptr<ssh_remote> p (new ssh_remote(s));
eacb2780 752 int rc = p->connect(host, port);
8d1a21b3
JS
753 if (rc == 0)
754 return p.release();
755 else if (rc == 127) // stapsh command not found
eacb2780 756 return new ssh_legacy_remote(s, host, port); // try legacy instead
8d1a21b3
JS
757 return NULL;
758}
759
760remote*
761ssh_remote::create(systemtap_session& s, const uri_decoder& ud)
762{
763 if (!ud.has_authority || ud.authority.empty())
764 throw runtime_error(_("ssh target requires a hostname"));
765 if (!ud.path.empty() && ud.path != "/")
766 throw runtime_error(_("ssh target URI doesn't support a /path"));
767 if (ud.has_query)
768 throw runtime_error(_("ssh target URI doesn't support a ?query"));
769 if (ud.has_fragment)
770 throw runtime_error(_("ssh target URI doesn't support a #fragment"));
771
772 return create(s, ud.authority);
773}
daa75206
JS
774
775
776remote*
777remote::create(systemtap_session& s, const string& uri)
778{
779 try
780 {
2bce499b 781 if (uri.find(':') != string::npos)
91fa953d
JS
782 {
783 const uri_decoder ud(uri);
eacb2780
JS
784
785 // An ssh "host:port" is ambiguous with a URI "scheme:path".
786 // So if it looks like a number, just assume ssh.
787 if (!ud.has_authority && !ud.has_query &&
788 !ud.has_fragment && !ud.path.empty() &&
789 ud.path.find_first_not_of("1234567890") == string::npos)
790 return ssh_remote::create(s, uri);
791
2bce499b
JS
792 if (ud.scheme == "direct")
793 return new direct(s);
794 else if (ud.scheme == "stapsh")
795 return new direct_stapsh(s);
91fa953d 796 if (ud.scheme == "ssh")
8d1a21b3 797 return ssh_remote::create(s, ud);
91fa953d 798 else
8d1a21b3
JS
799 throw runtime_error(_F("unrecognized URI scheme '%s' in remote: %s",
800 ud.scheme.c_str(), uri.c_str()));
91fa953d 801 }
daa75206
JS
802 else
803 // XXX assuming everything else is ssh for now...
8d1a21b3 804 return ssh_remote::create(s, uri);
daa75206
JS
805 }
806 catch (std::runtime_error& e)
807 {
8d1a21b3 808 cerr << e.what() << " on remote '" << uri << "'" << endl;
daa75206
JS
809 return NULL;
810 }
811}
812
e04a4b41
JS
813#ifndef HAVE_PPOLL
814// This is a poor-man's ppoll, only used by remote::run below. It does not
815// provide the same guarantee of atomicity as on systems with a true ppoll.
816//
817// In our use, this would cause trouble if a signal came in any time from the
818// moment we mask signals to prepare pollfds, to the moment we call poll in
819// emulation here. If there's no data on any of the pollfds, we will be stuck
820// waiting indefinitely.
821//
822// Since this is mainly about responsiveness of CTRL-C cleanup, we'll just
823// throw in a one-second forced timeout to ensure we have a chance to notice
824// there was an interrupt without too much delay.
825static int
826ppoll(struct pollfd *fds, nfds_t nfds,
827 const struct timespec *timeout_ts,
828 const sigset_t *sigmask)
829{
830 sigset_t origmask;
831 int timeout = (timeout_ts == NULL) ? 1000 // don't block forever...
832 : (timeout_ts->tv_sec * 1000 + timeout_ts->tv_nsec / 1000000);
833 sigprocmask(SIG_SETMASK, sigmask, &origmask);
834 int rc = poll(fds, nfds, timeout);
835 sigprocmask(SIG_SETMASK, &origmask, NULL);
836 return rc;
837}
838#endif
839
4f244435
JS
840int
841remote::run(const vector<remote*>& remotes)
842{
843 // NB: the first failure "wins"
844 int ret = 0, rc = 0;
845
846 for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i)
3fe776ae 847 {
756cfd49
JS
848 remote *r = remotes[i];
849 r->s->verbose = r->s->perpass_verbose[4];
850 if (r->s->use_remote_prefix)
851 r->prefix = lex_cast(i) + ": ";
852 rc = r->prepare();
3fe776ae
JS
853 if (rc)
854 return rc;
855 }
856
857 for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i)
4f244435
JS
858 {
859 rc = remotes[i]->start();
860 if (!ret)
861 ret = rc;
862 }
863
e96f2257 864 // mask signals while we're preparing to poll
a69b4b55
JS
865 {
866 stap_sigmasker masked;
e96f2257 867
a69b4b55
JS
868 // polling loop for remotes that have fds to watch
869 for (;;)
870 {
871 vector<pollfd> fds;
872 for (unsigned i = 0; i < remotes.size(); ++i)
873 remotes[i]->prepare_poll (fds);
874 if (fds.empty())
875 break;
876
877 rc = ppoll (&fds[0], fds.size(), NULL, &masked.old);
878 if (rc < 0 && errno != EINTR)
879 break;
880
881 for (unsigned i = 0; i < remotes.size(); ++i)
882 remotes[i]->handle_poll (fds);
883 }
884 }
e96f2257 885
4f244435
JS
886 for (unsigned i = 0; i < remotes.size(); ++i)
887 {
888 rc = remotes[i]->finish();
889 if (!ret)
890 ret = rc;
891 }
892
893 return ret;
894}
895
daa75206
JS
896
897/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.117987 seconds and 5 git commands to generate.