- initial import of revision 374 from cnc
[apt.git] / apt-pkg / contrib / cdromutl.cc
1 // -*- mode: cpp; mode: fold -*-
2 // Description                                                          /*{{{*/
3 // $Id: cdromutl.cc,v 1.1 2002/07/23 17:54:51 niemeyer Exp $
4 /* ######################################################################
5    
6    CDROM Utilities - Some functions to manipulate CDROM mounts.
7    
8    These are here for the cdrom method and apt-cdrom.
9    
10    ##################################################################### */
11                                                                         /*}}}*/
12 // Include Files                                                        /*{{{*/
13 #ifdef __GNUG__
14 #pragma implementation "apt-pkg/cdromutl.h"
15 #endif
16
17 // CNC:2004-03-19
18 #include <config.h>
19 #include <apt-pkg/luaiface.h>
20
21 #include <apt-pkg/cdromutl.h>
22 #include <apt-pkg/error.h>
23 #include <apt-pkg/md5.h>
24 #include <apt-pkg/fileutl.h>
25 #include <apt-pkg/configuration.h>
26
27 #include <apti18n.h>
28     
29 #include <sys/wait.h>
30 #include <sys/errno.h>
31 #include <sys/statvfs.h>
32 #include <dirent.h>
33 #include <fcntl.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <stdio.h>
37                                                                         /*}}}*/
38
39 // IsMounted - Returns true if the mount point is mounted               /*{{{*/
40 // ---------------------------------------------------------------------
41 /* This is a simple algorithm that should always work, we stat the mount point
42    and the '..' file in the mount point and see if they are on the same device.
43    By definition if they are the same then it is not mounted. This should 
44    account for symlinked mount points as well. */
45 bool IsMounted(string &Path)
46 {
47    if (Path.empty() == true)
48       return false;
49    
50    // Need that trailing slash for directories
51    if (Path[Path.length() - 1] != '/')
52       Path += '/';
53    
54    /* First we check if the path is actualy mounted, we do this by
55       stating the path and the previous directory (carefull of links!)
56       and comparing their device fields. */
57    struct stat Buf,Buf2;
58    if (stat(Path.c_str(),&Buf) != 0 || 
59        stat((Path + "../").c_str(),&Buf2) != 0)
60       return _error->Errno("stat",_("Unable to stat the mount point %s"),Path.c_str());
61
62    if (Buf.st_dev == Buf2.st_dev)
63       return false;
64    return true;
65 }
66                                                                         /*}}}*/
67 // UnmountCdrom - Unmount a cdrom                                       /*{{{*/
68 // ---------------------------------------------------------------------
69 /* Forking umount works much better than the umount syscall which can 
70    leave /etc/mtab inconsitant. We drop all messages this produces. */
71 bool UnmountCdrom(string Path)
72 {
73 // CNC:2004-03-19
74 #ifdef WITH_LUA
75    if (_lua->HasScripts("Scripts::Cdrom::Umount")) {
76       _lua->SetGlobal("done", false);
77       _lua->RunScripts("Scripts::Cdrom::Umount");
78       if (_lua->GetGlobalBool("done") == true)
79          return true;
80    }
81 #endif
82
83    if (IsMounted(Path) == false)
84       return true;
85    
86    int Child = ExecFork();
87
88    // The child
89    if (Child == 0)
90    {
91       // Make all the fds /dev/null
92       for (int I = 0; I != 3; I++)
93          dup2(open("/dev/null",O_RDWR),I);
94
95       if (_config->Exists("Acquire::cdrom::"+Path+"::UMount") == true)
96       {
97          if (system(_config->Find("Acquire::cdrom::"+Path+"::UMount").c_str()) != 0)
98             _exit(100);
99          _exit(0);               
100       }
101       else
102       {
103          const char *Args[10];
104          Args[0] = "umount";
105          Args[1] = Path.c_str();
106          Args[2] = 0;
107          execvp(Args[0],(char **)Args);      
108          _exit(100);
109       }      
110    }
111
112    // Wait for mount
113    return ExecWait(Child,"umount",true);
114 }
115                                                                         /*}}}*/
116 // MountCdrom - Mount a cdrom                                           /*{{{*/
117 // ---------------------------------------------------------------------
118 /* We fork mount and drop all messages */
119 bool MountCdrom(string Path)
120 {
121 // CNC:2004-03-19
122 #ifdef WITH_LUA
123    if (_lua->HasScripts("Scripts::Cdrom::Mount")) {
124       _lua->SetGlobal("done", false);
125       _lua->RunScripts("Scripts::Cdrom::Mount");
126       if (_lua->GetGlobalBool("done") == true)
127          return true;
128    }
129 #endif
130
131    if (IsMounted(Path) == true)
132       return true;
133    
134    int Child = ExecFork();
135
136    // The child
137    if (Child == 0)
138    {
139       // Make all the fds /dev/null
140       for (int I = 0; I != 3; I++)
141          dup2(open("/dev/null",O_RDWR),I);
142       
143       if (_config->Exists("Acquire::cdrom::"+Path+"::Mount") == true)
144       {
145          if (system(_config->Find("Acquire::cdrom::"+Path+"::Mount").c_str()) != 0)
146             _exit(100);
147          _exit(0);       
148       }
149       else
150       {
151          const char *Args[10];
152          Args[0] = "mount";
153          Args[1] = Path.c_str();
154          Args[2] = 0;
155          execvp(Args[0],(char **)Args);      
156          _exit(100);
157       }      
158    }
159
160    // Wait for mount
161    return ExecWait(Child,"mount",true);
162 }
163                                                                         /*}}}*/
164 // IdentCdrom - Generate a unique string for this CD                    /*{{{*/
165 // ---------------------------------------------------------------------
166 /* We convert everything we hash into a string, this prevents byte size/order
167    from effecting the outcome. */
168 bool IdentCdrom(string CD,string &Res,unsigned int Version)
169 {
170    MD5Summation Hash;
171
172    string StartDir = SafeGetCWD();
173    if (chdir(CD.c_str()) != 0)
174       return _error->Errno("chdir",_("Unable to change to %s"),CD.c_str());
175    
176    DIR *D = opendir(".");
177    if (D == 0)
178       return _error->Errno("opendir",_("Unable to read %s"),CD.c_str());
179       
180    /* Run over the directory, we assume that the reader order will never
181       change as the media is read-only. In theory if the kernel did
182       some sort of wacked caching this might not be true.. */
183    char S[300];
184    for (struct dirent *Dir = readdir(D); Dir != 0; Dir = readdir(D))
185    {
186       // Skip some files..
187       if (strcmp(Dir->d_name,".") == 0 ||
188           strcmp(Dir->d_name,"..") == 0)
189          continue;
190
191       if (Version <= 1)
192       {
193          sprintf(S,"%lu",(unsigned long)Dir->d_ino);
194       }
195       else
196       {
197          struct stat Buf;
198          if (stat(Dir->d_name,&Buf) != 0)
199             continue;
200          sprintf(S,"%lu",(unsigned long)Buf.st_mtime);
201       }
202       
203       Hash.Add(S);
204       Hash.Add(Dir->d_name);
205    };
206    
207    chdir(StartDir.c_str());
208    closedir(D);
209    
210    // Some stats from the fsys
211    if (_config->FindB("Debug::identcdrom",false) == false)
212    {
213       struct statvfs Buf;
214       if (statvfs(CD.c_str(),&Buf) != 0)
215          return _error->Errno("statfs",_("Failed to stat the cdrom"));
216       
217       // We use a kilobyte block size to advoid overflow
218       sprintf(S,"%lu %lu",(long)(Buf.f_blocks*(Buf.f_bsize/1024)),
219               (long)(Buf.f_bfree*(Buf.f_bsize/1024)));
220       Hash.Add(S);
221       sprintf(S,"-%u",Version);
222    }
223    else
224       sprintf(S,"-%u.debug",Version);
225    
226    Res = Hash.Result().Value() + S;
227    return true;   
228 }
229                                                                         /*}}}*/
230 // vim:sts=3:sw=3