]> git.saurik.com Git - apt.git/blame - methods/rsh.cc
activate AI_IDN by default to support IDN domains
[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/* */
23e64f6d 390RSHMethod::RSHMethod(std::string const &pProg) : aptMethod(pProg.c_str(),"1.0",SendConfig), Prog(pProg)
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 401{
23e64f6d 402 if (aptMethod::Configuration(Message) == false)
5e775e59
AL
403 return false;
404
23e64f6d
DK
405 std::string const timeconf = std::string("Acquire::") + Prog + "::Timeout";
406 TimeOut = _config->FindI(timeconf, TimeOut);
407 std::string const optsconf = std::string("Acquire::") + Prog + "::Options";
408 RshOptions = _config->Tree(optsconf.c_str());
5e775e59
AL
409
410 return true;
411}
412 /*}}}*/
b2e465d6
AL
413// RSHMethod::SigTerm - Clean up and timestamp the files on exit /*{{{*/
414// ---------------------------------------------------------------------
415/* */
65512241 416void RSHMethod::SigTerm(int)
b2e465d6
AL
417{
418 if (FailFd == -1)
419 _exit(100);
b2e465d6 420
9ce3cfc9 421 // Transfer the modification times
246bbb61 422 struct timeval times[2];
9ce3cfc9
DK
423 times[0].tv_sec = FailTime;
424 times[1].tv_sec = FailTime;
246bbb61
DK
425 times[0].tv_usec = times[1].tv_usec = 0;
426 utimes(FailFile.c_str(), times);
9ce3cfc9 427 close(FailFd);
b2e465d6
AL
428
429 _exit(100);
430}
431 /*}}}*/
432// RSHMethod::Fetch - Fetch a URI /*{{{*/
433// ---------------------------------------------------------------------
434/* */
435bool RSHMethod::Fetch(FetchItem *Itm)
436{
437 URI Get = Itm->Uri;
438 const char *File = Get.Path.c_str();
439 FetchResult Res;
440 Res.Filename = Itm->DestFile;
441 Res.IMSHit = false;
442
443 // Connect to the server
444 if (Server == 0 || Server->Comp(Get) == false) {
445 delete Server;
446 Server = new RSHConn(Get);
447 }
448
449 // Could not connect is a transient error..
450 if (Server->Open() == false) {
451 Server->Close();
452 Fail(true);
453 return true;
454 }
455
456 // We say this mainly because the pause here is for the
457 // ssh connection that is still going
dc738e7a 458 Status(_("Connecting to %s"), Get.Host.c_str());
b2e465d6
AL
459
460 // Get the files information
650faab0 461 unsigned long long Size;
b2e465d6
AL
462 if (Server->Size(File,Size) == false ||
463 Server->ModTime(File,FailTime) == false)
464 {
465 //Fail(true);
db0db9fe 466 //_error->Error(_("File not found")); // Will be handled by Size
b2e465d6
AL
467 return false;
468 }
469 Res.Size = Size;
470
471 // See if it is an IMS hit
472 if (Itm->LastModified == FailTime) {
473 Res.Size = 0;
474 Res.IMSHit = true;
475 URIDone(Res);
476 return true;
477 }
478
479 // See if the file exists
480 struct stat Buf;
481 if (stat(Itm->DestFile.c_str(),&Buf) == 0) {
650faab0 482 if (Size == (unsigned long long)Buf.st_size && FailTime == Buf.st_mtime) {
b2e465d6
AL
483 Res.Size = Buf.st_size;
484 Res.LastModified = Buf.st_mtime;
485 Res.ResumePoint = Buf.st_size;
486 URIDone(Res);
487 return true;
488 }
489
490 // Resume?
650faab0 491 if (FailTime == Buf.st_mtime && Size > (unsigned long long)Buf.st_size)
b2e465d6
AL
492 Res.ResumePoint = Buf.st_size;
493 }
494
495 // Open the file
9224ce3d 496 Hashes Hash(Itm->ExpectedHashes);
b2e465d6
AL
497 {
498 FileFd Fd(Itm->DestFile,FileFd::WriteAny);
499 if (_error->PendingError() == true)
500 return false;
501
502 URIStart(Res);
503
504 FailFile = Itm->DestFile;
3a8776a3 505 FailFile.c_str(); // Make sure we don't do a malloc in the signal handler
b2e465d6
AL
506 FailFd = Fd.Fd();
507
508 bool Missing;
63b1700f 509 if (Server->Get(File,Fd,Res.ResumePoint,Hash,Missing,Res.Size) == false)
b2e465d6
AL
510 {
511 Fd.Close();
512
513 // Timestamp
246bbb61 514 struct timeval times[2];
9ce3cfc9
DK
515 times[0].tv_sec = FailTime;
516 times[1].tv_sec = FailTime;
246bbb61
DK
517 times[0].tv_usec = times[1].tv_usec = 0;
518 utimes(FailFile.c_str(), times);
b2e465d6
AL
519
520 // If the file is missing we hard fail otherwise transient fail
521 if (Missing == true)
522 return false;
523 Fail(true);
524 return true;
525 }
526
527 Res.Size = Fd.Size();
246bbb61 528 struct timeval times[2];
9ce3cfc9
DK
529 times[0].tv_sec = FailTime;
530 times[1].tv_sec = FailTime;
246bbb61
DK
531 times[0].tv_usec = times[1].tv_usec = 0;
532 utimes(Fd.Name().c_str(), times);
9ce3cfc9 533 FailFd = -1;
b2e465d6
AL
534 }
535
536 Res.LastModified = FailTime;
a7c835af 537 Res.TakeHashes(Hash);
b2e465d6 538
b2e465d6
AL
539 URIDone(Res);
540
541 return true;
542}
543 /*}}}*/
544
65512241 545int main(int, const char *argv[])
b2e465d6 546{
b25423f6
MZ
547 setlocale(LC_ALL, "");
548
23e64f6d 549 RSHMethod Mth(flNotDir(argv[0]));
b2e465d6
AL
550 return Mth.Run();
551}