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