]>
git.saurik.com Git - apt.git/blob - cmdline/apt-cdrom.cc
1839fdab4bfa0a7401790b46dd68b346d76f81d9
1 // -*- mode: cpp; mode: fold -*-
3 // $Id: apt-cdrom.cc,v 1.1 1998/11/27 01:52:56 jgg Exp $
4 /* ######################################################################
7 ##################################################################### */
9 // Include Files /*{{{*/
10 #include <apt-pkg/cmndline.h>
11 #include <apt-pkg/error.h>
12 #include <apt-pkg/init.h>
13 #include <apt-pkg/md5.h>
14 #include <apt-pkg/fileutl.h>
15 #include <apt-pkg/progress.h>
16 #include <apt-pkg/tagfile.h>
24 #include <sys/errno.h>
33 // UnmountCdrom - Unmount a cdrom /*{{{*/
34 // ---------------------------------------------------------------------
36 bool UnmountCdrom(string Path
)
40 return _error
->Errno("fork","Failed to fork");
45 // Make all the fds /dev/null
46 for (int I
= 0; I
!= 10;)
48 for (int I
= 0; I
!= 3;)
49 dup2(open("/dev/null",O_RDWR
),I
);
53 Args
[1] = Path
.c_str();
55 execvp(Args
[0],(char **)Args
);
61 while (waitpid(Child
,&Status
,0) != Child
)
65 return _error
->Errno("waitpid","Couldn't wait for subprocess");
68 // Check for an error code.
69 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
74 // MountCdrom - Mount a cdrom /*{{{*/
75 // ---------------------------------------------------------------------
77 bool MountCdrom(string Path
)
81 return _error
->Errno("fork","Failed to fork");
86 // Make all the fds /dev/null
87 for (int I
= 0; I
!= 10;)
89 for (int I
= 0; I
!= 3;)
90 dup2(open("/dev/null",O_RDWR
),I
);
94 Args
[1] = Path
.c_str();
96 execvp(Args
[0],(char **)Args
);
102 while (waitpid(Child
,&Status
,0) != Child
)
106 return _error
->Errno("waitpid","Couldn't wait for subprocess");
109 // Check for an error code.
110 if (WIFEXITED(Status
) == 0 || WEXITSTATUS(Status
) != 0)
115 // IdentCdrom - Generate a unique string for this CD /*{{{*/
116 // ---------------------------------------------------------------------
117 /* We convert everything we hash into a string, this prevents byte size/order
118 from effecting the outcome. */
119 bool IdentCdrom(string CD
,string
&Res
)
123 string StartDir
= SafeGetCWD();
124 if (chdir(CD
.c_str()) != 0)
125 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
127 DIR *D
= opendir(".");
129 return _error
->Errno("opendir","Unable to read %s",CD
.c_str());
131 // Run over the directory
133 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
136 if (strcmp(Dir
->d_name
,".") == 0 ||
137 strcmp(Dir
->d_name
,"..") == 0)
140 sprintf(S
,"%lu",Dir
->d_ino
);
142 Hash
.Add(Dir
->d_name
);
145 chdir(StartDir
.c_str());
148 // Some stats from the fsys
150 if (statfs(CD
.c_str(),&Buf
) != 0)
151 return _error
->Errno("statfs","Failed to stat the cdrom");
153 sprintf(S
,"%u %u",Buf
.f_blocks
,Buf
.f_bfree
);
156 Res
= Hash
.Result().Value();
161 // FindPackage - Find the package files on the CDROM /*{{{*/
162 // ---------------------------------------------------------------------
163 /* We look over the cdrom for package files. This is a recursive
164 search that short circuits when it his a package file in the dir.
165 This speeds it up greatly as the majority of the size is in the
166 binary-* sub dirs. */
167 bool FindPackages(string CD
,vector
<string
> &List
, int Depth
= 0)
172 if (CD
[CD
.length()-1] != '/')
175 if (chdir(CD
.c_str()) != 0)
176 return _error
->Errno("chdir","Unable to change to %s",CD
.c_str());
178 /* Aha! We found some package files. We assume that everything under
179 this dir is controlled by those package files so we don't look down
182 if (stat("Packages",&Buf
) == 0 ||
183 stat("Packages.gz",&Buf
) == 0)
189 DIR *D
= opendir(".");
191 return _error
->Errno("opendir","Unable to read %s",CD
.c_str());
193 // Run over the directory
194 for (struct dirent
*Dir
= readdir(D
); Dir
!= 0; Dir
= readdir(D
))
197 if (strcmp(Dir
->d_name
,".") == 0 ||
198 strcmp(Dir
->d_name
,"..") == 0 ||
199 strcmp(Dir
->d_name
,"source") == 0 ||
200 strcmp(Dir
->d_name
,"experimental") == 0 ||
201 strcmp(Dir
->d_name
,"binary-all") == 0)
204 // See if the name is a sub directory
206 if (stat(Dir
->d_name
,&Buf
) != 0)
208 _error
->Errno("Stat","Stat failed for %s",Dir
->d_name
);
212 if (S_ISDIR(Buf
.st_mode
) == 0)
216 if (FindPackages(CD
+ Dir
->d_name
,List
,Depth
+1) == false)
219 if (chdir(CD
.c_str()) != 0)
220 return _error
->Errno("chdir","Unable to change to ",CD
.c_str());
225 return !_error
->PendingError();
228 // CopyPackages - Copy the package files from the CD /*{{{*/
229 // ---------------------------------------------------------------------
231 bool CopyPackages(string CDROM
,string Name
,vector
<string
> &List
)
233 OpTextProgress Progress
;
235 bool NoStat
= _config
->FindB("APT::CDROM::Fast",false);
237 // Prepare the progress indicator
238 unsigned long TotalSize
= 0;
239 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
242 if (stat(string(*I
+ "Packages").c_str(),&Buf
) != 0)
243 return _error
->Errno("stat","Stat failed for %s",
244 string(*I
+ "Packages").c_str());
245 TotalSize
+= Buf
.st_size
;
248 unsigned long CurrentSize
= 0;
249 unsigned int NotFound
= 0;
250 unsigned int WrongSize
= 0;
251 unsigned int Packages
= 0;
252 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
254 // Open the package file
255 FileFd
Pkg(*I
+ "Packages",FileFd::ReadOnly
);
256 pkgTagFile
Parser(Pkg
);
257 if (_error
->PendingError() == true)
260 // Open the output file
262 sprintf(S
,"cdrom:%s/%sPackages",Name
.c_str(),(*I
).c_str() + CDROM
.length());
263 string TargetF
= _config
->FindDir("Dir::State::lists") + "partial/";
264 FileFd
Target(TargetF
+ URItoFileName(S
),FileFd::WriteEmpty
);
265 if (_error
->PendingError() == true)
268 // Setup the progress meter
269 Progress
.OverallProgress(CurrentSize
,TotalSize
,Pkg
.Size(),
270 "Reading Package Lists");
273 Progress
.SubProgress(Pkg
.Size());
274 pkgTagSection Section
;
275 while (Parser
.Step(Section
) == true)
277 Progress
.Progress(Parser
.Offset());
279 string File
= Section
.FindS("Filename");
280 unsigned long Size
= Section
.FindI("Size");
281 if (File
.empty() || Size
== 0)
282 return _error
->Error("Cannot find filename or size tag");
284 // See if the file exists
289 if (stat(File
.c_str(),&Buf
) != 0)
296 if ((unsigned)Buf
.st_size
!= Size
)
305 // Copy it to the target package file
308 Section
.GetSection(Start
,Stop
);
309 if (Target
.Write(Start
,Stop
-Start
) == false)
313 CurrentSize
+= Pkg
.Size();
318 cout
<< "Wrote " << Packages
<< " package records" ;
320 cout
<< " with " << NotFound
<< " missing files";
321 if (NotFound
!= 0 && WrongSize
!= 0)
324 cout
<< " with " << WrongSize
<< " mismatched files";
326 if (NotFound
+ WrongSize
> 10)
327 cout
<< "Alot of package entires were discarded, perhaps this CD is funny?" << endl
;
330 // DropBinaryArch - Dump dirs with a string like /binary-<foo>/ /*{{{*/
331 // ---------------------------------------------------------------------
332 /* Here we drop everything that is not this machines arch */
333 bool DropBinaryArch(vector
<string
> &List
)
336 sprintf(S
,"/binary-%s/",_config
->Find("Apt::Architecture").c_str());
338 for (unsigned int I
= 0; I
< List
.size(); I
++)
340 const char *Str
= List
[I
].c_str();
343 if ((Res
= strstr(Str
,"/binary-")) == 0)
347 if (strlen(Res
) < strlen(S
))
349 List
.erase(List
.begin() + I
);
354 // See if it is our arch
355 if (stringcmp(Res
,Res
+ strlen(S
),S
) == 0)
359 List
.erase(List
.begin() + I
);
366 // Score - We compute a 'score' for a path /*{{{*/
367 // ---------------------------------------------------------------------
368 /* Paths are scored based on how close they come to what I consider
369 normal. That is ones that have 'dist' 'stable' 'frozen' will score
370 higher than ones without. */
371 int Score(string Path
)
374 if (Path
.find("stable/") != string::npos
)
376 if (Path
.find("frozen/") != string::npos
)
378 if (Path
.find("/dists/") != string::npos
)
380 if (Path
.find("/main/") != string::npos
)
382 if (Path
.find("/contrib/") != string::npos
)
384 if (Path
.find("/non-free/") != string::npos
)
386 if (Path
.find("/non-US/") != string::npos
)
391 // DropRepeats - Drop repeated files resulting from symlinks /*{{{*/
392 // ---------------------------------------------------------------------
393 /* Here we go and stat every file that we found and strip dup inodes. */
394 bool DropRepeats(vector
<string
> &List
)
396 // Get a list of all the inodes
397 ino_t
*Inodes
= new ino_t
[List
.size()];
398 for (unsigned int I
= 0; I
!= List
.size(); I
++)
401 if (stat(List
[I
].c_str(),&Buf
) != 0)
402 _error
->Errno("stat","Failed to stat %s",List
[I
].c_str());
403 Inodes
[I
] = Buf
.st_ino
;
407 for (unsigned int I
= 0; I
!= List
.size(); I
++)
409 for (unsigned int J
= I
+1; J
< List
.size(); J
++)
412 if (Inodes
[J
] != Inodes
[I
])
415 // We score the two paths.. and erase one
416 int ScoreA
= Score(List
[I
]);
417 int ScoreB
= Score(List
[J
]);
428 // Wipe erased entries
429 for (unsigned int I
= 0; I
< List
.size();)
431 if (List
[I
].empty() == false)
434 List
.erase(List
.begin()+I
);
440 // ConvertToSourceList - Takes the path list and converts it /*{{{*/
441 // ---------------------------------------------------------------------
442 /* This looks at each element and decides if it can be expressed using
443 dists/ form or if it requires an absolute specficiation. It also
444 strips the leading CDROM path from the paths. */
445 bool ConvertToSourcelist(string CD
,vector
<string
> &List
)
448 sprintf(S
,"binary-%s",_config
->Find("Apt::Architecture").c_str());
450 sort(List
.begin(),List
.end());
452 // Convert to source list notation
453 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
455 // Strip the cdrom base path
456 *I
= string(*I
,CD
.length());
458 // Too short to be a dists/ type
459 if ((*I
).length() < strlen("dists/"))
463 if (stringcmp((*I
).begin(),(*I
).begin()+strlen("dists/"),"dists/") != 0)
467 string::size_type Slash
= strlen("dists/");
468 string::size_type Slash2
= (*I
).find('/',Slash
+ 1);
469 if (Slash2
== string::npos
|| Slash2
+ 2 >= (*I
).length())
471 string Dist
= string(*I
,Slash
,Slash2
- Slash
);
473 // Isolate the component
474 Slash
= (*I
).find('/',Slash2
+1);
475 if (Slash
== string::npos
|| Slash
+ 2 >= (*I
).length())
477 string Comp
= string(*I
,Slash2
+1,Slash
- Slash2
-1);
479 // Verify the trailing binar - bit
480 Slash2
= (*I
).find('/',Slash
+ 1);
481 if (Slash
== string::npos
)
483 string Binary
= string(*I
,Slash
+1,Slash2
- Slash
-1);
488 *I
= Dist
+ ' ' + Comp
;
491 // Collect similar entries
492 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
495 string::size_type Space
= (*I
).find(' ');
496 if (Space
== string::npos
)
499 string Word1
= string(*I
,0,Space
);
500 for (vector
<string
>::iterator J
= List
.begin(); J
!= I
; J
++)
503 string::size_type Space2
= (*J
).find(' ');
504 if (Space2
== string::npos
)
507 if (string(*J
,0,Space2
) != Word1
)
510 *J
+= string(*I
,Space
);
515 // Wipe erased entries
516 for (unsigned int I
= 0; I
< List
.size();)
518 if (List
[I
].empty() == false)
521 List
.erase(List
.begin()+I
);
526 // Prompt - Simple prompt /*{{{*/
527 // ---------------------------------------------------------------------
529 void Prompt(const char *Text
)
532 cout
<< Text
<< ' ' << flush
;
533 read(STDIN_FILENO
,&C
,1);
538 // PromptLine - Prompt for an input line /*{{{*/
539 // ---------------------------------------------------------------------
541 string
PromptLine(const char *Text
)
543 cout
<< Text
<< ':' << endl
;
551 // DoAdd - Add a new CDROM /*{{{*/
552 // ---------------------------------------------------------------------
554 bool DoAdd(CommandLine
&)
557 string CDROM
= _config
->FindDir("Acquire::cdrom::mount","/cdrom/");
558 cout
<< "Using CD-ROM mount point " << CDROM
<< endl
;
561 Configuration Database
;
562 string DFile
= _config
->FindFile("Dir::State::cdroms");
563 if (FileExists(DFile
) == true)
565 if (ReadConfigFile(Database
,DFile
) == false)
566 return _error
->Error("Unable to read the cdrom database %s",
570 // Unmount the CD and get the user to put in the one they want
571 if (_config
->FindB("APT::CDROM::NoMount",false) == false)
573 cout
<< "Unmounting CD-ROM" << endl
;
576 // Mount the new CDROM
577 Prompt("Please insert a CD-ROM and press any key");
578 cout
<< "Mounting CD-ROM" << endl
;
579 if (MountCdrom(CDROM
) == false)
581 cout
<< "Failed to mount the cdrom." << endl
;
586 // Hash the CD to get an ID
587 cout
<< "Indentifying.. " << flush
;
589 if (IdentCdrom(CDROM
,ID
) == false)
591 cout
<< '[' << ID
<< ']' << endl
;
593 cout
<< "Scanning Disc for index files.. " << flush
;
594 // Get the CD structure
596 string StartDir
= SafeGetCWD();
597 if (FindPackages(CDROM
,List
) == false)
599 chdir(StartDir
.c_str());
602 DropBinaryArch(List
);
604 cout
<< "Found " << List
.size() << " package index files." << endl
;
606 if (List
.size() == 0)
607 return _error
->Error("Unable to locate any package files, perhaps this is not a debian CD-ROM");
609 // Check if the CD is in the database
611 if (Database
.Exists("CD::" + ID
) == false ||
612 _config
->FindB("APT::CDROM::Rename",false) == true)
614 cout
<< "Please provide a name for this CD-ROM, such as 'Debian 2.1r1 Disk 1'";
615 Name
= PromptLine("");
618 Name
= Database
.Find("CD::" + ID
);
619 cout
<< "This Disc is called '" << Name
<< "'" << endl
;
621 // Copy the package files to the state directory
622 if (CopyPackages(CDROM
,Name
,List
) == false)
625 ConvertToSourcelist(CDROM
,List
);
627 // Print the sourcelist entries
628 cout
<< "Source List entires for this Disc are:" << endl
;
629 for (vector
<string
>::iterator I
= List
.begin(); I
!= List
.end(); I
++)
630 cout
<< "deb \"cdrom:" << Name
<< "/\" " << *I
<< endl
;
636 // ShowHelp - Show the help screen /*{{{*/
637 // ---------------------------------------------------------------------
641 cout
<< PACKAGE
<< ' ' << VERSION
<< " for " << ARCHITECTURE
<<
642 " compiled on " << __DATE__
<< " " << __TIME__
<< endl
;
644 cout
<< "Usage: apt-cdrom [options] command" << endl
;
646 cout
<< "apt-cdrom is a tool to add CDROM's to APT's source list. The " << endl
;
647 cout
<< "CDROM mount point and device information is taken from apt.conf" << endl
;
648 cout
<< "and /etc/fstab." << endl
;
650 cout
<< "Commands:" << endl
;
651 cout
<< " add - Add a CDROM" << endl
;
653 cout
<< "Options:" << endl
;
654 cout
<< " -h This help text" << endl
;
655 cout
<< " -d CD-ROM mount point" << endl
;
656 cout
<< " -r Rename a recognized CD-ROM" << endl
;
657 cout
<< " -m No mounting" << endl
;
658 cout
<< " -c=? Read this configuration file" << endl
;
659 cout
<< " -o=? Set an arbitary configuration option, ie -o dir::cache=/tmp" << endl
;
660 cout
<< "See fstab(5)" << endl
;
665 int main(int argc
,const char *argv
[])
667 CommandLine::Args Args
[] = {
668 {'h',"help","help",0},
669 {'d',"cdrom","Acquire::cdrom::mount",CommandLine::HasArg
},
670 {'r',"rename","APT::CDROM::Rename",0},
671 {'m',"no-mount","APT::CDROM::NoMount",0},
672 {'f',"fast","APT::CDROM::Fast",0},
673 {'c',"config-file",0,CommandLine::ConfigFile
},
674 {'o',"option",0,CommandLine::ArbItem
},
676 CommandLine::Dispatch Cmds
[] = {
680 // Parse the command line and initialize the package library
681 CommandLine
CmdL(Args
,_config
);
682 if (pkgInitialize(*_config
) == false ||
683 CmdL
.Parse(argc
,argv
) == false)
685 _error
->DumpErrors();
689 // See if the help should be shown
690 if (_config
->FindB("help") == true ||
691 CmdL
.FileSize() == 0)
694 // Match the operation
695 CmdL
.DispatchArg(Cmds
);
697 // Print any errors or warnings found during parsing
698 if (_error
->empty() == false)
700 bool Errors
= _error
->PendingError();
701 _error
->DumpErrors();
702 return Errors
== true?100:0;