]>
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
10 #include <sys/utsname.h>
24 // Decode URIs as per RFC 3986, though not bothering to be strict
28 string scheme
, authority
, path
, query
, fragment
;
29 bool has_authority
, has_query
, has_fragment
;
31 uri_decoder(const string
& uri
):
32 uri(uri
), has_authority(false), has_query(false), has_fragment(false)
35 "^([^:]+):(//[^/?#]*)?([^?#]*)(\\?[^#]*)?(#.*)?$";
37 vector
<string
> matches
;
38 if (regexp_match(uri
, re
, matches
) != 0)
39 throw runtime_error("string doesn't appear to be a URI: " + uri
);
43 if (!matches
[2].empty())
46 authority
= matches
[2].substr(2);
51 if (!matches
[4].empty())
54 query
= matches
[4].substr(1);
57 if (!matches
[5].empty())
60 fragment
= matches
[5].substr(1);
66 // loopback target for running locally
67 class direct
: public remote
{
70 direct(systemtap_session
& s
): remote(s
), child(0) {}
79 string module
= s
->tmpdir
+ "/" + s
->module_name
+ ".ko";
80 pid_t pid
= stap_spawn (s
->verbose
, make_run_command (*s
, module
));
92 int ret
= stap_waitpid(s
->verbose
, child
);
98 class ssh_remote
: public remote
{
99 // NB: ssh commands use a tty (-t) so signals are passed along to the remote
101 string ssh_opts
, ssh_control
;
105 ssh_remote(systemtap_session
& s
, const string
& host
)
106 : remote(s
), host(host
), child(0)
111 ssh_remote(systemtap_session
& s
, const uri_decoder
& ud
)
112 : remote(s
), child(0)
114 if (!ud
.has_authority
|| ud
.authority
.empty())
115 throw runtime_error("ssh target requires a hostname");
116 if (!ud
.path
.empty() && ud
.path
!= "/")
117 throw runtime_error("ssh target URI doesn't support a /path");
119 throw runtime_error("ssh target URI doesn't support a ?query");
121 throw runtime_error("ssh target URI doesn't support a #fragment");
129 open_control_master();
134 catch (runtime_error
&)
136 close_control_master();
141 void open_control_master()
143 static unsigned index
= 0;
145 if (s
->tmpdir
.empty()) // sanity check, shouldn't happen
146 throw runtime_error("No tmpdir available for ssh control master");
148 ssh_control
= s
->tmpdir
+ "/ssh_remote_control_" + lex_cast(++index
);
149 ssh_opts
= " -q -o ControlPath=" + lex_cast_qstring(ssh_control
) + " ";
151 // NB: ssh -f will stay in the foreground until authentication is
152 // complete and the control socket is created, so we know it's ready to
153 // go when stap_system returns.
154 string cmd
= "ssh -f -N -M " + ssh_opts
+ lex_cast_qstring(host
);
155 int rc
= stap_system(s
->verbose
, cmd
);
159 err
<< "failed to create an ssh control master for " << host
161 throw runtime_error(err
.str());
165 clog
<< "Created ssh control master at "
166 << lex_cast_qstring(ssh_control
) << endl
;
169 void close_control_master()
171 if (ssh_control
.empty())
174 string cmd
= "ssh " + ssh_opts
+ lex_cast_qstring(host
)
175 + " -O exit 2>/dev/null >/dev/null";
176 int rc
= stap_system(s
->verbose
, cmd
);
178 cerr
<< "failed to stop the ssh control master for " << host
179 << " : rc=" << rc
<< endl
;
188 vector
<string
> uname
;
189 string uname_cmd
= "ssh -t " + ssh_opts
+ lex_cast_qstring(host
) + " uname -rm";
190 int rc
= stap_system_read(s
->verbose
, uname_cmd
, out
);
192 tokenize(out
.str(), uname
, " \t\r\n");
193 if (uname
.size() != 2)
194 throw runtime_error("failed to get uname from " + host
195 + " : rc=" + lex_cast(rc
));
196 const string
& release
= uname
[0];
197 const string
& arch
= uname
[1];
198 // XXX need to deal with command-line vs. implied arch/release
199 this->s
= s
->clone(arch
, release
);
205 virtual ~ssh_remote()
207 close_control_master();
213 string localmodule
= s
->tmpdir
+ "/" + s
->module_name
+ ".ko";
215 string qhost
= lex_cast_qstring(host
);
217 // Make a remote tempdir.
221 string cmd
= "ssh -t " + ssh_opts
+ qhost
+ " mktemp -d -t stapXXXXXX";
222 rc
= stap_system_read(s
->verbose
, cmd
, out
);
224 tokenize(out
.str(), vout
, "\r\n");
225 if (vout
.size() != 1)
227 cerr
<< "failed to make a tempdir on " << host
228 << " : rc=" << rc
<< endl
;
232 tmpmodule
= tmpdir
+ "/" + s
->module_name
+ ".ko";
235 // Transfer the module. XXX and uprobes.ko, sigs, etc.
237 string cmd
= "scp " + ssh_opts
+ localmodule
+ " " + qhost
+ ":" + tmpmodule
;
238 rc
= stap_system(s
->verbose
, cmd
);
240 cerr
<< "failed to copy the module to " << host
241 << " : rc=" << rc
<< endl
;
244 // Run the module on the remote.
246 string cmd
= "ssh -t " + ssh_opts
+ qhost
+ " "
247 + lex_cast_qstring(make_run_command(*s
, tmpmodule
));
248 pid_t pid
= stap_spawn(s
->verbose
, cmd
);
253 cerr
<< "failed to run the module on " << host
254 << " : ret=" << pid
<< endl
;
268 rc
= stap_waitpid(s
->verbose
, child
);
274 // Remove the tempdir.
275 // XXX need to make sure this runs even with e.g. CTRL-C exits
276 string qhost
= lex_cast_qstring(host
);
277 string cmd
= "ssh -t " + ssh_opts
+ qhost
+ " rm -r " + tmpdir
;
279 int rc2
= stap_system(s
->verbose
, cmd
);
281 cerr
<< "failed to delete the tempdir on " << host
282 << " : rc=" << rc2
<< endl
;
287 close_control_master();
295 remote::create(systemtap_session
& s
, const string
& uri
)
300 return new direct(s
);
301 else if (uri
.find(':') != string::npos
)
303 const uri_decoder
ud(uri
);
304 if (ud
.scheme
== "ssh")
305 return new ssh_remote(s
, ud
);
309 msg
<< "unrecognized URI scheme '" << ud
.scheme
310 << "' in remote: " << uri
;
311 throw runtime_error(msg
.str());
315 // XXX assuming everything else is ssh for now...
316 return new ssh_remote(s
, uri
);
318 catch (std::runtime_error
& e
)
320 cerr
<< e
.what() << endl
;
326 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.055095 seconds and 6 git commands to generate.