]> sourceware.org Git - systemtap.git/blame - remote.cxx
Ensure use of the function argument values from function entry in lwtools
[systemtap.git] / remote.cxx
CommitLineData
daa75206 1// systemtap remote execution
1b6156ad 2// Copyright (C) 2010-2015 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;
004f2b9c
JL
129 size_t data_size;
130 string target_stream;
131
132 enum {
133 STAPSH_READY, // ready to receive next command
134 STAPSH_DATA // currently printing data from a 'data' command
135 } stream_state;
e96f2257
JS
136
137 virtual void prepare_poll(vector<pollfd>& fds)
138 {
139 if (fdout >= 0 && OUT)
140 {
822a6a3d 141 pollfd p = { fdout, POLLIN, 0 };
e96f2257
JS
142 fds.push_back(p);
143 }
144
145 // need to send a signal?
146 if (fdin >= 0 && IN && interrupts_sent < pending_interrupts)
147 {
822a6a3d 148 pollfd p = { fdin, POLLOUT, 0 };
e96f2257
JS
149 fds.push_back(p);
150 }
151 }
152
153 virtual void handle_poll(vector<pollfd>& fds)
154 {
155 for (unsigned i=0; i < fds.size(); ++i)
e7e8737f 156 if (fds[i].fd == fdin || fds[i].fd == fdout)
e96f2257 157 {
e7e8737f 158 bool err = false;
e96f2257
JS
159
160 // need to send a signal?
e7e8737f 161 if (fds[i].revents & POLLOUT && IN &&
e96f2257
JS
162 interrupts_sent < pending_interrupts)
163 {
e7e8737f
JS
164 if (send_command("quit\n") == 0)
165 ++interrupts_sent;
166 else
167 err = true;
168 }
169
170 // have data to read?
171 if (fds[i].revents & POLLIN && OUT)
172 {
004f2b9c
JL
173 // if stapsh doesn't support commands, then just dump everything
174 if (!vector_has(options, string("data")))
e7e8737f 175 {
004f2b9c
JL
176 // NB: we could splice here, but we don't know how much
177 // data is available for reading. One way could be to
178 // splice in small chunks and poll() fdout to check if
179 // there's more.
180 char buf[4096];
181 size_t bytes_read;
182 while ((bytes_read = fread(buf, 1, sizeof(buf), OUT)) > 0)
183 printout(buf, bytes_read);
e7e8737f
JS
184 if (errno != EAGAIN)
185 err = true;
186 }
004f2b9c 187 else // we expect commands (just data for now)
e96f2257 188 {
004f2b9c
JL
189 // When the 'data' option is turned on, all outputs from
190 // staprun are first prepended with a line formatted as
191 // "data <stream> <size>\n", where <stream> is either
192 // "stdout" or "stderr", and <size> is the size of the
193 // data. Also, the line "quit\n" is sent from stapsh right
194 // before it exits.
195
196 if (stream_state == STAPSH_READY)
e96f2257 197 {
004f2b9c
JL
198 char cmdbuf[1024];
199 char *rc;
200 while ((rc = fgets(cmdbuf, sizeof(cmdbuf), OUT)) != NULL)
201 {
202 // check if it's a debug output from stapsh itself
203 string line = string(cmdbuf);
204 if (startswith(line, "stapsh:"))
205 clog << line;
206 else if (startswith(line, "quit\n"))
207 // && vector_has(options, string("data")))
208 // uncomment above if another command becomes
209 // possible
210 {
211 err = true; // close connection
212 break;
213 }
214 else if (startswith(line, "data"))
215 // && vector_has(options, string("data")))
216 // uncomment above if another command becomes
217 // possible
218 {
219 vector<string> cmd;
220 tokenize(line, cmd, " \t\r\n");
221 if (!is_valid_data_cmd(cmd))
222 {
223 clog << _("invalid data command from stapsh") << endl;
224 clog << _("received: ") << line;
225 err = true;
226 }
227 else
228 {
229 target_stream = cmd[1];
230 data_size = lex_cast<size_t>(cmd[2]);
231 stream_state = STAPSH_DATA;
232 }
233 break;
234 }
235 }
236 if (rc == NULL && errno != EAGAIN)
237 err = true;
238 }
239
240 if (stream_state == STAPSH_DATA)
241 {
242 if (data_size != 0)
243 {
244 // keep reading from OUT until either dry or data_size bytes done
245 char buf[4096];
246 size_t max_read = min<size_t>(sizeof(buf), data_size);
247 size_t bytes_read = 0;
248 while (max_read > 0
249 && (bytes_read = fread(buf, 1, max_read, OUT)) > 0)
250 {
251 printout(buf, bytes_read);
252 data_size -= bytes_read;
253 max_read = min<size_t>(sizeof(buf), data_size);
254 }
255 if (bytes_read == 0 && errno != EAGAIN)
256 err = true;
257 }
258 if (data_size == 0)
259 stream_state = STAPSH_READY;
e96f2257
JS
260 }
261 }
e96f2257 262 }
e7e8737f
JS
263
264 // any errors?
265 if (err || fds[i].revents & ~(POLLIN|POLLOUT))
266 close();
e96f2257
JS
267 }
268 }
269
35df6b43
JS
270 string get_reply()
271 {
e7e8737f
JS
272 // Some schemes like unix may have stdout and stderr mushed together.
273 // There shouldn't be anything except dbug messages on stderr before we
274 // actually start running, and there's no get_reply after that. So
275 // we'll just loop and skip those that start with "stapsh:".
35df6b43 276 char reply[4096];
e7e8737f
JS
277 while (fgets(reply, sizeof(reply), OUT))
278 {
279 if (!startswith(reply, "stapsh:"))
280 return reply;
281
004f2b9c
JL
282 // if data is not prefixed, we will have no way to distinguish
283 // between stdout and stderr during staprun runtime, so we might as
284 // well print to stdout now as well to be more consistent
285 if (vector_has(options, string("data")))
286 clog << reply; // must be stderr since only replies go to stdout
287 else
288 cout << reply; // output to stdout to be more consistent with later
e7e8737f
JS
289 }
290
291 // Reached EOF, nothing to reply...
292 return "";
35df6b43
JS
293 }
294
e96f2257
JS
295 int send_command(const string& cmd)
296 {
297 if (!IN)
298 return 2;
299 if (fputs(cmd.c_str(), IN) < 0 ||
300 fflush(IN) != 0)
301 return 1;
302 return 0;
303 }
304
305 int send_file(const string& filename, const string& dest)
306 {
307 int rc = 0;
308 FILE* f = fopen(filename.c_str(), "r");
309 if (!f)
310 return 1;
311
312 struct stat fs;
313 rc = fstat(fileno(f), &fs);
314 if (!rc)
315 {
316 ostringstream cmd;
317 cmd << "file " << fs.st_size << " " << dest << "\n";
318 rc = send_command(cmd.str());
319 }
320
321 off_t i = 0;
322 while (!rc && i < fs.st_size)
323 {
324 char buf[4096];
325 size_t r = sizeof(buf);
326 if (fs.st_size - i < (off_t)r)
327 r = fs.st_size - i;
328 r = fread(buf, 1, r, f);
329 if (r == 0)
330 rc = 1;
331 else
332 {
333 size_t w = fwrite(buf, 1, r, IN);
334 if (w != r)
335 rc = 1;
336 else
337 i += w;
338 }
339 }
340 if (!rc)
341 rc = fflush(IN);
342
343 fclose(f);
35df6b43
JS
344
345 if (!rc)
346 {
347 string reply = get_reply();
348 if (reply != "OK\n")
349 {
350 rc = 1;
a5923b0e 351 if (s->verbose > 0)
35df6b43
JS
352 {
353 if (reply.empty())
f3f6443c 354 clog << _("stapsh file ERROR: no reply") << endl;
35df6b43 355 else
f3f6443c 356 clog << _F("stapsh file replied %s", reply.c_str());
35df6b43
JS
357 }
358 }
359 }
360
e96f2257
JS
361 return rc;
362 }
363
bf9c0b28 364 static string qpencode(const string& str)
e96f2257 365 {
bf9c0b28
JS
366 ostringstream o;
367 o << setfill('0') << hex;
368 for (const char* s = str.c_str(); *s; ++s)
369 if (*s >= 33 && *s <= 126 && *s != 61)
370 o << *s;
371 else
372 o << '=' << setw(2) << (unsigned)(unsigned char) *s;
373 return o.str();
e96f2257
JS
374 }
375
004f2b9c
JL
376 static bool is_valid_data_cmd(const vector<string>& cmd)
377 {
378 bool err = false;
379
380 err = err || (cmd[0] != "data");
381 err = err || (cmd[1] != "stdout" && cmd[1] != "stderr");
382
383 if (!err) // try to cast as size_t
384 try { lex_cast<size_t>(cmd[2]); }
385 catch (...) { err = true; }
386
387 return !err;
388 }
389
390 virtual void printout(const char *buf, size_t size)
391 {
392 static string last_prefix;
393 static bool on_same_line;
394
395 if (size == 0)
396 return;
397
398 // don't prefix for stderr to be more consistent with ssh
399 if (!prefix.empty() && target_stream != "stderr")
400 {
401 vector<pair<const char*,int> > lines = split_lines(buf, size);
402 for (vector<pair<const char*,int> >::iterator it = lines.begin();
403 it != lines.end(); ++it)
404 {
405 if (last_prefix != prefix)
406 {
407 if (on_same_line)
408 cout << endl;
409 cout << prefix;
410 }
411 else if (!on_same_line)
412 cout << prefix;
413 cout.write(it->first, it->second);
414 }
415 cout.flush();
416 const char *last_line = lines.back().first;
417 const char last_char = last_line[lines.back().second-1];
418 on_same_line = !lines.empty() && last_char != '\n';
419 last_prefix = prefix;
420 }
421 else
422 {
423 // Otherwise dump the whole block
424 // NB: The buf could contain binary data,
425 // including \0, so write as a block instead of
426 // the usual << string.
427 if (target_stream == "stdout")
428 {
429 cout.write(buf, size);
430 cout.flush();
431 }
432 else // stderr
433 clog.write(buf, size);
434 }
435 }
436
e96f2257
JS
437 protected:
438 stapsh(systemtap_session& s)
439 : remote(s), interrupts_sent(0),
004f2b9c
JL
440 fdin(-1), fdout(-1), IN(0), OUT(0),
441 data_size(0), target_stream("stdout"), // default to stdout for schemes
442 stream_state(STAPSH_READY) // that don't pipe stderr (e.g. ssh)
e96f2257
JS
443 {}
444
55881ad4
JL
445 vector<string> options;
446
3fe776ae 447 virtual int prepare()
e96f2257
JS
448 {
449 int rc = 0;
450
451 string localmodule = s->tmpdir + "/" + s->module_name + ".ko";
452 string remotemodule = s->module_name + ".ko";
453 if ((rc = send_file(localmodule, remotemodule)))
454 return rc;
455
599c80df
JS
456 if (file_exists(localmodule + ".sgn") &&
457 (rc = send_file(localmodule + ".sgn", remotemodule + ".sgn")))
458 return rc;
459
460 if (!s->uprobes_path.empty())
461 {
462 string remoteuprobes = basename(s->uprobes_path.c_str());
463 if ((rc = send_file(s->uprobes_path, remoteuprobes)))
464 return rc;
465
466 if (file_exists(s->uprobes_path + ".sgn") &&
467 (rc = send_file(s->uprobes_path + ".sgn", remoteuprobes + ".sgn")))
468 return rc;
469 }
e96f2257 470
3fe776ae
JS
471 return rc;
472 }
473
474 virtual int start()
475 {
e96f2257
JS
476 // Send the staprun args
477 // NB: The remote is left to decide its own staprun path
bf9c0b28 478 ostringstream run("run", ios::out | ios::ate);
18630fb8 479 vector<string> cmd = make_run_command(*s, ".", remote_version);
5137a7a9
FCE
480
481 // PR13354: identify our remote index/url
482 if (strverscmp("1.7", remote_version.c_str()) <= 0 && // -r supported?
483 ! staprun_r_arg.empty())
484 {
485 cmd.push_back ("-r");
486 cmd.push_back (staprun_r_arg);
487 }
488
e96f2257 489 for (unsigned i = 1; i < cmd.size(); ++i)
bf9c0b28
JS
490 run << ' ' << qpencode(cmd[i]);
491 run << '\n';
e96f2257 492
3fe776ae 493 int rc = send_command(run.str());
e96f2257 494
35df6b43
JS
495 if (!rc)
496 {
497 string reply = get_reply();
498 if (reply != "OK\n")
499 {
500 rc = 1;
a5923b0e 501 if (s->verbose > 0)
35df6b43
JS
502 {
503 if (reply.empty())
f3f6443c 504 clog << _("stapsh run ERROR: no reply") << endl;
35df6b43 505 else
f3f6443c 506 clog << _F("stapsh run replied %s", reply.c_str());
35df6b43
JS
507 }
508 }
509 }
510
e96f2257
JS
511 if (!rc)
512 {
92c41b4e
JL
513 long flags;
514 if ((flags = fcntl(fdout, F_GETFL)) == -1
515 || fcntl(fdout, F_SETFL, flags | O_NONBLOCK) == -1)
516 {
517 clog << _("failed to change to non-blocking mode") << endl;
518 rc = 1;
519 }
e96f2257 520 }
92c41b4e
JL
521
522 if (rc)
a5923b0e
JS
523 // If run failed for any reason, then this
524 // connection is effectively dead to us.
525 close();
e96f2257
JS
526
527 return rc;
528 }
529
530 void close()
531 {
532 if (IN) fclose(IN);
533 if (OUT) fclose(OUT);
534 IN = OUT = NULL;
535 fdin = fdout = -1;
536 }
537
538 virtual int finish()
539 {
540 close();
541 return 0;
542 }
543
544 void set_child_fds(int in, int out)
545 {
546 if (fdin >= 0 || fdout >= 0 || IN || OUT)
8d1a21b3 547 throw runtime_error(_("stapsh file descriptors already set"));
e96f2257
JS
548
549 fdin = in;
550 fdout = out;
551 IN = fdopen(fdin, "w");
552 OUT = fdopen(fdout, "r");
553 if (!IN || !OUT)
8d1a21b3 554 throw runtime_error(_("invalid file descriptors for stapsh"));
e96f2257
JS
555
556 if (send_command("stap " VERSION "\n"))
8d1a21b3 557 throw runtime_error(_("error sending hello to stapsh"));
e96f2257 558
35df6b43
JS
559 string reply = get_reply();
560 if (reply.empty())
8d1a21b3 561 throw runtime_error(_("error receiving hello from stapsh"));
e96f2257
JS
562
563 // stapsh VERSION MACHINE RELEASE
564 vector<string> uname;
565 tokenize(reply, uname, " \t\r\n");
566 if (uname.size() != 4 || uname[0] != "stapsh")
03501815 567 throw runtime_error(_F("invalid hello from stapsh: %s", reply.c_str()));
4112f219
JS
568
569 // We assume that later versions will know how to talk to us.
570 // Looking backward, we use this for make_run_command().
571 this->remote_version = uname[1];
e96f2257
JS
572
573 this->s = s->clone(uname[2], uname[3]);
55881ad4
JL
574
575 // set any option requested
576 if (!this->options.empty())
577 {
578 // check first if the option command is supported
579 if (strverscmp("2.4", this->remote_version.c_str()) > 0)
580 throw runtime_error(_F("stapsh %s does not support options",
581 this->remote_version.c_str()));
582
583 for (vector<string>::iterator it = this->options.begin();
584 it != this->options.end(); ++it)
585 {
92c41b4e
JL
586 int rc = send_command("option " + *it + "\n");
587 if (rc != 0)
588 throw runtime_error(_F("could not set option %s: "
589 "send_command returned %d",
590 it->c_str(), rc));
55881ad4
JL
591 string reply = get_reply();
592 if (reply != "OK\n")
593 throw runtime_error(_F("could not set option %s: %s",
594 it->c_str(), reply.c_str()));
595 }
596 }
e96f2257
JS
597 }
598
599 public:
600 virtual ~stapsh() { close(); }
601};
602
603
88e08f53
JS
604// direct_stapsh is meant only for testing, as a way to exercise the stapsh
605// mechanisms without requiring test machines to have actual remote access.
e96f2257
JS
606class direct_stapsh : public stapsh {
607 private:
608 pid_t child;
609
610 direct_stapsh(systemtap_session& s)
611 : stapsh(s), child(0)
612 {
8d1a21b3
JS
613 int in, out;
614 vector<string> cmd;
615 cmd.push_back(BINDIR "/stapsh");
616 if (s.perpass_verbose[4] > 1)
617 cmd.push_back("-v");
618 if (s.perpass_verbose[4] > 2)
619 cmd.push_back("-v");
620
88e08f53
JS
621 // mask signals while we spawn, so we can simulate manual signals to
622 // the "remote" target, as we must for the real ssh_remote case.
a69b4b55
JS
623 {
624 stap_sigmasker masked;
625 child = stap_spawn_piped(s.verbose, cmd, &in, &out);
626 }
8d1a21b3 627
e96f2257 628 if (child <= 0)
8d1a21b3 629 throw runtime_error(_("error launching stapsh"));
e96f2257
JS
630
631 try
632 {
633 set_child_fds(in, out);
634 }
635 catch (runtime_error&)
636 {
637 finish();
638 throw;
639 }
640 }
641
642 virtual int finish()
643 {
644 int rc = stapsh::finish();
645 if (child <= 0)
646 return rc;
647
648 int rc2 = stap_waitpid(s->verbose, child);
649 child = 0;
650 return rc ?: rc2;
651 }
652
653 public:
654 friend class remote;
655
10a5bce5 656 virtual ~direct_stapsh() { finish(); }
e96f2257
JS
657};
658
659
e7e8737f
JS
660// Connect to an existing stapsh on a unix socket.
661class unix_stapsh : public stapsh {
662 private:
663
664 unix_stapsh(systemtap_session& s, const uri_decoder& ud)
665 : stapsh(s)
666 {
004f2b9c
JL
667 // Request that data be encapsulated since both stdout and stderr have
668 // to go over the same line. Also makes stapsh tell us when it quits.
669 this->options.push_back("data");
670
55881ad4
JL
671 // set verbosity to the requested level
672 for (unsigned i = 1; i < s.perpass_verbose[4]; i++)
673 this->options.push_back("verbose");
674
e7e8737f
JS
675 sockaddr_un server;
676 server.sun_family = AF_UNIX;
677 if (ud.path.empty())
678 throw runtime_error(_("unix target requires a /path"));
679 if (ud.path.size() > sizeof(server.sun_path) - 1)
680 throw runtime_error(_("unix target /path is too long"));
681 strcpy(server.sun_path, ud.path.c_str());
682
683 if (ud.has_authority)
684 throw runtime_error(_("unix target doesn't support a hostname"));
685 if (ud.has_query)
686 throw runtime_error(_("unix target URI doesn't support a ?query"));
687 if (ud.has_fragment)
688 throw runtime_error(_("unix target URI doesn't support a #fragment"));
689
690 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
691 if (fd <= 0)
692 throw runtime_error(_("error opening a socket"));
693
694 if (connect(fd, (struct sockaddr *)&server, SUN_LEN(&server)) < 0)
695 {
696 const char *msg = strerror(errno);
697 ::close(fd);
03501815
JL
698 throw runtime_error(_F("error connecting to socket %s: %s",
699 server.sun_path, msg));
e7e8737f
JS
700 }
701
702 // Try to dup it, so class stapsh can have truly separate fds for its
703 // fdopen handles. If it fails for some reason, it can still work with
704 // just one though.
705 int fd2 = dup(fd);
706 if (fd2 < 0)
707 fd2 = fd;
708
709 try
710 {
711 set_child_fds(fd, fd2);
712 }
713 catch (runtime_error&)
714 {
715 finish();
716 ::close(fd);
717 ::close(fd2);
718 throw;
719 }
720 }
721
722 public:
723 friend class remote;
724
725 virtual ~unix_stapsh() { finish(); }
726};
727
2246b6d3
JL
728class libvirt_stapsh : public stapsh {
729 private:
730
731 pid_t child;
732
733 libvirt_stapsh(systemtap_session& s, const uri_decoder& ud)
734 : stapsh(s)
735 {
736 string domain;
737 string libvirt_uri;
738
739 // Request that data be encapsulated since both stdout and stderr have
740 // to go over the same line. Also makes stapsh tell us when it quits.
741 this->options.push_back("data");
742
743 // set verbosity to the requested level
744 for (unsigned i = 1; i < s.perpass_verbose[4]; i++)
745 this->options.push_back("verbose");
746
747 // A valid libvirt URI has one of two forms:
748 // - libvirt://DOMAIN/LIBVIRT_URI?LIBVIRT_QUERY
749 // - libvirt:DOMAIN/LIBVIRT_URI?LIBVIRT_QUERY
750 // We only advertise the first form, but to be nice, we also accept the
751 // second form. In the first form, DOMAIN is authority, LIBVIRT_URI is
752 // path, and LIBVIRT_QUERY is the query. In the second form, the DOMAIN
753 // is part of the path.
754
755 if (ud.has_fragment)
756 throw runtime_error(_("libvirt target URI doesn't support a #fragment"));
757
758 if (ud.has_authority) // first form
759 {
760 domain = ud.authority;
761 if (!ud.path.empty())
762 {
763 libvirt_uri = ud.path.substr(1);
764 if (ud.has_query)
765 libvirt_uri += "?" + ud.query;
766 }
767 }
768 else // second form
769 {
770 if (ud.path.empty())
771 throw runtime_error(_("libvirt target URI requires a domain name"));
772
773 size_t slash = ud.path.find_first_of('/');
774 if (slash == string::npos)
775 domain = ud.path;
776 else
777 {
778 domain = ud.path.substr(0, slash);
779 libvirt_uri = ud.path.substr(slash+1);
780 }
781 }
782
783 int in, out;
784 vector<string> cmd;
785
786 string stapvirt = BINDIR "/stapvirt";
787 if (!file_exists(stapvirt))
788 throw runtime_error(_("stapvirt missing"));
789
790 cmd.push_back(stapvirt);
791
792 // carry verbosity into stapvirt
793 for (unsigned i = 1; i < s.perpass_verbose[4]; i++)
794 cmd.push_back("-v");
795
796 if (!libvirt_uri.empty())
797 {
798 cmd.push_back("-c");
799 cmd.push_back(libvirt_uri);
800 }
801
802 cmd.push_back("connect");
803 cmd.push_back(domain);
804
805 // mask signals for stapvirt since it relies instead on stap or stapsh
806 // closing its connection to know when to exit (otherwise, stapvirt
807 // would receive SIGINT on Ctrl-C and exit nonzero)
808
809 // NB: There is an interesting issue to note here. Some libvirt URIs
810 // will create child processes of stapvirt (e.g. qemu+ssh:// will
811 // create an ssh process under stapvirt, also qemu+ext://, see
812 // <libvirt.org/remote.html> for more info). These processes do not
813 // keep the mask of stapvirt we are creating here, but are still part
814 // of the same process group.
815 // This means that e.g. SIGINT will not be blocked. Thus, stapvirt
816 // explicitly adds a handler for SIGCHLD and forcefully disconnects
817 // upon receiving it (otherwise it would await indefinitely).
818 {
819 stap_sigmasker masked;
820 child = stap_spawn_piped(s.verbose, cmd, &in, &out);
821 }
822
823 if (child <= 0)
824 throw runtime_error(_("error launching stapvirt"));
825
826 try
827 {
828 set_child_fds(in, out);
829 }
830 catch (runtime_error&)
831 {
832 finish();
833 throw;
834 }
835 }
836
837 int finish()
838 {
839 int rc = stapsh::finish();
840 if (child <= 0)
841 return rc;
842
843 int rc2 = stap_waitpid(s->verbose, child);
844 child = 0;
845 return rc ?: rc2;
846 }
847
848 public:
849 friend class remote;
850
851 virtual ~libvirt_stapsh() { finish(); }
852};
e7e8737f 853
8d1a21b3 854// stapsh-based ssh_remote
e96f2257
JS
855class ssh_remote : public stapsh {
856 private:
857 pid_t child;
858
8d1a21b3 859 ssh_remote(systemtap_session& s): stapsh(s), child(0) {}
e96f2257 860
eacb2780 861 int connect(const string& host, const string& port)
e96f2257 862 {
8d1a21b3
JS
863 int rc = 0;
864 int in, out;
865 vector<string> cmd;
866 cmd.push_back("ssh");
867 cmd.push_back("-q");
868 cmd.push_back(host);
eacb2780
JS
869 if (!port.empty())
870 {
871 cmd.push_back("-p");
872 cmd.push_back(port);
873 }
8d1a21b3
JS
874
875 // This is crafted so that we get a silent failure with status 127 if
876 // the command is not found. The combination of -P and $cmd ensures
877 // that we pull the command out of the PATH, not aliases or such.
75daad84 878 string stapsh_cmd = "cmd=`type -P stapsh || exit 127` && exec \"$cmd\"";
8d1a21b3 879 if (s->perpass_verbose[4] > 1)
75daad84 880 stapsh_cmd.append(" -v");
8d1a21b3 881 if (s->perpass_verbose[4] > 2)
75daad84
JS
882 stapsh_cmd.append(" -v");
883 // NB: We need to explicitly choose bash, as $SHELL may be weird...
884 cmd.push_back("/bin/bash -c '" + stapsh_cmd + "'");
e96f2257 885
e96f2257
JS
886 // mask signals while we spawn, so we can manually send even tty
887 // signals *through* ssh rather than to ssh itself
a69b4b55
JS
888 {
889 stap_sigmasker masked;
890 child = stap_spawn_piped(s->verbose, cmd, &in, &out);
891 }
8d1a21b3 892
e96f2257 893 if (child <= 0)
8d1a21b3 894 throw runtime_error(_("error launching stapsh"));
e96f2257
JS
895
896 try
897 {
898 set_child_fds(in, out);
899 }
900 catch (runtime_error&)
901 {
c00db46e
JS
902 rc = finish();
903
904 // ssh itself signals errors with 255
905 if (rc == 255)
906 throw runtime_error(_("error establishing ssh connection"));
907
8d1a21b3
JS
908 // If rc == 127, that's command-not-found, so we let ::create()
909 // try again in legacy mode. But only do this if there's a single
910 // remote, as the old code didn't handle ttys well with multiple
911 // remotes. Otherwise, throw up again. *barf*
8d1a21b3
JS
912 if (rc != 127 || s->remote_uris.size() > 1)
913 throw;
e96f2257 914 }
8d1a21b3
JS
915
916 return rc;
e96f2257
JS
917 }
918
919 int finish()
920 {
921 int rc = stapsh::finish();
922 if (child <= 0)
923 return rc;
924
925 int rc2 = stap_waitpid(s->verbose, child);
926 child = 0;
927 return rc ?: rc2;
928 }
929
930 public:
8d1a21b3
JS
931
932 static remote* create(systemtap_session& s, const string& host);
933 static remote* create(systemtap_session& s, const uri_decoder& ud);
e96f2257 934
10a5bce5 935 virtual ~ssh_remote() { finish(); }
e96f2257 936};
8d1a21b3
JS
937
938
939// ssh connection without stapsh, for legacy stap installations
940// NB: ssh commands use a tty (-t) so signals are passed along to the remote.
941// It does this by putting the local tty in raw mode, so it only works for tty
942// signals, and only for a single remote at a time.
943class ssh_legacy_remote : public remote {
daa75206 944 private:
20f90026
JS
945 vector<string> ssh_args, scp_args;
946 string ssh_control;
eacb2780 947 string host, port, tmpdir;
ebff2ed0
JS
948 pid_t child;
949
eacb2780
JS
950 ssh_legacy_remote(systemtap_session& s, const string& host, const string& port)
951 : remote(s), host(host), port(port), child(0)
1544bdf9 952 {
4c156a0e 953 open_control_master();
1544bdf9
JS
954 try
955 {
956 get_uname();
957 }
958 catch (runtime_error&)
959 {
960 close_control_master();
961 throw;
962 }
91fa953d
JS
963 }
964
4c156a0e
JS
965 void open_control_master()
966 {
967 static unsigned index = 0;
968
969 if (s->tmpdir.empty()) // sanity check, shouldn't happen
58a834b1 970 throw runtime_error(_("No tmpdir available for ssh control master"));
4c156a0e
JS
971
972 ssh_control = s->tmpdir + "/ssh_remote_control_" + lex_cast(++index);
20f90026
JS
973
974 scp_args.clear();
975 scp_args.push_back("scp");
976 scp_args.push_back("-q");
977 scp_args.push_back("-o");
978 scp_args.push_back("ControlPath=" + ssh_control);
979
980 ssh_args = scp_args;
981 ssh_args[0] = "ssh";
982 ssh_args.push_back(host);
4c156a0e 983
eacb2780
JS
984 if (!port.empty())
985 {
986 scp_args.push_back("-P");
987 scp_args.push_back(port);
988 ssh_args.push_back("-p");
989 ssh_args.push_back(port);
990 }
991
4c156a0e
JS
992 // NB: ssh -f will stay in the foreground until authentication is
993 // complete and the control socket is created, so we know it's ready to
994 // go when stap_system returns.
20f90026
JS
995 vector<string> cmd = ssh_args;
996 cmd.push_back("-f");
997 cmd.push_back("-N");
998 cmd.push_back("-M");
4c156a0e
JS
999 int rc = stap_system(s->verbose, cmd);
1000 if (rc != 0)
8d1a21b3
JS
1001 throw runtime_error(_F("failed to create an ssh control master for %s : rc= %d",
1002 host.c_str(), rc));
4c156a0e
JS
1003
1004 if (s->verbose>1)
58a834b1
LB
1005 clog << _F("Created ssh control master at %s",
1006 lex_cast_qstring(ssh_control).c_str()) << endl;
4c156a0e
JS
1007 }
1008
1009 void close_control_master()
1010 {
1011 if (ssh_control.empty())
1012 return;
1013
20f90026
JS
1014 vector<string> cmd = ssh_args;
1015 cmd.push_back("-O");
1016 cmd.push_back("exit");
2323da5e 1017 int rc = stap_system(s->verbose, cmd, true, true);
4c156a0e 1018 if (rc != 0)
58a834b1
LB
1019 cerr << _F("failed to stop the ssh control master for %s : rc=%d",
1020 host.c_str(), rc) << endl;
4c156a0e
JS
1021
1022 ssh_control.clear();
20f90026
JS
1023 scp_args.clear();
1024 ssh_args.clear();
4c156a0e
JS
1025 }
1026
91fa953d 1027 void get_uname()
daa75206
JS
1028 {
1029 ostringstream out;
1030 vector<string> uname;
20f90026
JS
1031 vector<string> cmd = ssh_args;
1032 cmd.push_back("-t");
1033 cmd.push_back("uname -rm");
1034 int rc = stap_system_read(s->verbose, cmd, out);
daa75206
JS
1035 if (rc == 0)
1036 tokenize(out.str(), uname, " \t\r\n");
1037 if (uname.size() != 2)
58a834b1 1038 throw runtime_error(_F("failed to get uname from %s : rc= %d", host.c_str(), rc));
4c156a0e
JS
1039 const string& release = uname[0];
1040 const string& arch = uname[1];
1041 // XXX need to deal with command-line vs. implied arch/release
1042 this->s = s->clone(arch, release);
daa75206
JS
1043 }
1044
ebff2ed0 1045 int start()
daa75206
JS
1046 {
1047 int rc;
ebff2ed0
JS
1048 string localmodule = s->tmpdir + "/" + s->module_name + ".ko";
1049 string tmpmodule;
daa75206
JS
1050
1051 // Make a remote tempdir.
1052 {
1053 ostringstream out;
1054 vector<string> vout;
20f90026
JS
1055 vector<string> cmd = ssh_args;
1056 cmd.push_back("-t");
1057 cmd.push_back("mktemp -d -t stapXXXXXX");
ebff2ed0 1058 rc = stap_system_read(s->verbose, cmd, out);
daa75206 1059 if (rc == 0)
b2cbfd40 1060 tokenize(out.str(), vout, "\r\n");
daa75206
JS
1061 if (vout.size() != 1)
1062 {
58a834b1
LB
1063 cerr << _F("failed to make a tempdir on %s : rc=%d",
1064 host.c_str(), rc) << endl;
daa75206
JS
1065 return -1;
1066 }
1067 tmpdir = vout[0];
ebff2ed0 1068 tmpmodule = tmpdir + "/" + s->module_name + ".ko";
daa75206
JS
1069 }
1070
18630fb8
JS
1071 // Transfer the module.
1072 if (rc == 0)
1073 {
1074 vector<string> cmd = scp_args;
1075 cmd.push_back(localmodule);
1076 cmd.push_back(host + ":" + tmpmodule);
1077 rc = stap_system(s->verbose, cmd);
1078 if (rc != 0)
1079 cerr << _F("failed to copy the module to %s : rc=%d",
1080 host.c_str(), rc) << endl;
1081 }
1082
1083 // Transfer the module signature.
1084 if (rc == 0 && file_exists(localmodule + ".sgn"))
1085 {
1086 vector<string> cmd = scp_args;
1087 cmd.push_back(localmodule + ".sgn");
1088 cmd.push_back(host + ":" + tmpmodule + ".sgn");
1089 rc = stap_system(s->verbose, cmd);
1090 if (rc != 0)
1091 cerr << _F("failed to copy the module signature to %s : rc=%d",
1092 host.c_str(), rc) << endl;
1093 }
1094
1095 // What about transfering uprobes.ko? In this ssh "legacy" mode, we
1096 // don't the remote systemtap version, but -uPATH wasn't added until
1097 // 1.4. Rather than risking a getopt error, we'll just assume that
1098 // this isn't supported. The remote will just have to provide its own
1099 // uprobes.ko in SYSTEMTAP_RUNTIME or already loaded.
daa75206
JS
1100
1101 // Run the module on the remote.
1102 if (rc == 0) {
20f90026
JS
1103 vector<string> cmd = ssh_args;
1104 cmd.push_back("-t");
4112f219 1105 // We don't know the actual version, but all <=1.3 are approx equal.
18630fb8
JS
1106 vector<string> staprun_cmd = make_run_command(*s, tmpdir, "1.3");
1107 staprun_cmd[0] = "staprun"; // NB: The remote decides its own path
5137a7a9
FCE
1108 // NB: PR13354: we assume legacy installations don't have
1109 // staprun -r support, so we ignore staprun_r_arg.
18630fb8 1110 cmd.push_back(cmdstr_join(staprun_cmd));
ebff2ed0
JS
1111 pid_t pid = stap_spawn(s->verbose, cmd);
1112 if (pid > 0)
4c156a0e
JS
1113 child = pid;
1114 else
1115 {
58a834b1
LB
1116 cerr << _F("failed to run the module on %s : ret=%d",
1117 host.c_str(), pid) << endl;
4c156a0e
JS
1118 rc = -1;
1119 }
daa75206
JS
1120 }
1121
4c156a0e 1122 return rc;
ebff2ed0 1123 }
daa75206 1124
ebff2ed0
JS
1125 int finish()
1126 {
4c156a0e 1127 int rc = 0;
ebff2ed0 1128
4c156a0e
JS
1129 if (child > 0)
1130 {
1131 rc = stap_waitpid(s->verbose, child);
1132 child = 0;
1133 }
ebff2ed0
JS
1134
1135 if (!tmpdir.empty())
4c156a0e
JS
1136 {
1137 // Remove the tempdir.
1138 // XXX need to make sure this runs even with e.g. CTRL-C exits
20f90026
JS
1139 vector<string> cmd = ssh_args;
1140 cmd.push_back("-t");
1141 cmd.push_back("rm -r " + cmdstr_quoted(tmpdir));
4c156a0e
JS
1142 int rc2 = stap_system(s->verbose, cmd);
1143 if (rc2 != 0)
58a834b1
LB
1144 cerr << _F("failed to delete the tempdir on %s : rc=%d",
1145 host.c_str(), rc2) << endl;
4c156a0e
JS
1146 if (rc == 0)
1147 rc = rc2;
20f90026 1148 tmpdir.clear();
4c156a0e
JS
1149 }
1150
1151 close_control_master();
1152
1153 return rc;
daa75206 1154 }
4f244435
JS
1155
1156 public:
8d1a21b3 1157 friend class ssh_remote;
4f244435 1158
8d1a21b3 1159 virtual ~ssh_legacy_remote()
4f244435
JS
1160 {
1161 close_control_master();
1162 }
daa75206 1163};
8d1a21b3
JS
1164
1165
1166// Try to establish a stapsh connection to the remote, but fallback
1167// to the older mechanism if the command is not found.
1168remote*
eacb2780 1169ssh_remote::create(systemtap_session& s, const string& target)
8d1a21b3 1170{
eacb2780
JS
1171 string port, host = target;
1172 size_t i = host.find(':');
1173 if (i != string::npos)
1174 {
1175 port = host.substr(i + 1);
1176 host.erase(i);
1177 }
1178
1b6156ad
FCE
1179#if __cplusplus >= 201103L /* -std=c++11 */
1180 unique_ptr<ssh_remote> p (new ssh_remote(s));
1181#else
8d1a21b3 1182 auto_ptr<ssh_remote> p (new ssh_remote(s));
1b6156ad 1183#endif
eacb2780 1184 int rc = p->connect(host, port);
8d1a21b3
JS
1185 if (rc == 0)
1186 return p.release();
1187 else if (rc == 127) // stapsh command not found
eacb2780 1188 return new ssh_legacy_remote(s, host, port); // try legacy instead
8d1a21b3
JS
1189 return NULL;
1190}
1191
1192remote*
1193ssh_remote::create(systemtap_session& s, const uri_decoder& ud)
1194{
1195 if (!ud.has_authority || ud.authority.empty())
1196 throw runtime_error(_("ssh target requires a hostname"));
1197 if (!ud.path.empty() && ud.path != "/")
1198 throw runtime_error(_("ssh target URI doesn't support a /path"));
1199 if (ud.has_query)
1200 throw runtime_error(_("ssh target URI doesn't support a ?query"));
1201 if (ud.has_fragment)
1202 throw runtime_error(_("ssh target URI doesn't support a #fragment"));
1203
1204 return create(s, ud.authority);
1205}
daa75206
JS
1206
1207
1208remote*
5137a7a9 1209remote::create(systemtap_session& s, const string& uri, int idx)
daa75206 1210{
5137a7a9 1211 remote *it = 0;
daa75206
JS
1212 try
1213 {
2bce499b 1214 if (uri.find(':') != string::npos)
91fa953d
JS
1215 {
1216 const uri_decoder ud(uri);
eacb2780
JS
1217
1218 // An ssh "host:port" is ambiguous with a URI "scheme:path".
1219 // So if it looks like a number, just assume ssh.
1220 if (!ud.has_authority && !ud.has_query &&
1221 !ud.has_fragment && !ud.path.empty() &&
1222 ud.path.find_first_not_of("1234567890") == string::npos)
5137a7a9
FCE
1223 it = ssh_remote::create(s, uri);
1224 else if (ud.scheme == "direct")
1225 it = new direct(s);
2bce499b 1226 else if (ud.scheme == "stapsh")
5137a7a9 1227 it = new direct_stapsh(s);
e7e8737f 1228 else if (ud.scheme == "unix")
5137a7a9 1229 it = new unix_stapsh(s, ud);
2246b6d3
JL
1230 else if (ud.scheme == "libvirt")
1231 it = new libvirt_stapsh(s, ud);
5137a7a9
FCE
1232 else if (ud.scheme == "ssh")
1233 it = ssh_remote::create(s, ud);
91fa953d 1234 else
8d1a21b3
JS
1235 throw runtime_error(_F("unrecognized URI scheme '%s' in remote: %s",
1236 ud.scheme.c_str(), uri.c_str()));
91fa953d 1237 }
daa75206
JS
1238 else
1239 // XXX assuming everything else is ssh for now...
5137a7a9 1240 it = ssh_remote::create(s, uri);
daa75206
JS
1241 }
1242 catch (std::runtime_error& e)
1243 {
8d1a21b3 1244 cerr << e.what() << " on remote '" << uri << "'" << endl;
5137a7a9 1245 it = 0;
daa75206 1246 }
5137a7a9
FCE
1247
1248 if (it && idx >= 0) // PR13354: remote metadata for staprun -r IDX:URI
1249 {
1250 stringstream r_arg;
1251 r_arg << idx << ":" << uri;
1252 it->staprun_r_arg = r_arg.str();
1253 }
1254
1255 return it;
daa75206
JS
1256}
1257
4f244435
JS
1258int
1259remote::run(const vector<remote*>& remotes)
1260{
1261 // NB: the first failure "wins"
1262 int ret = 0, rc = 0;
1263
1264 for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i)
3fe776ae 1265 {
756cfd49
JS
1266 remote *r = remotes[i];
1267 r->s->verbose = r->s->perpass_verbose[4];
1268 if (r->s->use_remote_prefix)
1269 r->prefix = lex_cast(i) + ": ";
1270 rc = r->prepare();
3fe776ae
JS
1271 if (rc)
1272 return rc;
1273 }
1274
1275 for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i)
4f244435
JS
1276 {
1277 rc = remotes[i]->start();
1278 if (!ret)
1279 ret = rc;
1280 }
1281
e96f2257 1282 // mask signals while we're preparing to poll
a69b4b55
JS
1283 {
1284 stap_sigmasker masked;
e96f2257 1285
a69b4b55
JS
1286 // polling loop for remotes that have fds to watch
1287 for (;;)
1288 {
1289 vector<pollfd> fds;
1290 for (unsigned i = 0; i < remotes.size(); ++i)
1291 remotes[i]->prepare_poll (fds);
1292 if (fds.empty())
1293 break;
1294
1295 rc = ppoll (&fds[0], fds.size(), NULL, &masked.old);
1296 if (rc < 0 && errno != EINTR)
1297 break;
1298
1299 for (unsigned i = 0; i < remotes.size(); ++i)
1300 remotes[i]->handle_poll (fds);
1301 }
1302 }
e96f2257 1303
4f244435
JS
1304 for (unsigned i = 0; i < remotes.size(); ++i)
1305 {
1306 rc = remotes[i]->finish();
1307 if (!ret)
1308 ret = rc;
1309 }
1310
1311 return ret;
1312}
1313
daa75206
JS
1314
1315/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.2415 seconds and 5 git commands to generate.