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