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