]> sourceware.org Git - systemtap.git/blame - remote.cxx
PR16207 partial fix: Fix the chmod [nd_]syscall.exp tests on rawhide.
[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;
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 {
513 long flags = fcntl(fdout, F_GETFL) | O_NONBLOCK;
514 fcntl(fdout, F_SETFL, flags);
515 }
a5923b0e
JS
516 else
517 // If run failed for any reason, then this
518 // connection is effectively dead to us.
519 close();
e96f2257
JS
520
521 return rc;
522 }
523
524 void close()
525 {
526 if (IN) fclose(IN);
527 if (OUT) fclose(OUT);
528 IN = OUT = NULL;
529 fdin = fdout = -1;
530 }
531
532 virtual int finish()
533 {
534 close();
535 return 0;
536 }
537
538 void set_child_fds(int in, int out)
539 {
540 if (fdin >= 0 || fdout >= 0 || IN || OUT)
8d1a21b3 541 throw runtime_error(_("stapsh file descriptors already set"));
e96f2257
JS
542
543 fdin = in;
544 fdout = out;
545 IN = fdopen(fdin, "w");
546 OUT = fdopen(fdout, "r");
547 if (!IN || !OUT)
8d1a21b3 548 throw runtime_error(_("invalid file descriptors for stapsh"));
e96f2257
JS
549
550 if (send_command("stap " VERSION "\n"))
8d1a21b3 551 throw runtime_error(_("error sending hello to stapsh"));
e96f2257 552
35df6b43
JS
553 string reply = get_reply();
554 if (reply.empty())
8d1a21b3 555 throw runtime_error(_("error receiving hello from stapsh"));
e96f2257
JS
556
557 // stapsh VERSION MACHINE RELEASE
558 vector<string> uname;
559 tokenize(reply, uname, " \t\r\n");
560 if (uname.size() != 4 || uname[0] != "stapsh")
03501815 561 throw runtime_error(_F("invalid hello from stapsh: %s", reply.c_str()));
4112f219
JS
562
563 // We assume that later versions will know how to talk to us.
564 // Looking backward, we use this for make_run_command().
565 this->remote_version = uname[1];
e96f2257
JS
566
567 this->s = s->clone(uname[2], uname[3]);
55881ad4
JL
568
569 // set any option requested
570 if (!this->options.empty())
571 {
572 // check first if the option command is supported
573 if (strverscmp("2.4", this->remote_version.c_str()) > 0)
574 throw runtime_error(_F("stapsh %s does not support options",
575 this->remote_version.c_str()));
576
577 for (vector<string>::iterator it = this->options.begin();
578 it != this->options.end(); ++it)
579 {
580 send_command("option " + *it + "\n");
581 string reply = get_reply();
582 if (reply != "OK\n")
583 throw runtime_error(_F("could not set option %s: %s",
584 it->c_str(), reply.c_str()));
585 }
586 }
e96f2257
JS
587 }
588
589 public:
590 virtual ~stapsh() { close(); }
591};
592
593
88e08f53
JS
594// direct_stapsh is meant only for testing, as a way to exercise the stapsh
595// mechanisms without requiring test machines to have actual remote access.
e96f2257
JS
596class direct_stapsh : public stapsh {
597 private:
598 pid_t child;
599
600 direct_stapsh(systemtap_session& s)
601 : stapsh(s), child(0)
602 {
8d1a21b3
JS
603 int in, out;
604 vector<string> cmd;
605 cmd.push_back(BINDIR "/stapsh");
606 if (s.perpass_verbose[4] > 1)
607 cmd.push_back("-v");
608 if (s.perpass_verbose[4] > 2)
609 cmd.push_back("-v");
610
88e08f53
JS
611 // mask signals while we spawn, so we can simulate manual signals to
612 // the "remote" target, as we must for the real ssh_remote case.
a69b4b55
JS
613 {
614 stap_sigmasker masked;
615 child = stap_spawn_piped(s.verbose, cmd, &in, &out);
616 }
8d1a21b3 617
e96f2257 618 if (child <= 0)
8d1a21b3 619 throw runtime_error(_("error launching stapsh"));
e96f2257
JS
620
621 try
622 {
623 set_child_fds(in, out);
624 }
625 catch (runtime_error&)
626 {
627 finish();
628 throw;
629 }
630 }
631
632 virtual int finish()
633 {
634 int rc = stapsh::finish();
635 if (child <= 0)
636 return rc;
637
638 int rc2 = stap_waitpid(s->verbose, child);
639 child = 0;
640 return rc ?: rc2;
641 }
642
643 public:
644 friend class remote;
645
10a5bce5 646 virtual ~direct_stapsh() { finish(); }
e96f2257
JS
647};
648
649
e7e8737f
JS
650// Connect to an existing stapsh on a unix socket.
651class unix_stapsh : public stapsh {
652 private:
653
654 unix_stapsh(systemtap_session& s, const uri_decoder& ud)
655 : stapsh(s)
656 {
004f2b9c
JL
657 // Request that data be encapsulated since both stdout and stderr have
658 // to go over the same line. Also makes stapsh tell us when it quits.
659 this->options.push_back("data");
660
55881ad4
JL
661 // set verbosity to the requested level
662 for (unsigned i = 1; i < s.perpass_verbose[4]; i++)
663 this->options.push_back("verbose");
664
e7e8737f
JS
665 sockaddr_un server;
666 server.sun_family = AF_UNIX;
667 if (ud.path.empty())
668 throw runtime_error(_("unix target requires a /path"));
669 if (ud.path.size() > sizeof(server.sun_path) - 1)
670 throw runtime_error(_("unix target /path is too long"));
671 strcpy(server.sun_path, ud.path.c_str());
672
673 if (ud.has_authority)
674 throw runtime_error(_("unix target doesn't support a hostname"));
675 if (ud.has_query)
676 throw runtime_error(_("unix target URI doesn't support a ?query"));
677 if (ud.has_fragment)
678 throw runtime_error(_("unix target URI doesn't support a #fragment"));
679
680 int fd = socket(AF_UNIX, SOCK_STREAM, 0);
681 if (fd <= 0)
682 throw runtime_error(_("error opening a socket"));
683
684 if (connect(fd, (struct sockaddr *)&server, SUN_LEN(&server)) < 0)
685 {
686 const char *msg = strerror(errno);
687 ::close(fd);
03501815
JL
688 throw runtime_error(_F("error connecting to socket %s: %s",
689 server.sun_path, msg));
e7e8737f
JS
690 }
691
692 // Try to dup it, so class stapsh can have truly separate fds for its
693 // fdopen handles. If it fails for some reason, it can still work with
694 // just one though.
695 int fd2 = dup(fd);
696 if (fd2 < 0)
697 fd2 = fd;
698
699 try
700 {
701 set_child_fds(fd, fd2);
702 }
703 catch (runtime_error&)
704 {
705 finish();
706 ::close(fd);
707 ::close(fd2);
708 throw;
709 }
710 }
711
712 public:
713 friend class remote;
714
715 virtual ~unix_stapsh() { finish(); }
716};
717
2246b6d3
JL
718class libvirt_stapsh : public stapsh {
719 private:
720
721 pid_t child;
722
723 libvirt_stapsh(systemtap_session& s, const uri_decoder& ud)
724 : stapsh(s)
725 {
726 string domain;
727 string libvirt_uri;
728
729 // Request that data be encapsulated since both stdout and stderr have
730 // to go over the same line. Also makes stapsh tell us when it quits.
731 this->options.push_back("data");
732
733 // set verbosity to the requested level
734 for (unsigned i = 1; i < s.perpass_verbose[4]; i++)
735 this->options.push_back("verbose");
736
737 // A valid libvirt URI has one of two forms:
738 // - libvirt://DOMAIN/LIBVIRT_URI?LIBVIRT_QUERY
739 // - libvirt:DOMAIN/LIBVIRT_URI?LIBVIRT_QUERY
740 // We only advertise the first form, but to be nice, we also accept the
741 // second form. In the first form, DOMAIN is authority, LIBVIRT_URI is
742 // path, and LIBVIRT_QUERY is the query. In the second form, the DOMAIN
743 // is part of the path.
744
745 if (ud.has_fragment)
746 throw runtime_error(_("libvirt target URI doesn't support a #fragment"));
747
748 if (ud.has_authority) // first form
749 {
750 domain = ud.authority;
751 if (!ud.path.empty())
752 {
753 libvirt_uri = ud.path.substr(1);
754 if (ud.has_query)
755 libvirt_uri += "?" + ud.query;
756 }
757 }
758 else // second form
759 {
760 if (ud.path.empty())
761 throw runtime_error(_("libvirt target URI requires a domain name"));
762
763 size_t slash = ud.path.find_first_of('/');
764 if (slash == string::npos)
765 domain = ud.path;
766 else
767 {
768 domain = ud.path.substr(0, slash);
769 libvirt_uri = ud.path.substr(slash+1);
770 }
771 }
772
773 int in, out;
774 vector<string> cmd;
775
776 string stapvirt = BINDIR "/stapvirt";
777 if (!file_exists(stapvirt))
778 throw runtime_error(_("stapvirt missing"));
779
780 cmd.push_back(stapvirt);
781
782 // carry verbosity into stapvirt
783 for (unsigned i = 1; i < s.perpass_verbose[4]; i++)
784 cmd.push_back("-v");
785
786 if (!libvirt_uri.empty())
787 {
788 cmd.push_back("-c");
789 cmd.push_back(libvirt_uri);
790 }
791
792 cmd.push_back("connect");
793 cmd.push_back(domain);
794
795 // mask signals for stapvirt since it relies instead on stap or stapsh
796 // closing its connection to know when to exit (otherwise, stapvirt
797 // would receive SIGINT on Ctrl-C and exit nonzero)
798
799 // NB: There is an interesting issue to note here. Some libvirt URIs
800 // will create child processes of stapvirt (e.g. qemu+ssh:// will
801 // create an ssh process under stapvirt, also qemu+ext://, see
802 // <libvirt.org/remote.html> for more info). These processes do not
803 // keep the mask of stapvirt we are creating here, but are still part
804 // of the same process group.
805 // This means that e.g. SIGINT will not be blocked. Thus, stapvirt
806 // explicitly adds a handler for SIGCHLD and forcefully disconnects
807 // upon receiving it (otherwise it would await indefinitely).
808 {
809 stap_sigmasker masked;
810 child = stap_spawn_piped(s.verbose, cmd, &in, &out);
811 }
812
813 if (child <= 0)
814 throw runtime_error(_("error launching stapvirt"));
815
816 try
817 {
818 set_child_fds(in, out);
819 }
820 catch (runtime_error&)
821 {
822 finish();
823 throw;
824 }
825 }
826
827 int finish()
828 {
829 int rc = stapsh::finish();
830 if (child <= 0)
831 return rc;
832
833 int rc2 = stap_waitpid(s->verbose, child);
834 child = 0;
835 return rc ?: rc2;
836 }
837
838 public:
839 friend class remote;
840
841 virtual ~libvirt_stapsh() { finish(); }
842};
e7e8737f 843
8d1a21b3 844// stapsh-based ssh_remote
e96f2257
JS
845class ssh_remote : public stapsh {
846 private:
847 pid_t child;
848
8d1a21b3 849 ssh_remote(systemtap_session& s): stapsh(s), child(0) {}
e96f2257 850
eacb2780 851 int connect(const string& host, const string& port)
e96f2257 852 {
8d1a21b3
JS
853 int rc = 0;
854 int in, out;
855 vector<string> cmd;
856 cmd.push_back("ssh");
857 cmd.push_back("-q");
858 cmd.push_back(host);
eacb2780
JS
859 if (!port.empty())
860 {
861 cmd.push_back("-p");
862 cmd.push_back(port);
863 }
8d1a21b3
JS
864
865 // This is crafted so that we get a silent failure with status 127 if
866 // the command is not found. The combination of -P and $cmd ensures
867 // that we pull the command out of the PATH, not aliases or such.
75daad84 868 string stapsh_cmd = "cmd=`type -P stapsh || exit 127` && exec \"$cmd\"";
8d1a21b3 869 if (s->perpass_verbose[4] > 1)
75daad84 870 stapsh_cmd.append(" -v");
8d1a21b3 871 if (s->perpass_verbose[4] > 2)
75daad84
JS
872 stapsh_cmd.append(" -v");
873 // NB: We need to explicitly choose bash, as $SHELL may be weird...
874 cmd.push_back("/bin/bash -c '" + stapsh_cmd + "'");
e96f2257 875
e96f2257
JS
876 // mask signals while we spawn, so we can manually send even tty
877 // signals *through* ssh rather than to ssh itself
a69b4b55
JS
878 {
879 stap_sigmasker masked;
880 child = stap_spawn_piped(s->verbose, cmd, &in, &out);
881 }
8d1a21b3 882
e96f2257 883 if (child <= 0)
8d1a21b3 884 throw runtime_error(_("error launching stapsh"));
e96f2257
JS
885
886 try
887 {
888 set_child_fds(in, out);
889 }
890 catch (runtime_error&)
891 {
c00db46e
JS
892 rc = finish();
893
894 // ssh itself signals errors with 255
895 if (rc == 255)
896 throw runtime_error(_("error establishing ssh connection"));
897
8d1a21b3
JS
898 // If rc == 127, that's command-not-found, so we let ::create()
899 // try again in legacy mode. But only do this if there's a single
900 // remote, as the old code didn't handle ttys well with multiple
901 // remotes. Otherwise, throw up again. *barf*
8d1a21b3
JS
902 if (rc != 127 || s->remote_uris.size() > 1)
903 throw;
e96f2257 904 }
8d1a21b3
JS
905
906 return rc;
e96f2257
JS
907 }
908
909 int finish()
910 {
911 int rc = stapsh::finish();
912 if (child <= 0)
913 return rc;
914
915 int rc2 = stap_waitpid(s->verbose, child);
916 child = 0;
917 return rc ?: rc2;
918 }
919
920 public:
8d1a21b3
JS
921
922 static remote* create(systemtap_session& s, const string& host);
923 static remote* create(systemtap_session& s, const uri_decoder& ud);
e96f2257 924
10a5bce5 925 virtual ~ssh_remote() { finish(); }
e96f2257 926};
8d1a21b3
JS
927
928
929// ssh connection without stapsh, for legacy stap installations
930// NB: ssh commands use a tty (-t) so signals are passed along to the remote.
931// It does this by putting the local tty in raw mode, so it only works for tty
932// signals, and only for a single remote at a time.
933class ssh_legacy_remote : public remote {
daa75206 934 private:
20f90026
JS
935 vector<string> ssh_args, scp_args;
936 string ssh_control;
eacb2780 937 string host, port, tmpdir;
ebff2ed0
JS
938 pid_t child;
939
eacb2780
JS
940 ssh_legacy_remote(systemtap_session& s, const string& host, const string& port)
941 : remote(s), host(host), port(port), child(0)
1544bdf9 942 {
4c156a0e 943 open_control_master();
1544bdf9
JS
944 try
945 {
946 get_uname();
947 }
948 catch (runtime_error&)
949 {
950 close_control_master();
951 throw;
952 }
91fa953d
JS
953 }
954
4c156a0e
JS
955 void open_control_master()
956 {
957 static unsigned index = 0;
958
959 if (s->tmpdir.empty()) // sanity check, shouldn't happen
58a834b1 960 throw runtime_error(_("No tmpdir available for ssh control master"));
4c156a0e
JS
961
962 ssh_control = s->tmpdir + "/ssh_remote_control_" + lex_cast(++index);
20f90026
JS
963
964 scp_args.clear();
965 scp_args.push_back("scp");
966 scp_args.push_back("-q");
967 scp_args.push_back("-o");
968 scp_args.push_back("ControlPath=" + ssh_control);
969
970 ssh_args = scp_args;
971 ssh_args[0] = "ssh";
972 ssh_args.push_back(host);
4c156a0e 973
eacb2780
JS
974 if (!port.empty())
975 {
976 scp_args.push_back("-P");
977 scp_args.push_back(port);
978 ssh_args.push_back("-p");
979 ssh_args.push_back(port);
980 }
981
4c156a0e
JS
982 // NB: ssh -f will stay in the foreground until authentication is
983 // complete and the control socket is created, so we know it's ready to
984 // go when stap_system returns.
20f90026
JS
985 vector<string> cmd = ssh_args;
986 cmd.push_back("-f");
987 cmd.push_back("-N");
988 cmd.push_back("-M");
4c156a0e
JS
989 int rc = stap_system(s->verbose, cmd);
990 if (rc != 0)
8d1a21b3
JS
991 throw runtime_error(_F("failed to create an ssh control master for %s : rc= %d",
992 host.c_str(), rc));
4c156a0e
JS
993
994 if (s->verbose>1)
58a834b1
LB
995 clog << _F("Created ssh control master at %s",
996 lex_cast_qstring(ssh_control).c_str()) << endl;
4c156a0e
JS
997 }
998
999 void close_control_master()
1000 {
1001 if (ssh_control.empty())
1002 return;
1003
20f90026
JS
1004 vector<string> cmd = ssh_args;
1005 cmd.push_back("-O");
1006 cmd.push_back("exit");
2323da5e 1007 int rc = stap_system(s->verbose, cmd, true, true);
4c156a0e 1008 if (rc != 0)
58a834b1
LB
1009 cerr << _F("failed to stop the ssh control master for %s : rc=%d",
1010 host.c_str(), rc) << endl;
4c156a0e
JS
1011
1012 ssh_control.clear();
20f90026
JS
1013 scp_args.clear();
1014 ssh_args.clear();
4c156a0e
JS
1015 }
1016
91fa953d 1017 void get_uname()
daa75206
JS
1018 {
1019 ostringstream out;
1020 vector<string> uname;
20f90026
JS
1021 vector<string> cmd = ssh_args;
1022 cmd.push_back("-t");
1023 cmd.push_back("uname -rm");
1024 int rc = stap_system_read(s->verbose, cmd, out);
daa75206
JS
1025 if (rc == 0)
1026 tokenize(out.str(), uname, " \t\r\n");
1027 if (uname.size() != 2)
58a834b1 1028 throw runtime_error(_F("failed to get uname from %s : rc= %d", host.c_str(), rc));
4c156a0e
JS
1029 const string& release = uname[0];
1030 const string& arch = uname[1];
1031 // XXX need to deal with command-line vs. implied arch/release
1032 this->s = s->clone(arch, release);
daa75206
JS
1033 }
1034
ebff2ed0 1035 int start()
daa75206
JS
1036 {
1037 int rc;
ebff2ed0
JS
1038 string localmodule = s->tmpdir + "/" + s->module_name + ".ko";
1039 string tmpmodule;
daa75206
JS
1040
1041 // Make a remote tempdir.
1042 {
1043 ostringstream out;
1044 vector<string> vout;
20f90026
JS
1045 vector<string> cmd = ssh_args;
1046 cmd.push_back("-t");
1047 cmd.push_back("mktemp -d -t stapXXXXXX");
ebff2ed0 1048 rc = stap_system_read(s->verbose, cmd, out);
daa75206 1049 if (rc == 0)
b2cbfd40 1050 tokenize(out.str(), vout, "\r\n");
daa75206
JS
1051 if (vout.size() != 1)
1052 {
58a834b1
LB
1053 cerr << _F("failed to make a tempdir on %s : rc=%d",
1054 host.c_str(), rc) << endl;
daa75206
JS
1055 return -1;
1056 }
1057 tmpdir = vout[0];
ebff2ed0 1058 tmpmodule = tmpdir + "/" + s->module_name + ".ko";
daa75206
JS
1059 }
1060
18630fb8
JS
1061 // Transfer the module.
1062 if (rc == 0)
1063 {
1064 vector<string> cmd = scp_args;
1065 cmd.push_back(localmodule);
1066 cmd.push_back(host + ":" + tmpmodule);
1067 rc = stap_system(s->verbose, cmd);
1068 if (rc != 0)
1069 cerr << _F("failed to copy the module to %s : rc=%d",
1070 host.c_str(), rc) << endl;
1071 }
1072
1073 // Transfer the module signature.
1074 if (rc == 0 && file_exists(localmodule + ".sgn"))
1075 {
1076 vector<string> cmd = scp_args;
1077 cmd.push_back(localmodule + ".sgn");
1078 cmd.push_back(host + ":" + tmpmodule + ".sgn");
1079 rc = stap_system(s->verbose, cmd);
1080 if (rc != 0)
1081 cerr << _F("failed to copy the module signature to %s : rc=%d",
1082 host.c_str(), rc) << endl;
1083 }
1084
1085 // What about transfering uprobes.ko? In this ssh "legacy" mode, we
1086 // don't the remote systemtap version, but -uPATH wasn't added until
1087 // 1.4. Rather than risking a getopt error, we'll just assume that
1088 // this isn't supported. The remote will just have to provide its own
1089 // uprobes.ko in SYSTEMTAP_RUNTIME or already loaded.
daa75206
JS
1090
1091 // Run the module on the remote.
1092 if (rc == 0) {
20f90026
JS
1093 vector<string> cmd = ssh_args;
1094 cmd.push_back("-t");
4112f219 1095 // We don't know the actual version, but all <=1.3 are approx equal.
18630fb8
JS
1096 vector<string> staprun_cmd = make_run_command(*s, tmpdir, "1.3");
1097 staprun_cmd[0] = "staprun"; // NB: The remote decides its own path
5137a7a9
FCE
1098 // NB: PR13354: we assume legacy installations don't have
1099 // staprun -r support, so we ignore staprun_r_arg.
18630fb8 1100 cmd.push_back(cmdstr_join(staprun_cmd));
ebff2ed0
JS
1101 pid_t pid = stap_spawn(s->verbose, cmd);
1102 if (pid > 0)
4c156a0e
JS
1103 child = pid;
1104 else
1105 {
58a834b1
LB
1106 cerr << _F("failed to run the module on %s : ret=%d",
1107 host.c_str(), pid) << endl;
4c156a0e
JS
1108 rc = -1;
1109 }
daa75206
JS
1110 }
1111
4c156a0e 1112 return rc;
ebff2ed0 1113 }
daa75206 1114
ebff2ed0
JS
1115 int finish()
1116 {
4c156a0e 1117 int rc = 0;
ebff2ed0 1118
4c156a0e
JS
1119 if (child > 0)
1120 {
1121 rc = stap_waitpid(s->verbose, child);
1122 child = 0;
1123 }
ebff2ed0
JS
1124
1125 if (!tmpdir.empty())
4c156a0e
JS
1126 {
1127 // Remove the tempdir.
1128 // XXX need to make sure this runs even with e.g. CTRL-C exits
20f90026
JS
1129 vector<string> cmd = ssh_args;
1130 cmd.push_back("-t");
1131 cmd.push_back("rm -r " + cmdstr_quoted(tmpdir));
4c156a0e
JS
1132 int rc2 = stap_system(s->verbose, cmd);
1133 if (rc2 != 0)
58a834b1
LB
1134 cerr << _F("failed to delete the tempdir on %s : rc=%d",
1135 host.c_str(), rc2) << endl;
4c156a0e
JS
1136 if (rc == 0)
1137 rc = rc2;
20f90026 1138 tmpdir.clear();
4c156a0e
JS
1139 }
1140
1141 close_control_master();
1142
1143 return rc;
daa75206 1144 }
4f244435
JS
1145
1146 public:
8d1a21b3 1147 friend class ssh_remote;
4f244435 1148
8d1a21b3 1149 virtual ~ssh_legacy_remote()
4f244435
JS
1150 {
1151 close_control_master();
1152 }
daa75206 1153};
8d1a21b3
JS
1154
1155
1156// Try to establish a stapsh connection to the remote, but fallback
1157// to the older mechanism if the command is not found.
1158remote*
eacb2780 1159ssh_remote::create(systemtap_session& s, const string& target)
8d1a21b3 1160{
eacb2780
JS
1161 string port, host = target;
1162 size_t i = host.find(':');
1163 if (i != string::npos)
1164 {
1165 port = host.substr(i + 1);
1166 host.erase(i);
1167 }
1168
8d1a21b3 1169 auto_ptr<ssh_remote> p (new ssh_remote(s));
eacb2780 1170 int rc = p->connect(host, port);
8d1a21b3
JS
1171 if (rc == 0)
1172 return p.release();
1173 else if (rc == 127) // stapsh command not found
eacb2780 1174 return new ssh_legacy_remote(s, host, port); // try legacy instead
8d1a21b3
JS
1175 return NULL;
1176}
1177
1178remote*
1179ssh_remote::create(systemtap_session& s, const uri_decoder& ud)
1180{
1181 if (!ud.has_authority || ud.authority.empty())
1182 throw runtime_error(_("ssh target requires a hostname"));
1183 if (!ud.path.empty() && ud.path != "/")
1184 throw runtime_error(_("ssh target URI doesn't support a /path"));
1185 if (ud.has_query)
1186 throw runtime_error(_("ssh target URI doesn't support a ?query"));
1187 if (ud.has_fragment)
1188 throw runtime_error(_("ssh target URI doesn't support a #fragment"));
1189
1190 return create(s, ud.authority);
1191}
daa75206
JS
1192
1193
1194remote*
5137a7a9 1195remote::create(systemtap_session& s, const string& uri, int idx)
daa75206 1196{
5137a7a9 1197 remote *it = 0;
daa75206
JS
1198 try
1199 {
2bce499b 1200 if (uri.find(':') != string::npos)
91fa953d
JS
1201 {
1202 const uri_decoder ud(uri);
eacb2780
JS
1203
1204 // An ssh "host:port" is ambiguous with a URI "scheme:path".
1205 // So if it looks like a number, just assume ssh.
1206 if (!ud.has_authority && !ud.has_query &&
1207 !ud.has_fragment && !ud.path.empty() &&
1208 ud.path.find_first_not_of("1234567890") == string::npos)
5137a7a9
FCE
1209 it = ssh_remote::create(s, uri);
1210 else if (ud.scheme == "direct")
1211 it = new direct(s);
2bce499b 1212 else if (ud.scheme == "stapsh")
5137a7a9 1213 it = new direct_stapsh(s);
e7e8737f 1214 else if (ud.scheme == "unix")
5137a7a9 1215 it = new unix_stapsh(s, ud);
2246b6d3
JL
1216 else if (ud.scheme == "libvirt")
1217 it = new libvirt_stapsh(s, ud);
5137a7a9
FCE
1218 else if (ud.scheme == "ssh")
1219 it = ssh_remote::create(s, ud);
91fa953d 1220 else
8d1a21b3
JS
1221 throw runtime_error(_F("unrecognized URI scheme '%s' in remote: %s",
1222 ud.scheme.c_str(), uri.c_str()));
91fa953d 1223 }
daa75206
JS
1224 else
1225 // XXX assuming everything else is ssh for now...
5137a7a9 1226 it = ssh_remote::create(s, uri);
daa75206
JS
1227 }
1228 catch (std::runtime_error& e)
1229 {
8d1a21b3 1230 cerr << e.what() << " on remote '" << uri << "'" << endl;
5137a7a9 1231 it = 0;
daa75206 1232 }
5137a7a9
FCE
1233
1234 if (it && idx >= 0) // PR13354: remote metadata for staprun -r IDX:URI
1235 {
1236 stringstream r_arg;
1237 r_arg << idx << ":" << uri;
1238 it->staprun_r_arg = r_arg.str();
1239 }
1240
1241 return it;
daa75206
JS
1242}
1243
4f244435
JS
1244int
1245remote::run(const vector<remote*>& remotes)
1246{
1247 // NB: the first failure "wins"
1248 int ret = 0, rc = 0;
1249
1250 for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i)
3fe776ae 1251 {
756cfd49
JS
1252 remote *r = remotes[i];
1253 r->s->verbose = r->s->perpass_verbose[4];
1254 if (r->s->use_remote_prefix)
1255 r->prefix = lex_cast(i) + ": ";
1256 rc = r->prepare();
3fe776ae
JS
1257 if (rc)
1258 return rc;
1259 }
1260
1261 for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i)
4f244435
JS
1262 {
1263 rc = remotes[i]->start();
1264 if (!ret)
1265 ret = rc;
1266 }
1267
e96f2257 1268 // mask signals while we're preparing to poll
a69b4b55
JS
1269 {
1270 stap_sigmasker masked;
e96f2257 1271
a69b4b55
JS
1272 // polling loop for remotes that have fds to watch
1273 for (;;)
1274 {
1275 vector<pollfd> fds;
1276 for (unsigned i = 0; i < remotes.size(); ++i)
1277 remotes[i]->prepare_poll (fds);
1278 if (fds.empty())
1279 break;
1280
1281 rc = ppoll (&fds[0], fds.size(), NULL, &masked.old);
1282 if (rc < 0 && errno != EINTR)
1283 break;
1284
1285 for (unsigned i = 0; i < remotes.size(); ++i)
1286 remotes[i]->handle_poll (fds);
1287 }
1288 }
e96f2257 1289
4f244435
JS
1290 for (unsigned i = 0; i < remotes.size(); ++i)
1291 {
1292 rc = remotes[i]->finish();
1293 if (!ret)
1294 ret = rc;
1295 }
1296
1297 return ret;
1298}
1299
daa75206
JS
1300
1301/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.217092 seconds and 5 git commands to generate.