1
// -*- mode: cpp; mode: fold -*-
3
// $Id: rsh.cc,v 1.2 2001/02/20 07:03:18 jgg Exp $
4
/* ######################################################################
6
RSH method - Transfer files via rsh compatible program
8
Written by Ben Collins <bcollins@debian.org>, Copyright (c) 2000
9
Licensed under the GNU General Public License v2 [no exception clauses]
11
##################################################################### */
13
// Iclude Files /*{{{*/
15
#include <apt-pkg/error.h>
28
unsigned long TimeOut = 120;
29
time_t RSHMethod::FailTime = 0;
30
string RSHMethod::FailFile;
31
int RSHMethod::FailFd = -1;
33
// RSHConn::RSHConn - Constructor /*{{{*/
34
// ---------------------------------------------------------------------
36
RSHConn::RSHConn(URI Srv) : Len(0), WriteFd(-1), ReadFd(-1),
37
ServerName(Srv), Process(-1) {}
39
// RSHConn::RSHConn - Destructor /*{{{*/
40
// ---------------------------------------------------------------------
47
// RSHConn::Close - Forcibly terminate the connection /*{{{*/
48
// ---------------------------------------------------------------------
49
/* Often this is called when things have gone wrong to indicate that the
50
connection is no longer usable. */
59
ExecWait(Process,"",true);
65
// RSHConn::Open - Connect to a host /*{{{*/
66
// ---------------------------------------------------------------------
70
// Use the already open connection if possible.
74
if (Connect(ServerName.Host,ServerName.User) == false)
80
// RSHConn::Connect - Fire up rsh and connect /*{{{*/
81
// ---------------------------------------------------------------------
83
bool RSHConn::Connect(string Host, string User)
86
int Pipes[4] = {-1,-1,-1,-1};
87
if (pipe(Pipes) != 0 || pipe(Pipes+2) != 0)
89
_error->Errno("pipe","Failed to create IPC pipe to subprocess");
90
for (int I = 0; I != 4; I++)
94
for (int I = 0; I != 4; I++)
95
SetCloseExec(Pipes[I],true);
105
dup2(Pipes[1],STDOUT_FILENO);
106
dup2(Pipes[2],STDIN_FILENO);
108
// Probably should do
109
// dup2(open("/dev/null",O_RDONLY),STDERR_FILENO);
112
if (User.empty() == false) {
114
Args[i++] = User.c_str();
116
if (Host.empty() == false) {
117
Args[i++] = Host.c_str();
119
Args[i++] = "/bin/sh";
121
execvp(Args[0],(char **)Args);
127
SetNonBlock(Pipes[0],true);
128
SetNonBlock(Pipes[3],true);
135
// RSHConn::ReadLine - Very simple buffered read with timeout /*{{{*/
136
// ---------------------------------------------------------------------
138
bool RSHConn::ReadLine(string &Text)
140
if (Process == -1 || ReadFd == -1)
144
while (Len < sizeof(Buffer))
146
// Scan the buffer for a new line
147
for (unsigned int I = 0; I != Len; I++)
149
// Escape some special chars
154
if (Buffer[I] != '\n')
158
Text = string(Buffer,I);
159
memmove(Buffer,Buffer+I,Len - I);
164
// Wait for some data..
165
if (WaitFd(ReadFd,false,TimeOut) == false)
168
return _error->Error("Connection timeout");
172
int Res = read(ReadFd,Buffer + Len,sizeof(Buffer) - Len);
175
_error->Errno("read","Read error");
182
return _error->Error("A response overflowed the buffer.");
185
// RSHConn::WriteMsg - Send a message with optional remote sync. /*{{{*/
186
// ---------------------------------------------------------------------
187
/* The remote sync flag appends a || echo which will insert blank line
188
once the command completes. */
189
bool RSHConn::WriteMsg(string &Text,bool Sync,const char *Fmt,...)
194
// sprintf the description
196
vsnprintf(S,sizeof(S) - 4,Fmt,args);
198
strcat(S," 2> /dev/null || echo\n");
200
strcat(S," 2> /dev/null\n");
203
unsigned long Len = strlen(S);
204
unsigned long Start = 0;
207
if (WaitFd(WriteFd,true,TimeOut) == false)
211
return _error->Error("Connection timeout");
214
int Res = write(WriteFd,S + Start,Len);
217
_error->Errno("write","Write Error");
227
return ReadLine(Text);
231
// RSHConn::Size - Return the size of the file /*{{{*/
232
// ---------------------------------------------------------------------
233
/* Right now for successfull transfer the file size must be known in
235
bool RSHConn::Size(const char *Path,unsigned long &Size)
241
if (WriteMsg(Msg,true,"find %s -follow -printf '%%s\\n'",Path) == false)
244
// FIXME: Sense if the bad reply is due to a File Not Found.
247
Size = strtoul(Msg.c_str(),&End,10);
248
if (End == Msg.c_str())
249
return _error->Error("File Not Found");
253
// RSHConn::ModTime - Get the modification time in UTC /*{{{*/
254
// ---------------------------------------------------------------------
256
bool RSHConn::ModTime(const char *Path, time_t &Time)
259
// Query the mod time
262
if (WriteMsg(Msg,true,"TZ=UTC find %s -follow -printf '%%TY%%Tm%%Td%%TH%%TM%%TS\\n'",Path) == false)
270
// RSHConn::Get - Get a file /*{{{*/
271
// ---------------------------------------------------------------------
273
bool RSHConn::Get(const char *Path,FileFd &To,unsigned long Resume,
274
MD5Summation &MD5,bool &Missing, unsigned long Size)
278
// Round to a 2048 byte block
279
Resume = Resume - (Resume % 2048);
281
if (To.Truncate(Resume) == false)
283
if (To.Seek(0) == false)
287
if (MD5.AddFD(To.Fd(),Resume) == false) {
288
_error->Errno("read","Problem hashing file");
293
// FIXME: Detect file-not openable type errors.
295
if (WriteMsg(Jnk,false,"dd if=%s bs=2048 skip=%u", Path, Resume / 2048) == false)
299
unsigned int MyLen = Resume;
300
unsigned char Buffer[4096];
303
// Wait for some data..
304
if (WaitFd(ReadFd,false,TimeOut) == false)
307
return _error->Error("Data socket timed out");
311
int Res = read(ReadFd,Buffer,sizeof(Buffer));
315
return _error->Error("Connection closed prematurely");
327
if (To.Write(Buffer,Res) == false)
338
// RSHMethod::RSHMethod - Constructor /*{{{*/
339
// ---------------------------------------------------------------------
341
RSHMethod::RSHMethod() : pkgAcqMethod("1.0")
343
signal(SIGTERM,SigTerm);
344
signal(SIGINT,SigTerm);
349
// RSHMethod::SigTerm - Clean up and timestamp the files on exit /*{{{*/
350
// ---------------------------------------------------------------------
352
void RSHMethod::SigTerm(int sig)
360
UBuf.actime = FailTime;
361
UBuf.modtime = FailTime;
362
utime(FailFile.c_str(),&UBuf);
367
// RSHMethod::Fetch - Fetch a URI /*{{{*/
368
// ---------------------------------------------------------------------
370
bool RSHMethod::Fetch(FetchItem *Itm)
373
const char *File = Get.Path.c_str();
375
Res.Filename = Itm->DestFile;
378
// Connect to the server
379
if (Server == 0 || Server->Comp(Get) == false) {
381
Server = new RSHConn(Get);
384
// Could not connect is a transient error..
385
if (Server->Open() == false) {
391
// We say this mainly because the pause here is for the
392
// ssh connection that is still going
393
Status("Connecting to %s", Get.Host.c_str());
395
// Get the files information
397
if (Server->Size(File,Size) == false ||
398
Server->ModTime(File,FailTime) == false)
401
//_error->Error("File Not Found"); // Will be handled by Size
406
// See if it is an IMS hit
407
if (Itm->LastModified == FailTime) {
414
// See if the file exists
416
if (stat(Itm->DestFile.c_str(),&Buf) == 0) {
417
if (Size == (unsigned)Buf.st_size && FailTime == Buf.st_mtime) {
418
Res.Size = Buf.st_size;
419
Res.LastModified = Buf.st_mtime;
420
Res.ResumePoint = Buf.st_size;
426
if (FailTime == Buf.st_mtime && Size > (unsigned)Buf.st_size)
427
Res.ResumePoint = Buf.st_size;
433
FileFd Fd(Itm->DestFile,FileFd::WriteAny);
434
if (_error->PendingError() == true)
439
FailFile = Itm->DestFile;
440
FailFile.c_str(); // Make sure we dont do a malloc in the signal handler
444
if (Server->Get(File,Fd,Res.ResumePoint,MD5,Missing,Res.Size) == false)
450
UBuf.actime = FailTime;
451
UBuf.modtime = FailTime;
452
utime(FailFile.c_str(),&UBuf);
454
// If the file is missing we hard fail otherwise transient fail
461
Res.Size = Fd.Size();
464
Res.LastModified = FailTime;
465
Res.MD5Sum = MD5.Result();
469
UBuf.actime = FailTime;
470
UBuf.modtime = FailTime;
471
utime(Queue->DestFile.c_str(),&UBuf);
480
int main(int argc, const char *argv[])
483
Prog = strrchr(argv[0],'/');