]> git.saurik.com Git - apt.git/blame - methods/rsh.cc
wrap every unlink call to check for != /dev/null
[apt.git] / methods / rsh.cc
CommitLineData
b2e465d6
AL
1// -*- mode: cpp; mode: fold -*-
2// Description /*{{{*/
7db98ffc 3// $Id: rsh.cc,v 1.6.2.1 2004/01/16 18:58:50 mdz Exp $
b2e465d6
AL
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 /*}}}*/
5e775e59 13// Include Files /*{{{*/
ea542140
DK
14#include <config.h>
15
b2e465d6 16#include <apt-pkg/error.h>
472ff00e
DK
17#include <apt-pkg/fileutl.h>
18#include <apt-pkg/hashes.h>
19#include <apt-pkg/configuration.h>
453b82a3
DK
20#include <apt-pkg/acquire-method.h>
21#include <apt-pkg/strutl.h>
b2e465d6 22
453b82a3
DK
23#include <stdlib.h>
24#include <string.h>
b2e465d6
AL
25#include <sys/stat.h>
26#include <sys/time.h>
b2e465d6
AL
27#include <unistd.h>
28#include <signal.h>
29#include <stdio.h>
30#include <errno.h>
31#include <stdarg.h>
ea542140
DK
32#include "rsh.h"
33
d77559ac 34#include <apti18n.h>
b2e465d6
AL
35 /*}}}*/
36
37const char *Prog;
38unsigned long TimeOut = 120;
5e775e59 39Configuration::Item const *RshOptions = 0;
b2e465d6 40time_t RSHMethod::FailTime = 0;
8f3ba4e8 41std::string RSHMethod::FailFile;
b2e465d6
AL
42int RSHMethod::FailFd = -1;
43
44// RSHConn::RSHConn - Constructor /*{{{*/
45// ---------------------------------------------------------------------
46/* */
47RSHConn::RSHConn(URI Srv) : Len(0), WriteFd(-1), ReadFd(-1),
dcaa1185
DK
48 ServerName(Srv), Process(-1) {
49 Buffer[0] = '\0';
50}
b2e465d6
AL
51 /*}}}*/
52// RSHConn::RSHConn - Destructor /*{{{*/
53// ---------------------------------------------------------------------
54/* */
55RSHConn::~RSHConn()
56{
57 Close();
58}
59 /*}}}*/
60// RSHConn::Close - Forcibly terminate the connection /*{{{*/
61// ---------------------------------------------------------------------
62/* Often this is called when things have gone wrong to indicate that the
63 connection is no longer usable. */
64void RSHConn::Close()
65{
66 if (Process == -1)
67 return;
68
69 close(WriteFd);
70 close(ReadFd);
71 kill(Process,SIGINT);
72 ExecWait(Process,"",true);
73 WriteFd = -1;
74 ReadFd = -1;
75 Process = -1;
76}
77 /*}}}*/
78// RSHConn::Open - Connect to a host /*{{{*/
79// ---------------------------------------------------------------------
80/* */
81bool RSHConn::Open()
82{
83 // Use the already open connection if possible.
84 if (Process != -1)
85 return true;
86
c7872a2c 87 if (Connect(ServerName.Host,ServerName.Port,ServerName.User) == false)
b2e465d6
AL
88 return false;
89
90 return true;
91}
92 /*}}}*/
93// RSHConn::Connect - Fire up rsh and connect /*{{{*/
94// ---------------------------------------------------------------------
95/* */
c7872a2c 96bool RSHConn::Connect(std::string Host, unsigned int Port, std::string User)
b2e465d6 97{
c7872a2c
DH
98 char *PortStr = NULL;
99 if (Port != 0)
100 {
101 if (asprintf (&PortStr, "%d", Port) == -1 || PortStr == NULL)
102 return _error->Errno("asprintf", _("Failed"));
103 }
104
b2e465d6
AL
105 // Create the pipes
106 int Pipes[4] = {-1,-1,-1,-1};
107 if (pipe(Pipes) != 0 || pipe(Pipes+2) != 0)
108 {
dc738e7a 109 _error->Errno("pipe",_("Failed to create IPC pipe to subprocess"));
b2e465d6
AL
110 for (int I = 0; I != 4; I++)
111 close(Pipes[I]);
112 return false;
113 }
114 for (int I = 0; I != 4; I++)
115 SetCloseExec(Pipes[I],true);
116
117 Process = ExecFork();
118
119 // The child
120 if (Process == 0)
121 {
5e775e59
AL
122 const char *Args[400];
123 unsigned int i = 0;
b2e465d6
AL
124
125 dup2(Pipes[1],STDOUT_FILENO);
126 dup2(Pipes[2],STDIN_FILENO);
127
128 // Probably should do
129 // dup2(open("/dev/null",O_RDONLY),STDERR_FILENO);
130
c5734bad
MV
131 Args[i++] = Prog;
132
5e775e59
AL
133 // Insert user-supplied command line options
134 Configuration::Item const *Opts = RshOptions;
135 if (Opts != 0)
136 {
137 Opts = Opts->Child;
138 for (; Opts != 0; Opts = Opts->Next)
139 {
140 if (Opts->Value.empty() == true)
141 continue;
142 Args[i++] = Opts->Value.c_str();
143 }
144 }
145
b2e465d6
AL
146 if (User.empty() == false) {
147 Args[i++] = "-l";
148 Args[i++] = User.c_str();
149 }
c7872a2c
DH
150 if (PortStr != NULL) {
151 Args[i++] = "-p";
152 Args[i++] = PortStr;
153 }
b2e465d6
AL
154 if (Host.empty() == false) {
155 Args[i++] = Host.c_str();
156 }
157 Args[i++] = "/bin/sh";
158 Args[i] = 0;
159 execvp(Args[0],(char **)Args);
160 exit(100);
161 }
162
c7872a2c
DH
163 if (PortStr != NULL)
164 free(PortStr);
165
b2e465d6
AL
166 ReadFd = Pipes[0];
167 WriteFd = Pipes[3];
168 SetNonBlock(Pipes[0],true);
169 SetNonBlock(Pipes[3],true);
170 close(Pipes[1]);
171 close(Pipes[2]);
172
173 return true;
c7872a2c
DH
174}
175bool RSHConn::Connect(std::string Host, std::string User)
176{
177 return Connect(Host, 0, User);
b2e465d6
AL
178}
179 /*}}}*/
180// RSHConn::ReadLine - Very simple buffered read with timeout /*{{{*/
181// ---------------------------------------------------------------------
182/* */
8f3ba4e8 183bool RSHConn::ReadLine(std::string &Text)
b2e465d6
AL
184{
185 if (Process == -1 || ReadFd == -1)
186 return false;
187
188 // Suck in a line
189 while (Len < sizeof(Buffer))
190 {
191 // Scan the buffer for a new line
192 for (unsigned int I = 0; I != Len; I++)
193 {
194 // Escape some special chars
195 if (Buffer[I] == 0)
196 Buffer[I] = '?';
197
198 // End of line?
199 if (Buffer[I] != '\n')
200 continue;
201
202 I++;
8f3ba4e8 203 Text = std::string(Buffer,I);
b2e465d6
AL
204 memmove(Buffer,Buffer+I,Len - I);
205 Len -= I;
206 return true;
207 }
208
209 // Wait for some data..
210 if (WaitFd(ReadFd,false,TimeOut) == false)
211 {
212 Close();
dc738e7a 213 return _error->Error(_("Connection timeout"));
b2e465d6
AL
214 }
215
216 // Suck it back
217 int Res = read(ReadFd,Buffer + Len,sizeof(Buffer) - Len);
218 if (Res <= 0)
219 {
dc738e7a 220 _error->Errno("read",_("Read error"));
b2e465d6
AL
221 Close();
222 return false;
223 }
224 Len += Res;
225 }
226
dc738e7a 227 return _error->Error(_("A response overflowed the buffer."));
b2e465d6
AL
228}
229 /*}}}*/
230// RSHConn::WriteMsg - Send a message with optional remote sync. /*{{{*/
231// ---------------------------------------------------------------------
232/* The remote sync flag appends a || echo which will insert blank line
233 once the command completes. */
8f3ba4e8 234bool RSHConn::WriteMsg(std::string &Text,bool Sync,const char *Fmt,...)
b2e465d6
AL
235{
236 va_list args;
237 va_start(args,Fmt);
238
180b6932
MV
239 // sprintf into a buffer
240 char Tmp[1024];
241 vsnprintf(Tmp,sizeof(Tmp),Fmt,args);
11d0fb91
MV
242 va_end(args);
243
180b6932
MV
244 // concat to create the real msg
245 std::string Msg;
b2e465d6 246 if (Sync == true)
180b6932 247 Msg = std::string(Tmp) + " 2> /dev/null || echo\n";
b2e465d6 248 else
180b6932 249 Msg = std::string(Tmp) + " 2> /dev/null\n";
b2e465d6
AL
250
251 // Send it off
180b6932 252 const char *S = Msg.c_str();
b2e465d6
AL
253 unsigned long Len = strlen(S);
254 unsigned long Start = 0;
255 while (Len != 0)
256 {
257 if (WaitFd(WriteFd,true,TimeOut) == false)
258 {
259
260 Close();
dc738e7a 261 return _error->Error(_("Connection timeout"));
b2e465d6
AL
262 }
263
264 int Res = write(WriteFd,S + Start,Len);
265 if (Res <= 0)
266 {
db0db9fe 267 _error->Errno("write",_("Write error"));
b2e465d6
AL
268 Close();
269 return false;
270 }
271
272 Len -= Res;
273 Start += Res;
274 }
275
276 if (Sync == true)
277 return ReadLine(Text);
278 return true;
279}
280 /*}}}*/
281// RSHConn::Size - Return the size of the file /*{{{*/
282// ---------------------------------------------------------------------
1e3f4083 283/* Right now for successful transfer the file size must be known in
b2e465d6 284 advance. */
650faab0 285bool RSHConn::Size(const char *Path,unsigned long long &Size)
b2e465d6
AL
286{
287 // Query the size
8f3ba4e8 288 std::string Msg;
b2e465d6
AL
289 Size = 0;
290
291 if (WriteMsg(Msg,true,"find %s -follow -printf '%%s\\n'",Path) == false)
292 return false;
293
294 // FIXME: Sense if the bad reply is due to a File Not Found.
295
296 char *End;
650faab0 297 Size = strtoull(Msg.c_str(),&End,10);
b2e465d6 298 if (End == Msg.c_str())
db0db9fe 299 return _error->Error(_("File not found"));
b2e465d6
AL
300 return true;
301}
302 /*}}}*/
303// RSHConn::ModTime - Get the modification time in UTC /*{{{*/
304// ---------------------------------------------------------------------
305/* */
306bool RSHConn::ModTime(const char *Path, time_t &Time)
307{
308 Time = time(&Time);
309 // Query the mod time
8f3ba4e8 310 std::string Msg;
b2e465d6
AL
311
312 if (WriteMsg(Msg,true,"TZ=UTC find %s -follow -printf '%%TY%%Tm%%Td%%TH%%TM%%TS\\n'",Path) == false)
313 return false;
314
315 // Parse it
96cc64a5 316 return FTPMDTMStrToTime(Msg.c_str(), Time);
b2e465d6
AL
317}
318 /*}}}*/
319// RSHConn::Get - Get a file /*{{{*/
320// ---------------------------------------------------------------------
321/* */
650faab0
DK
322bool RSHConn::Get(const char *Path,FileFd &To,unsigned long long Resume,
323 Hashes &Hash,bool &Missing, unsigned long long Size)
b2e465d6
AL
324{
325 Missing = false;
326
327 // Round to a 2048 byte block
328 Resume = Resume - (Resume % 2048);
329
330 if (To.Truncate(Resume) == false)
331 return false;
332 if (To.Seek(0) == false)
333 return false;
334
335 if (Resume != 0) {
109eb151 336 if (Hash.AddFD(To,Resume) == false) {
dc738e7a 337 _error->Errno("read",_("Problem hashing file"));
b2e465d6
AL
338 return false;
339 }
340 }
341
342 // FIXME: Detect file-not openable type errors.
8f3ba4e8 343 std::string Jnk;
b2e465d6
AL
344 if (WriteMsg(Jnk,false,"dd if=%s bs=2048 skip=%u", Path, Resume / 2048) == false)
345 return false;
346
347 // Copy loop
650faab0 348 unsigned long long MyLen = Resume;
b2e465d6
AL
349 unsigned char Buffer[4096];
350 while (MyLen < Size)
351 {
352 // Wait for some data..
353 if (WaitFd(ReadFd,false,TimeOut) == false)
354 {
355 Close();
dc738e7a 356 return _error->Error(_("Data socket timed out"));
b2e465d6
AL
357 }
358
359 // Read the data..
360 int Res = read(ReadFd,Buffer,sizeof(Buffer));
361 if (Res == 0)
362 {
363 Close();
dc738e7a 364 return _error->Error(_("Connection closed prematurely"));
b2e465d6
AL
365 }
366
367 if (Res < 0)
368 {
369 if (errno == EAGAIN)
370 continue;
371 break;
372 }
373 MyLen += Res;
374
63b1700f 375 Hash.Add(Buffer,Res);
b2e465d6
AL
376 if (To.Write(Buffer,Res) == false)
377 {
378 Close();
379 return false;
380 }
381 }
382
383 return true;
384}
385 /*}}}*/
386
387// RSHMethod::RSHMethod - Constructor /*{{{*/
388// ---------------------------------------------------------------------
389/* */
5e775e59 390RSHMethod::RSHMethod() : pkgAcqMethod("1.0",SendConfig)
b2e465d6
AL
391{
392 signal(SIGTERM,SigTerm);
393 signal(SIGINT,SigTerm);
394 Server = 0;
395 FailFd = -1;
d3e8fbb3 396}
b2e465d6 397 /*}}}*/
5e775e59
AL
398// RSHMethod::Configuration - Handle a configuration message /*{{{*/
399// ---------------------------------------------------------------------
8f3ba4e8 400bool RSHMethod::Configuration(std::string Message)
5e775e59
AL
401{
402 char ProgStr[100];
403
404 if (pkgAcqMethod::Configuration(Message) == false)
405 return false;
406
407 snprintf(ProgStr, sizeof ProgStr, "Acquire::%s::Timeout", Prog);
408 TimeOut = _config->FindI(ProgStr,TimeOut);
409 snprintf(ProgStr, sizeof ProgStr, "Acquire::%s::Options", Prog);
410 RshOptions = _config->Tree(ProgStr);
411
412 return true;
413}
414 /*}}}*/
b2e465d6
AL
415// RSHMethod::SigTerm - Clean up and timestamp the files on exit /*{{{*/
416// ---------------------------------------------------------------------
417/* */
65512241 418void RSHMethod::SigTerm(int)
b2e465d6
AL
419{
420 if (FailFd == -1)
421 _exit(100);
b2e465d6 422
9ce3cfc9 423 // Transfer the modification times
246bbb61 424 struct timeval times[2];
9ce3cfc9
DK
425 times[0].tv_sec = FailTime;
426 times[1].tv_sec = FailTime;
246bbb61
DK
427 times[0].tv_usec = times[1].tv_usec = 0;
428 utimes(FailFile.c_str(), times);
9ce3cfc9 429 close(FailFd);
b2e465d6
AL
430
431 _exit(100);
432}
433 /*}}}*/
434// RSHMethod::Fetch - Fetch a URI /*{{{*/
435// ---------------------------------------------------------------------
436/* */
437bool RSHMethod::Fetch(FetchItem *Itm)
438{
439 URI Get = Itm->Uri;
440 const char *File = Get.Path.c_str();
441 FetchResult Res;
442 Res.Filename = Itm->DestFile;
443 Res.IMSHit = false;
444
445 // Connect to the server
446 if (Server == 0 || Server->Comp(Get) == false) {
447 delete Server;
448 Server = new RSHConn(Get);
449 }
450
451 // Could not connect is a transient error..
452 if (Server->Open() == false) {
453 Server->Close();
454 Fail(true);
455 return true;
456 }
457
458 // We say this mainly because the pause here is for the
459 // ssh connection that is still going
dc738e7a 460 Status(_("Connecting to %s"), Get.Host.c_str());
b2e465d6
AL
461
462 // Get the files information
650faab0 463 unsigned long long Size;
b2e465d6
AL
464 if (Server->Size(File,Size) == false ||
465 Server->ModTime(File,FailTime) == false)
466 {
467 //Fail(true);
db0db9fe 468 //_error->Error(_("File not found")); // Will be handled by Size
b2e465d6
AL
469 return false;
470 }
471 Res.Size = Size;
472
473 // See if it is an IMS hit
474 if (Itm->LastModified == FailTime) {
475 Res.Size = 0;
476 Res.IMSHit = true;
477 URIDone(Res);
478 return true;
479 }
480
481 // See if the file exists
482 struct stat Buf;
483 if (stat(Itm->DestFile.c_str(),&Buf) == 0) {
650faab0 484 if (Size == (unsigned long long)Buf.st_size && FailTime == Buf.st_mtime) {
b2e465d6
AL
485 Res.Size = Buf.st_size;
486 Res.LastModified = Buf.st_mtime;
487 Res.ResumePoint = Buf.st_size;
488 URIDone(Res);
489 return true;
490 }
491
492 // Resume?
650faab0 493 if (FailTime == Buf.st_mtime && Size > (unsigned long long)Buf.st_size)
b2e465d6
AL
494 Res.ResumePoint = Buf.st_size;
495 }
496
497 // Open the file
9224ce3d 498 Hashes Hash(Itm->ExpectedHashes);
b2e465d6
AL
499 {
500 FileFd Fd(Itm->DestFile,FileFd::WriteAny);
501 if (_error->PendingError() == true)
502 return false;
503
504 URIStart(Res);
505
506 FailFile = Itm->DestFile;
3a8776a3 507 FailFile.c_str(); // Make sure we don't do a malloc in the signal handler
b2e465d6
AL
508 FailFd = Fd.Fd();
509
510 bool Missing;
63b1700f 511 if (Server->Get(File,Fd,Res.ResumePoint,Hash,Missing,Res.Size) == false)
b2e465d6
AL
512 {
513 Fd.Close();
514
515 // Timestamp
246bbb61 516 struct timeval times[2];
9ce3cfc9
DK
517 times[0].tv_sec = FailTime;
518 times[1].tv_sec = FailTime;
246bbb61
DK
519 times[0].tv_usec = times[1].tv_usec = 0;
520 utimes(FailFile.c_str(), times);
b2e465d6
AL
521
522 // If the file is missing we hard fail otherwise transient fail
523 if (Missing == true)
524 return false;
525 Fail(true);
526 return true;
527 }
528
529 Res.Size = Fd.Size();
246bbb61 530 struct timeval times[2];
9ce3cfc9
DK
531 times[0].tv_sec = FailTime;
532 times[1].tv_sec = FailTime;
246bbb61
DK
533 times[0].tv_usec = times[1].tv_usec = 0;
534 utimes(Fd.Name().c_str(), times);
9ce3cfc9 535 FailFd = -1;
b2e465d6
AL
536 }
537
538 Res.LastModified = FailTime;
a7c835af 539 Res.TakeHashes(Hash);
b2e465d6 540
b2e465d6
AL
541 URIDone(Res);
542
543 return true;
544}
545 /*}}}*/
546
65512241 547int main(int, const char *argv[])
b2e465d6 548{
b25423f6
MZ
549 setlocale(LC_ALL, "");
550
b2e465d6
AL
551 RSHMethod Mth;
552 Prog = strrchr(argv[0],'/');
553 Prog++;
554 return Mth.Run();
555}