host/AzureRecoveryLib/linux/LinuxUtils.cpp (879 lines of code) (raw):

/* +------------------------------------------------------------------------------------+ Copyright(c) Microsoft Corp. 2015 +------------------------------------------------------------------------------------+ File : LinuxUtils.cpp Description : Linux Utility function History : 29-4-2015 (Venu Sivanadham) - Created +------------------------------------------------------------------------------------+ */ #include "LinuxUtils.h" #include "../common/Trace.h" #include "../common/Process.h" #include "../common/utils.h" #include "../config/RecoveryConfig.h" #include<vector> #include<sstream> #include<fstream> #include <sys/mount.h> #include <boost/assert.hpp> #include <boost/filesystem.hpp> #include <boost/foreach.hpp> #include <boost/algorithm/string.hpp> #include <boost/lexical_cast.hpp> namespace AzureRecovery { /* Method : ExecutePipe Description : Executes the commands with the pipes. Parameters : [out] cmdOut : std out of the pipe commands. Note: The cmdPipe should not contain double-quotes, instead use single-quotes. Return : 0 -> On Success 1 -> On failure */ DWORD ExecutePipe( const std::string& cmdPipe, std::stringstream& cmdOut ) { std::stringstream pipeCmd; pipeCmd << "bash -c \"" << cmdPipe << "\""; return RunCommand( pipeCmd.str(), "", cmdOut ); } /* Method : GetAzureTempPartitionMountPoint Description : Gets the mount-point of resource/temp disk partition. Parameters : None Return : Mount-Point of temp disk partition. */ std::string GetAzureTempPartitionMountPoint() { TRACE_FUNC_BEGIN; std::string azureTmpMountPoint = "/mnt/resource"; //Default value std::ifstream waagentConfIn(LinuxConfileName::waagent); if (waagentConfIn) { std::string confLine; while (std::getline(waagentConfIn, confLine)) { if (boost::starts_with(confLine, "ResourceDisk.MountPoint")) { TRACE_INFO("Found the temp disk configuration: %s\n", confLine.c_str()); std::vector<std::string> tokens; boost::split(tokens, confLine, boost::is_any_of("="), boost::token_compress_on); if (tokens.size() == 2 && !tokens[1].empty()) { azureTmpMountPoint = tokens[1]; } else { TRACE_WARNING("Invalid configuration format :"); } break; } } } else { TRACE_WARNING("could not open the waagent config file: %s\n", LinuxConfileName::waagent); } TRACE_INFO("Azure Temp parition mountpoint: %s\n", azureTmpMountPoint.c_str()); TRACE_FUNC_END; return azureTmpMountPoint; } /* Method : GetMountPointsAndPartitions Description : Gets the mount-points partitions mapping. Mount-Points to LVs will not be listed in the map. Parameters : [out] mountPointToPartition : map of mountPoint to partition name. Return : 0 -> On Success 1 -> On failure */ DWORD GetMountPointsAndPartitions(std::map<std::string, std::string>& mountPointToPartition) { TRACE_FUNC_BEGIN; DWORD dwRet = 0; std::stringstream cmdOut; std::string cmdListMount = "mount -l 2> /dev/null | grep -e \'^/dev/\'"; dwRet = ExecutePipe(cmdListMount, cmdOut); if (0 == dwRet) { std::string line; while (std::getline(cmdOut, line)) { TRACE_INFO("Processing Line: %s\n", line.c_str()); std::vector<std::string> tokens; boost::split(tokens, line, boost::is_space(), boost::token_compress_on); if (tokens.size() > 3) // It should be 6 tokens, but we are intereted in only 1st & 3rd tokens { // Index : 0 1 2 3 4 5 // Value : partition on mount-point type FS-Name (rw/ro) // TRACE_INFO("Mount-Point: %s, Partition: %s\n", tokens[2].c_str(), tokens[0].c_str()); mountPointToPartition.insert(std::make_pair(tokens[2], tokens[0])); } else { TRACE_WARNING("Unexpected mount command line format: %s\n", line.c_str()); } } } TRACE_FUNC_END; return dwRet; } /* Method : GetStandardDeviceOfMountPoint Description : Gets the standard disk device name of a partition which is mounted to mountPoint. If the mountPoint belong to a LV then it won't return the LV name. Parameters : [in] mountPoint : Mount point path. [in, out] mountPointToPartition: map of mount-point to partitions map. Return : Standard disk device name -> On Success Empty string value -> On failure */ std::string GetStandardDeviceOfMountPoint( const std::string& mountPoint, std::map<std::string, std::string>& mountPointToPartition) { TRACE_FUNC_BEGIN; std::string standardDevice; do { if( mountPointToPartition.empty() && GetMountPointsAndPartitions(mountPointToPartition) != 0) { TRACE_ERROR("Could not get the mount-points to partitions mapping.\n"); break; } std::map<std::string, std::string>::iterator iterPartition = mountPointToPartition.find(mountPoint); if( iterPartition == mountPointToPartition.end() ) { TRACE_ERROR("Could not get partition for the mountpoint %s.\n", mountPoint.c_str() ); break; } TRACE_INFO("Found partition [%s] for the mount-point: %s \n", iterPartition->second.c_str(), mountPoint.c_str()); if ( !boost::starts_with(iterPartition->second,"/dev/sd") ) { TRACE_ERROR ("%s is not a standard device partition.\n", iterPartition->second.c_str() ); break; } else { standardDevice = boost::trim_copy_if(iterPartition->second, boost::is_digit()); TRACE_INFO("Device-Name for the mount-point [%s] is: %s\n", mountPoint.c_str(), standardDevice.c_str() ); } } while ( false ); TRACE_FUNC_END; return standardDevice; } /* Method : GetTheDiskDevicesToSkip Description : Gets the list of disk devices to skip from discovery. They are boot disk & temp/resource disks. Parameters : [out] diskToSkip : disks list to skip. Return : 0 -> On Success 1 -> On failure */ DWORD GetTheDiskDevicesToSkip( std::list<std::string>& diskToSkip ) { TRACE_FUNC_BEGIN; DWORD dwRet = 0; do { std::map<std::string, std::string> mountPointToPartition; dwRet = GetMountPointsAndPartitions(mountPointToPartition); if(0 != dwRet) { TRACE_ERROR("Could not get the mount-points\n"); break; } // // Get root disk name // std::string deviceName = GetStandardDeviceOfMountPoint( AzureRecoveryConfig::Instance().GetSysMountPoint(), mountPointToPartition ); if ( deviceName.empty() ) { TRACE_INFO("Could not get the boot disk device name\n"); dwRet = 1; break; } diskToSkip.push_back(deviceName); // // Get the temp disk name // deviceName = GetStandardDeviceOfMountPoint( GetAzureTempPartitionMountPoint() , mountPointToPartition ); if ( deviceName.empty() ) { TRACE_INFO("Could not get the temp disk device name\n"); dwRet = 1; break; } diskToSkip.push_back(deviceName); } while ( false ); return dwRet; } /* Method : GetLunDeviceIdMappingOfDataDisks Description : Gets the Lun -> Device-Name mapping of all data disks on azure vm. Parameters : [out] lun_device : Filled with Lun->Device-name pairs map. Return : 0 -> On Success 1 -> On failure */ DWORD GetLunDeviceIdMappingOfDataDisks( std::map<UINT, std::string>& lun_device) { TRACE_FUNC_BEGIN; DWORD dwRet = 0; do { // // Get boot disk & temp disks of Hydration-VM and add to disksToSkip list. // std::list<std::string> disksToSkip; dwRet = GetTheDiskDevicesToSkip(disksToSkip); if(0 != dwRet) { TRACE_ERROR("Error identifying system disk & temp disk names.\n"); dwRet = 1; break; } int scsi_host = -1; std::stringstream procStdOut; dwRet = RunCommand(CmdLineTools::LSSCSI,"",procStdOut); if(0 != dwRet) { TRACE_ERROR("The command [%s] failed with error code: %d\n", CmdLineTools::LSSCSI, dwRet ); dwRet = 1; break; } std::string stdOutLine; while(std::getline(procStdOut,stdOutLine)) { boost::trim(stdOutLine); if(stdOutLine.empty()) { TRACE("Empty line\n"); // // continue with next line // continue; } std::string device_name, lun, target_number, channel, host; if(0 == ProcessLsScsiCmdLine( stdOutLine, device_name, lun, target_number, channel, host ) ) { try { UINT uLun = boost::lexical_cast<UINT>(lun); UINT uScsi_host = boost::lexical_cast<UINT>(host); // // Skip boot disk & azure-resource(temp) disks // if( std::find(disksToSkip.begin(), disksToSkip.end(), device_name) == disksToSkip.end()) { // 1st time store the data disks scsi host number. if( scsi_host == -1 ) scsi_host = uScsi_host; if ( scsi_host == uScsi_host ) { lun_device[uLun] = device_name; TRACE_INFO(" LUN %d => %s\n", uLun, device_name.c_str() ); } else { // If multiple host numbers appears then fail the operation // as the LUN positions may not be accurate. TRACE_ERROR("Multiple scsi host numbers[%d , %d] detected for data disks", scsi_host, uScsi_host ); dwRet = 1; break; } } else { TRACE_INFO("Skipping the device : %s\n", device_name.c_str()); } } catch( boost::bad_lexical_cast& e) { TRACE_ERROR("bad cast exception for LUN or Scsi-Host : %s , %s. Exception : %s\n", lun.c_str(), host.c_str(), e.what() ); } catch ( ... ) { TRACE_ERROR("Unknown exception while casting LUN or Scsi-Host : %s , %s.\n", lun.c_str(), host.c_str() ); } } // // Ignoring the line processing failures // } } while(false); return dwRet; } /* Method : ProcessLsScsiCmdLine Description : parses the lsscsi tool's stdout line and gets the LUN and device name from it. Parameters : [in] line : A line from lsscsi command output [out] device_name: Filled with name of standard device. [out] lun : Filled with Lun posistion on which the device appearing. [out] target : SCSI Target number of the disk. [out] channel : SCSI channel number of the disk. [out] scsi_host : SCSO host nummber of the disk. Return : 0 -> On Success 1 -> On failure Note: Failure will be returned if the lsscsi cmd output line is related to non disk type device. */ DWORD ProcessLsScsiCmdLine( const std::string & line, std::string & device_name, std::string & lun, std::string & target_number, std::string & channel, std::string & scsi_host) { TRACE_FUNC_BEGIN; DWORD dwRet = 0; do { // [H:C:T:L] Type Vendor Model Rev Device-Name // ================================================================ // [1:0:0:0] cd/dvd Msft Virtual CD/ROM 1.0 /dev/sr0 // [2:0:0:0] disk Msft Virtual Disk 1.0 /dev/sda // [3:0:1:0] disk Msft Virtual Disk 1.0 /dev/sdb // std::vector<std::string> tokens; boost::split(tokens, line, boost::is_space(), boost::token_compress_on); if ( tokens.size() < 6 ) { TRACE_ERROR("Could not get SCSI information. Unexpected line format.\n\t%s\n",line.c_str()); dwRet = 1; break; } // // Fail the openration if scsi line is not related to disk type // if( !boost::iequals(tokens[1],"disk") ) { TRACE_WARNING("Non disk type scsi info have found. Its type is: %s\n",tokens[1].c_str()); dwRet = 1; break; } // // scsi lun information format : [scsi_host:channel:target_number:LUN] // std::string strScsiInfo = tokens[0]; boost::trim_if(strScsiInfo,boost::is_any_of("[]")); std::vector<std::string> scsi_info; boost::split(scsi_info, strScsiInfo, boost::is_any_of(":"), boost::token_compress_on); if(scsi_info.size() != 4) { TRACE_ERROR("Unexpected SCSI info format [%s] from the line %s\n", strScsiInfo.c_str(), line.c_str()); dwRet = 1; break; } scsi_host = scsi_info[0]; channel = scsi_info[1]; target_number = scsi_info[2]; lun = scsi_info[3]; TRACE_INFO("SCSI Info: %s\n\t Host = %s, Channel = %s, Target = %s, Lun = %s\n", strScsiInfo.c_str(), scsi_host.c_str(), channel.c_str(), target_number.c_str(), lun.c_str() ); //Device name is always the last element. device_name = tokens[tokens.size()-1]; if( !boost::starts_with(device_name,"/dev/") ) { TRACE_ERROR("Invalid device name found. Unexpected scsi info line format.\n\t%s\n",line.c_str()); dwRet = 1; break; } TRACE_INFO("Device Name: %s\n", device_name.c_str()); } while(false); TRACE_FUNC_END; return dwRet; } /* Method : ScanDevices Description : Forces the system to rescan the disk devices for partitions / LVs Parameters : [in] srcTgtDeviceMap: Source to target device mapping Return : 0 -> On Success 1 -> On failure */ DWORD ScanDevices(const std::map<std::string, std::string>& srcTgtDeviceMap) { TRACE_FUNC_BEGIN; DWORD dwRet = 0; do { if(srcTgtDeviceMap.empty()) { TRACE_INFO("Device list is empty\n"); break; } // // Rescan the devices before add them to lvm.conf file // TRACE("Rescanning the devices\n"); std::map<std::string, std::string>::const_iterator iterDevice = srcTgtDeviceMap.begin(); for(; iterDevice != srcTgtDeviceMap.end(); iterDevice++) RunCommand(CmdLineTools::RescanDisk, iterDevice->second); // // Run vgscan to discover VGs. // std::stringstream vgScanOut; RunCommand(CmdLineTools::VgScan,"",vgScanOut); TRACE_INFO("\n%s\n", vgScanOut.str().c_str()); // Ignoring return codes, if the required devices are not discovered // by the scan command then failure happens at later stages. } while(false); TRACE_FUNC_END; return dwRet; } /* Method : GetPartitionsOfStandardOrMapperDevice Description : Gets all the partitions on a disk device. Parameters : [in] device_name : Name of the disk device [out] partitions : Filled with list of partitions of a device on success. Return : 0 -> On Success 1 -> On failure */ DWORD GetPartitionsOfStandardOrMapperDevice( const std::string& device_name, std::vector<std::string>& partitions) { TRACE_FUNC_BEGIN; DWORD dwRet = 0; BOOST_ASSERT(!device_name.empty()); do { std::string filterCmd; std::string filter = device_name; bool bMapperDevice = boost::starts_with(device_name, DEV_MAPPER_PREFIX); if (bMapperDevice) { // // mapper device type // boost::erase_head(filter, ACE_OS::strlen(DEV_MAPPER_PREFIX)); filterCmd = "dmsetup ls | grep " + filter + " | awk \'{ print $1 }\'"; } else { // // standard device type // filterCmd = "fdisk -l " + filter + " | grep \'^" + filter + "\' | awk \'{ print $1 }\'"; } std::stringstream cmdOutStream; if (0 != ExecutePipe(filterCmd, cmdOutStream)) { TRACE_INFO("\n%s\n",cmdOutStream.str().c_str()); TRACE_ERROR("Error collecting partitions of the device: %s.\n", device_name.c_str()); dwRet = 1; break; } if (cmdOutStream.rdbuf()->in_avail() == 0) { TRACE_INFO("Choosing the entire disk %s as the partition. \n", filter.c_str()); partitions.push_back(filter); } else { while (!cmdOutStream.eof()) { std::string partition; cmdOutStream >> partition; boost::trim(partition); if (partition.empty()) continue; TRACE_INFO("\nFound parition %s on disk %s\n", partition.c_str(), filter.c_str()); if (bMapperDevice) partition = DEV_MAPPER_PREFIX + partition; partitions.push_back(partition); } } } while(false); TRACE_FUNC_END; return dwRet; } /* Method : GetTargetStandardOrMapperDevicePartition Description : Gets the partition name corresponding to a particular source partition. Parameters : [in] srcPartition : Source partition name (Ex: /dev/sda2 ) [in] srcDisk : Source disk name to which the partition belongs [in] tgtDisk : Target disk name corresponding to srcDisk. [out] tgtPartition : Filled with target partition name corresponding to source srcPartition on success. Return : 0 -> On Success 1 -> On failure */ DWORD GetTargetStandardOrMapperDevicePartition( const std::string& srcPartition, const std::string& srcDisk, const std::string& tgtDisk, std::string& tgtPartition ) { TRACE_FUNC_BEGIN; DWORD dwRet = 0; do { if(srcPartition.empty() || srcDisk.empty() || tgtDisk.empty()) { TRACE_ERROR("Invalid argument.\n Source-Partition: %s\n Source-Disk: %s\n Target-Disk: %s\n", srcPartition.c_str(), srcDisk.c_str(), tgtDisk.c_str() ); dwRet = 1; break; } std::vector<std::string> partitions; dwRet = GetPartitionsOfStandardOrMapperDevice(tgtDisk, partitions); if(0 != dwRet) { TRACE_ERROR("Could not get all the partitions in disk %s\n", tgtDisk.c_str() ); dwRet = 1; break; } if(!boost::starts_with(srcPartition, srcDisk)) { TRACE_ERROR("Disk: %s, Partition: %s are not matching.\n", srcPartition.c_str(), srcDisk.c_str() ); dwRet = 1; break; } std::string partitionNum = boost::erase_head_copy(srcPartition, srcDisk.length()); // // Mapper device partitions may be prefixed with letter 'p' // So trim if there is any such letter. // boost::trim_left_if(partitionNum, boost::is_any_of("p")); if(!partitionNum.empty()) { // CAse when disk is split into partitions. std::vector<std::string>::const_iterator iterTgtPartition = partitions.begin(); for (; iterTgtPartition != partitions.end() ; iterTgtPartition++) { std::string tmpTgtPartition = tgtDisk + partitionNum; std::string altTgtPartition = tgtDisk + "p" + partitionNum; // // TODO: If multipather is not going to install on Hydration VM, // then remove the 'p' altTgtPartition comparison. // if (boost::equal(*iterTgtPartition, tmpTgtPartition) || boost::equal(*iterTgtPartition, altTgtPartition)) { tgtPartition = *iterTgtPartition; break; } } } else { // Case when a partition is laid out on entire disk. tgtPartition = tgtDisk; } if(tgtPartition.empty()) { TRACE_ERROR("Could not found corresponding target partition for the source partition: %s\n", srcPartition.c_str() ); dwRet = 1; } } while(false); TRACE_FUNC_END; return dwRet; } /* Method : ActivateVG Description : Activates the specified VG and rebuilds LVs configuration on the VG Parameters : [in] vgName: Volume group name. Return : On success it returns 0. On failure it returns the returns code from vgchange/vgmknodes commands. */ DWORD ActivateVG(const std::string& vgName) { TRACE_FUNC_BEGIN; DWORD dwRet = 0; BOOST_ASSERT(!vgName.empty()); do { // // Run vgchange to activate the VG // std::stringstream cmdOutStream; dwRet = RunCommand(CmdLineTools::ActivateVG, vgName, cmdOutStream); if(0 != dwRet) { TRACE_INFO("\n%s\n",cmdOutStream.str().c_str()); TRACE_ERROR("Could not activate VG : %s. Command failed with error: %d.\n", CmdLineTools::ActivateVG, dwRet); break; } cmdOutStream.clear(); // // Run vgmknodes to re-build the volume group dritectory & conf files // dwRet = RunCommand(CmdLineTools::VGMkNodes, vgName, cmdOutStream); if(0 != dwRet) { TRACE_INFO("\n%s\n",cmdOutStream.str().c_str()); // vgchange -ay has previously succeeded. Volume group mount should work without vgmknodes. // Trace error and do not fail. TRACE_ERROR("Could not activate VG : %s. Command failed with error: %d.\n", CmdLineTools::VGMkNodes, dwRet); dwRet = 0; } cmdOutStream.clear(); } while(false); TRACE_FUNC_END; return dwRet; } /* Method : DeactivateVG Description : De-Activates the give volume group on the system. Parameters : [in] vgName : Volume group name Return : 0 -> On Success vgchange return code -> On failure */ DWORD DeactivateVG(const std::string& vgName) { TRACE_FUNC_BEGIN; DWORD dwRet = 0; int nMaxRetry = MAX_RETRY_ATTEMPTS; BOOST_ASSERT(!vgName.empty()); do { // // Run vgchange to de-activate the VG // std::stringstream cmdOutStream; dwRet = RunCommand(CmdLineTools::DeActivateVG, vgName, cmdOutStream); if(0 != dwRet) { TRACE_INFO("\n%s\n", cmdOutStream.str().c_str()); TRACE_ERROR("De-activate VG : %s failed with error: %d. Retrying the command ...\n", CmdLineTools::DeActivateVG, dwRet); ACE_OS::sleep(5); } } while(dwRet && --nMaxRetry); TRACE_FUNC_END; return dwRet; } /* Method : MountPartition Description : Mounts the specified partition at a given mount-path. Parameters : [in] partition: Name of the partition/LV [in] mnt_path : Mount path [in] fs_type : File system type Return : 0 -> On Success mount return code -> On failure */ DWORD MountPartition( const std::string& partition, const std::string& mnt_path, const std::string& fs_type, const std::string& other_options) { TRACE_FUNC_BEGIN; DWORD dwRet = 0; BOOST_ASSERT(!partition.empty()); BOOST_ASSERT(!mnt_path.empty()); BOOST_ASSERT(!fs_type.empty()); int nMaxRetry = MAX_RETRY_ATTEMPTS; std::string mntArgs = "-t " + fs_type + " " + partition + " " + mnt_path; if(!other_options.empty()) mntArgs = other_options + " " + mntArgs; do { std::stringstream cmdOutStream; dwRet = RunCommand(CmdLineTools::Mount, mntArgs, cmdOutStream); if(0 != dwRet) { TRACE_INFO("\n%s\n", cmdOutStream.str().c_str()); // Return codes from 'man mount' // ----------------------------- if( 2 == dwRet || // system error (out of memory, cannot fork, no more loop devices) 4 == dwRet || // internal mount bug 16 == dwRet || // problems writing or locking /etc/mtab 32 == dwRet ) // mount failure { TRACE_ERROR("mount error %d for %s. Retrying the mount...\n", dwRet, mnt_path.c_str() ); // // Log dmsg for troubleshooting purpose. // LogDmsg(); // // Retry the operation // ACE_OS::sleep(5); } else { TRACE_ERROR("Could not mount the partitions %s at %s. Mount error %d\n", partition.c_str(), mnt_path.c_str(), dwRet ); } } }while(dwRet && --nMaxRetry); TRACE_FUNC_END; return dwRet; } /* Method : UnMountPartition Description : Un mounts the specified partition/volume Parameters : [in] mntPoint : mount point [in] recursive: recursively unmount if its true. Return : 0 -> On Success umount return code -> On failure */ DWORD UnMountPartition(const std::string& mntPoint, bool recursive, bool lazyUnmount) { TRACE_FUNC_BEGIN; DWORD dwRet = 0; int nMaxRetry = MAX_RETRY_ATTEMPTS; BOOST_ASSERT(!mntPoint.empty()); std::stringstream cmdOpts; if (recursive) { cmdOpts << "-R "; } if (lazyUnmount) { cmdOpts << "-l "; } cmdOpts << mntPoint; do { std::stringstream cmdOutStream; dwRet = RunCommand(CmdLineTools::UMount, cmdOpts.str(), cmdOutStream); if(0 != dwRet) { TRACE_INFO("\n%s\n", cmdOutStream.str().c_str()); TRACE_ERROR("umount error %d for %s. Retrying the umount...\n", dwRet, mntPoint.c_str() ); ACE_OS::sleep(5); } } while(dwRet && --nMaxRetry); TRACE_FUNC_END; return dwRet; } /* Method : FlushBlockDevices Description : Flushes the block device buffers using 'blockdev --flushbufs' command for each device. Parameters : [in] mapSrcTgtDevices: map of source to target device entries. Return : 0 -> On Success 1 -> On failure */ DWORD FlushBlockDevices( const std::map<std::string, std::string>& mapSrcTgtDevices) { TRACE_FUNC_BEGIN; DWORD dwFinalRet = 0; std::map<std::string, std::string>::const_iterator iterDisks = mapSrcTgtDevices.begin(); for(; iterDisks != mapSrcTgtDevices.end(); iterDisks++) { std::stringstream blockDevOut; DWORD dwRet = RunCommand(CmdLineTools::BlockDevFlushBuff, iterDisks->second, blockDevOut); if(dwRet != 0) { TRACE_INFO("\n%s\n", blockDevOut.str().c_str()); TRACE_ERROR("Could not flush the device %s. BlockDev Flush error %d\n", iterDisks->second.c_str(), dwRet ); dwFinalRet = 1; //Failure: One or more devices flush failed. // // Continue flushing other devices // } } TRACE_FUNC_END; return dwFinalRet; } /* Method : FlushBlockDevices Description : Flushes the block device buffers using 'blockdev --flushbufs' command for each device. Parameters : [in] devices: list of devices to flush. Return : 0 -> On Success 1 -> On failure */ DWORD FlushBlockDevices(const std::vector<std::string>& devices) { TRACE_FUNC_BEGIN; DWORD dwFinalRet = 0; BOOST_FOREACH(const std::string& device, devices) { TRACE_INFO("Flushing the device: %s\n", device.c_str()); std::stringstream blockDevOut; DWORD dwRet = RunCommand(CmdLineTools::BlockDevFlushBuff, device, blockDevOut); if (dwRet != 0) { TRACE_INFO("\n%s\n", blockDevOut.str().c_str()); TRACE_ERROR("Could not flush the device %s. BlockDev Flush error %d\n", device.c_str(), dwRet); dwFinalRet = 1; //Failure: One or more devices flush failed. // // Continue flushing other devices // } } TRACE_FUNC_END; return dwFinalRet; } /* Method : UnRegisterLVs Description : Clean-ups the LVs configuration from system so that they will not be visible to OS. Parameters : [in] LVs : set of logical volumes Return : 0 -> On Success 1 -> On failure */ DWORD UnRegisterLVs( const std::set<std::string>& LVs) { TRACE_FUNC_BEGIN; DWORD dwFinalRet = 0; std::set<std::string>::const_iterator iterLV = LVs.begin(); for(; iterLV != LVs.end(); iterLV++) { std::stringstream dmsetupOut; DWORD dwRet = RunCommand(CmdLineTools::DmSetupfRemove, *iterLV, dmsetupOut); if(dwRet != 0) { TRACE_INFO("\n%s\n", dmsetupOut.str().c_str()); TRACE_ERROR("Could not un-register the LV %s. dmsetup remove error %d\n", iterLV->c_str(), dwRet ); dwFinalRet = 1; //Failure: One or more LVs flush failed. // // Continue with other LVs // } } TRACE_FUNC_END; return dwFinalRet; } /* Method : DisableService Description : Disables the service by renaming the service symlink /etc/init.d/<service-name> to /etc/init.d/<service-name>_disabled so that when system starts the actual service won't start. Parameters : [in] root_path : root path [in] service_name : name of the service appearing under /etc/init.d/ Return : 0 -> On Success 1 -> On failure */ DWORD DisableService( const std::string& root_path, const std::string& service_name) { TRACE_FUNC_BEGIN; DWORD dwFinalRet = 0; std::string servicePathInitd = root_path + std::string(boost::ends_with(root_path, "/") ? "" : "/") + "etc/init.d/" + service_name; std::string servicePathSystemd = root_path + std::string(boost::ends_with(root_path, "/") ? "" : "/") + "etc/systemd/system/" + service_name + ".service"; if (!RenameFile(servicePathInitd, servicePathInitd + "_disabled") && !RenameFile(servicePathSystemd, servicePathSystemd + "_disabled")) { TRACE_ERROR("Could not disable service entries %s and %s .\n", servicePathInitd.c_str(), servicePathSystemd.c_str()); dwFinalRet = 1; //Failure } TRACE_FUNC_END; return dwFinalRet; } /* Method : LogDmsg Description : Logs dmesg for any filesystem errors. This is only for debugging purpose. Parameters : Return : */ void LogDmsg() { std::stringstream dmsgOut; ExecutePipe(CmdLineTools::DMesgTail, dmsgOut); TRACE_INFO("Printing dmesg log: \n%s\n", dmsgOut.str().c_str()); } /* Method : GetVgList Description : Gets the list of VGs visible to the system. Parameters : [out] vgs : vector of VG names. Return : 0 -> On Success 1 -> On failure */ DWORD GetVgList(std::vector<std::string> &vgs) { TRACE_FUNC_BEGIN; DWORD dwRet = 0; std::stringstream cmdOut; std::string listVgsArgs = "-o vg_name,vg_attr,lv_count --noheadings --separator ,"; dwRet = RunCommand(CmdLineTools::ListVGs, listVgsArgs, cmdOut); if (0 == dwRet) { std::string line; while (std::getline(cmdOut, line)) { TRACE_INFO("Processing Line: %s\n", line.c_str()); std::vector<std::string> tokens; boost::split(tokens, line, boost::is_any_of(","), boost::token_compress_on); if (tokens.size() == 3) { // Index : 0 1 2 // Value : vg_name,vg_attr,lv_count // // vg_attr,lv_count are for debugging purpose. std::string vg_name = boost::trim_copy(tokens[0]); TRACE_INFO("VG: %s, Attributes: %s, LV Count:%s\n", vg_name.c_str(), tokens[1].c_str(), tokens[2].c_str()); vgs.push_back(vg_name); } else { TRACE_WARNING("Unexpected line format, ignoring it.\n"); } } if (vgs.size() == 0) TRACE_WARNING("No VG detected.\n"); } else { TRACE_WARNING("Error listing the VGs. ErrorCode: %d.\n", dwRet); } TRACE_FUNC_END; return dwRet; } /* Method : ReadFstab Description : Reads the fstab content and prepares the entries. Parameters : [in] fstab_path : complete path of fstab file. [out] fs_entries : list of fstab entries. Return : */ void ReadFstab(const std::string &fstab_path, std::vector<fstab_entry>& fs_entries) { TRACE_FUNC_BEGIN; boost::filesystem::path fstab(fstab_path); std::ifstream inFstab(fstab.string().c_str()); std::string line; while (std::getline(inFstab, line)) { TRACE_INFO("Reading fstab Line: %s.\n", line.c_str()); fstab_entry fe; if (fstab_entry::Fill(fe, line)) { fs_entries.push_back(fe); } } inFstab.close(); TRACE_FUNC_END; } /* Method : GetOSDetailsFromScriptOutput Description : Reads the fstab content and prepares the entries. Parameters : [in] fstab_path : complete path of fstab file. [out] fs_entries : list of fstab entries. Return : */ void GetOSDetailsFromScriptOutput(std::stringstream &out, std::string& os_name, std::string& os_details) { TRACE_FUNC_BEGIN; const char DELEMETER[] = "="; std::string line; while(std::getline(out, line)) { TRACE_INFO("Processing line: %s\n", line.c_str()); boost::trim(line); if (line.empty() || !boost::contains(line, DELEMETER)) continue; std::vector<std::string> tokens; boost::split(tokens, line, boost::is_any_of(DELEMETER)); if (boost::iequals(tokens[0], "os")) { os_name = tokens[1]; TRACE_INFO("Source OS Version: %s\n", os_name.c_str()); } else if (boost::iequals(tokens[0], "release-file")) { std::string release_file = tokens[1]; BOOST_ASSERT(FileExists(release_file)); // Read release file content and append all lines to // os_details seperated by ';'. This data will be used // for telemetry purposes. // TODO: Should we make sure this content is not too long? std::string rel_file_line; std::ifstream relFile(release_file.c_str()); while (std::getline(relFile, rel_file_line)) os_details.append(rel_file_line + ";"); TRACE_INFO("Source OS Details: %s\n", os_details.c_str()); } else { TRACE_WARNING("Unknown entry found on os details script output. Line: %s.\n", line.c_str()); } } TRACE_FUNC_END; } /* Method : GetBtrfsSubVolumeIds Description : Gets the list of subvolume ids of given btrfs volume mountpoint. Parameters : [in] mountpoint : Valid mountpoint where btrfs filesystem is mounted. [out] subvol_ids : list of subvolume ids on success. Return : 0 on success, any other value is a failure. */ DWORD GetBtrfsSubVolumeIds(const std::string& mountpoint, std::vector<std::string>& subvol_ids) { TRACE_FUNC_BEGIN; DWORD dwRet = 0; std::stringstream cmdOut; subvol_ids.clear(); dwRet = RunCommand(CmdLineTools::BtrfsListSubVols, mountpoint, cmdOut); if (0 == dwRet) { std::string line; while (std::getline(cmdOut, line)) { TRACE_INFO("Processing Line: %s\n", line.c_str()); std::vector<std::string> tokens; boost::split(tokens, line, boost::is_space(), boost::token_compress_on); if (tokens.size() > 2) { // 2nd token is the subvol id. // Sample output: // ID 257 gen 88 top level 5 path root // ID 258 gen 88 top level 5 path home // ID 261 gen 64 top level 257 path var/lib/machines std::string subvol_id = tokens[1]; TRACE_INFO("Found subvolume with id: %s\n", subvol_id.c_str()); subvol_ids.push_back(subvol_id); } else { TRACE_WARNING("Unexpected line format, ignoring it.\n"); } } if (subvol_ids.size() == 0) TRACE_WARNING("No subvolume found.\n"); } else { TRACE_WARNING("Error listing subvolumes. ErrorCode: %d.\n", dwRet); } TRACE_FUNC_END; return dwRet; } /* Method : IsSupportedOS Description : Verifies if the src_os_distro is in supported distros list. Parameters : [in] src_os_distro : OS distro short name. Return : true if its supported disto, otherwise false. */ bool IsSupportedOS(const std::string &src_os_distro) { TRACE_FUNC_BEGIN; bool bSupported = false; BOOST_ASSERT(!src_os_distro.empty()); std::vector<std::string> os_distros; boost::split(os_distros, SupportedLinuxDistros, boost::is_any_of(",")); BOOST_FOREACH(const std::string& distro, os_distros) { if (boost::istarts_with(src_os_distro, distro)) { bSupported = true; break; } } TRACE_FUNC_END; return bSupported; } /* Method : GetSourceBlkidDetails Description : Reads the blkid output logged on the source vm root partition and gets the device to uuid mapping. Parameters : [in] blkid_out_file : blkid output file path. [out] dev_uuids : device to uuid mapping. Return : */ void GetSourceBlkidDetails(const std::string& blkid_out_file, std::map<std::string, std::string>& dev_uuids) { TRACE_FUNC_BEGIN; if (!FileExists(blkid_out_file)) { TRACE_INFO("%s file not found.\n", blkid_out_file.c_str()); TRACE_FUNC_END; return; } TRACE_INFO("%s found, parsing its content.\n", blkid_out_file.c_str()); boost::regex blkid_line_exp("^/dev/.*:.*\\sUUID=.*"); std::ifstream blkid_out(blkid_out_file.c_str()); std::string line; while (std::getline(blkid_out, line)) { TRACE_INFO("Processing Line: %s\n", line.c_str()); boost::trim(line); if (!boost::regex_match(line, blkid_line_exp)) { TRACE_INFO("Line is not in expected format, ignoring it.\n"); continue; } std::vector<std::string> tokens; boost::split(tokens, line, boost::is_space(), boost::token_compress_on); if (tokens.size() < 2) { TRACE_INFO("Invalid token count, ignoring it.\n"); continue; } std::string device = tokens[0]; boost::trim_right_if(device, boost::is_any_of(":")); std::string uuid; BOOST_FOREACH(const std::string& token, tokens) if (boost::istarts_with(token, "UUID=")) { uuid = token; break; } boost::ierase_first(uuid, "UUID="); boost::trim_if(uuid, boost::is_any_of("\"")); TRACE_INFO("Device uuid mapping: %s -> %s\n", device.c_str(), uuid.c_str()); if (!uuid.empty()) dev_uuids[device] = uuid; } TRACE_FUNC_END; } } //~ AzureRecovery