rsh.cc 14 KB

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