]>
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>
17 #include <sys/socket.h>
35 // Decode URIs as per RFC 3986, though not bothering to be strict
39 string scheme
, authority
, path
, query
, fragment
;
40 bool has_authority
, has_query
, has_fragment
;
42 uri_decoder(const string
& uri
):
43 uri(uri
), has_authority(false), has_query(false), has_fragment(false)
46 "^([^:]+):(//[^/?#]*)?([^?#]*)(\\?[^#]*)?(#.*)?$";
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()));
54 if (!matches
[2].empty())
57 authority
= matches
[2].substr(2);
62 if (!matches
[4].empty())
65 query
= matches
[4].substr(1);
68 if (!matches
[5].empty())
71 fragment
= matches
[5].substr(1);
77 // loopback target for running locally
78 class direct
: public remote
{
82 direct(systemtap_session
& s
): remote(s
), child(0) {}
86 args
= make_run_command(*s
);
87 if (! staprun_r_arg
.empty()) // PR13354
89 args
.push_back ("-r");
90 args
.push_back (staprun_r_arg
);
92 pid_t pid
= stap_spawn (s
->verbose
, args
);
104 int ret
= stap_waitpid(s
->verbose
, child
);
106 s
->print_warning(_F("%s exited with signal: %d (%s)",
107 args
.front().c_str(), ret
- 128,
108 strsignal(ret
- 128)));
110 s
->print_warning(_F("%s exited with status: %d",
111 args
.front().c_str(), ret
));
119 virtual ~direct() { finish(); }
123 class stapsh
: public remote
{
128 string remote_version
;
130 virtual void prepare_poll(vector
<pollfd
>& fds
)
132 if (fdout
>= 0 && OUT
)
134 pollfd p
= { fdout
, POLLIN
, 0 };
138 // need to send a signal?
139 if (fdin
>= 0 && IN
&& interrupts_sent
< pending_interrupts
)
141 pollfd p
= { fdin
, POLLOUT
, 0 };
146 virtual void handle_poll(vector
<pollfd
>& fds
)
148 for (unsigned i
=0; i
< fds
.size(); ++i
)
149 if (fds
[i
].fd
== fdin
|| fds
[i
].fd
== fdout
)
153 // need to send a signal?
154 if (fds
[i
].revents
& POLLOUT
&& IN
&&
155 interrupts_sent
< pending_interrupts
)
157 if (send_command("quit\n") == 0)
163 // have data to read?
164 if (fds
[i
].revents
& POLLIN
&& OUT
)
169 // If we have a line prefix, then read lines one at a
170 // time and copy out with the prefix.
172 while (fgets(buf
, sizeof(buf
), OUT
))
173 cout
<< prefix
<< buf
;
179 // Otherwise read an entire block of data at once.
180 size_t rc
= fread(buf
, 1, sizeof(buf
), OUT
);
183 // NB: The buf could contain binary data,
184 // including \0, so write as a block instead of
185 // the usual <<string.
194 if (err
|| fds
[i
].revents
& ~(POLLIN
|POLLOUT
))
201 // Some schemes like unix may have stdout and stderr mushed together.
202 // There shouldn't be anything except dbug messages on stderr before we
203 // actually start running, and there's no get_reply after that. So
204 // we'll just loop and skip those that start with "stapsh:".
206 while (fgets(reply
, sizeof(reply
), OUT
))
208 if (!startswith(reply
, "stapsh:"))
211 // Why not clog here? Well, once things get running we won't be
212 // able to distinguish stdout/err, so trying to fake it here would
213 // be less consistent than just keeping it merged.
217 // Reached EOF, nothing to reply...
221 int send_command(const string
& cmd
)
225 if (fputs(cmd
.c_str(), IN
) < 0 ||
231 int send_file(const string
& filename
, const string
& dest
)
234 FILE* f
= fopen(filename
.c_str(), "r");
239 rc
= fstat(fileno(f
), &fs
);
243 cmd
<< "file " << fs
.st_size
<< " " << dest
<< "\n";
244 rc
= send_command(cmd
.str());
248 while (!rc
&& i
< fs
.st_size
)
251 size_t r
= sizeof(buf
);
252 if (fs
.st_size
- i
< (off_t
)r
)
254 r
= fread(buf
, 1, r
, f
);
259 size_t w
= fwrite(buf
, 1, r
, IN
);
273 string reply
= get_reply();
280 clog
<< _("stapsh file ERROR: no reply") << endl
;
282 clog
<< _F("stapsh file replied %s", reply
.c_str());
290 static string
qpencode(const string
& str
)
293 o
<< setfill('0') << hex
;
294 for (const char* s
= str
.c_str(); *s
; ++s
)
295 if (*s
>= 33 && *s
<= 126 && *s
!= 61)
298 o
<< '=' << setw(2) << (unsigned)(unsigned char) *s
;
303 stapsh(systemtap_session
& s
)
304 : remote(s
), interrupts_sent(0),
305 fdin(-1), fdout(-1), IN(0), OUT(0)
308 virtual int prepare()
312 string localmodule
= s
->tmpdir
+ "/" + s
->module_name
+ ".ko";
313 string remotemodule
= s
->module_name
+ ".ko";
314 if ((rc
= send_file(localmodule
, remotemodule
)))
317 if (file_exists(localmodule
+ ".sgn") &&
318 (rc
= send_file(localmodule
+ ".sgn", remotemodule
+ ".sgn")))
321 if (!s
->uprobes_path
.empty())
323 string remoteuprobes
= basename(s
->uprobes_path
.c_str());
324 if ((rc
= send_file(s
->uprobes_path
, remoteuprobes
)))
327 if (file_exists(s
->uprobes_path
+ ".sgn") &&
328 (rc
= send_file(s
->uprobes_path
+ ".sgn", remoteuprobes
+ ".sgn")))
337 // Send the staprun args
338 // NB: The remote is left to decide its own staprun path
339 ostringstream
run("run", ios::out
| ios::ate
);
340 vector
<string
> cmd
= make_run_command(*s
, ".", remote_version
);
342 // PR13354: identify our remote index/url
343 if (strverscmp("1.7", remote_version
.c_str()) <= 0 && // -r supported?
344 ! staprun_r_arg
.empty())
346 cmd
.push_back ("-r");
347 cmd
.push_back (staprun_r_arg
);
350 for (unsigned i
= 1; i
< cmd
.size(); ++i
)
351 run
<< ' ' << qpencode(cmd
[i
]);
354 int rc
= send_command(run
.str());
358 string reply
= get_reply();
365 clog
<< _("stapsh run ERROR: no reply") << endl
;
367 clog
<< _F("stapsh run replied %s", reply
.c_str());
374 long flags
= fcntl(fdout
, F_GETFL
) | O_NONBLOCK
;
375 fcntl(fdout
, F_SETFL
, flags
);
378 // If run failed for any reason, then this
379 // connection is effectively dead to us.
388 if (OUT
) fclose(OUT
);
399 void set_child_fds(int in
, int out
)
401 if (fdin
>= 0 || fdout
>= 0 || IN
|| OUT
)
402 throw runtime_error(_("stapsh file descriptors already set"));
406 IN
= fdopen(fdin
, "w");
407 OUT
= fdopen(fdout
, "r");
409 throw runtime_error(_("invalid file descriptors for stapsh"));
411 if (send_command("stap " VERSION
"\n"))
412 throw runtime_error(_("error sending hello to stapsh"));
414 string reply
= get_reply();
416 throw runtime_error(_("error receiving hello from stapsh"));
418 // stapsh VERSION MACHINE RELEASE
419 vector
<string
> uname
;
420 tokenize(reply
, uname
, " \t\r\n");
421 if (uname
.size() != 4 || uname
[0] != "stapsh")
422 throw runtime_error(_("failed to get uname from stapsh"));
424 // We assume that later versions will know how to talk to us.
425 // Looking backward, we use this for make_run_command().
426 this->remote_version
= uname
[1];
428 this->s
= s
->clone(uname
[2], uname
[3]);
432 virtual ~stapsh() { close(); }
436 // direct_stapsh is meant only for testing, as a way to exercise the stapsh
437 // mechanisms without requiring test machines to have actual remote access.
438 class direct_stapsh
: public stapsh
{
442 direct_stapsh(systemtap_session
& s
)
443 : stapsh(s
), child(0)
447 cmd
.push_back(BINDIR
"/stapsh");
448 if (s
.perpass_verbose
[4] > 1)
450 if (s
.perpass_verbose
[4] > 2)
453 // mask signals while we spawn, so we can simulate manual signals to
454 // the "remote" target, as we must for the real ssh_remote case.
456 stap_sigmasker masked
;
457 child
= stap_spawn_piped(s
.verbose
, cmd
, &in
, &out
);
461 throw runtime_error(_("error launching stapsh"));
465 set_child_fds(in
, out
);
467 catch (runtime_error
&)
476 int rc
= stapsh::finish();
480 int rc2
= stap_waitpid(s
->verbose
, child
);
488 virtual ~direct_stapsh() { finish(); }
492 // Connect to an existing stapsh on a unix socket.
493 class unix_stapsh
: public stapsh
{
496 unix_stapsh(systemtap_session
& s
, const uri_decoder
& ud
)
500 server
.sun_family
= AF_UNIX
;
502 throw runtime_error(_("unix target requires a /path"));
503 if (ud
.path
.size() > sizeof(server
.sun_path
) - 1)
504 throw runtime_error(_("unix target /path is too long"));
505 strcpy(server
.sun_path
, ud
.path
.c_str());
507 if (ud
.has_authority
)
508 throw runtime_error(_("unix target doesn't support a hostname"));
510 throw runtime_error(_("unix target URI doesn't support a ?query"));
512 throw runtime_error(_("unix target URI doesn't support a #fragment"));
514 int fd
= socket(AF_UNIX
, SOCK_STREAM
, 0);
516 throw runtime_error(_("error opening a socket"));
518 if (connect(fd
, (struct sockaddr
*)&server
, SUN_LEN(&server
)) < 0)
520 const char *msg
= strerror(errno
);
522 throw runtime_error(_F("error connecting to socket: %s", msg
));
525 // Try to dup it, so class stapsh can have truly separate fds for its
526 // fdopen handles. If it fails for some reason, it can still work with
534 set_child_fds(fd
, fd2
);
536 catch (runtime_error
&)
548 virtual ~unix_stapsh() { finish(); }
552 // stapsh-based ssh_remote
553 class ssh_remote
: public stapsh
{
557 ssh_remote(systemtap_session
& s
): stapsh(s
), child(0) {}
559 int connect(const string
& host
, const string
& port
)
564 cmd
.push_back("ssh");
573 // This is crafted so that we get a silent failure with status 127 if
574 // the command is not found. The combination of -P and $cmd ensures
575 // that we pull the command out of the PATH, not aliases or such.
576 string stapsh_cmd
= "cmd=`type -P stapsh || exit 127` && exec \"$cmd\"";
577 if (s
->perpass_verbose
[4] > 1)
578 stapsh_cmd
.append(" -v");
579 if (s
->perpass_verbose
[4] > 2)
580 stapsh_cmd
.append(" -v");
581 // NB: We need to explicitly choose bash, as $SHELL may be weird...
582 cmd
.push_back("/bin/bash -c '" + stapsh_cmd
+ "'");
584 // mask signals while we spawn, so we can manually send even tty
585 // signals *through* ssh rather than to ssh itself
587 stap_sigmasker masked
;
588 child
= stap_spawn_piped(s
->verbose
, cmd
, &in
, &out
);
592 throw runtime_error(_("error launching stapsh"));
596 set_child_fds(in
, out
);
598 catch (runtime_error
&)
602 // ssh itself signals errors with 255
604 throw runtime_error(_("error establishing ssh connection"));
606 // If rc == 127, that's command-not-found, so we let ::create()
607 // try again in legacy mode. But only do this if there's a single
608 // remote, as the old code didn't handle ttys well with multiple
609 // remotes. Otherwise, throw up again. *barf*
610 if (rc
!= 127 || s
->remote_uris
.size() > 1)
619 int rc
= stapsh::finish();
623 int rc2
= stap_waitpid(s
->verbose
, child
);
630 static remote
* create(systemtap_session
& s
, const string
& host
);
631 static remote
* create(systemtap_session
& s
, const uri_decoder
& ud
);
633 virtual ~ssh_remote() { finish(); }
637 // ssh connection without stapsh, for legacy stap installations
638 // NB: ssh commands use a tty (-t) so signals are passed along to the remote.
639 // It does this by putting the local tty in raw mode, so it only works for tty
640 // signals, and only for a single remote at a time.
641 class ssh_legacy_remote
: public remote
{
643 vector
<string
> ssh_args
, scp_args
;
645 string host
, port
, tmpdir
;
648 ssh_legacy_remote(systemtap_session
& s
, const string
& host
, const string
& port
)
649 : remote(s
), host(host
), port(port
), child(0)
651 open_control_master();
656 catch (runtime_error
&)
658 close_control_master();
663 void open_control_master()
665 static unsigned index
= 0;
667 if (s
->tmpdir
.empty()) // sanity check, shouldn't happen
668 throw runtime_error(_("No tmpdir available for ssh control master"));
670 ssh_control
= s
->tmpdir
+ "/ssh_remote_control_" + lex_cast(++index
);
673 scp_args
.push_back("scp");
674 scp_args
.push_back("-q");
675 scp_args
.push_back("-o");
676 scp_args
.push_back("ControlPath=" + ssh_control
);
680 ssh_args
.push_back(host
);
684 scp_args
.push_back("-P");
685 scp_args
.push_back(port
);
686 ssh_args
.push_back("-p");
687 ssh_args
.push_back(port
);
690 // NB: ssh -f will stay in the foreground until authentication is
691 // complete and the control socket is created, so we know it's ready to
692 // go when stap_system returns.
693 vector
<string
> cmd
= ssh_args
;
697 int rc
= stap_system(s
->verbose
, cmd
);
699 throw runtime_error(_F("failed to create an ssh control master for %s : rc= %d",
703 clog
<< _F("Created ssh control master at %s",
704 lex_cast_qstring(ssh_control
).c_str()) << endl
;
707 void close_control_master()
709 if (ssh_control
.empty())
712 vector
<string
> cmd
= ssh_args
;
714 cmd
.push_back("exit");
715 int rc
= stap_system(s
->verbose
, cmd
, true, true);
717 cerr
<< _F("failed to stop the ssh control master for %s : rc=%d",
718 host
.c_str(), rc
) << endl
;
728 vector
<string
> uname
;
729 vector
<string
> cmd
= ssh_args
;
731 cmd
.push_back("uname -rm");
732 int rc
= stap_system_read(s
->verbose
, cmd
, out
);
734 tokenize(out
.str(), uname
, " \t\r\n");
735 if (uname
.size() != 2)
736 throw runtime_error(_F("failed to get uname from %s : rc= %d", host
.c_str(), rc
));
737 const string
& release
= uname
[0];
738 const string
& arch
= uname
[1];
739 // XXX need to deal with command-line vs. implied arch/release
740 this->s
= s
->clone(arch
, release
);
746 string localmodule
= s
->tmpdir
+ "/" + s
->module_name
+ ".ko";
749 // Make a remote tempdir.
753 vector
<string
> cmd
= ssh_args
;
755 cmd
.push_back("mktemp -d -t stapXXXXXX");
756 rc
= stap_system_read(s
->verbose
, cmd
, out
);
758 tokenize(out
.str(), vout
, "\r\n");
759 if (vout
.size() != 1)
761 cerr
<< _F("failed to make a tempdir on %s : rc=%d",
762 host
.c_str(), rc
) << endl
;
766 tmpmodule
= tmpdir
+ "/" + s
->module_name
+ ".ko";
769 // Transfer the module.
772 vector
<string
> cmd
= scp_args
;
773 cmd
.push_back(localmodule
);
774 cmd
.push_back(host
+ ":" + tmpmodule
);
775 rc
= stap_system(s
->verbose
, cmd
);
777 cerr
<< _F("failed to copy the module to %s : rc=%d",
778 host
.c_str(), rc
) << endl
;
781 // Transfer the module signature.
782 if (rc
== 0 && file_exists(localmodule
+ ".sgn"))
784 vector
<string
> cmd
= scp_args
;
785 cmd
.push_back(localmodule
+ ".sgn");
786 cmd
.push_back(host
+ ":" + tmpmodule
+ ".sgn");
787 rc
= stap_system(s
->verbose
, cmd
);
789 cerr
<< _F("failed to copy the module signature to %s : rc=%d",
790 host
.c_str(), rc
) << endl
;
793 // What about transfering uprobes.ko? In this ssh "legacy" mode, we
794 // don't the remote systemtap version, but -uPATH wasn't added until
795 // 1.4. Rather than risking a getopt error, we'll just assume that
796 // this isn't supported. The remote will just have to provide its own
797 // uprobes.ko in SYSTEMTAP_RUNTIME or already loaded.
799 // Run the module on the remote.
801 vector
<string
> cmd
= ssh_args
;
803 // We don't know the actual version, but all <=1.3 are approx equal.
804 vector
<string
> staprun_cmd
= make_run_command(*s
, tmpdir
, "1.3");
805 staprun_cmd
[0] = "staprun"; // NB: The remote decides its own path
806 // NB: PR13354: we assume legacy installations don't have
807 // staprun -r support, so we ignore staprun_r_arg.
808 cmd
.push_back(cmdstr_join(staprun_cmd
));
809 pid_t pid
= stap_spawn(s
->verbose
, cmd
);
814 cerr
<< _F("failed to run the module on %s : ret=%d",
815 host
.c_str(), pid
) << endl
;
829 rc
= stap_waitpid(s
->verbose
, child
);
835 // Remove the tempdir.
836 // XXX need to make sure this runs even with e.g. CTRL-C exits
837 vector
<string
> cmd
= ssh_args
;
839 cmd
.push_back("rm -r " + cmdstr_quoted(tmpdir
));
840 int rc2
= stap_system(s
->verbose
, cmd
);
842 cerr
<< _F("failed to delete the tempdir on %s : rc=%d",
843 host
.c_str(), rc2
) << endl
;
849 close_control_master();
855 friend class ssh_remote
;
857 virtual ~ssh_legacy_remote()
859 close_control_master();
864 // Try to establish a stapsh connection to the remote, but fallback
865 // to the older mechanism if the command is not found.
867 ssh_remote::create(systemtap_session
& s
, const string
& target
)
869 string port
, host
= target
;
870 size_t i
= host
.find(':');
871 if (i
!= string::npos
)
873 port
= host
.substr(i
+ 1);
877 auto_ptr
<ssh_remote
> p (new ssh_remote(s
));
878 int rc
= p
->connect(host
, port
);
881 else if (rc
== 127) // stapsh command not found
882 return new ssh_legacy_remote(s
, host
, port
); // try legacy instead
887 ssh_remote::create(systemtap_session
& s
, const uri_decoder
& ud
)
889 if (!ud
.has_authority
|| ud
.authority
.empty())
890 throw runtime_error(_("ssh target requires a hostname"));
891 if (!ud
.path
.empty() && ud
.path
!= "/")
892 throw runtime_error(_("ssh target URI doesn't support a /path"));
894 throw runtime_error(_("ssh target URI doesn't support a ?query"));
896 throw runtime_error(_("ssh target URI doesn't support a #fragment"));
898 return create(s
, ud
.authority
);
903 remote::create(systemtap_session
& s
, const string
& uri
, int idx
)
908 if (uri
.find(':') != string::npos
)
910 const uri_decoder
ud(uri
);
912 // An ssh "host:port" is ambiguous with a URI "scheme:path".
913 // So if it looks like a number, just assume ssh.
914 if (!ud
.has_authority
&& !ud
.has_query
&&
915 !ud
.has_fragment
&& !ud
.path
.empty() &&
916 ud
.path
.find_first_not_of("1234567890") == string::npos
)
917 it
= ssh_remote::create(s
, uri
);
918 else if (ud
.scheme
== "direct")
920 else if (ud
.scheme
== "stapsh")
921 it
= new direct_stapsh(s
);
922 else if (ud
.scheme
== "unix")
923 it
= new unix_stapsh(s
, ud
);
924 else if (ud
.scheme
== "ssh")
925 it
= ssh_remote::create(s
, ud
);
927 throw runtime_error(_F("unrecognized URI scheme '%s' in remote: %s",
928 ud
.scheme
.c_str(), uri
.c_str()));
931 // XXX assuming everything else is ssh for now...
932 it
= ssh_remote::create(s
, uri
);
934 catch (std::runtime_error
& e
)
936 cerr
<< e
.what() << " on remote '" << uri
<< "'" << endl
;
940 if (it
&& idx
>= 0) // PR13354: remote metadata for staprun -r IDX:URI
943 r_arg
<< idx
<< ":" << uri
;
944 it
->staprun_r_arg
= r_arg
.str();
951 remote::run(const vector
<remote
*>& remotes
)
953 // NB: the first failure "wins"
956 for (unsigned i
= 0; i
< remotes
.size() && !pending_interrupts
; ++i
)
958 remote
*r
= remotes
[i
];
959 r
->s
->verbose
= r
->s
->perpass_verbose
[4];
960 if (r
->s
->use_remote_prefix
)
961 r
->prefix
= lex_cast(i
) + ": ";
967 for (unsigned i
= 0; i
< remotes
.size() && !pending_interrupts
; ++i
)
969 rc
= remotes
[i
]->start();
974 // mask signals while we're preparing to poll
976 stap_sigmasker masked
;
978 // polling loop for remotes that have fds to watch
982 for (unsigned i
= 0; i
< remotes
.size(); ++i
)
983 remotes
[i
]->prepare_poll (fds
);
987 rc
= ppoll (&fds
[0], fds
.size(), NULL
, &masked
.old
);
988 if (rc
< 0 && errno
!= EINTR
)
991 for (unsigned i
= 0; i
< remotes
.size(); ++i
)
992 remotes
[i
]->handle_poll (fds
);
996 for (unsigned i
= 0; i
< remotes
.size(); ++i
)
998 rc
= remotes
[i
]->finish();
1007 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.077281 seconds and 6 git commands to generate.