]> sourceware.org Git - systemtap.git/blob - remote.cxx
copyright year bump for translator
[systemtap.git] / remote.cxx
1 // systemtap remote execution
2 // Copyright (C) 2010-2011 Red Hat Inc.
3 //
4 // This file is part of systemtap, and is free software. You can
5 // redistribute it and/or modify it under the terms of the GNU General
6 // Public License (GPL); either version 2, or (at your option) any
7 // later version.
8
9 #include "config.h"
10
11 extern "C" {
12 #include <fcntl.h>
13 #include <poll.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17 }
18
19 #include <cstdio>
20 #include <iomanip>
21 #include <memory>
22 #include <stdexcept>
23 #include <sstream>
24 #include <string>
25 #include <vector>
26
27 #include "buildrun.h"
28 #include "remote.h"
29 #include "util.h"
30
31 using namespace std;
32
33 // Decode URIs as per RFC 3986, though not bothering to be strict
34 class uri_decoder {
35 public:
36 const string uri;
37 string scheme, authority, path, query, fragment;
38 bool has_authority, has_query, has_fragment;
39
40 uri_decoder(const string& uri):
41 uri(uri), has_authority(false), has_query(false), has_fragment(false)
42 {
43 const string re =
44 "^([^:]+):(//[^/?#]*)?([^?#]*)(\\?[^#]*)?(#.*)?$";
45
46 vector<string> matches;
47 if (regexp_match(uri, re, matches) != 0)
48 throw runtime_error(_F("string doesn't appear to be a URI: %s", uri.c_str()));
49
50 scheme = matches[1];
51
52 if (!matches[2].empty())
53 {
54 has_authority = true;
55 authority = matches[2].substr(2);
56 }
57
58 path = matches[3];
59
60 if (!matches[4].empty())
61 {
62 has_query = true;
63 query = matches[4].substr(1);
64 }
65
66 if (!matches[5].empty())
67 {
68 has_fragment = true;
69 fragment = matches[5].substr(1);
70 }
71 }
72 };
73
74
75 // loopback target for running locally
76 class direct : public remote {
77 private:
78 pid_t child;
79 vector<string> args;
80 direct(systemtap_session& s): remote(s), child(0) {}
81
82 int start()
83 {
84 args = make_run_command(*s);
85 pid_t pid = stap_spawn (s->verbose, args);
86 if (pid <= 0)
87 return 1;
88 child = pid;
89 return 0;
90 }
91
92 int finish()
93 {
94 if (child <= 0)
95 return 1;
96
97 int ret = stap_waitpid(s->verbose, child);
98 if(ret)
99 clog << _F("Warning: %s exited with status: %d", args.front().c_str(), ret) << endl;
100 child = 0;
101 return ret;
102 }
103
104 public:
105 friend class remote;
106
107 virtual ~direct() { finish(); }
108 };
109
110
111 class stapsh : public remote {
112 private:
113 int interrupts_sent;
114 int fdin, fdout;
115 FILE *IN, *OUT;
116 string remote_version;
117
118 virtual void prepare_poll(vector<pollfd>& fds)
119 {
120 if (fdout >= 0 && OUT)
121 {
122 pollfd p = { fdout, POLLIN, 0 };
123 fds.push_back(p);
124 }
125
126 // need to send a signal?
127 if (fdin >= 0 && IN && interrupts_sent < pending_interrupts)
128 {
129 pollfd p = { fdin, POLLOUT, 0 };
130 fds.push_back(p);
131 }
132 }
133
134 virtual void handle_poll(vector<pollfd>& fds)
135 {
136 for (unsigned i=0; i < fds.size(); ++i)
137 if (fds[i].revents)
138 {
139 if (fdout >= 0 && OUT && fds[i].fd == fdout)
140 {
141 if (fds[i].revents & POLLIN)
142 {
143 char buf[4096];
144 if (!prefix.empty())
145 {
146 // If we have a line prefix, then read lines one at a
147 // time and copy out with the prefix.
148 errno = 0;
149 while (fgets(buf, sizeof(buf), OUT))
150 cout << prefix << buf;
151 if (errno == EAGAIN)
152 continue;
153 }
154 else
155 {
156 // Otherwise read an entire block of data at once.
157 size_t rc = fread(buf, 1, sizeof(buf), OUT);
158 if (rc > 0)
159 {
160 // NB: The buf could contain binary data,
161 // including \0, so write as a block instead of
162 // the usual <<string.
163 cout.write(buf, rc);
164 continue;
165 }
166 }
167 }
168 close();
169 }
170
171 // need to send a signal?
172 if (fdin >= 0 && IN && fds[i].fd == fdin &&
173 interrupts_sent < pending_interrupts)
174 {
175 if (fds[i].revents & POLLOUT)
176 {
177 if (send_command("quit\n") == 0)
178 {
179 ++interrupts_sent;
180 continue;
181 }
182 }
183 close();
184 }
185 }
186 }
187
188 string get_reply()
189 {
190 char reply[4096];
191 if (!fgets(reply, sizeof(reply), OUT))
192 reply[0] = '\0';
193 return reply;
194 }
195
196 int send_command(const string& cmd)
197 {
198 if (!IN)
199 return 2;
200 if (fputs(cmd.c_str(), IN) < 0 ||
201 fflush(IN) != 0)
202 return 1;
203 return 0;
204 }
205
206 int send_file(const string& filename, const string& dest)
207 {
208 int rc = 0;
209 FILE* f = fopen(filename.c_str(), "r");
210 if (!f)
211 return 1;
212
213 struct stat fs;
214 rc = fstat(fileno(f), &fs);
215 if (!rc)
216 {
217 ostringstream cmd;
218 cmd << "file " << fs.st_size << " " << dest << "\n";
219 rc = send_command(cmd.str());
220 }
221
222 off_t i = 0;
223 while (!rc && i < fs.st_size)
224 {
225 char buf[4096];
226 size_t r = sizeof(buf);
227 if (fs.st_size - i < (off_t)r)
228 r = fs.st_size - i;
229 r = fread(buf, 1, r, f);
230 if (r == 0)
231 rc = 1;
232 else
233 {
234 size_t w = fwrite(buf, 1, r, IN);
235 if (w != r)
236 rc = 1;
237 else
238 i += w;
239 }
240 }
241 if (!rc)
242 rc = fflush(IN);
243
244 fclose(f);
245
246 if (!rc)
247 {
248 string reply = get_reply();
249 if (reply != "OK\n")
250 {
251 rc = 1;
252 if (s->verbose > 0)
253 {
254 if (reply.empty())
255 clog << _("stapsh file ERROR: no reply") << endl;
256 else
257 clog << _F("stapsh file replied %s", reply.c_str());
258 }
259 }
260 }
261
262 return rc;
263 }
264
265 static string qpencode(const string& str)
266 {
267 ostringstream o;
268 o << setfill('0') << hex;
269 for (const char* s = str.c_str(); *s; ++s)
270 if (*s >= 33 && *s <= 126 && *s != 61)
271 o << *s;
272 else
273 o << '=' << setw(2) << (unsigned)(unsigned char) *s;
274 return o.str();
275 }
276
277 protected:
278 stapsh(systemtap_session& s)
279 : remote(s), interrupts_sent(0),
280 fdin(-1), fdout(-1), IN(0), OUT(0)
281 {}
282
283 virtual int prepare()
284 {
285 int rc = 0;
286
287 string localmodule = s->tmpdir + "/" + s->module_name + ".ko";
288 string remotemodule = s->module_name + ".ko";
289 if ((rc = send_file(localmodule, remotemodule)))
290 return rc;
291
292 if (file_exists(localmodule + ".sgn") &&
293 (rc = send_file(localmodule + ".sgn", remotemodule + ".sgn")))
294 return rc;
295
296 if (!s->uprobes_path.empty())
297 {
298 string remoteuprobes = basename(s->uprobes_path.c_str());
299 if ((rc = send_file(s->uprobes_path, remoteuprobes)))
300 return rc;
301
302 if (file_exists(s->uprobes_path + ".sgn") &&
303 (rc = send_file(s->uprobes_path + ".sgn", remoteuprobes + ".sgn")))
304 return rc;
305 }
306
307 return rc;
308 }
309
310 virtual int start()
311 {
312 // Send the staprun args
313 // NB: The remote is left to decide its own staprun path
314 ostringstream run("run", ios::out | ios::ate);
315 vector<string> cmd = make_run_command(*s, s->module_name + ".ko",
316 remote_version);
317 for (unsigned i = 1; i < cmd.size(); ++i)
318 run << ' ' << qpencode(cmd[i]);
319 run << '\n';
320
321 int rc = send_command(run.str());
322
323 if (!rc)
324 {
325 string reply = get_reply();
326 if (reply != "OK\n")
327 {
328 rc = 1;
329 if (s->verbose > 0)
330 {
331 if (reply.empty())
332 clog << _("stapsh run ERROR: no reply") << endl;
333 else
334 clog << _F("stapsh run replied %s", reply.c_str());
335 }
336 }
337 }
338
339 if (!rc)
340 {
341 long flags = fcntl(fdout, F_GETFL) | O_NONBLOCK;
342 fcntl(fdout, F_SETFL, flags);
343 }
344 else
345 // If run failed for any reason, then this
346 // connection is effectively dead to us.
347 close();
348
349 return rc;
350 }
351
352 void close()
353 {
354 if (IN) fclose(IN);
355 if (OUT) fclose(OUT);
356 IN = OUT = NULL;
357 fdin = fdout = -1;
358 }
359
360 virtual int finish()
361 {
362 close();
363 return 0;
364 }
365
366 void set_child_fds(int in, int out)
367 {
368 if (fdin >= 0 || fdout >= 0 || IN || OUT)
369 throw runtime_error(_("stapsh file descriptors already set"));
370
371 fdin = in;
372 fdout = out;
373 IN = fdopen(fdin, "w");
374 OUT = fdopen(fdout, "r");
375 if (!IN || !OUT)
376 throw runtime_error(_("invalid file descriptors for stapsh"));
377
378 if (send_command("stap " VERSION "\n"))
379 throw runtime_error(_("error sending hello to stapsh"));
380
381 string reply = get_reply();
382 if (reply.empty())
383 throw runtime_error(_("error receiving hello from stapsh"));
384
385 // stapsh VERSION MACHINE RELEASE
386 vector<string> uname;
387 tokenize(reply, uname, " \t\r\n");
388 if (uname.size() != 4 || uname[0] != "stapsh")
389 throw runtime_error(_("failed to get uname from stapsh"));
390
391 // We assume that later versions will know how to talk to us.
392 // Looking backward, we use this for make_run_command().
393 this->remote_version = uname[1];
394
395 this->s = s->clone(uname[2], uname[3]);
396 }
397
398 public:
399 virtual ~stapsh() { close(); }
400 };
401
402
403 // direct_stapsh is meant only for testing, as a way to exercise the stapsh
404 // mechanisms without requiring test machines to have actual remote access.
405 class direct_stapsh : public stapsh {
406 private:
407 pid_t child;
408
409 direct_stapsh(systemtap_session& s)
410 : stapsh(s), child(0)
411 {
412 int in, out;
413 vector<string> cmd;
414 cmd.push_back(BINDIR "/stapsh");
415 if (s.perpass_verbose[4] > 1)
416 cmd.push_back("-v");
417 if (s.perpass_verbose[4] > 2)
418 cmd.push_back("-v");
419
420 // mask signals while we spawn, so we can simulate manual signals to
421 // the "remote" target, as we must for the real ssh_remote case.
422 {
423 stap_sigmasker masked;
424 child = stap_spawn_piped(s.verbose, cmd, &in, &out);
425 }
426
427 if (child <= 0)
428 throw runtime_error(_("error launching stapsh"));
429
430 try
431 {
432 set_child_fds(in, out);
433 }
434 catch (runtime_error&)
435 {
436 finish();
437 throw;
438 }
439 }
440
441 virtual int finish()
442 {
443 int rc = stapsh::finish();
444 if (child <= 0)
445 return rc;
446
447 int rc2 = stap_waitpid(s->verbose, child);
448 child = 0;
449 return rc ?: rc2;
450 }
451
452 public:
453 friend class remote;
454
455 virtual ~direct_stapsh() { finish(); }
456 };
457
458
459 // stapsh-based ssh_remote
460 class ssh_remote : public stapsh {
461 private:
462 pid_t child;
463
464 ssh_remote(systemtap_session& s): stapsh(s), child(0) {}
465
466 int connect(const string& host, const string& port)
467 {
468 int rc = 0;
469 int in, out;
470 vector<string> cmd;
471 cmd.push_back("ssh");
472 cmd.push_back("-q");
473 cmd.push_back(host);
474 if (!port.empty())
475 {
476 cmd.push_back("-p");
477 cmd.push_back(port);
478 }
479
480 // This is crafted so that we get a silent failure with status 127 if
481 // the command is not found. The combination of -P and $cmd ensures
482 // that we pull the command out of the PATH, not aliases or such.
483 string stapsh_cmd = "cmd=`type -P stapsh || exit 127` && exec \"$cmd\"";
484 if (s->perpass_verbose[4] > 1)
485 stapsh_cmd.append(" -v");
486 if (s->perpass_verbose[4] > 2)
487 stapsh_cmd.append(" -v");
488 // NB: We need to explicitly choose bash, as $SHELL may be weird...
489 cmd.push_back("/bin/bash -c '" + stapsh_cmd + "'");
490
491 // mask signals while we spawn, so we can manually send even tty
492 // signals *through* ssh rather than to ssh itself
493 {
494 stap_sigmasker masked;
495 child = stap_spawn_piped(s->verbose, cmd, &in, &out);
496 }
497
498 if (child <= 0)
499 throw runtime_error(_("error launching stapsh"));
500
501 try
502 {
503 set_child_fds(in, out);
504 }
505 catch (runtime_error&)
506 {
507 rc = finish();
508
509 // ssh itself signals errors with 255
510 if (rc == 255)
511 throw runtime_error(_("error establishing ssh connection"));
512
513 // If rc == 127, that's command-not-found, so we let ::create()
514 // try again in legacy mode. But only do this if there's a single
515 // remote, as the old code didn't handle ttys well with multiple
516 // remotes. Otherwise, throw up again. *barf*
517 if (rc != 127 || s->remote_uris.size() > 1)
518 throw;
519 }
520
521 return rc;
522 }
523
524 int finish()
525 {
526 int rc = stapsh::finish();
527 if (child <= 0)
528 return rc;
529
530 int rc2 = stap_waitpid(s->verbose, child);
531 child = 0;
532 return rc ?: rc2;
533 }
534
535 public:
536
537 static remote* create(systemtap_session& s, const string& host);
538 static remote* create(systemtap_session& s, const uri_decoder& ud);
539
540 virtual ~ssh_remote() { finish(); }
541 };
542
543
544 // ssh connection without stapsh, for legacy stap installations
545 // NB: ssh commands use a tty (-t) so signals are passed along to the remote.
546 // It does this by putting the local tty in raw mode, so it only works for tty
547 // signals, and only for a single remote at a time.
548 class ssh_legacy_remote : public remote {
549 private:
550 vector<string> ssh_args, scp_args;
551 string ssh_control;
552 string host, port, tmpdir;
553 pid_t child;
554
555 ssh_legacy_remote(systemtap_session& s, const string& host, const string& port)
556 : remote(s), host(host), port(port), child(0)
557 {
558 open_control_master();
559 try
560 {
561 get_uname();
562 }
563 catch (runtime_error&)
564 {
565 close_control_master();
566 throw;
567 }
568 }
569
570 void open_control_master()
571 {
572 static unsigned index = 0;
573
574 if (s->tmpdir.empty()) // sanity check, shouldn't happen
575 throw runtime_error(_("No tmpdir available for ssh control master"));
576
577 ssh_control = s->tmpdir + "/ssh_remote_control_" + lex_cast(++index);
578
579 scp_args.clear();
580 scp_args.push_back("scp");
581 scp_args.push_back("-q");
582 scp_args.push_back("-o");
583 scp_args.push_back("ControlPath=" + ssh_control);
584
585 ssh_args = scp_args;
586 ssh_args[0] = "ssh";
587 ssh_args.push_back(host);
588
589 if (!port.empty())
590 {
591 scp_args.push_back("-P");
592 scp_args.push_back(port);
593 ssh_args.push_back("-p");
594 ssh_args.push_back(port);
595 }
596
597 // NB: ssh -f will stay in the foreground until authentication is
598 // complete and the control socket is created, so we know it's ready to
599 // go when stap_system returns.
600 vector<string> cmd = ssh_args;
601 cmd.push_back("-f");
602 cmd.push_back("-N");
603 cmd.push_back("-M");
604 int rc = stap_system(s->verbose, cmd);
605 if (rc != 0)
606 throw runtime_error(_F("failed to create an ssh control master for %s : rc= %d",
607 host.c_str(), rc));
608
609 if (s->verbose>1)
610 clog << _F("Created ssh control master at %s",
611 lex_cast_qstring(ssh_control).c_str()) << endl;
612 }
613
614 void close_control_master()
615 {
616 if (ssh_control.empty())
617 return;
618
619 vector<string> cmd = ssh_args;
620 cmd.push_back("-O");
621 cmd.push_back("exit");
622 int rc = stap_system(s->verbose, cmd, true, true);
623 if (rc != 0)
624 cerr << _F("failed to stop the ssh control master for %s : rc=%d",
625 host.c_str(), rc) << endl;
626
627 ssh_control.clear();
628 scp_args.clear();
629 ssh_args.clear();
630 }
631
632 void get_uname()
633 {
634 ostringstream out;
635 vector<string> uname;
636 vector<string> cmd = ssh_args;
637 cmd.push_back("-t");
638 cmd.push_back("uname -rm");
639 int rc = stap_system_read(s->verbose, cmd, out);
640 if (rc == 0)
641 tokenize(out.str(), uname, " \t\r\n");
642 if (uname.size() != 2)
643 throw runtime_error(_F("failed to get uname from %s : rc= %d", host.c_str(), rc));
644 const string& release = uname[0];
645 const string& arch = uname[1];
646 // XXX need to deal with command-line vs. implied arch/release
647 this->s = s->clone(arch, release);
648 }
649
650 int start()
651 {
652 int rc;
653 string localmodule = s->tmpdir + "/" + s->module_name + ".ko";
654 string tmpmodule;
655
656 // Make a remote tempdir.
657 {
658 ostringstream out;
659 vector<string> vout;
660 vector<string> cmd = ssh_args;
661 cmd.push_back("-t");
662 cmd.push_back("mktemp -d -t stapXXXXXX");
663 rc = stap_system_read(s->verbose, cmd, out);
664 if (rc == 0)
665 tokenize(out.str(), vout, "\r\n");
666 if (vout.size() != 1)
667 {
668 cerr << _F("failed to make a tempdir on %s : rc=%d",
669 host.c_str(), rc) << endl;
670 return -1;
671 }
672 tmpdir = vout[0];
673 tmpmodule = tmpdir + "/" + s->module_name + ".ko";
674 }
675
676 // Transfer the module. XXX and uprobes.ko, sigs, etc.
677 if (rc == 0) {
678 vector<string> cmd = scp_args;
679 cmd.push_back(localmodule);
680 cmd.push_back(host + ":" + tmpmodule);
681 rc = stap_system(s->verbose, cmd);
682 if (rc != 0)
683 cerr << _F("failed to copy the module to %s : rc=%d",
684 host.c_str(), rc) << endl;
685 }
686
687 // Run the module on the remote.
688 if (rc == 0) {
689 vector<string> cmd = ssh_args;
690 cmd.push_back("-t");
691 // We don't know the actual version, but all <=1.3 are approx equal.
692 cmd.push_back(cmdstr_join(make_run_command(*s, tmpmodule, "1.3")));
693 pid_t pid = stap_spawn(s->verbose, cmd);
694 if (pid > 0)
695 child = pid;
696 else
697 {
698 cerr << _F("failed to run the module on %s : ret=%d",
699 host.c_str(), pid) << endl;
700 rc = -1;
701 }
702 }
703
704 return rc;
705 }
706
707 int finish()
708 {
709 int rc = 0;
710
711 if (child > 0)
712 {
713 rc = stap_waitpid(s->verbose, child);
714 child = 0;
715 }
716
717 if (!tmpdir.empty())
718 {
719 // Remove the tempdir.
720 // XXX need to make sure this runs even with e.g. CTRL-C exits
721 vector<string> cmd = ssh_args;
722 cmd.push_back("-t");
723 cmd.push_back("rm -r " + cmdstr_quoted(tmpdir));
724 int rc2 = stap_system(s->verbose, cmd);
725 if (rc2 != 0)
726 cerr << _F("failed to delete the tempdir on %s : rc=%d",
727 host.c_str(), rc2) << endl;
728 if (rc == 0)
729 rc = rc2;
730 tmpdir.clear();
731 }
732
733 close_control_master();
734
735 return rc;
736 }
737
738 public:
739 friend class ssh_remote;
740
741 virtual ~ssh_legacy_remote()
742 {
743 close_control_master();
744 }
745 };
746
747
748 // Try to establish a stapsh connection to the remote, but fallback
749 // to the older mechanism if the command is not found.
750 remote*
751 ssh_remote::create(systemtap_session& s, const string& target)
752 {
753 string port, host = target;
754 size_t i = host.find(':');
755 if (i != string::npos)
756 {
757 port = host.substr(i + 1);
758 host.erase(i);
759 }
760
761 auto_ptr<ssh_remote> p (new ssh_remote(s));
762 int rc = p->connect(host, port);
763 if (rc == 0)
764 return p.release();
765 else if (rc == 127) // stapsh command not found
766 return new ssh_legacy_remote(s, host, port); // try legacy instead
767 return NULL;
768 }
769
770 remote*
771 ssh_remote::create(systemtap_session& s, const uri_decoder& ud)
772 {
773 if (!ud.has_authority || ud.authority.empty())
774 throw runtime_error(_("ssh target requires a hostname"));
775 if (!ud.path.empty() && ud.path != "/")
776 throw runtime_error(_("ssh target URI doesn't support a /path"));
777 if (ud.has_query)
778 throw runtime_error(_("ssh target URI doesn't support a ?query"));
779 if (ud.has_fragment)
780 throw runtime_error(_("ssh target URI doesn't support a #fragment"));
781
782 return create(s, ud.authority);
783 }
784
785
786 remote*
787 remote::create(systemtap_session& s, const string& uri)
788 {
789 try
790 {
791 if (uri.find(':') != string::npos)
792 {
793 const uri_decoder ud(uri);
794
795 // An ssh "host:port" is ambiguous with a URI "scheme:path".
796 // So if it looks like a number, just assume ssh.
797 if (!ud.has_authority && !ud.has_query &&
798 !ud.has_fragment && !ud.path.empty() &&
799 ud.path.find_first_not_of("1234567890") == string::npos)
800 return ssh_remote::create(s, uri);
801
802 if (ud.scheme == "direct")
803 return new direct(s);
804 else if (ud.scheme == "stapsh")
805 return new direct_stapsh(s);
806 if (ud.scheme == "ssh")
807 return ssh_remote::create(s, ud);
808 else
809 throw runtime_error(_F("unrecognized URI scheme '%s' in remote: %s",
810 ud.scheme.c_str(), uri.c_str()));
811 }
812 else
813 // XXX assuming everything else is ssh for now...
814 return ssh_remote::create(s, uri);
815 }
816 catch (std::runtime_error& e)
817 {
818 cerr << e.what() << " on remote '" << uri << "'" << endl;
819 return NULL;
820 }
821 }
822
823 #ifndef HAVE_PPOLL
824 // This is a poor-man's ppoll, only used by remote::run below. It does not
825 // provide the same guarantee of atomicity as on systems with a true ppoll.
826 //
827 // In our use, this would cause trouble if a signal came in any time from the
828 // moment we mask signals to prepare pollfds, to the moment we call poll in
829 // emulation here. If there's no data on any of the pollfds, we will be stuck
830 // waiting indefinitely.
831 //
832 // Since this is mainly about responsiveness of CTRL-C cleanup, we'll just
833 // throw in a one-second forced timeout to ensure we have a chance to notice
834 // there was an interrupt without too much delay.
835 static int
836 ppoll(struct pollfd *fds, nfds_t nfds,
837 const struct timespec *timeout_ts,
838 const sigset_t *sigmask)
839 {
840 sigset_t origmask;
841 int timeout = (timeout_ts == NULL) ? 1000 // don't block forever...
842 : (timeout_ts->tv_sec * 1000 + timeout_ts->tv_nsec / 1000000);
843 sigprocmask(SIG_SETMASK, sigmask, &origmask);
844 int rc = poll(fds, nfds, timeout);
845 sigprocmask(SIG_SETMASK, &origmask, NULL);
846 return rc;
847 }
848 #endif
849
850 int
851 remote::run(const vector<remote*>& remotes)
852 {
853 // NB: the first failure "wins"
854 int ret = 0, rc = 0;
855
856 for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i)
857 {
858 remote *r = remotes[i];
859 r->s->verbose = r->s->perpass_verbose[4];
860 if (r->s->use_remote_prefix)
861 r->prefix = lex_cast(i) + ": ";
862 rc = r->prepare();
863 if (rc)
864 return rc;
865 }
866
867 for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i)
868 {
869 rc = remotes[i]->start();
870 if (!ret)
871 ret = rc;
872 }
873
874 // mask signals while we're preparing to poll
875 {
876 stap_sigmasker masked;
877
878 // polling loop for remotes that have fds to watch
879 for (;;)
880 {
881 vector<pollfd> fds;
882 for (unsigned i = 0; i < remotes.size(); ++i)
883 remotes[i]->prepare_poll (fds);
884 if (fds.empty())
885 break;
886
887 rc = ppoll (&fds[0], fds.size(), NULL, &masked.old);
888 if (rc < 0 && errno != EINTR)
889 break;
890
891 for (unsigned i = 0; i < remotes.size(); ++i)
892 remotes[i]->handle_poll (fds);
893 }
894 }
895
896 for (unsigned i = 0; i < remotes.size(); ++i)
897 {
898 rc = remotes[i]->finish();
899 if (!ret)
900 ret = rc;
901 }
902
903 return ret;
904 }
905
906
907 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.074413 seconds and 5 git commands to generate.