/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2012 by Wenzel Jakob and others.
Mitsuba is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License Version 3
as published by the Free Software Foundation.
Mitsuba is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#if !defined(WIN32)
# include
#else
# include
#endif
#include
MTS_NAMESPACE_BEGIN
struct SSHStream::SSHStreamPrivate
{
const std::string userName, hostName;
const int port, timeout;
size_t received, sent;
#if defined(WIN32)
HANDLE childInRd, childInWr;
HANDLE childOutRd, childOutWr;
#else
int infd, outfd;
FILE *input, *output;
#endif
SSHStreamPrivate(const std::string& uname, const std::string& hname,
int p, int tm) :
userName(uname), hostName(hname), port(p), timeout(tm), received(0), sent(0),
#if defined(WIN32)
childInRd(0), childInWr(0), childOutRd(0), childOutWr(0)
#else
infd(-1), outfd(-1), input(0), output(0)
#endif
{}
};
SSHStream::SSHStream(const std::string &userName,
const std::string &hostName, const std::vector &cmdLine, int port, int timeout)
: d(new SSHStreamPrivate(userName, hostName, port, timeout)) {
setByteOrder(ENetworkByteOrder);
Log(EInfo, "Establishing a SSH connection to \"%s@%s\"",
userName.c_str(), hostName.c_str());
#if defined(WIN32)
/* Inherit pipe handles */
SECURITY_ATTRIBUTES sAttr;
sAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
sAttr.bInheritHandle = TRUE;
sAttr.lpSecurityDescriptor = NULL;
/* Create stdout pipe */
if (!CreatePipe(&(d->childOutRd), &(d->childOutWr), &sAttr, 0))
Log(EError, "Error in CreatePipe(): %s", lastErrorText().c_str());
/* Create stdin pipe */
if (!CreatePipe(&(d->childInRd), &(d->childInWr), &sAttr, 0))
Log(EError, "Error in CreatePipe(): %s", lastErrorText().c_str());
/* Only inherit one side of the pipes */
if (!SetHandleInformation(d->childOutRd, HANDLE_FLAG_INHERIT, 0))
Log(EError, "Error in SetHandleInformation(): %s", lastErrorText().c_str());
if (!SetHandleInformation(d->childInWr, HANDLE_FLAG_INHERIT, 0))
Log(EError, "Error in SetHandleInformation(): %s", lastErrorText().c_str());
/* Start the plink process */
PROCESS_INFORMATION pi;
STARTUPINFO si;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.hStdError = d->childOutWr;
si.hStdOutput = d->childOutWr;
si.hStdInput = d->childInRd;
si.dwFlags |= STARTF_USESTDHANDLES;
std::string params = formatString("-batch -P %i -T %s@%s",
d->port, d->userName.c_str(), d->hostName.c_str());
for (size_t i=0; ichildOutWr);
CloseHandle(d->childInRd);
#else
int infd[2], outfd[2];
if (pipe(outfd) == -1)
Log(EError, "Error in pipe(): %s", strerror(errno));
if (pipe(infd) == -1)
Log(EError, "Error in pipe(): %s", strerror(errno));
char **argv = new char*[15+cmdLine.size()];
int argc = 0;
argv[argc++] = strdup("ssh");
if (d->port != 22) {
argv[argc++] = strdup("-p"); argv[argc++] = strdup(formatString("%i", d->port).c_str());
}
argv[argc++] = strdup("-e"); argv[argc++] = strdup("none"); /* No escape character */
argv[argc++] = strdup("-T"); /* Disable pseudo TTY allocation (need to be able to transfer binary data) */
argv[argc++] = strdup("-o"); argv[argc++] = strdup("PasswordAuthentication no"); /* Don't proceed if password auth. is required */
argv[argc++] = strdup("-o"); argv[argc++] = strdup("StrictHostKeyChecking no"); /* Don't ask whether to accept host keys */
argv[argc++] = strdup("-o"); argv[argc++] = strdup(formatString("ConnectTimeout %i", d->timeout).c_str()); /* Don't get stuck too long */
argv[argc++] = strdup("-q"); /* Quiet */
argv[argc++] = strdup(formatString("%s@%s", d->userName.c_str(), d->hostName.c_str()).c_str());
for (size_t i=0; iinfd = infd[0];
d->outfd = outfd[1];
d->input = fdopen(infd[0], "rb");
d->output = fdopen(outfd[1], "wb");
}
for (int i=0; ichildInWr);
CloseHandle(d->childOutRd);
#else
fclose(d->input);
fclose(d->output);
#endif
}
const std::string& SSHStream::getHostName() const {
return d->hostName;
}
const std::string& SSHStream::getUserName() const {
return d->userName;
}
size_t SSHStream::getReceivedBytes() const {
return d->received;
}
size_t SSHStream::getSentBytes() const {
return d->sent;
}
std::string SSHStream::toString() const {
std::ostringstream oss;
oss << "SSHStream[userName='"<< d->userName << "', hostName='"
<< d->hostName << "', sent=" << (d->sent / 1024) << " KB, "
"received=" << (d->received/1024) << " KB]" << endl;
return oss.str();
}
void SSHStream::seek(size_t pos) {
Log(EError, "Cannot seek within a socket stream!");
}
size_t SSHStream::getPos() const {
Log(EError, "Cannot determine the position within a socket stream!");
return 0;
}
size_t SSHStream::getSize() const {
Log(EError, "Cannot determine the size of a socket stream!");
return 0;
}
void SSHStream::truncate(size_t size) {
Log(EError, "Cannot truncate a socket stream!");
}
void SSHStream::flush() {
#if defined(WIN32)
// No-op
#else
if (fflush(d->output) == EOF)
Log(EError, "Error in fflush(): %s!", strerror(errno));
#endif
}
void SSHStream::read(void *ptr, size_t size) {
static StatsCounter bytesRcvd("Network", "Bytes received (SSH)");
#if defined(WIN32)
size_t left = size;
char *data = (char *) ptr;
while (left > 0) {
DWORD nRead = 0;
if (!ReadFile(d->childOutRd, ptr, (DWORD) left, &nRead, NULL))
Log(EError, "Connection closed while reading: %s", lastErrorText().c_str());
left -= nRead;
data += nRead;
}
#else
if (fread(ptr, size, 1, d->input) != 1) {
if (feof(d->input))
Log(EError, "Error in fread(): end of file!");
else if (ferror(d->input))
Log(EError, "Error in fread(): stream error!");
/* Otherwise, ignore (strange, but seems to be required) */
}
#endif
d->received += size;
bytesRcvd += size;
}
void SSHStream::write(const void *ptr, size_t size) {
static StatsCounter bytesSent("Network", "Bytes sent (SSH)");
#if defined(WIN32)
size_t left = size;
char *data = (char *) ptr;
while (left > 0) {
DWORD nWritten = 0;
if (!WriteFile(d->childInWr, ptr, (DWORD) left, &nWritten, NULL))
Log(EError, "Connection closed while writing: %s", lastErrorText().c_str());
left -= nWritten;
data += nWritten;
}
#else
if (fwrite(ptr, size, 1, d->output) != 1) {
if (feof(d->output))
Log(EError, "Error in fwrite(): end of file!");
else if (ferror(d->output))
Log(EError, "Error in fwrite(): stream error!");
/* Otherwise, ignore (strange, but seems to be required) */
}
#endif
d->sent += size;
bytesSent += size;
}
bool SSHStream::canRead() const {
return true;
}
bool SSHStream::canWrite() const {
return true;
}
MTS_IMPLEMENT_CLASS(SSHStream, false, Stream)
MTS_NAMESPACE_END