libzypp  10.5.0
Mount.cc
Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00013 #include <mntent.h>
00014 
00015 #include <cstdio>
00016 #include <climits>
00017 #include <cerrno>
00018 
00019 #include <iostream>
00020 #include <fstream>
00021 #include <string>
00022 
00023 #include "zypp/base/ExternalDataSource.h"
00024 #include "zypp/base/Logger.h"
00025 #include "zypp/media/Mount.h"
00026 #include "zypp/media/MediaException.h"
00027 
00028 #include "zypp/PathInfo.h"
00029 
00030 #ifndef N_
00031 #define N_(STR) STR
00032 #endif
00033 
00034 using namespace std;
00035 
00036 namespace zypp {
00037   namespace media {
00038 
00039     std::ostream & operator<<( std::ostream & str, const MountEntry & obj )
00040     {
00041       str << obj.src << " on " << obj.dir << " type " << obj.type;
00042       if ( ! obj.opts.empty() )
00043         str << " (" << obj.opts << ")";
00044       return str;
00045     }
00046 
00047 
00048 Mount::Mount()
00049 {
00050     process = 0;
00051     exit_code = -1;
00052 }
00053 
00054 Mount::~Mount()
00055 {
00056    MIL <<  "~Mount()" << endl;
00057 
00058    if ( process )
00059       delete process;
00060 
00061    process = NULL;
00062 
00063    MIL << "~Mount() end" << endl;
00064 }
00065 
00066 void Mount::mount( const std::string & source,
00067                    const std::string & target,
00068                    const std::string & filesystem,
00069                    const std::string & options,
00070                    const Environment & environment )
00071 {
00072     const char *const argv[] = {
00073         "/bin/mount",
00074         "-t", filesystem.c_str(),
00075         "-o", options.c_str(),
00076         source.c_str(),
00077         target.c_str(),
00078         NULL
00079      };
00080 
00081     std::string err;
00082 
00083     this->run(argv, environment, ExternalProgram::Stderr_To_Stdout);
00084 
00085     if ( process == NULL )
00086     {
00087       ZYPP_THROW(MediaMountException("Mounting media failed", source, target));
00088     }
00089 
00090     string value;
00091     string output = process->receiveLine();
00092 
00093     // parse error messages
00094     while ( output.length() > 0)
00095     {
00096         string::size_type       ret;
00097 
00098         // extract \n
00099         ret = output.find_first_of ( "\n" );
00100         if ( ret != string::npos )
00101         {
00102             value.assign ( output, 0, ret );
00103         }
00104         else
00105         {
00106             value = output;
00107         }
00108 
00109         DBG << "stdout: " << value << endl;
00110 
00111         if  ( value.find ( "is already mounted on" ) != string::npos )
00112         {
00113             err = "Media already mounted";
00114         }
00115         else if  ( value.find ( "ermission denied" ) != string::npos )
00116         {
00117             err = "Permission denied";
00118         }
00119         else if  ( value.find ( "wrong fs type" ) != string::npos )
00120         {
00121             err = "Invalid filesystem on media";
00122         }
00123         else if  ( value.find ( "No medium found" ) != string::npos )
00124         {
00125             err = "No medium found";
00126         }
00127         else if  ( value.find ( "Not a directory" ) != string::npos )
00128         {
00129             if( filesystem == "nfs" || filesystem == "nfs4" )
00130             {
00131                 err = "Nfs path is not a directory";
00132             }
00133             else
00134             {
00135                err = "Unable to find directory on the media";
00136             }
00137         }
00138 
00139         output = process->receiveLine();
00140     }
00141 
00142     int status = Status();
00143 
00144     if ( status == 0 )
00145     {
00146         // return codes overwites parsed error message
00147         err = "";
00148     }
00149     else if ( status != 0 && err == "" )
00150     {
00151         err = "Mounting media failed";
00152     }
00153 
00154     if ( err != "" ) {
00155       WAR << "mount " << source << " " << target << ": " << err << endl;
00156       ZYPP_THROW(MediaMountException(err, source, target, value));
00157     } else {
00158       MIL << "mounted " << source << " " << target << endl;
00159     }
00160 }
00161 
00162 void Mount::umount( const std::string & path )
00163 {
00164     const char *const argv[] = {
00165         "/bin/umount",
00166         path.c_str(),
00167         NULL
00168      };
00169 
00170     std::string err;
00171 
00172     this->run(argv, ExternalProgram::Stderr_To_Stdout);
00173 
00174     if ( process == NULL )
00175     {
00176         ZYPP_THROW(MediaUnmountException("E_mount_failed", path));
00177     }
00178 
00179     string value;
00180     string output = process->receiveLine();
00181 
00182     // parse error messages
00183     while ( output.length() > 0)
00184     {
00185         string::size_type       ret;
00186 
00187         // extract \n
00188         ret = output.find_first_of ( "\n" );
00189         if ( ret != string::npos )
00190         {
00191             value.assign ( output, 0, ret );
00192         }
00193         else
00194         {
00195             value = output;
00196         }
00197 
00198         DBG << "stdout: " << value << endl;
00199 
00200         // if  ( value.find ( "not mounted" ) != string::npos )
00201         // {
00202         //    err = Error::E_already_mounted;
00203         // }
00204 
00205         if  ( value.find ( "device is busy" ) != string::npos )
00206         {
00207             err = "Device is busy";
00208         }
00209 
00210         output = process->receiveLine();
00211     }
00212 
00213     int status = Status();
00214 
00215     if ( status == 0 )
00216     {
00217         // return codes overwites parsed error message
00218         err = "";
00219     }
00220     else if ( status != 0 && err == "" )
00221     {
00222         err = "Unmounting media failed";
00223     }
00224 
00225     if ( err != "") {
00226       WAR << "umount " << path << ": " << err << endl;
00227       ZYPP_THROW(MediaUnmountException(err, path));
00228     } else {
00229       MIL << "unmounted " << path << endl;
00230     }
00231 }
00232 
00233 void Mount::run( const char *const *argv, const Environment& environment,
00234                  ExternalProgram::Stderr_Disposition disp )
00235 {
00236   exit_code = -1;
00237 
00238   if ( process != NULL )
00239   {
00240      delete process;
00241      process = NULL;
00242   }
00243   // Launch the program
00244 
00245   process = new ExternalProgram(argv, environment, disp, false, -1, true);
00246 }
00247 
00248 /*--------------------------------------------------------------*/
00249 /* Return the exit status of the Mount process, closing the     */
00250 /* connection if not already done                               */
00251 /*--------------------------------------------------------------*/
00252 int Mount::Status()
00253 {
00254    if ( process == NULL )
00255       return -1;
00256 
00257    exit_code = process->close();
00258    process->kill();
00259    delete process;
00260    process = 0;
00261 
00262    DBG << "exit code: " << exit_code << endl;
00263 
00264    return exit_code;
00265 }
00266 
00267 /* Forcably kill the process */
00268 void Mount::Kill()
00269 {
00270   if (process) process->kill();
00271 }
00272 
00273 // STATIC
00274 MountEntries
00275 Mount::getEntries(const std::string &mtab)
00276 {
00277   MountEntries             entries;
00278   std::vector<std::string> mtabs;
00279   bool                     verbose = false;
00280 
00281   if( mtab.empty())
00282   {
00283     mtabs.push_back("/proc/mounts");
00284     // Also read /etc/mtab if it is a file (on newer sytems
00285     // mtab is a symlink to /proc/mounts).
00286     // Reason for this is the different representation of
00287     // mounted loop devices:
00288     //   /etc/mtab:    /tmp/SLES-11-SP2-MINI-ISO-x86_64-Beta2-DVD.iso on /mnt type iso9660 (ro,loop=/dev/loop0)
00289     //   /proc/mounts: /dev/loop0 /mnt iso9660 ro,relatime 0 0
00290     if ( PathInfo( "/etc/mtab", PathInfo::LSTAT ).isFile() )
00291       mtabs.push_back("/etc/mtab");
00292   }
00293   else
00294   {
00295     mtabs.push_back(mtab);
00296   }
00297 
00298   std::vector<std::string>::const_iterator t;
00299   for( t=mtabs.begin(); t != mtabs.end(); ++t)
00300   {
00301     if( verbose)
00302     {
00303       DBG << "Reading mount table from '" << *t << "'" << std::endl;
00304     }
00305     FILE *fp = setmntent(t->c_str(), "re");
00306     if( fp)
00307     {
00308       char          buf[PATH_MAX * 4];
00309       struct mntent ent;
00310 
00311       memset(buf,  0, sizeof(buf));
00312       memset(&ent, 0, sizeof(ent));
00313 
00314       while( getmntent_r(fp, &ent, buf, sizeof(buf)) != NULL)
00315       {
00316         if( ent.mnt_fsname && *ent.mnt_fsname &&
00317             ent.mnt_dir    && *ent.mnt_dir    &&
00318             ent.mnt_type   && *ent.mnt_type   &&
00319             ent.mnt_opts   && *ent.mnt_opts)
00320         {
00321           MountEntry entry(
00322             ent.mnt_fsname, ent.mnt_dir,
00323             ent.mnt_type,   ent.mnt_opts,
00324             ent.mnt_freq,   ent.mnt_passno
00325           );
00326 
00327           // Attempt quick fix for bnc#710269:
00328           // MountEntry is "//dist/install/ on /var/adm/mount/AP_0x00000001 type cifs (ro,relatime,unc=\dist\install,username=,domain=suse.de"
00329           // but looking for "Looking for media(cifs<//dist/install>)attached(*/var/adm/mount/AP_0x00000001)"
00330           // Kick the trailing '/' in "//dist/install/"
00331           // TODO: Check and fix comparison in MediaHandler::checkAttached instead.
00332           if ( entry.src.size() > 1     // not for "/"
00333                && entry.src[entry.src.size()-1] == '/' )
00334           {
00335             entry.src.erase( entry.src.size()-1 );
00336           }
00337           entries.push_back(entry);
00338 
00339           memset(buf,  0, sizeof(buf));
00340           memset(&ent, 0, sizeof(ent));
00341         }
00342       }
00343       endmntent(fp);
00344 
00345       if( entries.empty())
00346       {
00347         WAR << "Unable to read any entry from the mount table '" << *t << "'"
00348             << std::endl;
00349       }
00350       else
00351       {
00352         // OK, have a non-empty mount table.
00353         t = mtabs.end();
00354         break;
00355       }
00356     }
00357     else
00358     {
00359       int err = errno;
00360       verbose = true;
00361       WAR << "Failed to read the mount table '" << *t << "': "
00362           << ::strerror(err)
00363           << std::endl;
00364       errno = err;
00365     }
00366   }
00367   return entries;
00368 }
00369 
00370   } // namespace media
00371 } // namespace zypp