diff options
author | Arch Librarian <arch@canonical.com> | 2004-09-20 16:56:32 +0000 |
---|---|---|
committer | Arch Librarian <arch@canonical.com> | 2004-09-20 16:56:32 +0000 |
commit | b2e465d6d32d2dc884f58b94acb7e35f671a87fe (patch) | |
tree | 5928383b9bde7b0ba9812e6526ad746466e558f7 /methods/rsh.cc | |
parent | 00b47c98ca4a4349686a082eba6d77decbb03a4d (diff) |
Join with aliencode
Author: jgg
Date: 2001-02-20 07:03:16 GMT
Join with aliencode
Diffstat (limited to 'methods/rsh.cc')
-rw-r--r-- | methods/rsh.cc | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/methods/rsh.cc b/methods/rsh.cc new file mode 100644 index 000000000..9e521edec --- /dev/null +++ b/methods/rsh.cc @@ -0,0 +1,486 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +// $Id: rsh.cc,v 1.2 2001/02/20 07:03:18 jgg Exp $ +/* ###################################################################### + + RSH method - Transfer files via rsh compatible program + + Written by Ben Collins <bcollins@debian.org>, Copyright (c) 2000 + Licensed under the GNU General Public License v2 [no exception clauses] + + ##################################################################### */ + /*}}}*/ +// Iclude Files /*{{{*/ +#include "rsh.h" +#include <apt-pkg/error.h> + +#include <sys/stat.h> +#include <sys/time.h> +#include <utime.h> +#include <unistd.h> +#include <signal.h> +#include <stdio.h> +#include <errno.h> +#include <stdarg.h> + /*}}}*/ + +const char *Prog; +unsigned long TimeOut = 120; +time_t RSHMethod::FailTime = 0; +string RSHMethod::FailFile; +int RSHMethod::FailFd = -1; + +// RSHConn::RSHConn - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +RSHConn::RSHConn(URI Srv) : Len(0), WriteFd(-1), ReadFd(-1), + ServerName(Srv), Process(-1) {} + /*}}}*/ +// RSHConn::RSHConn - Destructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +RSHConn::~RSHConn() +{ + Close(); +} + /*}}}*/ +// RSHConn::Close - Forcibly terminate the connection /*{{{*/ +// --------------------------------------------------------------------- +/* Often this is called when things have gone wrong to indicate that the + connection is no longer usable. */ +void RSHConn::Close() +{ + if (Process == -1) + return; + + close(WriteFd); + close(ReadFd); + kill(Process,SIGINT); + ExecWait(Process,"",true); + WriteFd = -1; + ReadFd = -1; + Process = -1; +} + /*}}}*/ +// RSHConn::Open - Connect to a host /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool RSHConn::Open() +{ + // Use the already open connection if possible. + if (Process != -1) + return true; + + if (Connect(ServerName.Host,ServerName.User) == false) + return false; + + return true; +} + /*}}}*/ +// RSHConn::Connect - Fire up rsh and connect /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool RSHConn::Connect(string Host, string User) +{ + // Create the pipes + int Pipes[4] = {-1,-1,-1,-1}; + if (pipe(Pipes) != 0 || pipe(Pipes+2) != 0) + { + _error->Errno("pipe","Failed to create IPC pipe to subprocess"); + for (int I = 0; I != 4; I++) + close(Pipes[I]); + return false; + } + for (int I = 0; I != 4; I++) + SetCloseExec(Pipes[I],true); + + Process = ExecFork(); + + // The child + if (Process == 0) + { + const char *Args[6]; + int i = 0; + + dup2(Pipes[1],STDOUT_FILENO); + dup2(Pipes[2],STDIN_FILENO); + + // Probably should do + // dup2(open("/dev/null",O_RDONLY),STDERR_FILENO); + + Args[i++] = Prog; + if (User.empty() == false) { + Args[i++] = "-l"; + Args[i++] = User.c_str(); + } + if (Host.empty() == false) { + Args[i++] = Host.c_str(); + } + Args[i++] = "/bin/sh"; + Args[i] = 0; + execvp(Args[0],(char **)Args); + exit(100); + } + + ReadFd = Pipes[0]; + WriteFd = Pipes[3]; + SetNonBlock(Pipes[0],true); + SetNonBlock(Pipes[3],true); + close(Pipes[1]); + close(Pipes[2]); + + return true; +} + /*}}}*/ +// RSHConn::ReadLine - Very simple buffered read with timeout /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool RSHConn::ReadLine(string &Text) +{ + if (Process == -1 || ReadFd == -1) + return false; + + // Suck in a line + while (Len < sizeof(Buffer)) + { + // Scan the buffer for a new line + for (unsigned int I = 0; I != Len; I++) + { + // Escape some special chars + if (Buffer[I] == 0) + Buffer[I] = '?'; + + // End of line? + if (Buffer[I] != '\n') + continue; + + I++; + Text = string(Buffer,I); + memmove(Buffer,Buffer+I,Len - I); + Len -= I; + return true; + } + + // Wait for some data.. + if (WaitFd(ReadFd,false,TimeOut) == false) + { + Close(); + return _error->Error("Connection timeout"); + } + + // Suck it back + int Res = read(ReadFd,Buffer + Len,sizeof(Buffer) - Len); + if (Res <= 0) + { + _error->Errno("read","Read error"); + Close(); + return false; + } + Len += Res; + } + + return _error->Error("A response overflowed the buffer."); +} + /*}}}*/ +// RSHConn::WriteMsg - Send a message with optional remote sync. /*{{{*/ +// --------------------------------------------------------------------- +/* The remote sync flag appends a || echo which will insert blank line + once the command completes. */ +bool RSHConn::WriteMsg(string &Text,bool Sync,const char *Fmt,...) +{ + va_list args; + va_start(args,Fmt); + + // sprintf the description + char S[512]; + vsnprintf(S,sizeof(S) - 4,Fmt,args); + if (Sync == true) + strcat(S," 2> /dev/null || echo\n"); + else + strcat(S," 2> /dev/null\n"); + + // Send it off + unsigned long Len = strlen(S); + unsigned long Start = 0; + while (Len != 0) + { + if (WaitFd(WriteFd,true,TimeOut) == false) + { + + Close(); + return _error->Error("Connection timeout"); + } + + int Res = write(WriteFd,S + Start,Len); + if (Res <= 0) + { + _error->Errno("write","Write Error"); + Close(); + return false; + } + + Len -= Res; + Start += Res; + } + + if (Sync == true) + return ReadLine(Text); + return true; +} + /*}}}*/ +// RSHConn::Size - Return the size of the file /*{{{*/ +// --------------------------------------------------------------------- +/* Right now for successfull transfer the file size must be known in + advance. */ +bool RSHConn::Size(const char *Path,unsigned long &Size) +{ + // Query the size + string Msg; + Size = 0; + + if (WriteMsg(Msg,true,"find %s -follow -printf '%%s\\n'",Path) == false) + return false; + + // FIXME: Sense if the bad reply is due to a File Not Found. + + char *End; + Size = strtoul(Msg.c_str(),&End,10); + if (End == Msg.c_str()) + return _error->Error("File Not Found"); + return true; +} + /*}}}*/ +// RSHConn::ModTime - Get the modification time in UTC /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool RSHConn::ModTime(const char *Path, time_t &Time) +{ + Time = time(&Time); + // Query the mod time + string Msg; + + if (WriteMsg(Msg,true,"TZ=UTC find %s -follow -printf '%%TY%%Tm%%Td%%TH%%TM%%TS\\n'",Path) == false) + return false; + + // Parse it + StrToTime(Msg,Time); + return true; +} + /*}}}*/ +// RSHConn::Get - Get a file /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool RSHConn::Get(const char *Path,FileFd &To,unsigned long Resume, + MD5Summation &MD5,bool &Missing, unsigned long Size) +{ + Missing = false; + + // Round to a 2048 byte block + Resume = Resume - (Resume % 2048); + + if (To.Truncate(Resume) == false) + return false; + if (To.Seek(0) == false) + return false; + + if (Resume != 0) { + if (MD5.AddFD(To.Fd(),Resume) == false) { + _error->Errno("read","Problem hashing file"); + return false; + } + } + + // FIXME: Detect file-not openable type errors. + string Jnk; + if (WriteMsg(Jnk,false,"dd if=%s bs=2048 skip=%u", Path, Resume / 2048) == false) + return false; + + // Copy loop + unsigned int MyLen = Resume; + unsigned char Buffer[4096]; + while (MyLen < Size) + { + // Wait for some data.. + if (WaitFd(ReadFd,false,TimeOut) == false) + { + Close(); + return _error->Error("Data socket timed out"); + } + + // Read the data.. + int Res = read(ReadFd,Buffer,sizeof(Buffer)); + if (Res == 0) + { + Close(); + return _error->Error("Connection closed prematurely"); + } + + if (Res < 0) + { + if (errno == EAGAIN) + continue; + break; + } + MyLen += Res; + + MD5.Add(Buffer,Res); + if (To.Write(Buffer,Res) == false) + { + Close(); + return false; + } + } + + return true; +} + /*}}}*/ + +// RSHMethod::RSHMethod - Constructor /*{{{*/ +// --------------------------------------------------------------------- +/* */ +RSHMethod::RSHMethod() : pkgAcqMethod("1.0") +{ + signal(SIGTERM,SigTerm); + signal(SIGINT,SigTerm); + Server = 0; + FailFd = -1; +}; + /*}}}*/ +// RSHMethod::SigTerm - Clean up and timestamp the files on exit /*{{{*/ +// --------------------------------------------------------------------- +/* */ +void RSHMethod::SigTerm(int sig) +{ + if (FailFd == -1) + _exit(100); + close(FailFd); + + // Timestamp + struct utimbuf UBuf; + UBuf.actime = FailTime; + UBuf.modtime = FailTime; + utime(FailFile.c_str(),&UBuf); + + _exit(100); +} + /*}}}*/ +// RSHMethod::Fetch - Fetch a URI /*{{{*/ +// --------------------------------------------------------------------- +/* */ +bool RSHMethod::Fetch(FetchItem *Itm) +{ + URI Get = Itm->Uri; + const char *File = Get.Path.c_str(); + FetchResult Res; + Res.Filename = Itm->DestFile; + Res.IMSHit = false; + + // Connect to the server + if (Server == 0 || Server->Comp(Get) == false) { + delete Server; + Server = new RSHConn(Get); + } + + // Could not connect is a transient error.. + if (Server->Open() == false) { + Server->Close(); + Fail(true); + return true; + } + + // We say this mainly because the pause here is for the + // ssh connection that is still going + Status("Connecting to %s", Get.Host.c_str()); + + // Get the files information + unsigned long Size; + if (Server->Size(File,Size) == false || + Server->ModTime(File,FailTime) == false) + { + //Fail(true); + //_error->Error("File Not Found"); // Will be handled by Size + return false; + } + Res.Size = Size; + + // See if it is an IMS hit + if (Itm->LastModified == FailTime) { + Res.Size = 0; + Res.IMSHit = true; + URIDone(Res); + return true; + } + + // See if the file exists + struct stat Buf; + if (stat(Itm->DestFile.c_str(),&Buf) == 0) { + if (Size == (unsigned)Buf.st_size && FailTime == Buf.st_mtime) { + Res.Size = Buf.st_size; + Res.LastModified = Buf.st_mtime; + Res.ResumePoint = Buf.st_size; + URIDone(Res); + return true; + } + + // Resume? + if (FailTime == Buf.st_mtime && Size > (unsigned)Buf.st_size) + Res.ResumePoint = Buf.st_size; + } + + // Open the file + MD5Summation MD5; + { + FileFd Fd(Itm->DestFile,FileFd::WriteAny); + if (_error->PendingError() == true) + return false; + + URIStart(Res); + + FailFile = Itm->DestFile; + FailFile.c_str(); // Make sure we dont do a malloc in the signal handler + FailFd = Fd.Fd(); + + bool Missing; + if (Server->Get(File,Fd,Res.ResumePoint,MD5,Missing,Res.Size) == false) + { + Fd.Close(); + + // Timestamp + struct utimbuf UBuf; + UBuf.actime = FailTime; + UBuf.modtime = FailTime; + utime(FailFile.c_str(),&UBuf); + + // If the file is missing we hard fail otherwise transient fail + if (Missing == true) + return false; + Fail(true); + return true; + } + + Res.Size = Fd.Size(); + } + + Res.LastModified = FailTime; + Res.MD5Sum = MD5.Result(); + + // Timestamp + struct utimbuf UBuf; + UBuf.actime = FailTime; + UBuf.modtime = FailTime; + utime(Queue->DestFile.c_str(),&UBuf); + FailFd = -1; + + URIDone(Res); + + return true; +} + /*}}}*/ + +int main(int argc, const char *argv[]) +{ + RSHMethod Mth; + Prog = strrchr(argv[0],'/'); + Prog++; + return Mth.Run(); +} |