]> git.saurik.com Git - apt.git/blob - apt-pkg/cdrom.cc
* apt-pkg/pkgcache.cc:
[apt.git] / apt-pkg / cdrom.cc
1 /*
2 */
3
4 #include<apt-pkg/init.h>
5 #include<apt-pkg/error.h>
6 #include<apt-pkg/cdromutl.h>
7 #include<apt-pkg/strutl.h>
8 #include<apt-pkg/cdrom.h>
9 #include<apt-pkg/aptconfiguration.h>
10
11 #include<sstream>
12 #include<fstream>
13 #include<config.h>
14 #include<apti18n.h>
15 #include <sys/stat.h>
16 #include <fcntl.h>
17 #include <dirent.h>
18 #include <unistd.h>
19 #include <stdio.h>
20 #include <algorithm>
21 #include <dlfcn.h>
22
23 #include "indexcopy.h"
24
25 using namespace std;
26
27 // FindPackages - Find the package files on the CDROM /*{{{*/
28 // ---------------------------------------------------------------------
29 /* We look over the cdrom for package files. This is a recursive
30 search that short circuits when it his a package file in the dir.
31 This speeds it up greatly as the majority of the size is in the
32 binary-* sub dirs. */
33 bool pkgCdrom::FindPackages(string CD,
34 vector<string> &List,
35 vector<string> &SList,
36 vector<string> &SigList,
37 vector<string> &TransList,
38 string &InfoDir, pkgCdromStatus *log,
39 unsigned int Depth)
40 {
41 static ino_t Inodes[9];
42 DIR *D;
43
44 // if we have a look we "pulse" now
45 if(log)
46 log->Update();
47
48 if (Depth >= 7)
49 return true;
50
51 if (CD[CD.length()-1] != '/')
52 CD += '/';
53
54 if (chdir(CD.c_str()) != 0)
55 return _error->Errno("chdir","Unable to change to %s",CD.c_str());
56
57 // Look for a .disk subdirectory
58 struct stat Buf;
59 if (stat(".disk",&Buf) == 0)
60 {
61 if (InfoDir.empty() == true)
62 InfoDir = CD + ".disk/";
63 }
64
65 // Don't look into directories that have been marked to ingore.
66 if (stat(".aptignr",&Buf) == 0)
67 return true;
68
69
70 /* Check _first_ for a signature file as apt-cdrom assumes that all files
71 under a Packages/Source file are in control of that file and stops
72 the scanning
73 */
74 if (stat("Release.gpg",&Buf) == 0)
75 {
76 SigList.push_back(CD);
77 }
78 /* Aha! We found some package files. We assume that everything under
79 this dir is controlled by those package files so we don't look down
80 anymore */
81 if (stat("Packages",&Buf) == 0 || stat("Packages.gz",&Buf) == 0)
82 {
83 List.push_back(CD);
84
85 // Continue down if thorough is given
86 if (_config->FindB("APT::CDROM::Thorough",false) == false)
87 return true;
88 }
89 if (stat("Sources.gz",&Buf) == 0 || stat("Sources",&Buf) == 0)
90 {
91 SList.push_back(CD);
92
93 // Continue down if thorough is given
94 if (_config->FindB("APT::CDROM::Thorough",false) == false)
95 return true;
96 }
97
98 // see if we find translatin indexes
99 if (stat("i18n",&Buf) == 0)
100 {
101 D = opendir("i18n");
102 for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
103 {
104 if(strstr(Dir->d_name,"Translation") != NULL)
105 {
106 if (_config->FindB("Debug::aptcdrom",false) == true)
107 std::clog << "found translations: " << Dir->d_name << "\n";
108 string file = Dir->d_name;
109 if(file.substr(file.size()-3,file.size()) == ".gz")
110 file = file.substr(0,file.size()-3);
111 TransList.push_back(CD+"i18n/"+ file);
112 }
113 }
114 closedir(D);
115 }
116
117
118 D = opendir(".");
119 if (D == 0)
120 return _error->Errno("opendir","Unable to read %s",CD.c_str());
121
122 // Run over the directory
123 for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
124 {
125 // Skip some files..
126 if (strcmp(Dir->d_name,".") == 0 ||
127 strcmp(Dir->d_name,"..") == 0 ||
128 //strcmp(Dir->d_name,"source") == 0 ||
129 strcmp(Dir->d_name,".disk") == 0 ||
130 strcmp(Dir->d_name,"experimental") == 0 ||
131 strcmp(Dir->d_name,"binary-all") == 0 ||
132 strcmp(Dir->d_name,"debian-installer") == 0)
133 continue;
134
135 // See if the name is a sub directory
136 struct stat Buf;
137 if (stat(Dir->d_name,&Buf) != 0)
138 continue;
139
140 if (S_ISDIR(Buf.st_mode) == 0)
141 continue;
142
143 unsigned int I;
144 for (I = 0; I != Depth; I++)
145 if (Inodes[I] == Buf.st_ino)
146 break;
147 if (I != Depth)
148 continue;
149
150 // Store the inodes weve seen
151 Inodes[Depth] = Buf.st_ino;
152
153 // Descend
154 if (FindPackages(CD + Dir->d_name,List,SList,SigList,TransList,InfoDir,log,Depth+1) == false)
155 break;
156
157 if (chdir(CD.c_str()) != 0)
158 return _error->Errno("chdir","Unable to change to %s",CD.c_str());
159 };
160
161 closedir(D);
162
163 return !_error->PendingError();
164 }
165 /*}}}*/
166 // Score - We compute a 'score' for a path /*{{{*/
167 // ---------------------------------------------------------------------
168 /* Paths are scored based on how close they come to what I consider
169 normal. That is ones that have 'dist' 'stable' 'testing' will score
170 higher than ones without. */
171 int pkgCdrom::Score(string Path)
172 {
173 int Res = 0;
174 if (Path.find("stable/") != string::npos)
175 Res += 29;
176 if (Path.find("/binary-") != string::npos)
177 Res += 20;
178 if (Path.find("testing/") != string::npos)
179 Res += 28;
180 if (Path.find("unstable/") != string::npos)
181 Res += 27;
182 if (Path.find("/dists/") != string::npos)
183 Res += 40;
184 if (Path.find("/main/") != string::npos)
185 Res += 20;
186 if (Path.find("/contrib/") != string::npos)
187 Res += 20;
188 if (Path.find("/non-free/") != string::npos)
189 Res += 20;
190 if (Path.find("/non-US/") != string::npos)
191 Res += 20;
192 if (Path.find("/source/") != string::npos)
193 Res += 10;
194 if (Path.find("/debian/") != string::npos)
195 Res -= 10;
196
197 // check for symlinks in the patch leading to the actual file
198 // a symlink gets a big penalty
199 struct stat Buf;
200 string statPath = flNotFile(Path);
201 string cdromPath = _config->FindDir("Acquire::cdrom::mount");
202 while(statPath != cdromPath && statPath != "./") {
203 statPath.resize(statPath.size()-1); // remove the trailing '/'
204 if (lstat(statPath.c_str(),&Buf) == 0) {
205 if(S_ISLNK(Buf.st_mode)) {
206 Res -= 60;
207 break;
208 }
209 }
210 statPath = flNotFile(statPath); // descent
211 }
212
213 return Res;
214 }
215 /*}}}*/
216 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
217 // ---------------------------------------------------------------------
218 /* Here we drop everything that is not this machines arch */
219 bool pkgCdrom::DropBinaryArch(vector<string> &List)
220 {
221
222 for (unsigned int I = 0; I < List.size(); I++)
223 {
224 const char *Str = List[I].c_str();
225 const char *Start, *End;
226 if ((Start = strstr(Str,"/binary-")) == 0)
227 continue;
228
229 // Between Start and End is the architecture
230 Start += 8;
231 if ((End = strstr(Start,"/")) != 0 && Start != End &&
232 APT::Configuration::checkArchitecture(string(Start, End)) == true)
233 continue; // okay, architecture is accepted
234
235 // not accepted -> Erase it
236 List.erase(List.begin() + I);
237 --I; // the next entry is at the same index after the erase
238 }
239
240 return true;
241 }
242 /*}}}*/
243 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
244 // ---------------------------------------------------------------------
245 /* Here we go and stat every file that we found and strip dup inodes. */
246 bool pkgCdrom::DropRepeats(vector<string> &List,const char *Name)
247 {
248 // Get a list of all the inodes
249 ino_t *Inodes = new ino_t[List.size()];
250 for (unsigned int I = 0; I != List.size(); I++)
251 {
252 struct stat Buf;
253 if (stat((List[I] + Name).c_str(),&Buf) != 0 &&
254 stat((List[I] + Name + ".gz").c_str(),&Buf) != 0)
255 _error->Errno("stat","Failed to stat %s%s",List[I].c_str(),
256 Name);
257 Inodes[I] = Buf.st_ino;
258 }
259
260 if (_error->PendingError() == true)
261 return false;
262
263 // Look for dups
264 for (unsigned int I = 0; I != List.size(); I++)
265 {
266 for (unsigned int J = I+1; J < List.size(); J++)
267 {
268 // No match
269 if (Inodes[J] != Inodes[I])
270 continue;
271
272 // We score the two paths.. and erase one
273 int ScoreA = Score(List[I]);
274 int ScoreB = Score(List[J]);
275 if (ScoreA < ScoreB)
276 {
277 List[I] = string();
278 break;
279 }
280
281 List[J] = string();
282 }
283 }
284 delete[] Inodes;
285
286 // Wipe erased entries
287 for (unsigned int I = 0; I < List.size();)
288 {
289 if (List[I].empty() == false)
290 I++;
291 else
292 List.erase(List.begin()+I);
293 }
294
295 return true;
296 }
297 /*}}}*/
298 // ReduceSourceList - Takes the path list and reduces it /*{{{*/
299 // ---------------------------------------------------------------------
300 /* This takes the list of source list expressed entires and collects
301 similar ones to form a single entry for each dist */
302 void pkgCdrom::ReduceSourcelist(string CD,vector<string> &List)
303 {
304 sort(List.begin(),List.end());
305
306 // Collect similar entries
307 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
308 {
309 // Find a space..
310 string::size_type Space = (*I).find(' ');
311 if (Space == string::npos)
312 continue;
313 string::size_type SSpace = (*I).find(' ',Space + 1);
314 if (SSpace == string::npos)
315 continue;
316
317 string Word1 = string(*I,Space,SSpace-Space);
318 string Prefix = string(*I,0,Space);
319 for (vector<string>::iterator J = List.begin(); J != I; J++)
320 {
321 // Find a space..
322 string::size_type Space2 = (*J).find(' ');
323 if (Space2 == string::npos)
324 continue;
325 string::size_type SSpace2 = (*J).find(' ',Space2 + 1);
326 if (SSpace2 == string::npos)
327 continue;
328
329 if (string(*J,0,Space2) != Prefix)
330 continue;
331 if (string(*J,Space2,SSpace2-Space2) != Word1)
332 continue;
333
334 *J += string(*I,SSpace);
335 *I = string();
336 }
337 }
338
339 // Wipe erased entries
340 for (unsigned int I = 0; I < List.size();)
341 {
342 if (List[I].empty() == false)
343 I++;
344 else
345 List.erase(List.begin()+I);
346 }
347 }
348 /*}}}*/
349 // WriteDatabase - Write the CDROM Database file /*{{{*/
350 // ---------------------------------------------------------------------
351 /* We rewrite the configuration class associated with the cdrom database. */
352 bool pkgCdrom::WriteDatabase(Configuration &Cnf)
353 {
354 string DFile = _config->FindFile("Dir::State::cdroms");
355 string NewFile = DFile + ".new";
356
357 unlink(NewFile.c_str());
358 ofstream Out(NewFile.c_str());
359 if (!Out)
360 return _error->Errno("ofstream::ofstream",
361 "Failed to open %s.new",DFile.c_str());
362
363 /* Write out all of the configuration directives by walking the
364 configuration tree */
365 const Configuration::Item *Top = Cnf.Tree(0);
366 for (; Top != 0;)
367 {
368 // Print the config entry
369 if (Top->Value.empty() == false)
370 Out << Top->FullTag() + " \"" << Top->Value << "\";" << endl;
371
372 if (Top->Child != 0)
373 {
374 Top = Top->Child;
375 continue;
376 }
377
378 while (Top != 0 && Top->Next == 0)
379 Top = Top->Parent;
380 if (Top != 0)
381 Top = Top->Next;
382 }
383
384 Out.close();
385
386 link(DFile.c_str(),string(DFile + '~').c_str());
387 if (rename(NewFile.c_str(),DFile.c_str()) != 0)
388 return _error->Errno("rename","Failed to rename %s.new to %s",
389 DFile.c_str(),DFile.c_str());
390
391 return true;
392 }
393 /*}}}*/
394 // WriteSourceList - Write an updated sourcelist /*{{{*/
395 // ---------------------------------------------------------------------
396 /* This reads the old source list and copies it into the new one. It
397 appends the new CDROM entires just after the first block of comments.
398 This places them first in the file. It also removes any old entries
399 that were the same. */
400 bool pkgCdrom::WriteSourceList(string Name,vector<string> &List,bool Source)
401 {
402 if (List.size() == 0)
403 return true;
404
405 string File = _config->FindFile("Dir::Etc::sourcelist");
406
407 // Open the stream for reading
408 ifstream F((FileExists(File)?File.c_str():"/dev/null"),
409 ios::in );
410 if (!F != 0)
411 return _error->Errno("ifstream::ifstream","Opening %s",File.c_str());
412
413 string NewFile = File + ".new";
414 unlink(NewFile.c_str());
415 ofstream Out(NewFile.c_str());
416 if (!Out)
417 return _error->Errno("ofstream::ofstream",
418 "Failed to open %s.new",File.c_str());
419
420 // Create a short uri without the path
421 string ShortURI = "cdrom:[" + Name + "]/";
422 string ShortURI2 = "cdrom:" + Name + "/"; // For Compatibility
423
424 string Type;
425 if (Source == true)
426 Type = "deb-src";
427 else
428 Type = "deb";
429
430 char Buffer[300];
431 int CurLine = 0;
432 bool First = true;
433 while (F.eof() == false)
434 {
435 F.getline(Buffer,sizeof(Buffer));
436 CurLine++;
437 if (F.fail() && !F.eof())
438 return _error->Error(_("Line %u too long in source list %s."),
439 CurLine,File.c_str());
440 _strtabexpand(Buffer,sizeof(Buffer));
441 _strstrip(Buffer);
442
443 // Comment or blank
444 if (Buffer[0] == '#' || Buffer[0] == 0)
445 {
446 Out << Buffer << endl;
447 continue;
448 }
449
450 if (First == true)
451 {
452 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
453 {
454 string::size_type Space = (*I).find(' ');
455 if (Space == string::npos)
456 return _error->Error("Internal error");
457 Out << Type << " cdrom:[" << Name << "]/" << string(*I,0,Space) <<
458 " " << string(*I,Space+1) << endl;
459 }
460 }
461 First = false;
462
463 // Grok it
464 string cType;
465 string URI;
466 const char *C = Buffer;
467 if (ParseQuoteWord(C,cType) == false ||
468 ParseQuoteWord(C,URI) == false)
469 {
470 Out << Buffer << endl;
471 continue;
472 }
473
474 // Emit lines like this one
475 if (cType != Type || (string(URI,0,ShortURI.length()) != ShortURI &&
476 string(URI,0,ShortURI.length()) != ShortURI2))
477 {
478 Out << Buffer << endl;
479 continue;
480 }
481 }
482
483 // Just in case the file was empty
484 if (First == true)
485 {
486 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
487 {
488 string::size_type Space = (*I).find(' ');
489 if (Space == string::npos)
490 return _error->Error("Internal error");
491
492 Out << "deb cdrom:[" << Name << "]/" << string(*I,0,Space) <<
493 " " << string(*I,Space+1) << endl;
494 }
495 }
496
497 Out.close();
498
499 rename(File.c_str(),string(File + '~').c_str());
500 if (rename(NewFile.c_str(),File.c_str()) != 0)
501 return _error->Errno("rename","Failed to rename %s.new to %s",
502 File.c_str(),File.c_str());
503
504 return true;
505 }
506 /*}}}*/
507 bool pkgCdrom::Ident(string &ident, pkgCdromStatus *log) /*{{{*/
508 {
509 stringstream msg;
510
511 // Startup
512 string CDROM = _config->FindDir("Acquire::cdrom::mount");
513 if (CDROM[0] == '.')
514 CDROM= SafeGetCWD() + '/' + CDROM;
515
516 if(log) {
517 msg.str("");
518 ioprintf(msg, _("Using CD-ROM mount point %s\nMounting CD-ROM\n"),
519 CDROM.c_str());
520 log->Update(msg.str());
521 }
522 if (MountCdrom(CDROM) == false)
523 return _error->Error("Failed to mount the cdrom.");
524
525 // Hash the CD to get an ID
526 if(log)
527 log->Update(_("Identifying.. "));
528
529
530 if (IdentCdrom(CDROM,ident) == false)
531 {
532 ident = "";
533 return false;
534 }
535
536 msg.str("");
537 ioprintf(msg, "[%s]\n",ident.c_str());
538 log->Update(msg.str());
539
540
541 // Read the database
542 Configuration Database;
543 string DFile = _config->FindFile("Dir::State::cdroms");
544 if (FileExists(DFile) == true)
545 {
546 if (ReadConfigFile(Database,DFile) == false)
547 return _error->Error("Unable to read the cdrom database %s",
548 DFile.c_str());
549 }
550 if(log) {
551 msg.str("");
552 ioprintf(msg, _("Stored label: %s\n"),
553 Database.Find("CD::"+ident).c_str());
554 log->Update(msg.str());
555 }
556
557 // Unmount and finish
558 if (_config->FindB("APT::CDROM::NoMount",false) == false) {
559 log->Update(_("Unmounting CD-ROM...\n"), STEP_LAST);
560 UnmountCdrom(CDROM);
561 }
562
563 return true;
564 }
565 /*}}}*/
566 bool pkgCdrom::Add(pkgCdromStatus *log) /*{{{*/
567 {
568 stringstream msg;
569
570 // Startup
571 string CDROM = _config->FindDir("Acquire::cdrom::mount");
572 if (CDROM[0] == '.')
573 CDROM= SafeGetCWD() + '/' + CDROM;
574
575 if(log) {
576 log->SetTotal(STEP_LAST);
577 msg.str("");
578 ioprintf(msg, _("Using CD-ROM mount point %s\n"), CDROM.c_str());
579 log->Update(msg.str(), STEP_PREPARE);
580 }
581
582 // Read the database
583 Configuration Database;
584 string DFile = _config->FindFile("Dir::State::cdroms");
585 if (FileExists(DFile) == true)
586 {
587 if (ReadConfigFile(Database,DFile) == false)
588 return _error->Error("Unable to read the cdrom database %s",
589 DFile.c_str());
590 }
591
592 // Unmount the CD and get the user to put in the one they want
593 if (_config->FindB("APT::CDROM::NoMount",false) == false)
594 {
595 if(log)
596 log->Update(_("Unmounting CD-ROM\n"), STEP_UNMOUNT);
597 UnmountCdrom(CDROM);
598
599 if(log) {
600 log->Update(_("Waiting for disc...\n"), STEP_WAIT);
601 if(!log->ChangeCdrom()) {
602 // user aborted
603 return false;
604 }
605 }
606
607 // Mount the new CDROM
608 log->Update(_("Mounting CD-ROM...\n"), STEP_MOUNT);
609 if (MountCdrom(CDROM) == false)
610 return _error->Error("Failed to mount the cdrom.");
611 }
612
613 // Hash the CD to get an ID
614 if(log)
615 log->Update(_("Identifying.. "), STEP_IDENT);
616 string ID;
617 if (IdentCdrom(CDROM,ID) == false)
618 {
619 log->Update("\n");
620 return false;
621 }
622 if(log)
623 log->Update("["+ID+"]\n");
624
625 if(log)
626 log->Update(_("Scanning disc for index files..\n"),STEP_SCAN);
627
628 // Get the CD structure
629 vector<string> List;
630 vector<string> SourceList;
631 vector<string> SigList;
632 vector<string> TransList;
633 string StartDir = SafeGetCWD();
634 string InfoDir;
635 if (FindPackages(CDROM,List,SourceList, SigList,TransList,InfoDir,log) == false)
636 {
637 log->Update("\n");
638 return false;
639 }
640
641 chdir(StartDir.c_str());
642
643 if (_config->FindB("Debug::aptcdrom",false) == true)
644 {
645 cout << "I found (binary):" << endl;
646 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
647 cout << *I << endl;
648 cout << "I found (source):" << endl;
649 for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); I++)
650 cout << *I << endl;
651 cout << "I found (Signatures):" << endl;
652 for (vector<string>::iterator I = SigList.begin(); I != SigList.end(); I++)
653 cout << *I << endl;
654 }
655
656 //log->Update(_("Cleaning package lists..."), STEP_CLEAN);
657
658 // Fix up the list
659 DropBinaryArch(List);
660 DropRepeats(List,"Packages");
661 DropRepeats(SourceList,"Sources");
662 DropRepeats(SigList,"Release.gpg");
663 DropRepeats(TransList,"");
664 if(log) {
665 msg.str("");
666 ioprintf(msg, _("Found %zu package indexes, %zu source indexes, "
667 "%zu translation indexes and %zu signatures\n"),
668 List.size(), SourceList.size(), TransList.size(),
669 SigList.size());
670 log->Update(msg.str(), STEP_SCAN);
671 }
672
673 if (List.size() == 0 && SourceList.size() == 0)
674 {
675 if (_config->FindB("APT::CDROM::NoMount",false) == false)
676 UnmountCdrom(CDROM);
677 return _error->Error(_("Unable to locate any package files, perhaps this is not a Debian Disc or the wrong architecture?"));
678 }
679
680 // Check if the CD is in the database
681 string Name;
682 if (Database.Exists("CD::" + ID) == false ||
683 _config->FindB("APT::CDROM::Rename",false) == true)
684 {
685 // Try to use the CDs label if at all possible
686 if (InfoDir.empty() == false &&
687 FileExists(InfoDir + "/info") == true)
688 {
689 ifstream F(string(InfoDir + "/info").c_str());
690 if (!F == 0)
691 getline(F,Name);
692
693 if (Name.empty() == false)
694 {
695 // Escape special characters
696 string::iterator J = Name.begin();
697 for (; J != Name.end(); J++)
698 if (*J == '"' || *J == ']' || *J == '[')
699 *J = '_';
700
701 if(log) {
702 msg.str("");
703 ioprintf(msg, _("Found label '%s'\n"), Name.c_str());
704 log->Update(msg.str());
705 }
706 Database.Set("CD::" + ID + "::Label",Name);
707 }
708 }
709
710 if (_config->FindB("APT::CDROM::Rename",false) == true ||
711 Name.empty() == true)
712 {
713 if(!log)
714 {
715 if (_config->FindB("APT::CDROM::NoMount",false) == false)
716 UnmountCdrom(CDROM);
717 return _error->Error("No disc name found and no way to ask for it");
718 }
719
720 while(true) {
721 if(!log->AskCdromName(Name)) {
722 // user canceld
723 return false;
724 }
725 cout << "Name: '" << Name << "'" << endl;
726
727 if (Name.empty() == false &&
728 Name.find('"') == string::npos &&
729 Name.find('[') == string::npos &&
730 Name.find(']') == string::npos)
731 break;
732 log->Update(_("That is not a valid name, try again.\n"));
733 }
734 }
735 }
736 else
737 Name = Database.Find("CD::" + ID);
738
739 // Escape special characters
740 string::iterator J = Name.begin();
741 for (; J != Name.end(); J++)
742 if (*J == '"' || *J == ']' || *J == '[')
743 *J = '_';
744
745 Database.Set("CD::" + ID,Name);
746 if(log) {
747 msg.str("");
748 ioprintf(msg, _("This disc is called: \n'%s'\n"), Name.c_str());
749 log->Update(msg.str());
750 }
751
752 log->Update(_("Copying package lists..."), STEP_COPY);
753 // take care of the signatures and copy them if they are ok
754 // (we do this before PackageCopy as it modifies "List" and "SourceList")
755 SigVerify SignVerify;
756 SignVerify.CopyAndVerify(CDROM, Name, SigList, List, SourceList);
757
758 // Copy the package files to the state directory
759 PackageCopy Copy;
760 SourceCopy SrcCopy;
761 TranslationsCopy TransCopy;
762 if (Copy.CopyPackages(CDROM,Name,List, log) == false ||
763 SrcCopy.CopyPackages(CDROM,Name,SourceList, log) == false ||
764 TransCopy.CopyTranslations(CDROM,Name,TransList, log) == false)
765 return false;
766
767 // reduce the List so that it takes less space in sources.list
768 ReduceSourcelist(CDROM,List);
769 ReduceSourcelist(CDROM,SourceList);
770
771 // Write the database and sourcelist
772 if (_config->FindB("APT::cdrom::NoAct",false) == false)
773 {
774 if (WriteDatabase(Database) == false)
775 return false;
776
777 if(log) {
778 log->Update(_("Writing new source list\n"), STEP_WRITE);
779 }
780 if (WriteSourceList(Name,List,false) == false ||
781 WriteSourceList(Name,SourceList,true) == false)
782 return false;
783 }
784
785 // Print the sourcelist entries
786 if(log)
787 log->Update(_("Source list entries for this disc are:\n"));
788
789 for (vector<string>::iterator I = List.begin(); I != List.end(); I++)
790 {
791 string::size_type Space = (*I).find(' ');
792 if (Space == string::npos)
793 {
794 if (_config->FindB("APT::CDROM::NoMount",false) == false)
795 UnmountCdrom(CDROM);
796 return _error->Error("Internal error");
797 }
798
799 if(log) {
800 msg.str("");
801 msg << "deb cdrom:[" << Name << "]/" << string(*I,0,Space) <<
802 " " << string(*I,Space+1) << endl;
803 log->Update(msg.str());
804 }
805 }
806
807 for (vector<string>::iterator I = SourceList.begin(); I != SourceList.end(); I++)
808 {
809 string::size_type Space = (*I).find(' ');
810 if (Space == string::npos)
811 {
812 if (_config->FindB("APT::CDROM::NoMount",false) == false)
813 UnmountCdrom(CDROM);
814 return _error->Error("Internal error");
815 }
816
817 if(log) {
818 msg.str("");
819 msg << "deb-src cdrom:[" << Name << "]/" << string(*I,0,Space) <<
820 " " << string(*I,Space+1) << endl;
821 log->Update(msg.str());
822 }
823 }
824
825 // Unmount and finish
826 if (_config->FindB("APT::CDROM::NoMount",false) == false) {
827 log->Update(_("Unmounting CD-ROM...\n"), STEP_LAST);
828 UnmountCdrom(CDROM);
829 }
830
831 return true;
832 }
833 /*}}}*/
834 pkgUdevCdromDevices::pkgUdevCdromDevices() /*{{{*/
835 : libudev_handle(NULL)
836 {
837
838 }
839 /*}}}*/
840
841 bool
842 pkgUdevCdromDevices::Dlopen() /*{{{*/
843 {
844 // alread open
845 if(libudev_handle != NULL)
846 return true;
847
848 // see if we can get libudev
849 void *h = ::dlopen("libudev.so.0", RTLD_LAZY);
850 if(h == NULL)
851 return false;
852
853 // get the pointers to the udev structs
854 libudev_handle = h;
855 udev_new = (udev* (*)(void)) dlsym(h, "udev_new");
856 udev_enumerate_add_match_property = (int (*)(udev_enumerate*, const char*, const char*))dlsym(h, "udev_enumerate_add_match_property");
857 #if 0 // FIXME: uncomment on next ABI break
858 udev_enumerate_add_match_sysattr = (int (*)(udev_enumerate*, const char*, const char*))dlsym(h, "udev_enumerate_add_match_sysattr");
859 #endif
860 udev_enumerate_scan_devices = (int (*)(udev_enumerate*))dlsym(h, "udev_enumerate_scan_devices");
861 udev_enumerate_get_list_entry = (udev_list_entry* (*)(udev_enumerate*))dlsym(h, "udev_enumerate_get_list_entry");
862 udev_device_new_from_syspath = (udev_device* (*)(udev*, const char*))dlsym(h, "udev_device_new_from_syspath");
863 udev_enumerate_get_udev = (udev* (*)(udev_enumerate*))dlsym(h, "udev_enumerate_get_udev");
864 udev_list_entry_get_name = (const char* (*)(udev_list_entry*))dlsym(h, "udev_list_entry_get_name");
865 udev_device_get_devnode = (const char* (*)(udev_device*))dlsym(h, "udev_device_get_devnode");
866 udev_enumerate_new = (udev_enumerate* (*)(udev*))dlsym(h, "udev_enumerate_new");
867 udev_list_entry_get_next = (udev_list_entry* (*)(udev_list_entry*))dlsym(h, "udev_list_entry_get_next");
868 udev_device_get_property_value = (const char* (*)(udev_device *, const char *))dlsym(h, "udev_device_get_property_value");
869
870 return true;
871 }
872 /*}}}*/
873
874 /*{{{*/
875 // compatiblity only with the old API/ABI, can be removed on the next
876 // ABI break
877 vector<CdromDevice>
878 pkgUdevCdromDevices::Scan()
879 {
880 bool CdromOnly = _config->FindB("APT::cdrom::CdromOnly", true);
881 return ScanForRemovable(CdromOnly);
882 };
883 /*}}}*/
884 /*{{{*/
885 vector<CdromDevice>
886 pkgUdevCdromDevices::ScanForRemovable(bool CdromOnly)
887 {
888 vector<CdromDevice> cdrom_devices;
889 struct udev_enumerate *enumerate;
890 struct udev_list_entry *l, *devices;
891 struct udev *udev_ctx;
892
893 if(libudev_handle == NULL)
894 return cdrom_devices;
895
896 udev_ctx = udev_new();
897 enumerate = udev_enumerate_new (udev_ctx);
898 if (CdromOnly)
899 udev_enumerate_add_match_property(enumerate, "ID_CDROM", "1");
900 else {
901 #if 1 // FIXME: remove the next two lines on the next ABI break
902 int (*udev_enumerate_add_match_sysattr)(struct udev_enumerate *udev_enumerate, const char *property, const char *value);
903 udev_enumerate_add_match_sysattr = (int (*)(udev_enumerate*, const char*, const char*))dlsym(libudev_handle, "udev_enumerate_add_match_sysattr");
904 #endif
905 udev_enumerate_add_match_sysattr(enumerate, "removable", "1");
906 }
907
908 udev_enumerate_scan_devices (enumerate);
909 devices = udev_enumerate_get_list_entry (enumerate);
910 for (l = devices; l != NULL; l = udev_list_entry_get_next (l))
911 {
912 CdromDevice cdrom;
913 struct udev_device *udevice;
914 udevice = udev_device_new_from_syspath (udev_enumerate_get_udev (enumerate), udev_list_entry_get_name (l));
915 if (udevice == NULL)
916 continue;
917 const char* devnode = udev_device_get_devnode(udevice);
918
919 // try fstab_dir first
920 string mountpath;
921 const char* mp = udev_device_get_property_value(udevice, "FSTAB_DIR");
922 if (mp)
923 mountpath = string(mp);
924 else
925 mountpath = FindMountPointForDevice(devnode);
926
927 // fill in the struct
928 cdrom.DeviceName = string(devnode);
929 if (mountpath != "") {
930 cdrom.MountPath = mountpath;
931 string s = string(mountpath);
932 cdrom.Mounted = IsMounted(s);
933 } else {
934 cdrom.Mounted = false;
935 cdrom.MountPath = "";
936 }
937 cdrom_devices.push_back(cdrom);
938 }
939 return cdrom_devices;
940 }
941 /*}}}*/
942
943 pkgUdevCdromDevices::~pkgUdevCdromDevices() /*{{{*/
944 {
945 if (libudev_handle != NULL)
946 dlclose(libudev_handle);
947 }
948 /*}}}*/