apt-cdrom.cc 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
  1. // -*- mode: cpp; mode: fold -*-
  2. // Description /*{{{*/
  3. // $Id: apt-cdrom.cc,v 1.3 1998/11/28 00:00:36 jgg Exp $
  4. /* ######################################################################
  5. APT CDROM - Tool for handling APT's CDROM database.
  6. Currently the only option is 'add' which will take the current CD
  7. in the drive and add it into the database.
  8. ##################################################################### */
  9. /*}}}*/
  10. // Include Files /*{{{*/
  11. #include <apt-pkg/cmndline.h>
  12. #include <apt-pkg/error.h>
  13. #include <apt-pkg/init.h>
  14. #include <apt-pkg/md5.h>
  15. #include <apt-pkg/fileutl.h>
  16. #include <apt-pkg/progress.h>
  17. #include <apt-pkg/tagfile.h>
  18. #include <strutl.h>
  19. #include <config.h>
  20. #include <iostream>
  21. #include <fstream>
  22. #include <vector>
  23. #include <algorithm>
  24. #include <sys/wait.h>
  25. #include <sys/errno.h>
  26. #include <sys/vfs.h>
  27. #include <sys/stat.h>
  28. #include <fcntl.h>
  29. #include <dirent.h>
  30. #include <unistd.h>
  31. #include <stdio.h>
  32. /*}}}*/
  33. // UnmountCdrom - Unmount a cdrom /*{{{*/
  34. // ---------------------------------------------------------------------
  35. /* */
  36. bool UnmountCdrom(string Path)
  37. {
  38. int Child = fork();
  39. if (Child < -1)
  40. return _error->Errno("fork","Failed to fork");
  41. // The child
  42. if (Child == 0)
  43. {
  44. // Make all the fds /dev/null
  45. for (int I = 0; I != 10; I++)
  46. close(I);
  47. for (int I = 0; I != 3; I++)
  48. dup2(open("/dev/null",O_RDWR),I);
  49. const char *Args[10];
  50. Args[0] = "umount";
  51. Args[1] = Path.c_str();
  52. Args[2] = 0;
  53. execvp(Args[0],(char **)Args);
  54. exit(100);
  55. }
  56. // Wait for mount
  57. int Status = 0;
  58. while (waitpid(Child,&Status,0) != Child)
  59. {
  60. if (errno == EINTR)
  61. continue;
  62. return _error->Errno("waitpid","Couldn't wait for subprocess");
  63. }
  64. // Check for an error code.
  65. if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
  66. return false;
  67. return true;
  68. }
  69. /*}}}*/
  70. // MountCdrom - Mount a cdrom /*{{{*/
  71. // ---------------------------------------------------------------------
  72. /* We fork mount.. */
  73. bool MountCdrom(string Path)
  74. {
  75. int Child = fork();
  76. if (Child < -1)
  77. return _error->Errno("fork","Failed to fork");
  78. // The child
  79. if (Child == 0)
  80. {
  81. // Make all the fds /dev/null
  82. for (int I = 0; I != 10; I++)
  83. close(I);
  84. for (int I = 0; I != 3; I++)
  85. dup2(open("/dev/null",O_RDWR),I);
  86. const char *Args[10];
  87. Args[0] = "mount";
  88. Args[1] = Path.c_str();
  89. Args[2] = 0;
  90. execvp(Args[0],(char **)Args);
  91. exit(100);
  92. }
  93. // Wait for mount
  94. int Status = 0;
  95. while (waitpid(Child,&Status,0) != Child)
  96. {
  97. if (errno == EINTR)
  98. continue;
  99. return _error->Errno("waitpid","Couldn't wait for subprocess");
  100. }
  101. // Check for an error code.
  102. if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
  103. return false;
  104. return true;
  105. }
  106. /*}}}*/
  107. // IdentCdrom - Generate a unique string for this CD /*{{{*/
  108. // ---------------------------------------------------------------------
  109. /* We convert everything we hash into a string, this prevents byte size/order
  110. from effecting the outcome. */
  111. bool IdentCdrom(string CD,string &Res)
  112. {
  113. MD5Summation Hash;
  114. string StartDir = SafeGetCWD();
  115. if (chdir(CD.c_str()) != 0)
  116. return _error->Errno("chdir","Unable to change to %s",CD.c_str());
  117. DIR *D = opendir(".");
  118. if (D == 0)
  119. return _error->Errno("opendir","Unable to read %s",CD.c_str());
  120. // Run over the directory
  121. char S[300];
  122. for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
  123. {
  124. // Skip some files..
  125. if (strcmp(Dir->d_name,".") == 0 ||
  126. strcmp(Dir->d_name,"..") == 0)
  127. continue;
  128. sprintf(S,"%lu",Dir->d_ino);
  129. Hash.Add(S);
  130. Hash.Add(Dir->d_name);
  131. };
  132. chdir(StartDir.c_str());
  133. closedir(D);
  134. // Some stats from the fsys
  135. struct statfs Buf;
  136. if (statfs(CD.c_str(),&Buf) != 0)
  137. return _error->Errno("statfs","Failed to stat the cdrom");
  138. sprintf(S,"%u %u",Buf.f_blocks,Buf.f_bfree);
  139. Hash.Add(S);
  140. Res = Hash.Result().Value();
  141. return true;
  142. }
  143. /*}}}*/
  144. // FindPackages - Find the package files on the CDROM /*{{{*/
  145. // ---------------------------------------------------------------------
  146. /* We look over the cdrom for package files. This is a recursive
  147. search that short circuits when it his a package file in the dir.
  148. This speeds it up greatly as the majority of the size is in the
  149. binary-* sub dirs. */
  150. bool FindPackages(string CD,vector<string> &List, int Depth = 0)
  151. {
  152. if (Depth >= 7)
  153. return true;
  154. if (CD[CD.length()-1] != '/')
  155. CD += '/';
  156. if (chdir(CD.c_str()) != 0)
  157. return _error->Errno("chdir","Unable to change to %s",CD.c_str());
  158. /* Aha! We found some package files. We assume that everything under
  159. this dir is controlled by those package files so we don't look down
  160. anymore */
  161. struct stat Buf;
  162. if (stat("Packages",&Buf) == 0 ||
  163. stat("Packages.gz",&Buf) == 0)
  164. {
  165. List.push_back(CD);
  166. return true;
  167. }
  168. DIR *D = opendir(".");
  169. if (D == 0)
  170. return _error->Errno("opendir","Unable to read %s",CD.c_str());
  171. // Run over the directory
  172. for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
  173. {
  174. // Skip some files..
  175. if (strcmp(Dir->d_name,".") == 0 ||
  176. strcmp(Dir->d_name,"..") == 0 ||
  177. strcmp(Dir->d_name,"source") == 0 ||
  178. strcmp(Dir->d_name,"experimental") == 0 ||
  179. strcmp(Dir->d_name,"binary-all") == 0)
  180. continue;
  181. // See if the name is a sub directory
  182. struct stat Buf;
  183. if (stat(Dir->d_name,&Buf) != 0)
  184. continue;
  185. if (S_ISDIR(Buf.st_mode) == 0)
  186. continue;
  187. // Descend
  188. if (FindPackages(CD + Dir->d_name,List,Depth+1) == false)
  189. break;
  190. if (chdir(CD.c_str()) != 0)
  191. return _error->Errno("chdir","Unable to change to ",CD.c_str());
  192. };
  193. closedir(D);
  194. return !_error->PendingError();
  195. }
  196. /*}}}*/
  197. // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
  198. // ---------------------------------------------------------------------
  199. /* Here we drop everything that is not this machines arch */
  200. bool DropBinaryArch(vector<string> &List)
  201. {
  202. char S[300];
  203. sprintf(S,"/binary-%s/",_config->Find("Apt::Architecture").c_str());
  204. for (unsigned int I = 0; I < List.size(); I++)
  205. {
  206. const char *Str = List[I].c_str();
  207. const char *Res;
  208. if ((Res = strstr(Str,"/binary-")) == 0)
  209. continue;
  210. // Weird, remove it.
  211. if (strlen(Res) < strlen(S))
  212. {
  213. List.erase(List.begin() + I);
  214. I--;
  215. continue;
  216. }
  217. // See if it is our arch
  218. if (stringcmp(Res,Res + strlen(S),S) == 0)
  219. continue;
  220. // Erase it
  221. List.erase(List.begin() + I);
  222. I--;
  223. }
  224. return true;
  225. }
  226. /*}}}*/
  227. // Score - We compute a 'score' for a path /*{{{*/
  228. // ---------------------------------------------------------------------
  229. /* Paths are scored based on how close they come to what I consider
  230. normal. That is ones that have 'dist' 'stable' 'frozen' will score
  231. higher than ones without. */
  232. int Score(string Path)
  233. {
  234. int Res = 0;
  235. if (Path.find("stable/") != string::npos)
  236. Res += 2;
  237. if (Path.find("frozen/") != string::npos)
  238. Res += 2;
  239. if (Path.find("/dists/") != string::npos)
  240. Res += 4;
  241. if (Path.find("/main/") != string::npos)
  242. Res += 2;
  243. if (Path.find("/contrib/") != string::npos)
  244. Res += 2;
  245. if (Path.find("/non-free/") != string::npos)
  246. Res += 2;
  247. if (Path.find("/non-US/") != string::npos)
  248. Res += 2;
  249. return Res;
  250. }
  251. /*}}}*/
  252. // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
  253. // ---------------------------------------------------------------------
  254. /* Here we go and stat every file that we found and strip dup inodes. */
  255. bool DropRepeats(vector<string> &List)
  256. {
  257. // Get a list of all the inodes
  258. ino_t *Inodes = new ino_t[List.size()];
  259. for (unsigned int I = 0; I != List.size(); I++)
  260. {
  261. struct stat Buf;
  262. if (stat(List[I].c_str(),&Buf) != 0)
  263. _error->Errno("stat","Failed to stat %s",List[I].c_str());
  264. Inodes[I] = Buf.st_ino;
  265. }
  266. // Look for dups
  267. for (unsigned int I = 0; I != List.size(); I++)
  268. {
  269. for (unsigned int J = I+1; J < List.size(); J++)
  270. {
  271. // No match
  272. if (Inodes[J] != Inodes[I])
  273. continue;
  274. // We score the two paths.. and erase one
  275. int ScoreA = Score(List[I]);
  276. int ScoreB = Score(List[J]);
  277. if (ScoreA < ScoreB)
  278. {
  279. List[I] = string();
  280. break;
  281. }
  282. List[J] = string();
  283. }
  284. }
  285. // Wipe erased entries
  286. for (unsigned int I = 0; I < List.size();)
  287. {
  288. if (List[I].empty() == false)
  289. I++;
  290. else
  291. List.erase(List.begin()+I);
  292. }
  293. return true;
  294. }
  295. /*}}}*/
  296. // ConvertToSourceList - Convert a Path to a sourcelist entry /*{{{*/
  297. // ---------------------------------------------------------------------
  298. /* We look for things in dists/ notation and convert them to
  299. <dist> <component> form otherwise it is left alone. This also strips
  300. the CD path. */
  301. void ConvertToSourceList(string CD,string &Path)
  302. {
  303. char S[300];
  304. sprintf(S,"binary-%s",_config->Find("Apt::Architecture").c_str());
  305. // Strip the cdrom base path
  306. Path = string(Path,CD.length());
  307. // Too short to be a dists/ type
  308. if (Path.length() < strlen("dists/"))
  309. return;
  310. // Not a dists type.
  311. if (stringcmp(Path.begin(),Path.begin()+strlen("dists/"),"dists/") != 0)
  312. return;
  313. // Isolate the dist
  314. string::size_type Slash = strlen("dists/");
  315. string::size_type Slash2 = Path.find('/',Slash + 1);
  316. if (Slash2 == string::npos || Slash2 + 2 >= Path.length())
  317. return;
  318. string Dist = string(Path,Slash,Slash2 - Slash);
  319. // Isolate the component
  320. Slash = Path.find('/',Slash2+1);
  321. if (Slash == string::npos || Slash + 2 >= Path.length())
  322. return;
  323. string Comp = string(Path,Slash2+1,Slash - Slash2-1);
  324. // Verify the trailing binar - bit
  325. Slash2 = Path.find('/',Slash + 1);
  326. if (Slash == string::npos)
  327. return;
  328. string Binary = string(Path,Slash+1,Slash2 - Slash-1);
  329. if (Binary != S)
  330. return;
  331. Path = Dist + ' ' + Comp;
  332. }
  333. /*}}}*/
  334. // GrabFirst - Return the first Depth path components /*{{{*/
  335. // ---------------------------------------------------------------------
  336. /* */
  337. bool GrabFirst(string Path,string &To,unsigned int Depth)
  338. {
  339. string::size_type I = 0;
  340. do
  341. {
  342. I = Path.find('/',I+1);
  343. Depth--;
  344. }
  345. while (I != string::npos && Depth != 0);
  346. if (I == string::npos)
  347. return false;
  348. To = string(Path,0,I+1);
  349. return true;
  350. }
  351. /*}}}*/
  352. // CopyPackages - Copy the package files from the CD /*{{{*/
  353. // ---------------------------------------------------------------------
  354. /* */
  355. bool CopyPackages(string CDROM,string Name,vector<string> &List)
  356. {
  357. OpTextProgress Progress;
  358. bool NoStat = _config->FindB("APT::CDROM::Fast",false);
  359. // Prepare the progress indicator
  360. unsigned long TotalSize = 0;
  361. for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
  362. {
  363. struct stat Buf;
  364. if (stat(string(*I + "Packages").c_str(),&Buf) != 0)
  365. return _error->Errno("stat","Stat failed for %s",
  366. string(*I + "Packages").c_str());
  367. TotalSize += Buf.st_size;
  368. }
  369. unsigned long CurrentSize = 0;
  370. unsigned int NotFound = 0;
  371. unsigned int WrongSize = 0;
  372. unsigned int Packages = 0;
  373. for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
  374. {
  375. string OrigPath = string(*I,CDROM.length());
  376. // Open the package file
  377. FileFd Pkg(*I + "Packages",FileFd::ReadOnly);
  378. pkgTagFile Parser(Pkg);
  379. if (_error->PendingError() == true)
  380. return false;
  381. // Open the output file
  382. char S[400];
  383. sprintf(S,"cdrom:%s/%sPackages",Name.c_str(),(*I).c_str() + CDROM.length());
  384. string TargetF = _config->FindDir("Dir::State::lists") + "partial/";
  385. TargetF += URItoFileName(S);
  386. if (_config->FindB("APT::CDROM::NoAct",false) == true)
  387. TargetF = "/dev/null";
  388. FileFd Target(TargetF,FileFd::WriteEmpty);
  389. if (_error->PendingError() == true)
  390. return false;
  391. // Setup the progress meter
  392. Progress.OverallProgress(CurrentSize,TotalSize,Pkg.Size(),
  393. "Reading Package Lists");
  394. // Parse
  395. Progress.SubProgress(Pkg.Size());
  396. pkgTagSection Section;
  397. string Prefix;
  398. unsigned long Hits = 0;
  399. while (Parser.Step(Section) == true)
  400. {
  401. Progress.Progress(Parser.Offset());
  402. string File = Section.FindS("Filename");
  403. unsigned long Size = Section.FindI("Size");
  404. if (File.empty() || Size == 0)
  405. return _error->Error("Cannot find filename or size tag");
  406. // See if the file exists
  407. if (NoStat == false || Hits < 10)
  408. {
  409. struct stat Buf;
  410. unsigned int Depth = 1;
  411. string MyPrefix = Prefix;
  412. while (1)
  413. {
  414. if (stat(string(CDROM + MyPrefix + File).c_str(),&Buf) != 0)
  415. {
  416. if (Prefix.empty() == true)
  417. {
  418. if (GrabFirst(OrigPath,MyPrefix,Depth++) == true)
  419. continue;
  420. }
  421. NotFound++;
  422. Depth = 0;
  423. break;
  424. }
  425. else
  426. break;
  427. }
  428. // Failed
  429. if (Depth == 0)
  430. continue;
  431. // Store the new prefix
  432. if (Depth != 1)
  433. Prefix = MyPrefix;
  434. // Size match
  435. if ((unsigned)Buf.st_size != Size)
  436. {
  437. WrongSize++;
  438. continue;
  439. }
  440. }
  441. Packages++;
  442. Hits++;
  443. // Copy it to the target package file
  444. const char *Start;
  445. const char *Stop;
  446. Section.GetSection(Start,Stop);
  447. if (Target.Write(Start,Stop-Start) == false)
  448. return false;
  449. }
  450. if (_config->FindB("APT::CDROM::NoAct",false) == false)
  451. {
  452. // Move out of the partial directory
  453. Target.Close();
  454. string FinalF = _config->FindDir("Dir::State::lists");
  455. FinalF += URItoFileName(S);
  456. if (rename(TargetF.c_str(),FinalF.c_str()) != 0)
  457. return _error->Errno("rename","Failed to rename");
  458. // Copy the release file
  459. sprintf(S,"cdrom:%s/%sRelease",Name.c_str(),(*I).c_str() + CDROM.length());
  460. string TargetF = _config->FindDir("Dir::State::lists") + "partial/";
  461. TargetF += URItoFileName(S);
  462. if (FileExists(*I + "Release") == true)
  463. {
  464. FileFd Target(TargetF,FileFd::WriteEmpty);
  465. FileFd Rel(*I + "Release",FileFd::ReadOnly);
  466. if (_error->PendingError() == true)
  467. return false;
  468. if (CopyFile(Rel,Target) == false)
  469. return false;
  470. }
  471. else
  472. {
  473. // Empty release file
  474. FileFd Target(TargetF,FileFd::WriteEmpty);
  475. }
  476. // Rename the release file
  477. FinalF = _config->FindDir("Dir::State::lists");
  478. FinalF += URItoFileName(S);
  479. if (rename(TargetF.c_str(),FinalF.c_str()) != 0)
  480. return _error->Errno("rename","Failed to rename");
  481. }
  482. /* Mangle the source to be in the proper notation with
  483. prefix dist [component] */
  484. *I = string(*I,Prefix.length());
  485. ConvertToSourceList(CDROM,*I);
  486. *I = Prefix + ' ' + *I;
  487. CurrentSize += Pkg.Size();
  488. }
  489. Progress.Done();
  490. // Some stats
  491. cout << "Wrote " << Packages << " package records" ;
  492. if (NotFound != 0)
  493. cout << " with " << NotFound << " missing files";
  494. if (NotFound != 0 && WrongSize != 0)
  495. cout << " and";
  496. if (WrongSize != 0)
  497. cout << " with " << WrongSize << " mismatched files";
  498. cout << '.' << endl;
  499. if (Packages == 0)
  500. return _error->Error("No valid package records were found.");
  501. if (NotFound + WrongSize > 10)
  502. cout << "Alot of package entires were discarded, perhaps this CD is funny?" << endl;
  503. }
  504. /*}}}*/
  505. // ReduceSourceList - Takes the path list and reduces it /*{{{*/
  506. // ---------------------------------------------------------------------
  507. /* This takes the list of source list expressed entires and collects
  508. similar ones to form a single entry for each dist */
  509. bool ReduceSourcelist(string CD,vector<string> &List)
  510. {
  511. sort(List.begin(),List.end());
  512. // Collect similar entries
  513. for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
  514. {
  515. // Find a space..
  516. string::size_type Space = (*I).find(' ');
  517. if (Space == string::npos)
  518. continue;
  519. string::size_type SSpace = (*I).find(' ',Space + 1);
  520. if (SSpace == string::npos)
  521. continue;
  522. string Word1 = string(*I,Space,SSpace-Space);
  523. for (vector<string>::iterator J = List.begin(); J != I; J++)
  524. {
  525. // Find a space..
  526. string::size_type Space2 = (*J).find(' ');
  527. if (Space2 == string::npos)
  528. continue;
  529. string::size_type SSpace2 = (*J).find(' ',Space2 + 1);
  530. if (SSpace2 == string::npos)
  531. continue;
  532. if (string(*J,Space2,SSpace2-Space2) != Word1)
  533. continue;
  534. *J += string(*I,SSpace);
  535. *I = string();
  536. }
  537. }
  538. // Wipe erased entries
  539. for (unsigned int I = 0; I < List.size();)
  540. {
  541. if (List[I].empty() == false)
  542. I++;
  543. else
  544. List.erase(List.begin()+I);
  545. }
  546. }
  547. /*}}}*/
  548. // WriteDatabase - Write the CDROM Database file /*{{{*/
  549. // ---------------------------------------------------------------------
  550. /* We rewrite the configuration class associated with the cdrom database. */
  551. bool WriteDatabase(Configuration &Cnf)
  552. {
  553. string DFile = _config->FindFile("Dir::State::cdroms");
  554. string NewFile = DFile + ".new";
  555. unlink(NewFile.c_str());
  556. ofstream Out(NewFile.c_str());
  557. if (!Out)
  558. return _error->Errno("ofstream::ofstream",
  559. "Failed to open %s.new",DFile.c_str());
  560. /* Write out all of the configuration directives by walking the
  561. configuration tree */
  562. const Configuration::Item *Top = Cnf.Tree(0);
  563. for (; Top != 0;)
  564. {
  565. // Print the config entry
  566. if (Top->Value.empty() == false)
  567. Out << Top->FullTag() + " \"" << Top->Value << "\";" << endl;
  568. if (Top->Child != 0)
  569. {
  570. Top = Top->Child;
  571. continue;
  572. }
  573. while (Top != 0 && Top->Next == 0)
  574. Top = Top->Parent;
  575. if (Top != 0)
  576. Top = Top->Next;
  577. }
  578. Out.close();
  579. rename(DFile.c_str(),string(DFile + '~').c_str());
  580. if (rename(NewFile.c_str(),DFile.c_str()) != 0)
  581. return _error->Errno("rename","Failed to rename %s.new to %s",
  582. DFile.c_str(),DFile.c_str());
  583. return true;
  584. }
  585. /*}}}*/
  586. // WriteSourceList - Write an updated sourcelist /*{{{*/
  587. // ---------------------------------------------------------------------
  588. /* This reads the old source list and copies it into the new one. It
  589. appends the new CDROM entires just after the first block of comments.
  590. This places them first in the file. It also removes any old entries
  591. that were the same. */
  592. bool WriteSourceList(string Name,vector<string> &List)
  593. {
  594. string File = _config->FindFile("Dir::Etc::sourcelist");
  595. // Open the stream for reading
  596. ifstream F(File.c_str(),ios::in | ios::nocreate);
  597. if (!F != 0)
  598. return _error->Errno("ifstream::ifstream","Opening %s",File.c_str());
  599. string NewFile = File + ".new";
  600. unlink(NewFile.c_str());
  601. ofstream Out(NewFile.c_str());
  602. if (!Out)
  603. return _error->Errno("ofstream::ofstream",
  604. "Failed to open %s.new",File.c_str());
  605. // Create a short uri without the path
  606. string ShortURI = "cdrom:" + Name + "/";
  607. char Buffer[300];
  608. int CurLine = 0;
  609. bool First = true;
  610. while (F.eof() == false)
  611. {
  612. F.getline(Buffer,sizeof(Buffer));
  613. CurLine++;
  614. _strtabexpand(Buffer,sizeof(Buffer));
  615. _strstrip(Buffer);
  616. // Comment or blank
  617. if (Buffer[0] == '#' || Buffer[0] == 0)
  618. {
  619. Out << Buffer << endl;
  620. continue;
  621. }
  622. if (First == true)
  623. {
  624. for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
  625. {
  626. string::size_type Space = (*I).find(' ');
  627. if (Space == string::npos)
  628. return _error->Error("Internal error");
  629. Out << "deb \"cdrom:" << Name << "/" << string(*I,0,Space) <<
  630. "\" " << string(*I,Space+1) << endl;
  631. }
  632. }
  633. First = false;
  634. // Grok it
  635. string Type;
  636. string URI;
  637. char *C = Buffer;
  638. if (ParseQuoteWord(C,Type) == false ||
  639. ParseQuoteWord(C,URI) == false)
  640. {
  641. Out << Buffer << endl;
  642. continue;
  643. }
  644. // Emit lines like this one
  645. if (Type != "deb" || string(URI,0,ShortURI.length()) != ShortURI)
  646. {
  647. Out << Buffer << endl;
  648. continue;
  649. }
  650. }
  651. // Just in case the file was empty
  652. if (First == true)
  653. {
  654. for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
  655. {
  656. string::size_type Space = (*I).find(' ');
  657. if (Space == string::npos)
  658. return _error->Error("Internal error");
  659. Out << "deb \"cdrom:" << Name << "/" << string(*I,0,Space) <<
  660. "\" " << string(*I,Space+1) << endl;
  661. }
  662. }
  663. Out.close();
  664. rename(File.c_str(),string(File + '~').c_str());
  665. if (rename(NewFile.c_str(),File.c_str()) != 0)
  666. return _error->Errno("rename","Failed to rename %s.new to %s",
  667. File.c_str(),File.c_str());
  668. return true;
  669. }
  670. /*}}}*/
  671. // Prompt - Simple prompt /*{{{*/
  672. // ---------------------------------------------------------------------
  673. /* */
  674. void Prompt(const char *Text)
  675. {
  676. char C;
  677. cout << Text << ' ' << flush;
  678. read(STDIN_FILENO,&C,1);
  679. if (C != '\n')
  680. cout << endl;
  681. }
  682. /*}}}*/
  683. // PromptLine - Prompt for an input line /*{{{*/
  684. // ---------------------------------------------------------------------
  685. /* */
  686. string PromptLine(const char *Text)
  687. {
  688. cout << Text << ':' << endl;
  689. string Res;
  690. getline(cin,Res);
  691. return Res;
  692. }
  693. /*}}}*/
  694. // DoAdd - Add a new CDROM /*{{{*/
  695. // ---------------------------------------------------------------------
  696. /* This does the main add bit.. We show some status and things. The
  697. sequence is to mount/umount the CD, Ident it then scan it for package
  698. files and reduce that list. Then we copy over the package files and
  699. verify them. Then rewrite the database files */
  700. bool DoAdd(CommandLine &)
  701. {
  702. // Startup
  703. string CDROM = _config->FindDir("Acquire::cdrom::mount","/cdrom/");
  704. cout << "Using CD-ROM mount point " << CDROM << endl;
  705. // Read the database
  706. Configuration Database;
  707. string DFile = _config->FindFile("Dir::State::cdroms");
  708. if (FileExists(DFile) == true)
  709. {
  710. if (ReadConfigFile(Database,DFile) == false)
  711. return _error->Error("Unable to read the cdrom database %s",
  712. DFile.c_str());
  713. }
  714. // Unmount the CD and get the user to put in the one they want
  715. if (_config->FindB("APT::CDROM::NoMount",false) == false)
  716. {
  717. cout << "Unmounting CD-ROM" << endl;
  718. UnmountCdrom(CDROM);
  719. // Mount the new CDROM
  720. Prompt("Please insert a Disc in the drive and press any key");
  721. cout << "Mounting CD-ROM" << endl;
  722. if (MountCdrom(CDROM) == false)
  723. {
  724. cout << "Failed to mount the cdrom." << endl;
  725. return false;
  726. }
  727. }
  728. // Hash the CD to get an ID
  729. cout << "Identifying.. " << flush;
  730. string ID;
  731. if (IdentCdrom(CDROM,ID) == false)
  732. return false;
  733. cout << '[' << ID << ']' << endl;
  734. cout << "Scanning Disc for index files.. " << flush;
  735. // Get the CD structure
  736. vector<string> List;
  737. string StartDir = SafeGetCWD();
  738. if (FindPackages(CDROM,List) == false)
  739. return false;
  740. chdir(StartDir.c_str());
  741. if (_config->FindB("Debug::aptcdrom",false) == true)
  742. {
  743. cout << "I found:" << endl;
  744. for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
  745. {
  746. cout << *I << endl;
  747. }
  748. }
  749. // Fix up the list
  750. DropBinaryArch(List);
  751. DropRepeats(List);
  752. cout << "Found " << List.size() << " package index files." << endl;
  753. if (List.size() == 0)
  754. return _error->Error("Unable to locate any package files, perhaps this is not a Debian Disc");
  755. // Check if the CD is in the database
  756. string Name;
  757. if (Database.Exists("CD::" + ID) == false ||
  758. _config->FindB("APT::CDROM::Rename",false) == true)
  759. {
  760. // Try to use the CDs label if at all possible
  761. if (FileExists(CDROM + "/.disk/info") == true)
  762. {
  763. ifstream F(string(CDROM+ "/.disk/info").c_str());
  764. if (!F == 0)
  765. getline(F,Name);
  766. if (Name.empty() == false)
  767. {
  768. cout << "Found label '" << Name << "'" << endl;
  769. Database.Set("CD::" + ID + "::Label",Name);
  770. }
  771. }
  772. if (_config->FindB("APT::CDROM::Rename",false) == true ||
  773. Name.empty() == false)
  774. {
  775. cout << "Please provide a name for this Disc, such as 'Debian 2.1r1 Disk 1'";
  776. while (1)
  777. {
  778. Name = PromptLine("");
  779. if (Name.empty() == false &&
  780. Name.find('/') == string::npos)
  781. break;
  782. cout << "That is not a valid name, try again " << endl;
  783. }
  784. }
  785. }
  786. else
  787. Name = Database.Find("CD::" + ID);
  788. Database.Set("CD::" + ID,Name);
  789. cout << "This Disc is called '" << Name << "'" << endl;
  790. // Copy the package files to the state directory
  791. if (CopyPackages(CDROM,Name,List) == false)
  792. return false;
  793. ReduceSourcelist(CDROM,List);
  794. // Write the database and sourcelist
  795. if (_config->FindB("APT::cdrom::NoAct",false) == false)
  796. {
  797. if (WriteDatabase(Database) == false)
  798. return false;
  799. cout << "Writing new source list" << endl;
  800. if (WriteSourceList(Name,List) == false)
  801. return false;
  802. }
  803. // Print the sourcelist entries
  804. cout << "Source List entires for this Disc are:" << endl;
  805. for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
  806. {
  807. string::size_type Space = (*I).find(' ');
  808. if (Space == string::npos)
  809. return _error->Error("Internal error");
  810. cout << "deb \"cdrom:" << Name << "/" << string(*I,0,Space) <<
  811. "\" " << string(*I,Space+1) << endl;
  812. }
  813. return true;
  814. }
  815. /*}}}*/
  816. // ShowHelp - Show the help screen /*{{{*/
  817. // ---------------------------------------------------------------------
  818. /* */
  819. int ShowHelp()
  820. {
  821. cout << PACKAGE << ' ' << VERSION << " for " << ARCHITECTURE <<
  822. " compiled on " << __DATE__ << " " << __TIME__ << endl;
  823. cout << "Usage: apt-cdrom [options] command" << endl;
  824. cout << endl;
  825. cout << "apt-cdrom is a tool to add CDROM's to APT's source list. The " << endl;
  826. cout << "CDROM mount point and device information is taken from apt.conf" << endl;
  827. cout << "and /etc/fstab." << endl;
  828. cout << endl;
  829. cout << "Commands:" << endl;
  830. cout << " add - Add a CDROM" << endl;
  831. cout << endl;
  832. cout << "Options:" << endl;
  833. cout << " -h This help text" << endl;
  834. cout << " -d CD-ROM mount point" << endl;
  835. cout << " -r Rename a recognized CD-ROM" << endl;
  836. cout << " -m No mounting" << endl;
  837. cout << " -f Fast mode, don't check package files" << endl;
  838. cout << " -c=? Read this configuration file" << endl;
  839. cout << " -o=? Set an arbitary configuration option, ie -o dir::cache=/tmp" << endl;
  840. cout << "See fstab(5)" << endl;
  841. return 100;
  842. }
  843. /*}}}*/
  844. int main(int argc,const char *argv[])
  845. {
  846. CommandLine::Args Args[] = {
  847. {'h',"help","help",0},
  848. {'d',"cdrom","Acquire::cdrom::mount",CommandLine::HasArg},
  849. {'r',"rename","APT::CDROM::Rename",0},
  850. {'m',"no-mount","APT::CDROM::NoMount",0},
  851. {'f',"fast","APT::CDROM::Fast",0},
  852. {'n',"just-print","APT::CDROM::NoAct",0},
  853. {'n',"recon","APT::CDROM::NoAct",0},
  854. {'n',"no-act","APT::CDROM::NoAct",0},
  855. {'c',"config-file",0,CommandLine::ConfigFile},
  856. {'o',"option",0,CommandLine::ArbItem},
  857. {0,0,0,0}};
  858. CommandLine::Dispatch Cmds[] = {
  859. {"add",&DoAdd},
  860. {0,0}};
  861. // Parse the command line and initialize the package library
  862. CommandLine CmdL(Args,_config);
  863. if (pkgInitialize(*_config) == false ||
  864. CmdL.Parse(argc,argv) == false)
  865. {
  866. _error->DumpErrors();
  867. return 100;
  868. }
  869. // See if the help should be shown
  870. if (_config->FindB("help") == true ||
  871. CmdL.FileSize() == 0)
  872. return ShowHelp();
  873. // Match the operation
  874. CmdL.DispatchArg(Cmds);
  875. // Print any errors or warnings found during parsing
  876. if (_error->empty() == false)
  877. {
  878. bool Errors = _error->PendingError();
  879. _error->DumpErrors();
  880. return Errors == true?100:0;
  881. }
  882. return 0;
  883. }