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