]> sourceware.org Git - systemtap.git/blob - remote.cxx
Silence the closing of the ssh control master
[systemtap.git] / remote.cxx
1 // systemtap remote execution
2 // Copyright (C) 2010 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 extern "C" {
10 #include <sys/utsname.h>
11 }
12
13 #include <stdexcept>
14 #include <sstream>
15 #include <string>
16 #include <vector>
17
18 #include "buildrun.h"
19 #include "remote.h"
20 #include "util.h"
21
22 using namespace std;
23
24 // Decode URIs as per RFC 3986, though not bothering to be strict
25 class uri_decoder {
26 public:
27 const string uri;
28 string scheme, authority, path, query, fragment;
29 bool has_authority, has_query, has_fragment;
30
31 uri_decoder(const string& uri):
32 uri(uri), has_authority(false), has_query(false), has_fragment(false)
33 {
34 const string re =
35 "^([^:]+):(//[^/?#]*)?([^?#]*)(\\?[^#]*)?(#.*)?$";
36
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);
40
41 scheme = matches[1];
42
43 if (!matches[2].empty())
44 {
45 has_authority = true;
46 authority = matches[2].substr(2);
47 }
48
49 path = matches[3];
50
51 if (!matches[4].empty())
52 {
53 has_query = true;
54 query = matches[4].substr(1);
55 }
56
57 if (!matches[5].empty())
58 {
59 has_fragment = true;
60 fragment = matches[5].substr(1);
61 }
62 }
63 };
64
65
66 // loopback target for running locally
67 class direct : public remote {
68 private:
69 pid_t child;
70 direct(systemtap_session& s): remote(s), child(0) {}
71
72 public:
73 friend class remote;
74
75 virtual ~direct() {}
76
77 int start()
78 {
79 pid_t pid = stap_spawn (s->verbose, make_run_command (*s));
80 if (pid <= 0)
81 return 1;
82 child = pid;
83 return 0;
84 }
85
86 int finish()
87 {
88 if (child <= 0)
89 return 1;
90
91 int ret = stap_waitpid(s->verbose, child);
92 child = 0;
93 return ret;
94 }
95 };
96
97 class ssh_remote : public remote {
98 // NB: ssh commands use a tty (-t) so signals are passed along to the remote
99 private:
100 vector<string> ssh_args, scp_args;
101 string ssh_control;
102 string host, tmpdir;
103 pid_t child;
104
105 ssh_remote(systemtap_session& s, const string& host)
106 : remote(s), host(host), child(0)
107 {
108 init();
109 }
110
111 ssh_remote(systemtap_session& s, const uri_decoder& ud)
112 : remote(s), child(0)
113 {
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");
118 if (ud.has_query)
119 throw runtime_error("ssh target URI doesn't support a ?query");
120 if (ud.has_fragment)
121 throw runtime_error("ssh target URI doesn't support a #fragment");
122
123 host = ud.authority;
124 init();
125 }
126
127 void init()
128 {
129 open_control_master();
130 try
131 {
132 get_uname();
133 }
134 catch (runtime_error&)
135 {
136 close_control_master();
137 throw;
138 }
139 }
140
141 void open_control_master()
142 {
143 static unsigned index = 0;
144
145 if (s->tmpdir.empty()) // sanity check, shouldn't happen
146 throw runtime_error("No tmpdir available for ssh control master");
147
148 ssh_control = s->tmpdir + "/ssh_remote_control_" + lex_cast(++index);
149
150 scp_args.clear();
151 scp_args.push_back("scp");
152 scp_args.push_back("-q");
153 scp_args.push_back("-o");
154 scp_args.push_back("ControlPath=" + ssh_control);
155
156 ssh_args = scp_args;
157 ssh_args[0] = "ssh";
158 ssh_args.push_back(host);
159
160 // NB: ssh -f will stay in the foreground until authentication is
161 // complete and the control socket is created, so we know it's ready to
162 // go when stap_system returns.
163 vector<string> cmd = ssh_args;
164 cmd.push_back("-f");
165 cmd.push_back("-N");
166 cmd.push_back("-M");
167 int rc = stap_system(s->verbose, cmd);
168 if (rc != 0)
169 {
170 ostringstream err;
171 err << "failed to create an ssh control master for " << host
172 << " : rc=" << rc;
173 throw runtime_error(err.str());
174 }
175
176 if (s->verbose>1)
177 clog << "Created ssh control master at "
178 << lex_cast_qstring(ssh_control) << endl;
179 }
180
181 void close_control_master()
182 {
183 if (ssh_control.empty())
184 return;
185
186 vector<string> cmd = ssh_args;
187 cmd.push_back("-O");
188 cmd.push_back("exit");
189 int rc = stap_system(s->verbose, cmd, true, true);
190 if (rc != 0)
191 cerr << "failed to stop the ssh control master for " << host
192 << " : rc=" << rc << endl;
193
194 ssh_control.clear();
195 scp_args.clear();
196 ssh_args.clear();
197 }
198
199 void get_uname()
200 {
201 ostringstream out;
202 vector<string> uname;
203 vector<string> cmd = ssh_args;
204 cmd.push_back("-t");
205 cmd.push_back("uname -rm");
206 int rc = stap_system_read(s->verbose, cmd, out);
207 if (rc == 0)
208 tokenize(out.str(), uname, " \t\r\n");
209 if (uname.size() != 2)
210 throw runtime_error("failed to get uname from " + host
211 + " : rc=" + lex_cast(rc));
212 const string& release = uname[0];
213 const string& arch = uname[1];
214 // XXX need to deal with command-line vs. implied arch/release
215 this->s = s->clone(arch, release);
216 }
217
218 public:
219 friend class remote;
220
221 virtual ~ssh_remote()
222 {
223 close_control_master();
224 }
225
226 int start()
227 {
228 int rc;
229 string localmodule = s->tmpdir + "/" + s->module_name + ".ko";
230 string tmpmodule;
231
232 // Make a remote tempdir.
233 {
234 ostringstream out;
235 vector<string> vout;
236 vector<string> cmd = ssh_args;
237 cmd.push_back("-t");
238 cmd.push_back("mktemp -d -t stapXXXXXX");
239 rc = stap_system_read(s->verbose, cmd, out);
240 if (rc == 0)
241 tokenize(out.str(), vout, "\r\n");
242 if (vout.size() != 1)
243 {
244 cerr << "failed to make a tempdir on " << host
245 << " : rc=" << rc << endl;
246 return -1;
247 }
248 tmpdir = vout[0];
249 tmpmodule = tmpdir + "/" + s->module_name + ".ko";
250 }
251
252 // Transfer the module. XXX and uprobes.ko, sigs, etc.
253 if (rc == 0) {
254 vector<string> cmd = scp_args;
255 cmd.push_back(localmodule);
256 cmd.push_back(host + ":" + tmpmodule);
257 rc = stap_system(s->verbose, cmd);
258 if (rc != 0)
259 cerr << "failed to copy the module to " << host
260 << " : rc=" << rc << endl;
261 }
262
263 // Run the module on the remote.
264 if (rc == 0) {
265 vector<string> cmd = ssh_args;
266 cmd.push_back("-t");
267 cmd.push_back(cmdstr_join(make_run_command(*s, tmpmodule)));
268 pid_t pid = stap_spawn(s->verbose, cmd);
269 if (pid > 0)
270 child = pid;
271 else
272 {
273 cerr << "failed to run the module on " << host
274 << " : ret=" << pid << endl;
275 rc = -1;
276 }
277 }
278
279 return rc;
280 }
281
282 int finish()
283 {
284 int rc = 0;
285
286 if (child > 0)
287 {
288 rc = stap_waitpid(s->verbose, child);
289 child = 0;
290 }
291
292 if (!tmpdir.empty())
293 {
294 // Remove the tempdir.
295 // XXX need to make sure this runs even with e.g. CTRL-C exits
296 vector<string> cmd = ssh_args;
297 cmd.push_back("-t");
298 cmd.push_back("rm -r " + cmdstr_quoted(tmpdir));
299 int rc2 = stap_system(s->verbose, cmd);
300 if (rc2 != 0)
301 cerr << "failed to delete the tempdir on " << host
302 << " : rc=" << rc2 << endl;
303 if (rc == 0)
304 rc = rc2;
305 tmpdir.clear();
306 }
307
308 close_control_master();
309
310 return rc;
311 }
312 };
313
314
315 remote*
316 remote::create(systemtap_session& s, const string& uri)
317 {
318 try
319 {
320 if (uri == "direct")
321 return new direct(s);
322 else if (uri.find(':') != string::npos)
323 {
324 const uri_decoder ud(uri);
325 if (ud.scheme == "ssh")
326 return new ssh_remote(s, ud);
327 else
328 {
329 ostringstream msg;
330 msg << "unrecognized URI scheme '" << ud.scheme
331 << "' in remote: " << uri;
332 throw runtime_error(msg.str());
333 }
334 }
335 else
336 // XXX assuming everything else is ssh for now...
337 return new ssh_remote(s, uri);
338 }
339 catch (std::runtime_error& e)
340 {
341 cerr << e.what() << endl;
342 return NULL;
343 }
344 }
345
346
347 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.071929 seconds and 6 git commands to generate.