]>
sourceware.org Git - systemtap.git/blob - remote.cxx
1 // systemtap remote execution
2 // Copyright (C) 2010 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
{
79 direct(systemtap_session
& s
): remote(s
), child(0) {}
83 pid_t pid
= stap_spawn (s
->verbose
, make_run_command (*s
));
95 int ret
= stap_waitpid(s
->verbose
, child
);
107 class stapsh
: public remote
{
113 virtual void prepare_poll(vector
<pollfd
>& fds
)
115 if (fdout
>= 0 && OUT
)
117 pollfd p
= { fdout
, POLLIN
};
121 // need to send a signal?
122 if (fdin
>= 0 && IN
&& interrupts_sent
< pending_interrupts
)
124 pollfd p
= { fdin
, POLLOUT
};
129 virtual void handle_poll(vector
<pollfd
>& fds
)
131 for (unsigned i
=0; i
< fds
.size(); ++i
)
134 if (fdout
>= 0 && OUT
&& fds
[i
].fd
== fdout
)
136 if (fds
[i
].revents
& POLLIN
)
141 // If we have a line prefix, then read lines one at a
142 // time and copy out with the prefix.
144 while (fgets(buf
, sizeof(buf
), OUT
))
145 cout
<< prefix
<< buf
;
151 // Otherwise read an entire block of data at once.
152 size_t rc
= fread(buf
, 1, sizeof(buf
), OUT
);
155 // NB: The buf could contain binary data,
156 // including \0, so write as a block instead of
157 // the usual <<string.
166 // need to send a signal?
167 if (fdin
>= 0 && IN
&& fds
[i
].fd
== fdin
&&
168 interrupts_sent
< pending_interrupts
)
170 if (fds
[i
].revents
& POLLOUT
)
172 if (send_command("quit\n") == 0)
186 if (!fgets(reply
, sizeof(reply
), OUT
))
191 int send_command(const string
& cmd
)
195 if (fputs(cmd
.c_str(), IN
) < 0 ||
201 int send_file(const string
& filename
, const string
& dest
)
204 FILE* f
= fopen(filename
.c_str(), "r");
209 rc
= fstat(fileno(f
), &fs
);
213 cmd
<< "file " << fs
.st_size
<< " " << dest
<< "\n";
214 rc
= send_command(cmd
.str());
218 while (!rc
&& i
< fs
.st_size
)
221 size_t r
= sizeof(buf
);
222 if (fs
.st_size
- i
< (off_t
)r
)
224 r
= fread(buf
, 1, r
, f
);
229 size_t w
= fwrite(buf
, 1, r
, IN
);
243 string reply
= get_reply();
250 clog
<< _("stapsh file ERROR: no reply") << endl
;
252 clog
<< _F("stapsh file replied %s", reply
.c_str());
260 static string
qpencode(const string
& str
)
263 o
<< setfill('0') << hex
;
264 for (const char* s
= str
.c_str(); *s
; ++s
)
265 if (*s
>= 33 && *s
<= 126 && *s
!= 61)
268 o
<< '=' << setw(2) << (unsigned)(unsigned char) *s
;
273 stapsh(systemtap_session
& s
)
274 : remote(s
), interrupts_sent(0),
275 fdin(-1), fdout(-1), IN(0), OUT(0)
278 virtual int prepare()
282 string localmodule
= s
->tmpdir
+ "/" + s
->module_name
+ ".ko";
283 string remotemodule
= s
->module_name
+ ".ko";
284 if ((rc
= send_file(localmodule
, remotemodule
)))
287 if (file_exists(localmodule
+ ".sgn") &&
288 (rc
= send_file(localmodule
+ ".sgn", remotemodule
+ ".sgn")))
291 if (!s
->uprobes_path
.empty())
293 string remoteuprobes
= basename(s
->uprobes_path
.c_str());
294 if ((rc
= send_file(s
->uprobes_path
, remoteuprobes
)))
297 if (file_exists(s
->uprobes_path
+ ".sgn") &&
298 (rc
= send_file(s
->uprobes_path
+ ".sgn", remoteuprobes
+ ".sgn")))
307 // Send the staprun args
308 // NB: The remote is left to decide its own staprun path
309 ostringstream
run("run", ios::out
| ios::ate
);
310 vector
<string
> cmd
= make_run_command(*s
, s
->module_name
+ ".ko");
311 for (unsigned i
= 1; i
< cmd
.size(); ++i
)
312 run
<< ' ' << qpencode(cmd
[i
]);
315 int rc
= send_command(run
.str());
319 string reply
= get_reply();
326 clog
<< _("stapsh run ERROR: no reply") << endl
;
328 clog
<< _F("stapsh run replied %s", reply
.c_str());
335 long flags
= fcntl(fdout
, F_GETFL
) | O_NONBLOCK
;
336 fcntl(fdout
, F_SETFL
, flags
);
345 if (OUT
) fclose(OUT
);
356 void set_child_fds(int in
, int out
)
358 if (fdin
>= 0 || fdout
>= 0 || IN
|| OUT
)
359 throw runtime_error(_("stapsh file descriptors already set"));
363 IN
= fdopen(fdin
, "w");
364 OUT
= fdopen(fdout
, "r");
366 throw runtime_error(_("invalid file descriptors for stapsh"));
368 if (send_command("stap " VERSION
"\n"))
369 throw runtime_error(_("error sending hello to stapsh"));
371 string reply
= get_reply();
373 throw runtime_error(_("error receiving hello from stapsh"));
375 // stapsh VERSION MACHINE RELEASE
376 vector
<string
> uname
;
377 tokenize(reply
, uname
, " \t\r\n");
378 if (uname
.size() != 4 || uname
[0] != "stapsh")
379 throw runtime_error(_("failed to get uname from stapsh"));
380 // XXX check VERSION compatibility
382 this->s
= s
->clone(uname
[2], uname
[3]);
386 virtual ~stapsh() { close(); }
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.
392 class direct_stapsh
: public stapsh
{
396 direct_stapsh(systemtap_session
& s
)
397 : stapsh(s
), child(0)
401 cmd
.push_back(BINDIR
"/stapsh");
402 if (s
.perpass_verbose
[4] > 1)
404 if (s
.perpass_verbose
[4] > 2)
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
;
411 sigaddset (&mask
, SIGHUP
);
412 sigaddset (&mask
, SIGPIPE
);
413 sigaddset (&mask
, SIGINT
);
414 sigaddset (&mask
, SIGTERM
);
415 sigprocmask (SIG_BLOCK
, &mask
, &oldmask
);
417 child
= stap_spawn_piped(s
.verbose
, cmd
, &in
, &out
);
419 // back to normal signals
420 sigprocmask (SIG_SETMASK
, &oldmask
, NULL
);
423 throw runtime_error(_("error launching stapsh"));
427 set_child_fds(in
, out
);
429 catch (runtime_error
&)
438 int rc
= stapsh::finish();
442 int rc2
= stap_waitpid(s
->verbose
, child
);
450 virtual ~direct_stapsh() {}
454 // stapsh-based ssh_remote
455 class ssh_remote
: public stapsh
{
459 ssh_remote(systemtap_session
& s
): stapsh(s
), child(0) {}
461 int connect(const string
& host
)
466 cmd
.push_back("ssh");
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");
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
;
483 sigaddset (&mask
, SIGHUP
);
484 sigaddset (&mask
, SIGPIPE
);
485 sigaddset (&mask
, SIGINT
);
486 sigaddset (&mask
, SIGTERM
);
487 sigprocmask (SIG_BLOCK
, &mask
, &oldmask
);
489 child
= stap_spawn_piped(s
->verbose
, cmd
, &in
, &out
);
491 // back to normal signals
492 sigprocmask (SIG_SETMASK
, &oldmask
, NULL
);
495 throw runtime_error(_("error launching stapsh"));
499 set_child_fds(in
, out
);
501 catch (runtime_error
&)
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*
508 if (rc
!= 127 || s
->remote_uris
.size() > 1)
517 int rc
= stapsh::finish();
521 int rc2
= stap_waitpid(s
->verbose
, child
);
528 static remote
* create(systemtap_session
& s
, const string
& host
);
529 static remote
* create(systemtap_session
& s
, const uri_decoder
& ud
);
531 virtual ~ssh_remote() { }
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.
539 class ssh_legacy_remote
: public remote
{
541 vector
<string
> ssh_args
, scp_args
;
546 ssh_legacy_remote(systemtap_session
& s
, const string
& host
)
547 : remote(s
), host(host
), child(0)
549 open_control_master();
554 catch (runtime_error
&)
556 close_control_master();
561 void open_control_master()
563 static unsigned index
= 0;
565 if (s
->tmpdir
.empty()) // sanity check, shouldn't happen
566 throw runtime_error(_("No tmpdir available for ssh control master"));
568 ssh_control
= s
->tmpdir
+ "/ssh_remote_control_" + lex_cast(++index
);
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
);
578 ssh_args
.push_back(host
);
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.
583 vector
<string
> cmd
= ssh_args
;
587 int rc
= stap_system(s
->verbose
, cmd
);
589 throw runtime_error(_F("failed to create an ssh control master for %s : rc= %d",
593 clog
<< _F("Created ssh control master at %s",
594 lex_cast_qstring(ssh_control
).c_str()) << endl
;
597 void close_control_master()
599 if (ssh_control
.empty())
602 vector
<string
> cmd
= ssh_args
;
604 cmd
.push_back("exit");
605 int rc
= stap_system(s
->verbose
, cmd
, true, true);
607 cerr
<< _F("failed to stop the ssh control master for %s : rc=%d",
608 host
.c_str(), rc
) << endl
;
618 vector
<string
> uname
;
619 vector
<string
> cmd
= ssh_args
;
621 cmd
.push_back("uname -rm");
622 int rc
= stap_system_read(s
->verbose
, cmd
, out
);
624 tokenize(out
.str(), uname
, " \t\r\n");
625 if (uname
.size() != 2)
626 throw runtime_error(_F("failed to get uname from %s : rc= %d", host
.c_str(), rc
));
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
);
636 string localmodule
= s
->tmpdir
+ "/" + s
->module_name
+ ".ko";
639 // Make a remote tempdir.
643 vector
<string
> cmd
= ssh_args
;
645 cmd
.push_back("mktemp -d -t stapXXXXXX");
646 rc
= stap_system_read(s
->verbose
, cmd
, out
);
648 tokenize(out
.str(), vout
, "\r\n");
649 if (vout
.size() != 1)
651 cerr
<< _F("failed to make a tempdir on %s : rc=%d",
652 host
.c_str(), rc
) << endl
;
656 tmpmodule
= tmpdir
+ "/" + s
->module_name
+ ".ko";
659 // Transfer the module. XXX and uprobes.ko, sigs, etc.
661 vector
<string
> cmd
= scp_args
;
662 cmd
.push_back(localmodule
);
663 cmd
.push_back(host
+ ":" + tmpmodule
);
664 rc
= stap_system(s
->verbose
, cmd
);
666 cerr
<< _F("failed to copy the module to %s : rc=%d",
667 host
.c_str(), rc
) << endl
;
670 // Run the module on the remote.
672 vector
<string
> cmd
= ssh_args
;
674 cmd
.push_back(cmdstr_join(make_run_command(*s
, tmpmodule
)));
675 pid_t pid
= stap_spawn(s
->verbose
, cmd
);
680 cerr
<< _F("failed to run the module on %s : ret=%d",
681 host
.c_str(), pid
) << endl
;
695 rc
= stap_waitpid(s
->verbose
, child
);
701 // Remove the tempdir.
702 // XXX need to make sure this runs even with e.g. CTRL-C exits
703 vector
<string
> cmd
= ssh_args
;
705 cmd
.push_back("rm -r " + cmdstr_quoted(tmpdir
));
706 int rc2
= stap_system(s
->verbose
, cmd
);
708 cerr
<< _F("failed to delete the tempdir on %s : rc=%d",
709 host
.c_str(), rc2
) << endl
;
715 close_control_master();
721 friend class ssh_remote
;
723 virtual ~ssh_legacy_remote()
725 close_control_master();
730 // Try to establish a stapsh connection to the remote, but fallback
731 // to the older mechanism if the command is not found.
733 ssh_remote::create(systemtap_session
& s
, const string
& host
)
735 auto_ptr
<ssh_remote
> p (new ssh_remote(s
));
736 int rc
= p
->connect(host
);
739 else if (rc
== 127) // stapsh command not found
740 return new ssh_legacy_remote(s
, host
); // try legacy instead
745 ssh_remote::create(systemtap_session
& s
, const uri_decoder
& ud
)
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"));
752 throw runtime_error(_("ssh target URI doesn't support a ?query"));
754 throw runtime_error(_("ssh target URI doesn't support a #fragment"));
756 return create(s
, ud
.authority
);
761 remote::create(systemtap_session
& s
, const string
& uri
)
766 return new direct(s
);
767 else if (uri
== "stapsh")
768 return new direct_stapsh(s
);
769 else if (uri
.find(':') != string::npos
)
771 const uri_decoder
ud(uri
);
772 if (ud
.scheme
== "ssh")
773 return ssh_remote::create(s
, ud
);
775 throw runtime_error(_F("unrecognized URI scheme '%s' in remote: %s",
776 ud
.scheme
.c_str(), uri
.c_str()));
779 // XXX assuming everything else is ssh for now...
780 return ssh_remote::create(s
, uri
);
782 catch (std::runtime_error
& e
)
784 cerr
<< e
.what() << " on remote '" << uri
<< "'" << endl
;
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.
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.
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.
802 ppoll(struct pollfd
*fds
, nfds_t nfds
,
803 const struct timespec
*timeout_ts
,
804 const sigset_t
*sigmask
)
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
);
817 remote::run(const vector
<remote
*>& remotes
)
819 // NB: the first failure "wins"
822 for (unsigned i
= 0; i
< remotes
.size() && !pending_interrupts
; ++i
)
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
) + ": ";
833 for (unsigned i
= 0; i
< remotes
.size() && !pending_interrupts
; ++i
)
835 rc
= remotes
[i
]->start();
840 // mask signals while we're preparing to poll
841 sigset_t mask
, oldmask
;
843 sigaddset (&mask
, SIGHUP
);
844 sigaddset (&mask
, SIGPIPE
);
845 sigaddset (&mask
, SIGINT
);
846 sigaddset (&mask
, SIGTERM
);
847 sigprocmask (SIG_BLOCK
, &mask
, &oldmask
);
849 for (;;) // polling loop for remotes that have fds to watch
852 for (unsigned i
= 0; i
< remotes
.size(); ++i
)
853 remotes
[i
]->prepare_poll (fds
);
857 rc
= ppoll (&fds
[0], fds
.size(), NULL
, &oldmask
);
858 if (rc
< 0 && errno
!= EINTR
)
861 for (unsigned i
= 0; i
< remotes
.size(); ++i
)
862 remotes
[i
]->handle_poll (fds
);
865 sigprocmask (SIG_SETMASK
, &oldmask
, NULL
);
867 for (unsigned i
= 0; i
< remotes
.size(); ++i
)
869 rc
= remotes
[i
]->finish();
878 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.077282 seconds and 6 git commands to generate.