~mvo/apt/mvo

« back to all changes in this revision

Viewing changes to methods/rsh.cc

  • Committer: Arch Librarian
  • Date: 2004-09-20 16:56:32 UTC
  • Revision ID: Arch-1:apt@arch.ubuntu.com%apt--MAIN--0--patch-614
Join with aliencode
Author: jgg
Date: 2001-02-20 07:03:16 GMT
Join with aliencode

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// -*- mode: cpp; mode: fold -*-
 
2
// Description                                                          /*{{{*/
 
3
// $Id: rsh.cc,v 1.2 2001/02/20 07:03:18 jgg Exp $
 
4
/* ######################################################################
 
5
 
 
6
   RSH method - Transfer files via rsh compatible program
 
7
 
 
8
   Written by Ben Collins <bcollins@debian.org>, Copyright (c) 2000
 
9
   Licensed under the GNU General Public License v2 [no exception clauses]
 
10
 
 
11
   ##################################################################### */
 
12
                                                                        /*}}}*/
 
13
// Iclude Files                                                         /*{{{*/
 
14
#include "rsh.h"
 
15
#include <apt-pkg/error.h>
 
16
 
 
17
#include <sys/stat.h>
 
18
#include <sys/time.h>
 
19
#include <utime.h>
 
20
#include <unistd.h>
 
21
#include <signal.h>
 
22
#include <stdio.h>
 
23
#include <errno.h>
 
24
#include <stdarg.h>
 
25
                                                                        /*}}}*/
 
26
 
 
27
const char *Prog;
 
28
unsigned long TimeOut = 120;
 
29
time_t RSHMethod::FailTime = 0;
 
30
string RSHMethod::FailFile;
 
31
int RSHMethod::FailFd = -1;
 
32
 
 
33
// RSHConn::RSHConn - Constructor                                       /*{{{*/
 
34
// ---------------------------------------------------------------------
 
35
/* */
 
36
RSHConn::RSHConn(URI Srv) : Len(0), WriteFd(-1), ReadFd(-1),
 
37
                            ServerName(Srv), Process(-1) {}
 
38
                                                                        /*}}}*/
 
39
// RSHConn::RSHConn - Destructor                                        /*{{{*/
 
40
// ---------------------------------------------------------------------
 
41
/* */
 
42
RSHConn::~RSHConn()
 
43
{
 
44
   Close();
 
45
}
 
46
                                                                        /*}}}*/
 
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. */
 
51
void RSHConn::Close()
 
52
{
 
53
   if (Process == -1)
 
54
      return;
 
55
   
 
56
   close(WriteFd);
 
57
   close(ReadFd);
 
58
   kill(Process,SIGINT);
 
59
   ExecWait(Process,"",true);
 
60
   WriteFd = -1;
 
61
   ReadFd = -1;
 
62
   Process = -1;
 
63
}
 
64
                                                                        /*}}}*/
 
65
// RSHConn::Open - Connect to a host                                    /*{{{*/
 
66
// ---------------------------------------------------------------------
 
67
/* */
 
68
bool RSHConn::Open()
 
69
{
 
70
   // Use the already open connection if possible.
 
71
   if (Process != -1)
 
72
      return true;
 
73
 
 
74
   if (Connect(ServerName.Host,ServerName.User) == false)
 
75
      return false;
 
76
 
 
77
   return true;
 
78
}
 
79
                                                                        /*}}}*/
 
80
// RSHConn::Connect - Fire up rsh and connect                           /*{{{*/
 
81
// ---------------------------------------------------------------------
 
82
/* */
 
83
bool RSHConn::Connect(string Host, string User)
 
84
{
 
85
   // Create the pipes
 
86
   int Pipes[4] = {-1,-1,-1,-1};
 
87
   if (pipe(Pipes) != 0 || pipe(Pipes+2) != 0)
 
88
   {
 
89
      _error->Errno("pipe","Failed to create IPC pipe to subprocess");
 
90
      for (int I = 0; I != 4; I++)
 
91
         close(Pipes[I]);
 
92
      return false;
 
93
   }
 
94
   for (int I = 0; I != 4; I++)
 
95
      SetCloseExec(Pipes[I],true);
 
96
   
 
97
   Process = ExecFork();
 
98
 
 
99
   // The child
 
100
   if (Process == 0)
 
101
   {
 
102
      const char *Args[6];
 
103
      int i = 0;
 
104
 
 
105
      dup2(Pipes[1],STDOUT_FILENO);
 
106
      dup2(Pipes[2],STDIN_FILENO);
 
107
 
 
108
      // Probably should do
 
109
      // dup2(open("/dev/null",O_RDONLY),STDERR_FILENO);
 
110
 
 
111
      Args[i++] = Prog;
 
112
      if (User.empty() == false) {
 
113
         Args[i++] = "-l";
 
114
         Args[i++] = User.c_str();
 
115
      }
 
116
      if (Host.empty() == false) {
 
117
         Args[i++] = Host.c_str();
 
118
      }
 
119
      Args[i++] = "/bin/sh";
 
120
      Args[i] = 0;
 
121
      execvp(Args[0],(char **)Args);
 
122
      exit(100);
 
123
   }
 
124
 
 
125
   ReadFd = Pipes[0];
 
126
   WriteFd = Pipes[3];
 
127
   SetNonBlock(Pipes[0],true);
 
128
   SetNonBlock(Pipes[3],true);
 
129
   close(Pipes[1]);
 
130
   close(Pipes[2]);
 
131
   
 
132
   return true;
 
133
}
 
134
                                                                        /*}}}*/
 
135
// RSHConn::ReadLine - Very simple buffered read with timeout           /*{{{*/
 
136
// ---------------------------------------------------------------------
 
137
/* */
 
138
bool RSHConn::ReadLine(string &Text)
 
139
{
 
140
   if (Process == -1 || ReadFd == -1)
 
141
      return false;
 
142
   
 
143
   // Suck in a line
 
144
   while (Len < sizeof(Buffer))
 
145
   {
 
146
      // Scan the buffer for a new line
 
147
      for (unsigned int I = 0; I != Len; I++)
 
148
      {
 
149
         // Escape some special chars
 
150
         if (Buffer[I] == 0)
 
151
            Buffer[I] = '?';
 
152
 
 
153
         // End of line?
 
154
         if (Buffer[I] != '\n')
 
155
            continue;
 
156
 
 
157
         I++;
 
158
         Text = string(Buffer,I);
 
159
         memmove(Buffer,Buffer+I,Len - I);
 
160
         Len -= I;
 
161
         return true;
 
162
      }
 
163
 
 
164
      // Wait for some data..
 
165
      if (WaitFd(ReadFd,false,TimeOut) == false)
 
166
      {
 
167
         Close();
 
168
         return _error->Error("Connection timeout");
 
169
      }
 
170
 
 
171
      // Suck it back
 
172
      int Res = read(ReadFd,Buffer + Len,sizeof(Buffer) - Len);
 
173
      if (Res <= 0)
 
174
      {
 
175
         _error->Errno("read","Read error");
 
176
         Close();
 
177
         return false;
 
178
      }
 
179
      Len += Res;
 
180
   }
 
181
 
 
182
   return _error->Error("A response overflowed the buffer.");
 
183
}
 
184
                                                                        /*}}}*/
 
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,...)
 
190
{
 
191
   va_list args;
 
192
   va_start(args,Fmt);
 
193
 
 
194
   // sprintf the description
 
195
   char S[512];
 
196
   vsnprintf(S,sizeof(S) - 4,Fmt,args);
 
197
   if (Sync == true)
 
198
      strcat(S," 2> /dev/null || echo\n");
 
199
   else
 
200
      strcat(S," 2> /dev/null\n");
 
201
 
 
202
   // Send it off
 
203
   unsigned long Len = strlen(S);
 
204
   unsigned long Start = 0;
 
205
   while (Len != 0)
 
206
   {
 
207
      if (WaitFd(WriteFd,true,TimeOut) == false)
 
208
      {
 
209
         
 
210
         Close();
 
211
         return _error->Error("Connection timeout");
 
212
      }      
 
213
      
 
214
      int Res = write(WriteFd,S + Start,Len);
 
215
      if (Res <= 0)
 
216
      {
 
217
         _error->Errno("write","Write Error");
 
218
         Close();
 
219
         return false;
 
220
      }
 
221
 
 
222
      Len -= Res;
 
223
      Start += Res;
 
224
   }
 
225
 
 
226
   if (Sync == true)
 
227
      return ReadLine(Text);
 
228
   return true;
 
229
}
 
230
                                                                        /*}}}*/
 
231
// RSHConn::Size - Return the size of the file                          /*{{{*/
 
232
// ---------------------------------------------------------------------
 
233
/* Right now for successfull transfer the file size must be known in 
 
234
   advance. */
 
235
bool RSHConn::Size(const char *Path,unsigned long &Size)
 
236
{
 
237
   // Query the size
 
238
   string Msg;
 
239
   Size = 0;
 
240
 
 
241
   if (WriteMsg(Msg,true,"find %s -follow -printf '%%s\\n'",Path) == false)
 
242
      return false;
 
243
   
 
244
   // FIXME: Sense if the bad reply is due to a File Not Found. 
 
245
   
 
246
   char *End;
 
247
   Size = strtoul(Msg.c_str(),&End,10);
 
248
   if (End == Msg.c_str())
 
249
      return _error->Error("File Not Found");
 
250
   return true;
 
251
}
 
252
                                                                        /*}}}*/
 
253
// RSHConn::ModTime - Get the modification time in UTC                  /*{{{*/
 
254
// ---------------------------------------------------------------------
 
255
/* */
 
256
bool RSHConn::ModTime(const char *Path, time_t &Time)
 
257
{
 
258
   Time = time(&Time);
 
259
   // Query the mod time
 
260
   string Msg;
 
261
 
 
262
   if (WriteMsg(Msg,true,"TZ=UTC find %s -follow -printf '%%TY%%Tm%%Td%%TH%%TM%%TS\\n'",Path) == false)
 
263
      return false;
 
264
 
 
265
   // Parse it
 
266
   StrToTime(Msg,Time);
 
267
   return true;
 
268
}
 
269
                                                                        /*}}}*/
 
270
// RSHConn::Get - Get a file                                            /*{{{*/
 
271
// ---------------------------------------------------------------------
 
272
/* */
 
273
bool RSHConn::Get(const char *Path,FileFd &To,unsigned long Resume,
 
274
                  MD5Summation &MD5,bool &Missing, unsigned long Size)
 
275
{
 
276
   Missing = false;
 
277
 
 
278
   // Round to a 2048 byte block
 
279
   Resume = Resume - (Resume % 2048);
 
280
 
 
281
   if (To.Truncate(Resume) == false)
 
282
      return false;
 
283
   if (To.Seek(0) == false)
 
284
      return false;
 
285
 
 
286
   if (Resume != 0) {
 
287
      if (MD5.AddFD(To.Fd(),Resume) == false) {
 
288
         _error->Errno("read","Problem hashing file");
 
289
         return false;
 
290
      }
 
291
   }
 
292
   
 
293
   // FIXME: Detect file-not openable type errors.
 
294
   string Jnk;
 
295
   if (WriteMsg(Jnk,false,"dd if=%s bs=2048 skip=%u", Path, Resume / 2048) == false)
 
296
      return false;
 
297
 
 
298
   // Copy loop
 
299
   unsigned int MyLen = Resume;
 
300
   unsigned char Buffer[4096];
 
301
   while (MyLen < Size)
 
302
   {
 
303
      // Wait for some data..
 
304
      if (WaitFd(ReadFd,false,TimeOut) == false)
 
305
      {
 
306
         Close();
 
307
         return _error->Error("Data socket timed out");
 
308
      }
 
309
 
 
310
      // Read the data..
 
311
      int Res = read(ReadFd,Buffer,sizeof(Buffer));
 
312
      if (Res == 0)
 
313
      {
 
314
         Close();
 
315
         return _error->Error("Connection closed prematurely");
 
316
      }
 
317
      
 
318
      if (Res < 0)
 
319
      {
 
320
         if (errno == EAGAIN)
 
321
            continue;
 
322
         break;
 
323
      }
 
324
      MyLen += Res;
 
325
 
 
326
      MD5.Add(Buffer,Res);
 
327
      if (To.Write(Buffer,Res) == false)
 
328
      {
 
329
         Close();
 
330
         return false;
 
331
      }
 
332
   }
 
333
 
 
334
   return true;
 
335
}
 
336
                                                                        /*}}}*/
 
337
 
 
338
// RSHMethod::RSHMethod - Constructor                                   /*{{{*/
 
339
// ---------------------------------------------------------------------
 
340
/* */
 
341
RSHMethod::RSHMethod() : pkgAcqMethod("1.0")
 
342
{
 
343
   signal(SIGTERM,SigTerm);
 
344
   signal(SIGINT,SigTerm);
 
345
   Server = 0;
 
346
   FailFd = -1;
 
347
};
 
348
                                                                        /*}}}*/
 
349
// RSHMethod::SigTerm - Clean up and timestamp the files on exit        /*{{{*/
 
350
// ---------------------------------------------------------------------
 
351
/* */
 
352
void RSHMethod::SigTerm(int sig)
 
353
{
 
354
   if (FailFd == -1)
 
355
      _exit(100);
 
356
   close(FailFd);
 
357
 
 
358
   // Timestamp
 
359
   struct utimbuf UBuf;
 
360
   UBuf.actime = FailTime;
 
361
   UBuf.modtime = FailTime;
 
362
   utime(FailFile.c_str(),&UBuf);
 
363
 
 
364
   _exit(100);
 
365
}
 
366
                                                                        /*}}}*/
 
367
// RSHMethod::Fetch - Fetch a URI                                       /*{{{*/
 
368
// ---------------------------------------------------------------------
 
369
/* */
 
370
bool RSHMethod::Fetch(FetchItem *Itm)
 
371
{
 
372
   URI Get = Itm->Uri;
 
373
   const char *File = Get.Path.c_str();
 
374
   FetchResult Res;
 
375
   Res.Filename = Itm->DestFile;
 
376
   Res.IMSHit = false;
 
377
 
 
378
   // Connect to the server
 
379
   if (Server == 0 || Server->Comp(Get) == false) {
 
380
      delete Server;
 
381
      Server = new RSHConn(Get);
 
382
   }
 
383
 
 
384
   // Could not connect is a transient error..
 
385
   if (Server->Open() == false) {
 
386
      Server->Close();
 
387
      Fail(true);
 
388
      return true;
 
389
   }
 
390
 
 
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());
 
394
 
 
395
   // Get the files information
 
396
   unsigned long Size;
 
397
   if (Server->Size(File,Size) == false ||
 
398
       Server->ModTime(File,FailTime) == false)
 
399
   {
 
400
      //Fail(true);
 
401
      //_error->Error("File Not Found"); // Will be handled by Size
 
402
      return false;
 
403
   }
 
404
   Res.Size = Size;
 
405
 
 
406
   // See if it is an IMS hit
 
407
   if (Itm->LastModified == FailTime) {
 
408
      Res.Size = 0;
 
409
      Res.IMSHit = true;
 
410
      URIDone(Res);
 
411
      return true;
 
412
   }
 
413
 
 
414
   // See if the file exists
 
415
   struct stat Buf;
 
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;
 
421
         URIDone(Res);
 
422
         return true;
 
423
      }
 
424
 
 
425
      // Resume?
 
426
      if (FailTime == Buf.st_mtime && Size > (unsigned)Buf.st_size)
 
427
         Res.ResumePoint = Buf.st_size;
 
428
   }
 
429
 
 
430
   // Open the file
 
431
   MD5Summation MD5;
 
432
   {
 
433
      FileFd Fd(Itm->DestFile,FileFd::WriteAny);
 
434
      if (_error->PendingError() == true)
 
435
         return false;
 
436
      
 
437
      URIStart(Res);
 
438
 
 
439
      FailFile = Itm->DestFile;
 
440
      FailFile.c_str();   // Make sure we dont do a malloc in the signal handler
 
441
      FailFd = Fd.Fd();
 
442
 
 
443
      bool Missing;
 
444
      if (Server->Get(File,Fd,Res.ResumePoint,MD5,Missing,Res.Size) == false)
 
445
      {
 
446
         Fd.Close();
 
447
 
 
448
         // Timestamp
 
449
         struct utimbuf UBuf;
 
450
         UBuf.actime = FailTime;
 
451
         UBuf.modtime = FailTime;
 
452
         utime(FailFile.c_str(),&UBuf);
 
453
 
 
454
         // If the file is missing we hard fail otherwise transient fail
 
455
         if (Missing == true)
 
456
            return false;
 
457
         Fail(true);
 
458
         return true;
 
459
      }
 
460
 
 
461
      Res.Size = Fd.Size();
 
462
   }
 
463
 
 
464
   Res.LastModified = FailTime;
 
465
   Res.MD5Sum = MD5.Result();
 
466
 
 
467
   // Timestamp
 
468
   struct utimbuf UBuf;
 
469
   UBuf.actime = FailTime;
 
470
   UBuf.modtime = FailTime;
 
471
   utime(Queue->DestFile.c_str(),&UBuf);
 
472
   FailFd = -1;
 
473
 
 
474
   URIDone(Res);
 
475
 
 
476
   return true;
 
477
}
 
478
                                                                        /*}}}*/
 
479
 
 
480
int main(int argc, const char *argv[])
 
481
{
 
482
   RSHMethod Mth;
 
483
   Prog = strrchr(argv[0],'/');
 
484
   Prog++;
 
485
   return Mth.Run();
 
486
}