]> sourceware.org Git - systemtap.git/blame - remote.cxx
stapsh-unix.exp: give time for socat to get set up
[systemtap.git] / remote.cxx
CommitLineData
daa75206 1// systemtap remote execution
1e6e9ec1 2// Copyright (C) 2010-2011 Red Hat Inc.
daa75206
JS
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
e04a4b41
JS
9#include "config.h"
10
daa75206 11extern "C" {
e96f2257 12#include <fcntl.h>
e04a4b41 13#include <poll.h>
e96f2257
JS
14#include <sys/types.h>
15#include <sys/stat.h>
16#include <unistd.h>
e7e8737f
JS
17#include <sys/socket.h>
18#include <sys/un.h>
daa75206
JS
19}
20
e96f2257 21#include <cstdio>
bf9c0b28 22#include <iomanip>
8d1a21b3 23#include <memory>
daa75206
JS
24#include <stdexcept>
25#include <sstream>
26#include <string>
27#include <vector>
28
29#include "buildrun.h"
30#include "remote.h"
31#include "util.h"
32
33using namespace std;
34
91fa953d
JS
35// Decode URIs as per RFC 3986, though not bothering to be strict
36class uri_decoder {
37 public:
38 const string uri;
39 string scheme, authority, path, query, fragment;
40 bool has_authority, has_query, has_fragment;
41
42 uri_decoder(const string& uri):
43 uri(uri), has_authority(false), has_query(false), has_fragment(false)
44 {
45 const string re =
46 "^([^:]+):(//[^/?#]*)?([^?#]*)(\\?[^#]*)?(#.*)?$";
47
48 vector<string> matches;
49 if (regexp_match(uri, re, matches) != 0)
58a834b1 50 throw runtime_error(_F("string doesn't appear to be a URI: %s", uri.c_str()));
91fa953d
JS
51
52 scheme = matches[1];
53
54 if (!matches[2].empty())
55 {
56 has_authority = true;
57 authority = matches[2].substr(2);
58 }
59
60 path = matches[3];
61
62 if (!matches[4].empty())
63 {
64 has_query = true;
65 query = matches[4].substr(1);
66 }
67
68 if (!matches[5].empty())
69 {
70 has_fragment = true;
71 fragment = matches[5].substr(1);
72 }
73 }
74};
75
76
daa75206
JS
77// loopback target for running locally
78class direct : public remote {
ebff2ed0
JS
79 private:
80 pid_t child;
5def5d2a 81 vector<string> args;
ebff2ed0 82 direct(systemtap_session& s): remote(s), child(0) {}
daa75206 83
ebff2ed0
JS
84 int start()
85 {
5def5d2a 86 args = make_run_command(*s);
5137a7a9
FCE
87 if (! staprun_r_arg.empty()) // PR13354
88 {
89 args.push_back ("-r");
90 args.push_back (staprun_r_arg);
91 }
5def5d2a 92 pid_t pid = stap_spawn (s->verbose, args);
4c156a0e
JS
93 if (pid <= 0)
94 return 1;
95 child = pid;
96 return 0;
ebff2ed0
JS
97 }
98
99 int finish()
100 {
4c156a0e
JS
101 if (child <= 0)
102 return 1;
ebff2ed0 103
4c156a0e 104 int ret = stap_waitpid(s->verbose, child);
df324c9f
JS
105 if (ret > 128)
106 s->print_warning(_F("%s exited with signal: %d (%s)",
107 args.front().c_str(), ret - 128,
108 strsignal(ret - 128)));
109 else if (ret > 0)
110 s->print_warning(_F("%s exited with status: %d",
111 args.front().c_str(), ret));
4c156a0e
JS
112 child = 0;
113 return ret;
ebff2ed0 114 }
4f244435
JS
115
116 public:
117 friend class remote;
118
10a5bce5 119 virtual ~direct() { finish(); }
daa75206
JS
120};
121
e96f2257
JS
122
123class stapsh : public remote {
124 private:
125 int interrupts_sent;
126 int fdin, fdout;
127 FILE *IN, *OUT;
4112f219 128 string remote_version;
e96f2257
JS
129
130 virtual void prepare_poll(vector<pollfd>& fds)
131 {
132 if (fdout >= 0 && OUT)
133 {
822a6a3d 134 pollfd p = { fdout, POLLIN, 0 };
e96f2257
JS
135 fds.push_back(p);
136 }
137
138 // need to send a signal?
139 if (fdin >= 0 && IN && interrupts_sent < pending_interrupts)
140 {
822a6a3d 141 pollfd p = { fdin, POLLOUT, 0 };
e96f2257
JS
142 fds.push_back(p);
143 }
144 }
145
146 virtual void handle_poll(vector<pollfd>& fds)
147 {
148 for (unsigned i=0; i < fds.size(); ++i)
e7e8737f 149 if (fds[i].fd == fdin || fds[i].fd == fdout)
e96f2257 150 {
e7e8737f 151 bool err = false;
e96f2257
JS
152
153 // need to send a signal?
e7e8737f 154 if (fds[i].revents & POLLOUT && IN &&
e96f2257
JS
155 interrupts_sent < pending_interrupts)
156 {
e7e8737f
JS
157 if (send_command("quit\n") == 0)
158 ++interrupts_sent;
159 else
160 err = true;
161 }
162
163 // have data to read?
164 if (fds[i].revents & POLLIN && OUT)
165 {
166 char buf[4096];
167 if (!prefix.empty())
168 {
169 // If we have a line prefix, then read lines one at a
170 // time and copy out with the prefix.
171 errno = 0;
172 while (fgets(buf, sizeof(buf), OUT))
173 cout << prefix << buf;
174 if (errno != EAGAIN)
175 err = true;
176 }
177 else
e96f2257 178 {
e7e8737f
JS
179 // Otherwise read an entire block of data at once.
180 size_t rc = fread(buf, 1, sizeof(buf), OUT);
181 if (rc > 0)
e96f2257 182 {
e7e8737f
JS
183 // NB: The buf could contain binary data,
184 // including \0, so write as a block instead of
185 // the usual <<string.
186 cout.write(buf, rc);
e96f2257 187 }
e7e8737f
JS
188 else
189 err = true;
e96f2257 190 }
e96f2257 191 }
e7e8737f
JS
192
193 // any errors?
194 if (err || fds[i].revents & ~(POLLIN|POLLOUT))
195 close();
e96f2257
JS
196 }
197 }
198
35df6b43
JS
199 string get_reply()
200 {
e7e8737f
JS
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:".
35df6b43 205 char reply[4096];
e7e8737f
JS
206 while (fgets(reply, sizeof(reply), OUT))
207 {
208 if (!startswith(reply, "stapsh:"))
209 return reply;
210
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.
214 cout << reply;
215 }
216
217 // Reached EOF, nothing to reply...
218 return "";
35df6b43
JS
219 }
220
e96f2257
JS
221 int send_command(const string& cmd)
222 {
223 if (!IN)
224 return 2;
225 if (fputs(cmd.c_str(), IN) < 0 ||
226 fflush(IN) != 0)
227 return 1;
228 return 0;
229 }
230
231 int send_file(const string& filename, const string& dest)
232 {
233 int rc = 0;
234 FILE* f = fopen(filename.c_str(), "r");
235 if (!f)
236 return 1;
237
238 struct stat fs;
239 rc = fstat(fileno(f), &fs);
240 if (!rc)
241 {
242 ostringstream cmd;
243 cmd << "file " << fs.st_size << " " << dest << "\n";
244 rc = send_command(cmd.str());
245 }
246
247 off_t i = 0;
248 while (!rc && i < fs.st_size)
249 {
250 char buf[4096];
251 size_t r = sizeof(buf);
252 if (fs.st_size - i < (off_t)r)
253 r = fs.st_size - i;
254 r = fread(buf, 1, r, f);
255 if (r == 0)
256 rc = 1;
257 else
258 {
259 size_t w = fwrite(buf, 1, r, IN);
260 if (w != r)
261 rc = 1;
262 else
263 i += w;
264 }
265 }
266 if (!rc)
267 rc = fflush(IN);
268
269 fclose(f);
35df6b43
JS
270
271 if (!rc)
272 {
273 string reply = get_reply();
274 if (reply != "OK\n")
275 {
276 rc = 1;
a5923b0e 277 if (s->verbose > 0)
35df6b43
JS
278 {
279 if (reply.empty())
f3f6443c 280 clog << _("stapsh file ERROR: no reply") << endl;
35df6b43 281 else
f3f6443c 282 clog << _F("stapsh file replied %s", reply.c_str());
35df6b43
JS
283 }
284 }
285 }
286
e96f2257
JS
287 return rc;
288 }
289
bf9c0b28 290 static string qpencode(const string& str)
e96f2257 291 {
bf9c0b28
JS
292 ostringstream o;
293 o << setfill('0') << hex;
294 for (const char* s = str.c_str(); *s; ++s)
295 if (*s >= 33 && *s <= 126 && *s != 61)
296 o << *s;
297 else
298 o << '=' << setw(2) << (unsigned)(unsigned char) *s;
299 return o.str();
e96f2257
JS
300 }
301
302 protected:
303 stapsh(systemtap_session& s)
304 : remote(s), interrupts_sent(0),
305 fdin(-1), fdout(-1), IN(0), OUT(0)
306 {}
307
3fe776ae 308 virtual int prepare()
e96f2257
JS
309 {
310 int rc = 0;
311
312 string localmodule = s->tmpdir + "/" + s->module_name + ".ko";
313 string remotemodule = s->module_name + ".ko";
314 if ((rc = send_file(localmodule, remotemodule)))
315 return rc;
316
599c80df
JS
317 if (file_exists(localmodule + ".sgn") &&
318 (rc = send_file(localmodule + ".sgn", remotemodule + ".sgn")))
319 return rc;
320
321 if (!s->uprobes_path.empty())
322 {
323 string remoteuprobes = basename(s->uprobes_path.c_str());
324 if ((rc = send_file(s->uprobes_path, remoteuprobes)))
325 return rc;
326
327 if (file_exists(s->uprobes_path + ".sgn") &&
328 (rc = send_file(s->uprobes_path + ".sgn", remoteuprobes + ".sgn")))
329 return rc;
330 }
e96f2257 331
3fe776ae
JS
332 return rc;
333 }
334
335 virtual int start()
336 {
e96f2257
JS
337 // Send the staprun args
338 // NB: The remote is left to decide its own staprun path
bf9c0b28 339 ostringstream run("run", ios::out | ios::ate);
18630fb8 340 vector<string> cmd = make_run_command(*s, ".", remote_version);
5137a7a9
FCE
341
342 // PR13354: identify our remote index/url
343 if (strverscmp("1.7", remote_version.c_str()) <= 0 && // -r supported?
344 ! staprun_r_arg.empty())
345 {
346 cmd.push_back ("-r");
347 cmd.push_back (staprun_r_arg);
348 }
349
e96f2257 350 for (unsigned i = 1; i < cmd.size(); ++i)
bf9c0b28
JS
351 run << ' ' << qpencode(cmd[i]);
352 run << '\n';
e96f2257 353
3fe776ae 354 int rc = send_command(run.str());
e96f2257 355
35df6b43
JS
356 if (!rc)
357 {
358 string reply = get_reply();
359 if (reply != "OK\n")
360 {
361 rc = 1;
a5923b0e 362 if (s->verbose > 0)
35df6b43
JS
363 {
364 if (reply.empty())
f3f6443c 365 clog << _("stapsh run ERROR: no reply") << endl;
35df6b43 366 else
f3f6443c 367 clog << _F("stapsh run replied %s", reply.c_str());
35df6b43
JS
368 }
369 }
370 }
371
e96f2257
JS
372 if (!rc)
373 {
374 long flags = fcntl(fdout, F_GETFL) | O_NONBLOCK;
375 fcntl(fdout, F_SETFL, flags);
376 }
a5923b0e
JS
377 else
378 // If run failed for any reason, then this
379 // connection is effectively dead to us.
380 close();
e96f2257
JS
381
382 return rc;
383 }
384
385 void close()
386 {
387 if (IN) fclose(IN);
388 if (OUT) fclose(OUT);
389 IN = OUT = NULL;
390 fdin = fdout = -1;
391 }
392
393 virtual int finish()
394 {
395 close();
396 return 0;
397 }
398
399 void set_child_fds(int in, int out)
400 {
401 if (fdin >= 0 || fdout >= 0 || IN || OUT)
8d1a21b3 402 throw runtime_error(_("stapsh file descriptors already set"));
e96f2257
JS
403
404 fdin = in;
405 fdout = out;
406 IN = fdopen(fdin, "w");
407 OUT = fdopen(fdout, "r");
408 if (!IN || !OUT)
8d1a21b3 409 throw runtime_error(_("invalid file descriptors for stapsh"));
e96f2257
JS
410
411 if (send_command("stap " VERSION "\n"))
8d1a21b3 412 throw runtime_error(_("error sending hello to stapsh"));
e96f2257 413
35df6b43
JS
414 string reply = get_reply();
415 if (reply.empty())
8d1a21b3 416 throw runtime_error(_("error receiving hello from stapsh"));
e96f2257
JS
417
418 // stapsh VERSION MACHINE RELEASE
419 vector<string> uname;
420 tokenize(reply, uname, " \t\r\n");
421 if (uname.size() != 4 || uname[0] != "stapsh")
f3f6443c 422 throw runtime_error(_("failed to get uname from stapsh"));
4112f219
JS
423
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];
e96f2257
JS
427
428 this->s = s->clone(uname[2], uname[3]);
429 }
430
431 public:
432 virtual ~stapsh() { close(); }
433};
434
435
88e08f53
JS
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.
e96f2257
JS
438class direct_stapsh : public stapsh {
439 private:
440 pid_t child;
441
442 direct_stapsh(systemtap_session& s)
443 : stapsh(s), child(0)
444 {
8d1a21b3
JS
445 int in, out;
446 vector<string> cmd;
447 cmd.push_back(BINDIR "/stapsh");
448 if (s.perpass_verbose[4] > 1)
449 cmd.push_back("-v");
450 if (s.perpass_verbose[4] > 2)
451 cmd.push_back("-v");
452
88e08f53
JS
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.
a69b4b55
JS
455 {
456 stap_sigmasker masked;
457 child = stap_spawn_piped(s.verbose, cmd, &in, &out);
458 }
8d1a21b3 459
e96f2257 460 if (child <= 0)
8d1a21b3 461 throw runtime_error(_("error launching stapsh"));
e96f2257
JS
462
463 try
464 {
465 set_child_fds(in, out);
466 }
467 catch (runtime_error&)
468 {
469 finish();
470 throw;
471 }
472 }
473
474 virtual int finish()
475 {
476 int rc = stapsh::finish();
477 if (child <= 0)
478 return rc;
479
480 int rc2 = stap_waitpid(s->verbose, child);
481 child = 0;
482 return rc ?: rc2;
483 }
484
485 public:
486 friend class remote;
487
10a5bce5 488 virtual ~direct_stapsh() { finish(); }
e96f2257
JS
489};
490
491
e7e8737f
JS
492// Connect to an existing stapsh on a unix socket.
493class unix_stapsh : public stapsh {
494 private:
495
496 unix_stapsh(systemtap_session& s, const uri_decoder& ud)
497 : stapsh(s)
498 {
499 sockaddr_un server;
500 server.sun_family = AF_UNIX;
501 if (ud.path.empty())
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());
506
507 if (ud.has_authority)
508 throw runtime_error(_("unix target doesn't support a hostname"));
509 if (ud.has_query)
510 throw runtime_error(_("unix target URI doesn't support a ?query"));
511 if (ud.has_fragment)
512 throw runtime_error(_("unix target URI doesn't support a #fragment"));
513
514 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
515 if (fd <= 0)
516 throw runtime_error(_("error opening a socket"));
517
518 if (connect(fd, (struct sockaddr *)&server, SUN_LEN(&server)) < 0)
519 {
520 const char *msg = strerror(errno);
521 ::close(fd);
522 throw runtime_error(_F("error connecting to socket: %s", msg));
523 }
524
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
527 // just one though.
528 int fd2 = dup(fd);
529 if (fd2 < 0)
530 fd2 = fd;
531
532 try
533 {
534 set_child_fds(fd, fd2);
535 }
536 catch (runtime_error&)
537 {
538 finish();
539 ::close(fd);
540 ::close(fd2);
541 throw;
542 }
543 }
544
545 public:
546 friend class remote;
547
548 virtual ~unix_stapsh() { finish(); }
549};
550
551
8d1a21b3 552// stapsh-based ssh_remote
e96f2257
JS
553class ssh_remote : public stapsh {
554 private:
555 pid_t child;
556
8d1a21b3 557 ssh_remote(systemtap_session& s): stapsh(s), child(0) {}
e96f2257 558
eacb2780 559 int connect(const string& host, const string& port)
e96f2257 560 {
8d1a21b3
JS
561 int rc = 0;
562 int in, out;
563 vector<string> cmd;
564 cmd.push_back("ssh");
565 cmd.push_back("-q");
566 cmd.push_back(host);
eacb2780
JS
567 if (!port.empty())
568 {
569 cmd.push_back("-p");
570 cmd.push_back(port);
571 }
8d1a21b3
JS
572
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.
75daad84 576 string stapsh_cmd = "cmd=`type -P stapsh || exit 127` && exec \"$cmd\"";
8d1a21b3 577 if (s->perpass_verbose[4] > 1)
75daad84 578 stapsh_cmd.append(" -v");
8d1a21b3 579 if (s->perpass_verbose[4] > 2)
75daad84
JS
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 + "'");
e96f2257 583
e96f2257
JS
584 // mask signals while we spawn, so we can manually send even tty
585 // signals *through* ssh rather than to ssh itself
a69b4b55
JS
586 {
587 stap_sigmasker masked;
588 child = stap_spawn_piped(s->verbose, cmd, &in, &out);
589 }
8d1a21b3 590
e96f2257 591 if (child <= 0)
8d1a21b3 592 throw runtime_error(_("error launching stapsh"));
e96f2257
JS
593
594 try
595 {
596 set_child_fds(in, out);
597 }
598 catch (runtime_error&)
599 {
c00db46e
JS
600 rc = finish();
601
602 // ssh itself signals errors with 255
603 if (rc == 255)
604 throw runtime_error(_("error establishing ssh connection"));
605
8d1a21b3
JS
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*
8d1a21b3
JS
610 if (rc != 127 || s->remote_uris.size() > 1)
611 throw;
e96f2257 612 }
8d1a21b3
JS
613
614 return rc;
e96f2257
JS
615 }
616
617 int finish()
618 {
619 int rc = stapsh::finish();
620 if (child <= 0)
621 return rc;
622
623 int rc2 = stap_waitpid(s->verbose, child);
624 child = 0;
625 return rc ?: rc2;
626 }
627
628 public:
8d1a21b3
JS
629
630 static remote* create(systemtap_session& s, const string& host);
631 static remote* create(systemtap_session& s, const uri_decoder& ud);
e96f2257 632
10a5bce5 633 virtual ~ssh_remote() { finish(); }
e96f2257 634};
8d1a21b3
JS
635
636
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.
641class ssh_legacy_remote : public remote {
daa75206 642 private:
20f90026
JS
643 vector<string> ssh_args, scp_args;
644 string ssh_control;
eacb2780 645 string host, port, tmpdir;
ebff2ed0
JS
646 pid_t child;
647
eacb2780
JS
648 ssh_legacy_remote(systemtap_session& s, const string& host, const string& port)
649 : remote(s), host(host), port(port), child(0)
1544bdf9 650 {
4c156a0e 651 open_control_master();
1544bdf9
JS
652 try
653 {
654 get_uname();
655 }
656 catch (runtime_error&)
657 {
658 close_control_master();
659 throw;
660 }
91fa953d
JS
661 }
662
4c156a0e
JS
663 void open_control_master()
664 {
665 static unsigned index = 0;
666
667 if (s->tmpdir.empty()) // sanity check, shouldn't happen
58a834b1 668 throw runtime_error(_("No tmpdir available for ssh control master"));
4c156a0e
JS
669
670 ssh_control = s->tmpdir + "/ssh_remote_control_" + lex_cast(++index);
20f90026
JS
671
672 scp_args.clear();
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);
677
678 ssh_args = scp_args;
679 ssh_args[0] = "ssh";
680 ssh_args.push_back(host);
4c156a0e 681
eacb2780
JS
682 if (!port.empty())
683 {
684 scp_args.push_back("-P");
685 scp_args.push_back(port);
686 ssh_args.push_back("-p");
687 ssh_args.push_back(port);
688 }
689
4c156a0e
JS
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.
20f90026
JS
693 vector<string> cmd = ssh_args;
694 cmd.push_back("-f");
695 cmd.push_back("-N");
696 cmd.push_back("-M");
4c156a0e
JS
697 int rc = stap_system(s->verbose, cmd);
698 if (rc != 0)
8d1a21b3
JS
699 throw runtime_error(_F("failed to create an ssh control master for %s : rc= %d",
700 host.c_str(), rc));
4c156a0e
JS
701
702 if (s->verbose>1)
58a834b1
LB
703 clog << _F("Created ssh control master at %s",
704 lex_cast_qstring(ssh_control).c_str()) << endl;
4c156a0e
JS
705 }
706
707 void close_control_master()
708 {
709 if (ssh_control.empty())
710 return;
711
20f90026
JS
712 vector<string> cmd = ssh_args;
713 cmd.push_back("-O");
714 cmd.push_back("exit");
2323da5e 715 int rc = stap_system(s->verbose, cmd, true, true);
4c156a0e 716 if (rc != 0)
58a834b1
LB
717 cerr << _F("failed to stop the ssh control master for %s : rc=%d",
718 host.c_str(), rc) << endl;
4c156a0e
JS
719
720 ssh_control.clear();
20f90026
JS
721 scp_args.clear();
722 ssh_args.clear();
4c156a0e
JS
723 }
724
91fa953d 725 void get_uname()
daa75206
JS
726 {
727 ostringstream out;
728 vector<string> uname;
20f90026
JS
729 vector<string> cmd = ssh_args;
730 cmd.push_back("-t");
731 cmd.push_back("uname -rm");
732 int rc = stap_system_read(s->verbose, cmd, out);
daa75206
JS
733 if (rc == 0)
734 tokenize(out.str(), uname, " \t\r\n");
735 if (uname.size() != 2)
58a834b1 736 throw runtime_error(_F("failed to get uname from %s : rc= %d", host.c_str(), rc));
4c156a0e
JS
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);
daa75206
JS
741 }
742
ebff2ed0 743 int start()
daa75206
JS
744 {
745 int rc;
ebff2ed0
JS
746 string localmodule = s->tmpdir + "/" + s->module_name + ".ko";
747 string tmpmodule;
daa75206
JS
748
749 // Make a remote tempdir.
750 {
751 ostringstream out;
752 vector<string> vout;
20f90026
JS
753 vector<string> cmd = ssh_args;
754 cmd.push_back("-t");
755 cmd.push_back("mktemp -d -t stapXXXXXX");
ebff2ed0 756 rc = stap_system_read(s->verbose, cmd, out);
daa75206 757 if (rc == 0)
b2cbfd40 758 tokenize(out.str(), vout, "\r\n");
daa75206
JS
759 if (vout.size() != 1)
760 {
58a834b1
LB
761 cerr << _F("failed to make a tempdir on %s : rc=%d",
762 host.c_str(), rc) << endl;
daa75206
JS
763 return -1;
764 }
765 tmpdir = vout[0];
ebff2ed0 766 tmpmodule = tmpdir + "/" + s->module_name + ".ko";
daa75206
JS
767 }
768
18630fb8
JS
769 // Transfer the module.
770 if (rc == 0)
771 {
772 vector<string> cmd = scp_args;
773 cmd.push_back(localmodule);
774 cmd.push_back(host + ":" + tmpmodule);
775 rc = stap_system(s->verbose, cmd);
776 if (rc != 0)
777 cerr << _F("failed to copy the module to %s : rc=%d",
778 host.c_str(), rc) << endl;
779 }
780
781 // Transfer the module signature.
782 if (rc == 0 && file_exists(localmodule + ".sgn"))
783 {
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);
788 if (rc != 0)
789 cerr << _F("failed to copy the module signature to %s : rc=%d",
790 host.c_str(), rc) << endl;
791 }
792
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.
daa75206
JS
798
799 // Run the module on the remote.
800 if (rc == 0) {
20f90026
JS
801 vector<string> cmd = ssh_args;
802 cmd.push_back("-t");
4112f219 803 // We don't know the actual version, but all <=1.3 are approx equal.
18630fb8
JS
804 vector<string> staprun_cmd = make_run_command(*s, tmpdir, "1.3");
805 staprun_cmd[0] = "staprun"; // NB: The remote decides its own path
5137a7a9
FCE
806 // NB: PR13354: we assume legacy installations don't have
807 // staprun -r support, so we ignore staprun_r_arg.
18630fb8 808 cmd.push_back(cmdstr_join(staprun_cmd));
ebff2ed0
JS
809 pid_t pid = stap_spawn(s->verbose, cmd);
810 if (pid > 0)
4c156a0e
JS
811 child = pid;
812 else
813 {
58a834b1
LB
814 cerr << _F("failed to run the module on %s : ret=%d",
815 host.c_str(), pid) << endl;
4c156a0e
JS
816 rc = -1;
817 }
daa75206
JS
818 }
819
4c156a0e 820 return rc;
ebff2ed0 821 }
daa75206 822
ebff2ed0
JS
823 int finish()
824 {
4c156a0e 825 int rc = 0;
ebff2ed0 826
4c156a0e
JS
827 if (child > 0)
828 {
829 rc = stap_waitpid(s->verbose, child);
830 child = 0;
831 }
ebff2ed0
JS
832
833 if (!tmpdir.empty())
4c156a0e
JS
834 {
835 // Remove the tempdir.
836 // XXX need to make sure this runs even with e.g. CTRL-C exits
20f90026
JS
837 vector<string> cmd = ssh_args;
838 cmd.push_back("-t");
839 cmd.push_back("rm -r " + cmdstr_quoted(tmpdir));
4c156a0e
JS
840 int rc2 = stap_system(s->verbose, cmd);
841 if (rc2 != 0)
58a834b1
LB
842 cerr << _F("failed to delete the tempdir on %s : rc=%d",
843 host.c_str(), rc2) << endl;
4c156a0e
JS
844 if (rc == 0)
845 rc = rc2;
20f90026 846 tmpdir.clear();
4c156a0e
JS
847 }
848
849 close_control_master();
850
851 return rc;
daa75206 852 }
4f244435
JS
853
854 public:
8d1a21b3 855 friend class ssh_remote;
4f244435 856
8d1a21b3 857 virtual ~ssh_legacy_remote()
4f244435
JS
858 {
859 close_control_master();
860 }
daa75206 861};
8d1a21b3
JS
862
863
864// Try to establish a stapsh connection to the remote, but fallback
865// to the older mechanism if the command is not found.
866remote*
eacb2780 867ssh_remote::create(systemtap_session& s, const string& target)
8d1a21b3 868{
eacb2780
JS
869 string port, host = target;
870 size_t i = host.find(':');
871 if (i != string::npos)
872 {
873 port = host.substr(i + 1);
874 host.erase(i);
875 }
876
8d1a21b3 877 auto_ptr<ssh_remote> p (new ssh_remote(s));
eacb2780 878 int rc = p->connect(host, port);
8d1a21b3
JS
879 if (rc == 0)
880 return p.release();
881 else if (rc == 127) // stapsh command not found
eacb2780 882 return new ssh_legacy_remote(s, host, port); // try legacy instead
8d1a21b3
JS
883 return NULL;
884}
885
886remote*
887ssh_remote::create(systemtap_session& s, const uri_decoder& ud)
888{
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"));
893 if (ud.has_query)
894 throw runtime_error(_("ssh target URI doesn't support a ?query"));
895 if (ud.has_fragment)
896 throw runtime_error(_("ssh target URI doesn't support a #fragment"));
897
898 return create(s, ud.authority);
899}
daa75206
JS
900
901
902remote*
5137a7a9 903remote::create(systemtap_session& s, const string& uri, int idx)
daa75206 904{
5137a7a9 905 remote *it = 0;
daa75206
JS
906 try
907 {
2bce499b 908 if (uri.find(':') != string::npos)
91fa953d
JS
909 {
910 const uri_decoder ud(uri);
eacb2780
JS
911
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)
5137a7a9
FCE
917 it = ssh_remote::create(s, uri);
918 else if (ud.scheme == "direct")
919 it = new direct(s);
2bce499b 920 else if (ud.scheme == "stapsh")
5137a7a9 921 it = new direct_stapsh(s);
e7e8737f 922 else if (ud.scheme == "unix")
5137a7a9
FCE
923 it = new unix_stapsh(s, ud);
924 else if (ud.scheme == "ssh")
925 it = ssh_remote::create(s, ud);
91fa953d 926 else
8d1a21b3
JS
927 throw runtime_error(_F("unrecognized URI scheme '%s' in remote: %s",
928 ud.scheme.c_str(), uri.c_str()));
91fa953d 929 }
daa75206
JS
930 else
931 // XXX assuming everything else is ssh for now...
5137a7a9 932 it = ssh_remote::create(s, uri);
daa75206
JS
933 }
934 catch (std::runtime_error& e)
935 {
8d1a21b3 936 cerr << e.what() << " on remote '" << uri << "'" << endl;
5137a7a9 937 it = 0;
daa75206 938 }
5137a7a9
FCE
939
940 if (it && idx >= 0) // PR13354: remote metadata for staprun -r IDX:URI
941 {
942 stringstream r_arg;
943 r_arg << idx << ":" << uri;
944 it->staprun_r_arg = r_arg.str();
945 }
946
947 return it;
daa75206
JS
948}
949
4f244435
JS
950int
951remote::run(const vector<remote*>& remotes)
952{
953 // NB: the first failure "wins"
954 int ret = 0, rc = 0;
955
956 for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i)
3fe776ae 957 {
756cfd49
JS
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) + ": ";
962 rc = r->prepare();
3fe776ae
JS
963 if (rc)
964 return rc;
965 }
966
967 for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i)
4f244435
JS
968 {
969 rc = remotes[i]->start();
970 if (!ret)
971 ret = rc;
972 }
973
e96f2257 974 // mask signals while we're preparing to poll
a69b4b55
JS
975 {
976 stap_sigmasker masked;
e96f2257 977
a69b4b55
JS
978 // polling loop for remotes that have fds to watch
979 for (;;)
980 {
981 vector<pollfd> fds;
982 for (unsigned i = 0; i < remotes.size(); ++i)
983 remotes[i]->prepare_poll (fds);
984 if (fds.empty())
985 break;
986
987 rc = ppoll (&fds[0], fds.size(), NULL, &masked.old);
988 if (rc < 0 && errno != EINTR)
989 break;
990
991 for (unsigned i = 0; i < remotes.size(); ++i)
992 remotes[i]->handle_poll (fds);
993 }
994 }
e96f2257 995
4f244435
JS
996 for (unsigned i = 0; i < remotes.size(); ++i)
997 {
998 rc = remotes[i]->finish();
999 if (!ret)
1000 ret = rc;
1001 }
1002
1003 return ret;
1004}
1005
daa75206
JS
1006
1007/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.173705 seconds and 5 git commands to generate.