]>
sourceware.org Git - systemtap.git/blob - remote.cxx
1 // systemtap remote execution
2 // Copyright (C) 2010-2011 Red Hat Inc.
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
14 #include <sys/types.h>
33 // Decode URIs as per RFC 3986, though not bothering to be strict
37 string scheme
, authority
, path
, query
, fragment
;
38 bool has_authority
, has_query
, has_fragment
;
40 uri_decoder(const string
& uri
):
41 uri(uri
), has_authority(false), has_query(false), has_fragment(false)
44 "^([^:]+):(//[^/?#]*)?([^?#]*)(\\?[^#]*)?(#.*)?$";
46 vector
<string
> matches
;
47 if (regexp_match(uri
, re
, matches
) != 0)
48 throw runtime_error(_F("string doesn't appear to be a URI: %s", uri
.c_str()));
52 if (!matches
[2].empty())
55 authority
= matches
[2].substr(2);
60 if (!matches
[4].empty())
63 query
= matches
[4].substr(1);
66 if (!matches
[5].empty())
69 fragment
= matches
[5].substr(1);
75 // loopback target for running locally
76 class direct
: public remote
{
80 direct(systemtap_session
& s
): remote(s
), child(0) {}
84 args
= make_run_command(*s
);
85 pid_t pid
= stap_spawn (s
->verbose
, args
);
97 int ret
= stap_waitpid(s
->verbose
, child
);
99 clog
<< _F("Warning: %s exited with status: %d", args
.front().c_str(), ret
) << endl
;
107 virtual ~direct() { finish(); }
111 class stapsh
: public remote
{
116 string remote_version
;
118 virtual void prepare_poll(vector
<pollfd
>& fds
)
120 if (fdout
>= 0 && OUT
)
122 pollfd p
= { fdout
, POLLIN
, 0 };
126 // need to send a signal?
127 if (fdin
>= 0 && IN
&& interrupts_sent
< pending_interrupts
)
129 pollfd p
= { fdin
, POLLOUT
, 0 };
134 virtual void handle_poll(vector
<pollfd
>& fds
)
136 for (unsigned i
=0; i
< fds
.size(); ++i
)
139 if (fdout
>= 0 && OUT
&& fds
[i
].fd
== fdout
)
141 if (fds
[i
].revents
& POLLIN
)
146 // If we have a line prefix, then read lines one at a
147 // time and copy out with the prefix.
149 while (fgets(buf
, sizeof(buf
), OUT
))
150 cout
<< prefix
<< buf
;
156 // Otherwise read an entire block of data at once.
157 size_t rc
= fread(buf
, 1, sizeof(buf
), OUT
);
160 // NB: The buf could contain binary data,
161 // including \0, so write as a block instead of
162 // the usual <<string.
171 // need to send a signal?
172 if (fdin
>= 0 && IN
&& fds
[i
].fd
== fdin
&&
173 interrupts_sent
< pending_interrupts
)
175 if (fds
[i
].revents
& POLLOUT
)
177 if (send_command("quit\n") == 0)
191 if (!fgets(reply
, sizeof(reply
), OUT
))
196 int send_command(const string
& cmd
)
200 if (fputs(cmd
.c_str(), IN
) < 0 ||
206 int send_file(const string
& filename
, const string
& dest
)
209 FILE* f
= fopen(filename
.c_str(), "r");
214 rc
= fstat(fileno(f
), &fs
);
218 cmd
<< "file " << fs
.st_size
<< " " << dest
<< "\n";
219 rc
= send_command(cmd
.str());
223 while (!rc
&& i
< fs
.st_size
)
226 size_t r
= sizeof(buf
);
227 if (fs
.st_size
- i
< (off_t
)r
)
229 r
= fread(buf
, 1, r
, f
);
234 size_t w
= fwrite(buf
, 1, r
, IN
);
248 string reply
= get_reply();
255 clog
<< _("stapsh file ERROR: no reply") << endl
;
257 clog
<< _F("stapsh file replied %s", reply
.c_str());
265 static string
qpencode(const string
& str
)
268 o
<< setfill('0') << hex
;
269 for (const char* s
= str
.c_str(); *s
; ++s
)
270 if (*s
>= 33 && *s
<= 126 && *s
!= 61)
273 o
<< '=' << setw(2) << (unsigned)(unsigned char) *s
;
278 stapsh(systemtap_session
& s
)
279 : remote(s
), interrupts_sent(0),
280 fdin(-1), fdout(-1), IN(0), OUT(0)
283 virtual int prepare()
287 string localmodule
= s
->tmpdir
+ "/" + s
->module_name
+ ".ko";
288 string remotemodule
= s
->module_name
+ ".ko";
289 if ((rc
= send_file(localmodule
, remotemodule
)))
292 if (file_exists(localmodule
+ ".sgn") &&
293 (rc
= send_file(localmodule
+ ".sgn", remotemodule
+ ".sgn")))
296 if (!s
->uprobes_path
.empty())
298 string remoteuprobes
= basename(s
->uprobes_path
.c_str());
299 if ((rc
= send_file(s
->uprobes_path
, remoteuprobes
)))
302 if (file_exists(s
->uprobes_path
+ ".sgn") &&
303 (rc
= send_file(s
->uprobes_path
+ ".sgn", remoteuprobes
+ ".sgn")))
312 // Send the staprun args
313 // NB: The remote is left to decide its own staprun path
314 ostringstream
run("run", ios::out
| ios::ate
);
315 vector
<string
> cmd
= make_run_command(*s
, s
->module_name
+ ".ko",
317 for (unsigned i
= 1; i
< cmd
.size(); ++i
)
318 run
<< ' ' << qpencode(cmd
[i
]);
321 int rc
= send_command(run
.str());
325 string reply
= get_reply();
332 clog
<< _("stapsh run ERROR: no reply") << endl
;
334 clog
<< _F("stapsh run replied %s", reply
.c_str());
341 long flags
= fcntl(fdout
, F_GETFL
) | O_NONBLOCK
;
342 fcntl(fdout
, F_SETFL
, flags
);
345 // If run failed for any reason, then this
346 // connection is effectively dead to us.
355 if (OUT
) fclose(OUT
);
366 void set_child_fds(int in
, int out
)
368 if (fdin
>= 0 || fdout
>= 0 || IN
|| OUT
)
369 throw runtime_error(_("stapsh file descriptors already set"));
373 IN
= fdopen(fdin
, "w");
374 OUT
= fdopen(fdout
, "r");
376 throw runtime_error(_("invalid file descriptors for stapsh"));
378 if (send_command("stap " VERSION
"\n"))
379 throw runtime_error(_("error sending hello to stapsh"));
381 string reply
= get_reply();
383 throw runtime_error(_("error receiving hello from stapsh"));
385 // stapsh VERSION MACHINE RELEASE
386 vector
<string
> uname
;
387 tokenize(reply
, uname
, " \t\r\n");
388 if (uname
.size() != 4 || uname
[0] != "stapsh")
389 throw runtime_error(_("failed to get uname from stapsh"));
391 // We assume that later versions will know how to talk to us.
392 // Looking backward, we use this for make_run_command().
393 this->remote_version
= uname
[1];
395 this->s
= s
->clone(uname
[2], uname
[3]);
399 virtual ~stapsh() { close(); }
403 // direct_stapsh is meant only for testing, as a way to exercise the stapsh
404 // mechanisms without requiring test machines to have actual remote access.
405 class direct_stapsh
: public stapsh
{
409 direct_stapsh(systemtap_session
& s
)
410 : stapsh(s
), child(0)
414 cmd
.push_back(BINDIR
"/stapsh");
415 if (s
.perpass_verbose
[4] > 1)
417 if (s
.perpass_verbose
[4] > 2)
420 // mask signals while we spawn, so we can simulate manual signals to
421 // the "remote" target, as we must for the real ssh_remote case.
423 stap_sigmasker masked
;
424 child
= stap_spawn_piped(s
.verbose
, cmd
, &in
, &out
);
428 throw runtime_error(_("error launching stapsh"));
432 set_child_fds(in
, out
);
434 catch (runtime_error
&)
443 int rc
= stapsh::finish();
447 int rc2
= stap_waitpid(s
->verbose
, child
);
455 virtual ~direct_stapsh() { finish(); }
459 // stapsh-based ssh_remote
460 class ssh_remote
: public stapsh
{
464 ssh_remote(systemtap_session
& s
): stapsh(s
), child(0) {}
466 int connect(const string
& host
, const string
& port
)
471 cmd
.push_back("ssh");
480 // This is crafted so that we get a silent failure with status 127 if
481 // the command is not found. The combination of -P and $cmd ensures
482 // that we pull the command out of the PATH, not aliases or such.
483 string stapsh_cmd
= "cmd=`type -P stapsh || exit 127` && exec \"$cmd\"";
484 if (s
->perpass_verbose
[4] > 1)
485 stapsh_cmd
.append(" -v");
486 if (s
->perpass_verbose
[4] > 2)
487 stapsh_cmd
.append(" -v");
488 // NB: We need to explicitly choose bash, as $SHELL may be weird...
489 cmd
.push_back("/bin/bash -c '" + stapsh_cmd
+ "'");
491 // mask signals while we spawn, so we can manually send even tty
492 // signals *through* ssh rather than to ssh itself
494 stap_sigmasker masked
;
495 child
= stap_spawn_piped(s
->verbose
, cmd
, &in
, &out
);
499 throw runtime_error(_("error launching stapsh"));
503 set_child_fds(in
, out
);
505 catch (runtime_error
&)
509 // ssh itself signals errors with 255
511 throw runtime_error(_("error establishing ssh connection"));
513 // If rc == 127, that's command-not-found, so we let ::create()
514 // try again in legacy mode. But only do this if there's a single
515 // remote, as the old code didn't handle ttys well with multiple
516 // remotes. Otherwise, throw up again. *barf*
517 if (rc
!= 127 || s
->remote_uris
.size() > 1)
526 int rc
= stapsh::finish();
530 int rc2
= stap_waitpid(s
->verbose
, child
);
537 static remote
* create(systemtap_session
& s
, const string
& host
);
538 static remote
* create(systemtap_session
& s
, const uri_decoder
& ud
);
540 virtual ~ssh_remote() { finish(); }
544 // ssh connection without stapsh, for legacy stap installations
545 // NB: ssh commands use a tty (-t) so signals are passed along to the remote.
546 // It does this by putting the local tty in raw mode, so it only works for tty
547 // signals, and only for a single remote at a time.
548 class ssh_legacy_remote
: public remote
{
550 vector
<string
> ssh_args
, scp_args
;
552 string host
, port
, tmpdir
;
555 ssh_legacy_remote(systemtap_session
& s
, const string
& host
, const string
& port
)
556 : remote(s
), host(host
), port(port
), child(0)
558 open_control_master();
563 catch (runtime_error
&)
565 close_control_master();
570 void open_control_master()
572 static unsigned index
= 0;
574 if (s
->tmpdir
.empty()) // sanity check, shouldn't happen
575 throw runtime_error(_("No tmpdir available for ssh control master"));
577 ssh_control
= s
->tmpdir
+ "/ssh_remote_control_" + lex_cast(++index
);
580 scp_args
.push_back("scp");
581 scp_args
.push_back("-q");
582 scp_args
.push_back("-o");
583 scp_args
.push_back("ControlPath=" + ssh_control
);
587 ssh_args
.push_back(host
);
591 scp_args
.push_back("-P");
592 scp_args
.push_back(port
);
593 ssh_args
.push_back("-p");
594 ssh_args
.push_back(port
);
597 // NB: ssh -f will stay in the foreground until authentication is
598 // complete and the control socket is created, so we know it's ready to
599 // go when stap_system returns.
600 vector
<string
> cmd
= ssh_args
;
604 int rc
= stap_system(s
->verbose
, cmd
);
606 throw runtime_error(_F("failed to create an ssh control master for %s : rc= %d",
610 clog
<< _F("Created ssh control master at %s",
611 lex_cast_qstring(ssh_control
).c_str()) << endl
;
614 void close_control_master()
616 if (ssh_control
.empty())
619 vector
<string
> cmd
= ssh_args
;
621 cmd
.push_back("exit");
622 int rc
= stap_system(s
->verbose
, cmd
, true, true);
624 cerr
<< _F("failed to stop the ssh control master for %s : rc=%d",
625 host
.c_str(), rc
) << endl
;
635 vector
<string
> uname
;
636 vector
<string
> cmd
= ssh_args
;
638 cmd
.push_back("uname -rm");
639 int rc
= stap_system_read(s
->verbose
, cmd
, out
);
641 tokenize(out
.str(), uname
, " \t\r\n");
642 if (uname
.size() != 2)
643 throw runtime_error(_F("failed to get uname from %s : rc= %d", host
.c_str(), rc
));
644 const string
& release
= uname
[0];
645 const string
& arch
= uname
[1];
646 // XXX need to deal with command-line vs. implied arch/release
647 this->s
= s
->clone(arch
, release
);
653 string localmodule
= s
->tmpdir
+ "/" + s
->module_name
+ ".ko";
656 // Make a remote tempdir.
660 vector
<string
> cmd
= ssh_args
;
662 cmd
.push_back("mktemp -d -t stapXXXXXX");
663 rc
= stap_system_read(s
->verbose
, cmd
, out
);
665 tokenize(out
.str(), vout
, "\r\n");
666 if (vout
.size() != 1)
668 cerr
<< _F("failed to make a tempdir on %s : rc=%d",
669 host
.c_str(), rc
) << endl
;
673 tmpmodule
= tmpdir
+ "/" + s
->module_name
+ ".ko";
676 // Transfer the module. XXX and uprobes.ko, sigs, etc.
678 vector
<string
> cmd
= scp_args
;
679 cmd
.push_back(localmodule
);
680 cmd
.push_back(host
+ ":" + tmpmodule
);
681 rc
= stap_system(s
->verbose
, cmd
);
683 cerr
<< _F("failed to copy the module to %s : rc=%d",
684 host
.c_str(), rc
) << endl
;
687 // Run the module on the remote.
689 vector
<string
> cmd
= ssh_args
;
691 // We don't know the actual version, but all <=1.3 are approx equal.
692 cmd
.push_back(cmdstr_join(make_run_command(*s
, tmpmodule
, "1.3")));
693 pid_t pid
= stap_spawn(s
->verbose
, cmd
);
698 cerr
<< _F("failed to run the module on %s : ret=%d",
699 host
.c_str(), pid
) << endl
;
713 rc
= stap_waitpid(s
->verbose
, child
);
719 // Remove the tempdir.
720 // XXX need to make sure this runs even with e.g. CTRL-C exits
721 vector
<string
> cmd
= ssh_args
;
723 cmd
.push_back("rm -r " + cmdstr_quoted(tmpdir
));
724 int rc2
= stap_system(s
->verbose
, cmd
);
726 cerr
<< _F("failed to delete the tempdir on %s : rc=%d",
727 host
.c_str(), rc2
) << endl
;
733 close_control_master();
739 friend class ssh_remote
;
741 virtual ~ssh_legacy_remote()
743 close_control_master();
748 // Try to establish a stapsh connection to the remote, but fallback
749 // to the older mechanism if the command is not found.
751 ssh_remote::create(systemtap_session
& s
, const string
& target
)
753 string port
, host
= target
;
754 size_t i
= host
.find(':');
755 if (i
!= string::npos
)
757 port
= host
.substr(i
+ 1);
761 auto_ptr
<ssh_remote
> p (new ssh_remote(s
));
762 int rc
= p
->connect(host
, port
);
765 else if (rc
== 127) // stapsh command not found
766 return new ssh_legacy_remote(s
, host
, port
); // try legacy instead
771 ssh_remote::create(systemtap_session
& s
, const uri_decoder
& ud
)
773 if (!ud
.has_authority
|| ud
.authority
.empty())
774 throw runtime_error(_("ssh target requires a hostname"));
775 if (!ud
.path
.empty() && ud
.path
!= "/")
776 throw runtime_error(_("ssh target URI doesn't support a /path"));
778 throw runtime_error(_("ssh target URI doesn't support a ?query"));
780 throw runtime_error(_("ssh target URI doesn't support a #fragment"));
782 return create(s
, ud
.authority
);
787 remote::create(systemtap_session
& s
, const string
& uri
)
791 if (uri
.find(':') != string::npos
)
793 const uri_decoder
ud(uri
);
795 // An ssh "host:port" is ambiguous with a URI "scheme:path".
796 // So if it looks like a number, just assume ssh.
797 if (!ud
.has_authority
&& !ud
.has_query
&&
798 !ud
.has_fragment
&& !ud
.path
.empty() &&
799 ud
.path
.find_first_not_of("1234567890") == string::npos
)
800 return ssh_remote::create(s
, uri
);
802 if (ud
.scheme
== "direct")
803 return new direct(s
);
804 else if (ud
.scheme
== "stapsh")
805 return new direct_stapsh(s
);
806 if (ud
.scheme
== "ssh")
807 return ssh_remote::create(s
, ud
);
809 throw runtime_error(_F("unrecognized URI scheme '%s' in remote: %s",
810 ud
.scheme
.c_str(), uri
.c_str()));
813 // XXX assuming everything else is ssh for now...
814 return ssh_remote::create(s
, uri
);
816 catch (std::runtime_error
& e
)
818 cerr
<< e
.what() << " on remote '" << uri
<< "'" << endl
;
824 // This is a poor-man's ppoll, only used by remote::run below. It does not
825 // provide the same guarantee of atomicity as on systems with a true ppoll.
827 // In our use, this would cause trouble if a signal came in any time from the
828 // moment we mask signals to prepare pollfds, to the moment we call poll in
829 // emulation here. If there's no data on any of the pollfds, we will be stuck
830 // waiting indefinitely.
832 // Since this is mainly about responsiveness of CTRL-C cleanup, we'll just
833 // throw in a one-second forced timeout to ensure we have a chance to notice
834 // there was an interrupt without too much delay.
836 ppoll(struct pollfd
*fds
, nfds_t nfds
,
837 const struct timespec
*timeout_ts
,
838 const sigset_t
*sigmask
)
841 int timeout
= (timeout_ts
== NULL
) ? 1000 // don't block forever...
842 : (timeout_ts
->tv_sec
* 1000 + timeout_ts
->tv_nsec
/ 1000000);
843 sigprocmask(SIG_SETMASK
, sigmask
, &origmask
);
844 int rc
= poll(fds
, nfds
, timeout
);
845 sigprocmask(SIG_SETMASK
, &origmask
, NULL
);
851 remote::run(const vector
<remote
*>& remotes
)
853 // NB: the first failure "wins"
856 for (unsigned i
= 0; i
< remotes
.size() && !pending_interrupts
; ++i
)
858 remote
*r
= remotes
[i
];
859 r
->s
->verbose
= r
->s
->perpass_verbose
[4];
860 if (r
->s
->use_remote_prefix
)
861 r
->prefix
= lex_cast(i
) + ": ";
867 for (unsigned i
= 0; i
< remotes
.size() && !pending_interrupts
; ++i
)
869 rc
= remotes
[i
]->start();
874 // mask signals while we're preparing to poll
876 stap_sigmasker masked
;
878 // polling loop for remotes that have fds to watch
882 for (unsigned i
= 0; i
< remotes
.size(); ++i
)
883 remotes
[i
]->prepare_poll (fds
);
887 rc
= ppoll (&fds
[0], fds
.size(), NULL
, &masked
.old
);
888 if (rc
< 0 && errno
!= EINTR
)
891 for (unsigned i
= 0; i
< remotes
.size(); ++i
)
892 remotes
[i
]->handle_poll (fds
);
896 for (unsigned i
= 0; i
< remotes
.size(); ++i
)
898 rc
= remotes
[i
]->finish();
907 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.074413 seconds and 5 git commands to generate.