added media-specific backends, some of them still need some updates...
[platform/upstream/libzypp.git] / zypp / media / MediaCD.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/media/MediaCD.cc
10  *
11 */
12
13 #include <iostream>
14
15 #include "zypp/base/Logger.h"
16 #include "zypp/ExternalProgram.h"
17 #include "zypp/media/Mount.h"
18 #include "zypp/media/MediaCD.h"
19
20 #include <cstring> // strerror
21
22 #include <errno.h>
23 #include <dirent.h>
24
25 #include <sys/ioctl.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28
29 #include <linux/cdrom.h>
30
31
32 using namespace std;
33
34 namespace zypp {
35   namespace media {
36
37 ///////////////////////////////////////////////////////////////////
38 //
39 //      CLASS NAME : MediaCD
40 //
41 ///////////////////////////////////////////////////////////////////
42
43     ///////////////////////////////////////////////////////////////////
44     //
45     //
46     //  METHOD NAME : MediaCD::MediaCD
47     //  METHOD TYPE : Constructor
48     //
49     //  DESCRIPTION :
50     //
51     MediaCD::MediaCD( const Url &      url_r,
52                   const Pathname & attach_point_hint_r )
53       : MediaHandler( url_r, attach_point_hint_r,
54                     url_r.getPathName(), // urlpath below attachpoint
55                     false ), // does_download
56       _lastdev(-1)
57     {
58       string devices = _url.getQueryParam("devices");
59       if (!devices.empty())
60       {
61         string::size_type pos;
62         DBG << "parse " << devices << endl;
63         while(!devices.empty())
64         {
65             pos = devices.find(',');
66             string device = devices.substr(0,pos);
67             if (!device.empty())
68             {
69                 _devices.push_back(device);
70                 DBG << "use device " << device << endl;
71             }
72             if (pos!=string::npos)
73                 devices=devices.substr(pos+1);
74             else
75                 devices.erase();
76         }
77       }
78       else
79       {
80         //default is /dev/cdrom; for dvd: /dev/dvd if it exists
81         //TODO: make configurable
82           string device( "/dev/cdrom" );
83         if ( _url.getScheme() == "dvd" && PathInfo( "/dev/dvd" ).isBlk() ) {
84           device = "/dev/dvd";
85         }
86         DBG << "use default device " << device << endl;
87         _devices.push_back(device);
88       }
89     }
90
91     ///////////////////////////////////////////////////////////////////
92     //
93     //
94     //  METHOD NAME : MediaCD::openTray
95     //  METHOD TYPE : bool
96     //
97     bool MediaCD::openTray( const string & device_r )
98     {
99       int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK );
100       if ( fd == -1 ) {
101         WAR << "Unable to open '" << device_r << "' (" << ::strerror( errno ) << ")" << endl;
102         return false;
103       }
104       int res = ::ioctl( fd, CDROMEJECT );
105       ::close( fd );
106       if ( res ) {
107         WAR << "Eject " << device_r << " failed (" << ::strerror( errno ) << ")" << endl;
108         return false;
109       }
110       MIL << "Eject " << device_r << endl;
111       return true;
112     }
113
114     ///////////////////////////////////////////////////////////////////
115     //
116     //
117     //  METHOD NAME : MediaCD::closeTray
118     //  METHOD TYPE : bool
119     //
120     bool MediaCD::closeTray( const string & device_r )
121     {
122       int fd = ::open( device_r.c_str(), O_RDONLY|O_NONBLOCK );
123       if ( fd == -1 ) {
124         WAR << "Unable to open '" << device_r << "' (" << ::strerror( errno ) << ")" << endl;
125         return false;
126       }
127       int res = ::ioctl( fd, CDROMCLOSETRAY );
128       ::close( fd );
129       if ( res ) {
130         WAR << "Close tray " << device_r << " failed (" << ::strerror( errno ) << ")" << endl;
131         return false;
132       }
133       DBG << "Close tray " << device_r << endl;
134       return true;
135     }
136
137     ///////////////////////////////////////////////////////////////////
138     //
139     //
140     //  METHOD NAME : MediaCD::attachTo
141     //  METHOD TYPE : PMError
142     //
143     //  DESCRIPTION : Asserted that not already attached, and attachPoint is a directory.
144     //
145     void MediaCD::attachTo(bool next)
146     {
147       Mount mount;
148       const char *mountpoint = attachPoint().asString().c_str();
149       bool mountsucceeded = false;
150       int count = 0;
151     
152       DBG << "next " << next << " last " << _lastdev << " lastdevice " << _mounteddevice << endl;
153     
154       if (next && _lastdev == -1)
155         ZYPP_THROW(MediaNotSupportedException(url()));
156     
157       string options = _url.getQueryParam("mountoptions");
158       if (options.empty())
159       {
160         options="ro";
161       }
162     
163       //TODO: make configurable
164       list<string> filesystems;
165     
166       // if DVD, try UDF filesystem before iso9660
167       if ( _url.getScheme() == "dvd" )
168         filesystems.push_back("udf");
169     
170       filesystems.push_back("iso9660");
171     
172       // try all devices in sequence
173       for (DeviceList::iterator it = _devices.begin()
174         ; !mountsucceeded && it != _devices.end()
175         ; ++it, count++ )
176       {
177         DBG << "count " << count << endl;
178         if (next && count<=_lastdev )
179         {
180                 DBG << "skip" << endl;
181                 continue;
182         }
183     
184         // close tray
185         closeTray( *it );
186     
187         // try all filesystems in sequence
188         for(list<string>::iterator fsit = filesystems.begin()
189             ; !mountsucceeded && fsit != filesystems.end()
190             ; ++fsit)
191         {
192           try {
193             mount.mount (*it, mountpoint, *fsit, options);
194             _mounteddevice = *it;
195             _lastdev = count;
196             mountsucceeded = true;
197           }
198           catch (const MediaException & excpt_r)
199           {
200             ZYPP_CAUGHT(excpt_r);
201           }
202         }
203       }
204     
205       if (!mountsucceeded)
206       {
207         _mounteddevice.erase();
208         _lastdev = -1;
209         // FIXME not suitable for MediaMountExeption, as device cannot be
210         // specified here
211         // errors might be different as well
212         ZYPP_THROW(MediaException("Error::E_mount_failed"));
213       }
214       DBG << _lastdev << " " << count << endl;
215     }
216
217
218     ///////////////////////////////////////////////////////////////////
219     //
220     //
221     //  METHOD NAME : MediaCD::releaseFrom
222     //  METHOD TYPE : PMError
223     //
224     //  DESCRIPTION : Asserted that media is attached.
225     //
226     void MediaCD::releaseFrom( bool eject )
227     {
228       if (_mounteddevice.empty())               // no device mounted
229       {
230         if (eject)                      // eject wanted -> eject all devices
231         {
232             for (DeviceList::iterator it = _devices.begin()
233                 ; it != _devices.end()
234                 ; ++it )
235             {
236                 openTray( *it );
237             }
238             return;
239         }
240         ZYPP_THROW(MediaNotAttachedException(url()));
241       }
242     
243       Mount mount;
244       mount.umount(attachPoint().asString());
245     
246       // eject device
247       if (eject)
248       {
249         openTray( _mounteddevice );
250       }
251     
252       _mounteddevice.erase();
253     }
254
255     ///////////////////////////////////////////////////////////////////
256     //
257     //
258     //  METHOD NAME : MediaCD::forceEject
259     //  METHOD TYPE : void
260     //
261     // Asserted that media is not attached.
262     //
263     void MediaCD::forceEject()
264     {
265       if ( _mounteddevice.empty() ) {   // no device mounted
266         for ( DeviceList::iterator it = _devices.begin(); it != _devices.end(); ++it ) {
267           if ( openTray( *it ) )
268             break; // on 1st success
269         }
270       }
271     }
272
273     ///////////////////////////////////////////////////////////////////
274     //
275     //  METHOD NAME : MediaCD::getFile
276     //  METHOD TYPE : PMError
277     //
278     //  DESCRIPTION : Asserted that media is attached.
279     //
280     void MediaCD::getFile( const Pathname & filename ) const
281     {
282       MediaHandler::getFile( filename );
283     }
284
285     ///////////////////////////////////////////////////////////////////
286     //
287     //  METHOD NAME : MediaCD::getDir
288     //  METHOD TYPE : PMError
289     //
290     //  DESCRIPTION : Asserted that media is attached.
291     //
292     void MediaCD::getDir( const Pathname & dirname, bool recurse_r ) const
293     {
294       MediaHandler::getDir( dirname, recurse_r );
295     }
296
297     ///////////////////////////////////////////////////////////////////
298     //
299     //
300     //  METHOD NAME : MediaCD::getDirInfo
301     //  METHOD TYPE : PMError
302     //
303     //  DESCRIPTION : Asserted that media is attached and retlist is empty.
304     //
305     void MediaCD::getDirInfo( std::list<std::string> & retlist,
306                               const Pathname & dirname, bool dots ) const
307     {
308       MediaHandler::getDirInfo( retlist, dirname, dots );
309     }
310
311     ///////////////////////////////////////////////////////////////////
312     //
313     //
314     //  METHOD NAME : MediaCD::getDirInfo
315     //  METHOD TYPE : PMError
316     //
317     //  DESCRIPTION : Asserted that media is attached and retlist is empty.
318     //
319     void MediaCD::getDirInfo( filesystem::DirContent & retlist,
320                               const Pathname & dirname, bool dots ) const
321     {
322       MediaHandler::getDirInfo( retlist, dirname, dots );
323     }
324
325   } // namespace media
326 } // namespace zypp