remote.cxx - systemtap
Functions defined
Source code
#include "config.h"
extern "C" {
#include <fcntl.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
}
#include <cstdio>
#include <iomanip>
#include <memory>
#include <stdexcept>
#include <sstream>
#include <string>
#include <vector>
#include "buildrun.h"
#include "remote.h"
#include "util.h"
using namespace std;
class uri_decoder {
public:
const string uri;
string scheme, authority, path, query, fragment;
bool has_authority, has_query, has_fragment;
uri_decoder(const string& uri):
uri(uri), has_authority(false), has_query(false), has_fragment(false)
{
const string re =
"^([^:]+):(//[^/?#]*)?([^?#]*)(\\?[^#]*)?(#.*)?$";
vector<string> matches;
if (regexp_match(uri, re, matches) != 0)
throw runtime_error(_F("string doesn't appear to be a URI: %s", uri.c_str()));
scheme = matches[1];
if (!matches[2].empty())
{
has_authority = true;
authority = matches[2].substr(2);
}
path = matches[3];
if (!matches[4].empty())
{
has_query = true;
query = matches[4].substr(1);
}
if (!matches[5].empty())
{
has_fragment = true;
fragment = matches[5].substr(1);
}
}
};
class direct : public remote {
private:
pid_t child;
vector<string> args;
direct(systemtap_session& s): remote(s), child(0) {}
int start()
{
args = make_run_command(*s);
if (! staprun_r_arg.empty()) {
args.push_back ("-r");
args.push_back (staprun_r_arg);
}
pid_t pid = stap_spawn (s->verbose, args);
if (pid <= 0)
return 1;
child = pid;
return 0;
}
int finish()
{
if (child <= 0)
return 1;
int ret = stap_waitpid(s->verbose, child);
if (ret > 128)
s->print_warning(_F("%s exited with signal: %d (%s)",
args.front().c_str(), ret - 128,
strsignal(ret - 128)));
else if (ret > 0)
s->print_warning(_F("%s exited with status: %d",
args.front().c_str(), ret));
child = 0;
return ret;
}
public:
friend class remote;
virtual ~direct() { finish(); }
};
class stapsh : public remote {
private:
int interrupts_sent;
int fdin, fdout;
FILE *IN, *OUT;
string remote_version;
size_t data_size;
string target_stream;
enum {
STAPSH_READY, STAPSH_DATA } stream_state;
virtual void prepare_poll(vector<pollfd>& fds)
{
if (fdout >= 0 && OUT)
{
pollfd p = { fdout, POLLIN, 0 };
fds.push_back(p);
}
if (fdin >= 0 && IN && interrupts_sent < pending_interrupts)
{
pollfd p = { fdin, POLLOUT, 0 };
fds.push_back(p);
}
}
virtual void handle_poll(vector<pollfd>& fds)
{
for (unsigned i=0; i < fds.size(); ++i)
if (fds[i].fd == fdin || fds[i].fd == fdout)
{
bool err = false;
if (fds[i].revents & POLLOUT && IN &&
interrupts_sent < pending_interrupts)
{
if (send_command("quit\n") == 0)
++interrupts_sent;
else
err = true;
}
if (fds[i].revents & POLLIN && OUT)
{
if (!vector_has(options, string("data")))
{
char buf[4096];
size_t bytes_read;
while ((bytes_read = fread(buf, 1, sizeof(buf), OUT)) > 0)
printout(buf, bytes_read);
if (errno != EAGAIN)
err = true;
}
else {
if (stream_state == STAPSH_READY)
{
char cmdbuf[1024];
char *rc;
while ((rc = fgets(cmdbuf, sizeof(cmdbuf), OUT)) != NULL)
{
string line = string(cmdbuf);
if (startswith(line, "stapsh:"))
clog << line;
else if (startswith(line, "quit\n"))
{
err = true; break;
}
else if (startswith(line, "data"))
{
vector<string> cmd;
tokenize(line, cmd, " \t\r\n");
if (!is_valid_data_cmd(cmd))
{
clog << _("invalid data command from stapsh") << endl;
clog << _("received: ") << line;
err = true;
}
else
{
target_stream = cmd[1];
data_size = lex_cast<size_t>(cmd[2]);
stream_state = STAPSH_DATA;
}
break;
}
}
if (rc == NULL && errno != EAGAIN)
err = true;
}
if (stream_state == STAPSH_DATA)
{
if (data_size != 0)
{
char buf[4096];
size_t max_read = min<size_t>(sizeof(buf), data_size);
size_t bytes_read = 0;
while (max_read > 0
&& (bytes_read = fread(buf, 1, max_read, OUT)) > 0)
{
printout(buf, bytes_read);
data_size -= bytes_read;
max_read = min<size_t>(sizeof(buf), data_size);
}
if (bytes_read == 0 && errno != EAGAIN)
err = true;
}
if (data_size == 0)
stream_state = STAPSH_READY;
}
}
}
if (err || fds[i].revents & ~(POLLIN|POLLOUT))
close();
}
}
string get_reply()
{
char reply[4096];
while (fgets(reply, sizeof(reply), OUT))
{
if (!startswith(reply, "stapsh:"))
return reply;
if (vector_has(options, string("data")))
clog << reply; else
cout << reply; }
return "";
}
int send_command(const string& cmd)
{
if (!IN)
return 2;
if (fputs(cmd.c_str(), IN) < 0 ||
fflush(IN) != 0)
return 1;
return 0;
}
int send_file(const string& filename, const string& dest)
{
int rc = 0;
FILE* f = fopen(filename.c_str(), "r");
if (!f)
return 1;
struct stat fs;
rc = fstat(fileno(f), &fs);
if (!rc)
{
ostringstream cmd;
cmd << "file " << fs.st_size << " " << dest << "\n";
rc = send_command(cmd.str());
}
off_t i = 0;
while (!rc && i < fs.st_size)
{
char buf[4096];
size_t r = sizeof(buf);
if (fs.st_size - i < (off_t)r)
r = fs.st_size - i;
r = fread(buf, 1, r, f);
if (r == 0)
rc = 1;
else
{
size_t w = fwrite(buf, 1, r, IN);
if (w != r)
rc = 1;
else
i += w;
}
}
if (!rc)
rc = fflush(IN);
fclose(f);
if (!rc)
{
string reply = get_reply();
if (reply != "OK\n")
{
rc = 1;
if (s->verbose > 0)
{
if (reply.empty())
clog << _("stapsh file ERROR: no reply") << endl;
else
clog << _F("stapsh file replied %s", reply.c_str());
}
}
}
return rc;
}
static string qpencode(const string& str)
{
ostringstream o;
o << setfill('0') << hex;
for (const char* s = str.c_str(); *s; ++s)
if (*s >= 33 && *s <= 126 && *s != 61)
o << *s;
else
o << '=' << setw(2) << (unsigned)(unsigned char) *s;
return o.str();
}
static bool is_valid_data_cmd(const vector<string>& cmd)
{
bool err = false;
err = err || (cmd[0] != "data");
err = err || (cmd[1] != "stdout" && cmd[1] != "stderr");
if (!err) try { lex_cast<size_t>(cmd[2]); }
catch (...) { err = true; }
return !err;
}
virtual void printout(const char *buf, size_t size)
{
static string last_prefix;
static bool on_same_line;
if (size == 0)
return;
if (!prefix.empty() && target_stream != "stderr")
{
vector<pair<const char*,int> > lines = split_lines(buf, size);
for (vector<pair<const char*,int> >::iterator it = lines.begin();
it != lines.end(); ++it)
{
if (last_prefix != prefix)
{
if (on_same_line)
cout << endl;
cout << prefix;
}
else if (!on_same_line)
cout << prefix;
cout.write(it->first, it->second);
}
cout.flush();
const char *last_line = lines.back().first;
const char last_char = last_line[lines.back().second-1];
on_same_line = !lines.empty() && last_char != '\n';
last_prefix = prefix;
}
else
{
if (target_stream == "stdout")
{
cout.write(buf, size);
cout.flush();
}
else clog.write(buf, size);
}
}
protected:
stapsh(systemtap_session& s)
: remote(s), interrupts_sent(0),
fdin(-1), fdout(-1), IN(0), OUT(0),
data_size(0), target_stream("stdout"), stream_state(STAPSH_READY) {}
vector<string> options;
virtual int prepare()
{
int rc = 0;
string localmodule = s->tmpdir + "/" + s->module_name + ".ko";
string remotemodule = s->module_name + ".ko";
if ((rc = send_file(localmodule, remotemodule)))
return rc;
if (file_exists(localmodule + ".sgn") &&
(rc = send_file(localmodule + ".sgn", remotemodule + ".sgn")))
return rc;
if (!s->uprobes_path.empty())
{
string remoteuprobes = basename(s->uprobes_path.c_str());
if ((rc = send_file(s->uprobes_path, remoteuprobes)))
return rc;
if (file_exists(s->uprobes_path + ".sgn") &&
(rc = send_file(s->uprobes_path + ".sgn", remoteuprobes + ".sgn")))
return rc;
}
return rc;
}
virtual int start()
{
ostringstream run("run", ios::out | ios::ate);
vector<string> cmd = make_run_command(*s, ".", remote_version);
if (strverscmp("1.7", remote_version.c_str()) <= 0 && ! staprun_r_arg.empty())
{
cmd.push_back ("-r");
cmd.push_back (staprun_r_arg);
}
for (unsigned i = 1; i < cmd.size(); ++i)
run << ' ' << qpencode(cmd[i]);
run << '\n';
int rc = send_command(run.str());
if (!rc)
{
string reply = get_reply();
if (reply != "OK\n")
{
rc = 1;
if (s->verbose > 0)
{
if (reply.empty())
clog << _("stapsh run ERROR: no reply") << endl;
else
clog << _F("stapsh run replied %s", reply.c_str());
}
}
}
if (!rc)
{
long flags;
if ((flags = fcntl(fdout, F_GETFL)) == -1
|| fcntl(fdout, F_SETFL, flags | O_NONBLOCK) == -1)
{
clog << _("failed to change to non-blocking mode") << endl;
rc = 1;
}
}
if (rc)
close();
return rc;
}
void close()
{
if (IN) fclose(IN);
if (OUT) fclose(OUT);
IN = OUT = NULL;
fdin = fdout = -1;
}
virtual int finish()
{
close();
return 0;
}
void set_child_fds(int in, int out)
{
if (fdin >= 0 || fdout >= 0 || IN || OUT)
throw runtime_error(_("stapsh file descriptors already set"));
fdin = in;
fdout = out;
IN = fdopen(fdin, "w");
OUT = fdopen(fdout, "r");
if (!IN || !OUT)
throw runtime_error(_("invalid file descriptors for stapsh"));
if (send_command("stap " VERSION "\n"))
throw runtime_error(_("error sending hello to stapsh"));
string reply = get_reply();
if (reply.empty())
throw runtime_error(_("error receiving hello from stapsh"));
vector<string> uname;
tokenize(reply, uname, " \t\r\n");
if (uname.size() != 4 || uname[0] != "stapsh")
throw runtime_error(_F("invalid hello from stapsh: %s", reply.c_str()));
this->remote_version = uname[1];
this->s = s->clone(uname[2], uname[3]);
if (!this->options.empty())
{
if (strverscmp("2.4", this->remote_version.c_str()) > 0)
throw runtime_error(_F("stapsh %s does not support options",
this->remote_version.c_str()));
for (vector<string>::iterator it = this->options.begin();
it != this->options.end(); ++it)
{
int rc = send_command("option " + *it + "\n");
if (rc != 0)
throw runtime_error(_F("could not set option %s: "
"send_command returned %d",
it->c_str(), rc));
string reply = get_reply();
if (reply != "OK\n")
throw runtime_error(_F("could not set option %s: %s",
it->c_str(), reply.c_str()));
}
}
}
public:
virtual ~stapsh() { close(); }
};
class direct_stapsh : public stapsh {
private:
pid_t child;
direct_stapsh(systemtap_session& s)
: stapsh(s), child(0)
{
int in, out;
vector<string> cmd;
cmd.push_back(BINDIR "/stapsh");
if (s.perpass_verbose[4] > 1)
cmd.push_back("-v");
if (s.perpass_verbose[4] > 2)
cmd.push_back("-v");
{
stap_sigmasker masked;
child = stap_spawn_piped(s.verbose, cmd, &in, &out);
}
if (child <= 0)
throw runtime_error(_("error launching stapsh"));
try
{
set_child_fds(in, out);
}
catch (runtime_error&)
{
finish();
throw;
}
}
virtual int finish()
{
int rc = stapsh::finish();
if (child <= 0)
return rc;
int rc2 = stap_waitpid(s->verbose, child);
child = 0;
return rc ?: rc2;
}
public:
friend class remote;
virtual ~direct_stapsh() { finish(); }
};
class unix_stapsh : public stapsh {
private:
unix_stapsh(systemtap_session& s, const uri_decoder& ud)
: stapsh(s)
{
this->options.push_back("data");
for (unsigned i = 1; i < s.perpass_verbose[4]; i++)
this->options.push_back("verbose");
sockaddr_un server;
server.sun_family = AF_UNIX;
if (ud.path.empty())
throw runtime_error(_("unix target requires a /path"));
if (ud.path.size() > sizeof(server.sun_path) - 1)
throw runtime_error(_("unix target /path is too long"));
strcpy(server.sun_path, ud.path.c_str());
if (ud.has_authority)
throw runtime_error(_("unix target doesn't support a hostname"));
if (ud.has_query)
throw runtime_error(_("unix target URI doesn't support a ?query"));
if (ud.has_fragment)
throw runtime_error(_("unix target URI doesn't support a #fragment"));
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd <= 0)
throw runtime_error(_("error opening a socket"));
if (connect(fd, (struct sockaddr *)&server, SUN_LEN(&server)) < 0)
{
const char *msg = strerror(errno);
::close(fd);
throw runtime_error(_F("error connecting to socket %s: %s",
server.sun_path, msg));
}
int fd2 = dup(fd);
if (fd2 < 0)
fd2 = fd;
try
{
set_child_fds(fd, fd2);
}
catch (runtime_error&)
{
finish();
::close(fd);
::close(fd2);
throw;
}
}
public:
friend class remote;
virtual ~unix_stapsh() { finish(); }
};
class libvirt_stapsh : public stapsh {
private:
pid_t child;
libvirt_stapsh(systemtap_session& s, const uri_decoder& ud)
: stapsh(s)
{
string domain;
string libvirt_uri;
this->options.push_back("data");
for (unsigned i = 1; i < s.perpass_verbose[4]; i++)
this->options.push_back("verbose");
if (ud.has_fragment)
throw runtime_error(_("libvirt target URI doesn't support a #fragment"));
if (ud.has_authority) {
domain = ud.authority;
if (!ud.path.empty())
{
libvirt_uri = ud.path.substr(1);
if (ud.has_query)
libvirt_uri += "?" + ud.query;
}
}
else {
if (ud.path.empty())
throw runtime_error(_("libvirt target URI requires a domain name"));
size_t slash = ud.path.find_first_of('/');
if (slash == string::npos)
domain = ud.path;
else
{
domain = ud.path.substr(0, slash);
libvirt_uri = ud.path.substr(slash+1);
}
}
int in, out;
vector<string> cmd;
string stapvirt = BINDIR "/stapvirt";
if (!file_exists(stapvirt))
throw runtime_error(_("stapvirt missing"));
cmd.push_back(stapvirt);
for (unsigned i = 1; i < s.perpass_verbose[4]; i++)
cmd.push_back("-v");
if (!libvirt_uri.empty())
{
cmd.push_back("-c");
cmd.push_back(libvirt_uri);
}
cmd.push_back("connect");
cmd.push_back(domain);
{
stap_sigmasker masked;
child = stap_spawn_piped(s.verbose, cmd, &in, &out);
}
if (child <= 0)
throw runtime_error(_("error launching stapvirt"));
try
{
set_child_fds(in, out);
}
catch (runtime_error&)
{
finish();
throw;
}
}
int finish()
{
int rc = stapsh::finish();
if (child <= 0)
return rc;
int rc2 = stap_waitpid(s->verbose, child);
child = 0;
return rc ?: rc2;
}
public:
friend class remote;
virtual ~libvirt_stapsh() { finish(); }
};
class ssh_remote : public stapsh {
private:
pid_t child;
ssh_remote(systemtap_session& s): stapsh(s), child(0) {}
int connect(const string& host, const string& port)
{
int rc = 0;
int in, out;
vector<string> cmd;
cmd.push_back("ssh");
cmd.push_back("-q");
cmd.push_back(host);
if (!port.empty())
{
cmd.push_back("-p");
cmd.push_back(port);
}
string stapsh_cmd = "cmd=`type -P stapsh || exit 127` && exec \"$cmd\"";
if (s->perpass_verbose[4] > 1)
stapsh_cmd.append(" -v");
if (s->perpass_verbose[4] > 2)
stapsh_cmd.append(" -v");
cmd.push_back("/bin/bash -c '" + stapsh_cmd + "'");
{
stap_sigmasker masked;
child = stap_spawn_piped(s->verbose, cmd, &in, &out);
}
if (child <= 0)
throw runtime_error(_("error launching stapsh"));
try
{
set_child_fds(in, out);
}
catch (runtime_error&)
{
rc = finish();
if (rc == 255)
throw runtime_error(_("error establishing ssh connection"));
if (rc != 127 || s->remote_uris.size() > 1)
throw;
}
return rc;
}
int finish()
{
int rc = stapsh::finish();
if (child <= 0)
return rc;
int rc2 = stap_waitpid(s->verbose, child);
child = 0;
return rc ?: rc2;
}
public:
static remote* create(systemtap_session& s, const string& host);
static remote* create(systemtap_session& s, const uri_decoder& ud);
virtual ~ssh_remote() { finish(); }
};
class ssh_legacy_remote : public remote {
private:
vector<string> ssh_args, scp_args;
string ssh_control;
string host, port, tmpdir;
pid_t child;
ssh_legacy_remote(systemtap_session& s, const string& host, const string& port)
: remote(s), host(host), port(port), child(0)
{
open_control_master();
try
{
get_uname();
}
catch (runtime_error&)
{
close_control_master();
throw;
}
}
void open_control_master()
{
static unsigned index = 0;
if (s->tmpdir.empty()) throw runtime_error(_("No tmpdir available for ssh control master"));
ssh_control = s->tmpdir + "/ssh_remote_control_" + lex_cast(++index);
scp_args.clear();
scp_args.push_back("scp");
scp_args.push_back("-q");
scp_args.push_back("-o");
scp_args.push_back("ControlPath=" + ssh_control);
ssh_args = scp_args;
ssh_args[0] = "ssh";
ssh_args.push_back(host);
if (!port.empty())
{
scp_args.push_back("-P");
scp_args.push_back(port);
ssh_args.push_back("-p");
ssh_args.push_back(port);
}
vector<string> cmd = ssh_args;
cmd.push_back("-f");
cmd.push_back("-N");
cmd.push_back("-M");
int rc = stap_system(s->verbose, cmd);
if (rc != 0)
throw runtime_error(_F("failed to create an ssh control master for %s : rc= %d",
host.c_str(), rc));
if (s->verbose>1)
clog << _F("Created ssh control master at %s",
lex_cast_qstring(ssh_control).c_str()) << endl;
}
void close_control_master()
{
if (ssh_control.empty())
return;
vector<string> cmd = ssh_args;
cmd.push_back("-O");
cmd.push_back("exit");
int rc = stap_system(s->verbose, cmd, true, true);
if (rc != 0)
cerr << _F("failed to stop the ssh control master for %s : rc=%d",
host.c_str(), rc) << endl;
ssh_control.clear();
scp_args.clear();
ssh_args.clear();
}
void get_uname()
{
ostringstream out;
vector<string> uname;
vector<string> cmd = ssh_args;
cmd.push_back("-t");
cmd.push_back("uname -rm");
int rc = stap_system_read(s->verbose, cmd, out);
if (rc == 0)
tokenize(out.str(), uname, " \t\r\n");
if (uname.size() != 2)
throw runtime_error(_F("failed to get uname from %s : rc= %d", host.c_str(), rc));
const string& release = uname[0];
const string& arch = uname[1];
XXX this->s = s->clone(arch, release);
}
int start()
{
int rc;
string localmodule = s->tmpdir + "/" + s->module_name + ".ko";
string tmpmodule;
{
ostringstream out;
vector<string> vout;
vector<string> cmd = ssh_args;
cmd.push_back("-t");
cmd.push_back("mktemp -d -t stapXXXXXX");
rc = stap_system_read(s->verbose, cmd, out);
if (rc == 0)
tokenize(out.str(), vout, "\r\n");
if (vout.size() != 1)
{
cerr << _F("failed to make a tempdir on %s : rc=%d",
host.c_str(), rc) << endl;
return -1;
}
tmpdir = vout[0];
tmpmodule = tmpdir + "/" + s->module_name + ".ko";
}
if (rc == 0)
{
vector<string> cmd = scp_args;
cmd.push_back(localmodule);
cmd.push_back(host + ":" + tmpmodule);
rc = stap_system(s->verbose, cmd);
if (rc != 0)
cerr << _F("failed to copy the module to %s : rc=%d",
host.c_str(), rc) << endl;
}
if (rc == 0 && file_exists(localmodule + ".sgn"))
{
vector<string> cmd = scp_args;
cmd.push_back(localmodule + ".sgn");
cmd.push_back(host + ":" + tmpmodule + ".sgn");
rc = stap_system(s->verbose, cmd);
if (rc != 0)
cerr << _F("failed to copy the module signature to %s : rc=%d",
host.c_str(), rc) << endl;
}
if (rc == 0) {
vector<string> cmd = ssh_args;
cmd.push_back("-t");
vector<string> staprun_cmd = make_run_command(*s, tmpdir, "1.3");
staprun_cmd[0] = "staprun"; cmd.push_back(cmdstr_join(staprun_cmd));
pid_t pid = stap_spawn(s->verbose, cmd);
if (pid > 0)
child = pid;
else
{
cerr << _F("failed to run the module on %s : ret=%d",
host.c_str(), pid) << endl;
rc = -1;
}
}
return rc;
}
int finish()
{
int rc = 0;
if (child > 0)
{
rc = stap_waitpid(s->verbose, child);
child = 0;
}
if (!tmpdir.empty())
{
XXX vector<string> cmd = ssh_args;
cmd.push_back("-t");
cmd.push_back("rm -r " + cmdstr_quoted(tmpdir));
int rc2 = stap_system(s->verbose, cmd);
if (rc2 != 0)
cerr << _F("failed to delete the tempdir on %s : rc=%d",
host.c_str(), rc2) << endl;
if (rc == 0)
rc = rc2;
tmpdir.clear();
}
close_control_master();
return rc;
}
public:
friend class ssh_remote;
virtual ~ssh_legacy_remote()
{
close_control_master();
}
};
remote*
ssh_remote::create(systemtap_session& s, const string& target)
{
string port, host = target;
size_t i = host.find(':');
if (i != string::npos)
{
port = host.substr(i + 1);
host.erase(i);
}
auto_ptr<ssh_remote> p (new ssh_remote(s));
int rc = p->connect(host, port);
if (rc == 0)
return p.release();
else if (rc == 127) return new ssh_legacy_remote(s, host, port); return NULL;
}
remote*
ssh_remote::create(systemtap_session& s, const uri_decoder& ud)
{
if (!ud.has_authority || ud.authority.empty())
throw runtime_error(_("ssh target requires a hostname"));
if (!ud.path.empty() && ud.path != "/")
throw runtime_error(_("ssh target URI doesn't support a /path"));
if (ud.has_query)
throw runtime_error(_("ssh target URI doesn't support a ?query"));
if (ud.has_fragment)
throw runtime_error(_("ssh target URI doesn't support a #fragment"));
return create(s, ud.authority);
}
remote*
remote::create(systemtap_session& s, const string& uri, int idx)
{
remote *it = 0;
try
{
if (uri.find(':') != string::npos)
{
const uri_decoder ud(uri);
if (!ud.has_authority && !ud.has_query &&
!ud.has_fragment && !ud.path.empty() &&
ud.path.find_first_not_of("1234567890") == string::npos)
it = ssh_remote::create(s, uri);
else if (ud.scheme == "direct")
it = new direct(s);
else if (ud.scheme == "stapsh")
it = new direct_stapsh(s);
else if (ud.scheme == "unix")
it = new unix_stapsh(s, ud);
else if (ud.scheme == "libvirt")
it = new libvirt_stapsh(s, ud);
else if (ud.scheme == "ssh")
it = ssh_remote::create(s, ud);
else
throw runtime_error(_F("unrecognized URI scheme '%s' in remote: %s",
ud.scheme.c_str(), uri.c_str()));
}
else
XXX it = ssh_remote::create(s, uri);
}
catch (std::runtime_error& e)
{
cerr << e.what() << " on remote '" << uri << "'" << endl;
it = 0;
}
if (it && idx >= 0) {
stringstream r_arg;
r_arg << idx << ":" << uri;
it->staprun_r_arg = r_arg.str();
}
return it;
}
int
remote::run(const vector<remote*>& remotes)
{
int ret = 0, rc = 0;
for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i)
{
remote *r = remotes[i];
r->s->verbose = r->s->perpass_verbose[4];
if (r->s->use_remote_prefix)
r->prefix = lex_cast(i) + ": ";
rc = r->prepare();
if (rc)
return rc;
}
for (unsigned i = 0; i < remotes.size() && !pending_interrupts; ++i)
{
rc = remotes[i]->start();
if (!ret)
ret = rc;
}
{
stap_sigmasker masked;
for (;;)
{
vector<pollfd> fds;
for (unsigned i = 0; i < remotes.size(); ++i)
remotes[i]->prepare_poll (fds);
if (fds.empty())
break;
rc = ppoll (&fds[0], fds.size(), NULL, &masked.old);
if (rc < 0 && errno != EINTR)
break;
for (unsigned i = 0; i < remotes.size(); ++i)
remotes[i]->handle_poll (fds);
}
}
for (unsigned i = 0; i < remotes.size(); ++i)
{
rc = remotes[i]->finish();
if (!ret)
ret = rc;
}
return ret;
}