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