Beispiel #1
0
    /**
       Matches the given major/minor device ID with the text read from the
       first line of the file at the given path.

       The files contents are read.  The file must have at least one line of
       text, or it is considered an exception (probably the caller sent a
       bad path in).  The first line shoud be ASCII text (a proper subset
       of UTF-8) in the form "<major> ':' <minor>".  All other text is
       ignored, but a 'normal' Sysfs dev file should contain only that text,
       so any additional text will generate a warning.

       /param[in] path   the path to a file that uses the Sysfs 'dev' file
                         format.

       /param[in] major  the expected major device ID value.

     /param[in] minor  the expected minor device ID value.

       \return This method will return true if the first line of the file at
               the given path contains "<major> ':' <minor>" and these values
               match the values passed in; false otherwise.

       \throws SCXFileSystemException  if file cannot be read or it has no
               lines of text.
     */
    bool SCXLVMUtils::MatchIdInFile(const SCXCoreLib::SCXFilePath & path, unsigned int major, unsigned int minor)
    {
        bool result = false;

        SCXCoreLib::SCXLogHandle log = SCXCoreLib::SCXLogHandleFactory::GetLogHandle(L"scx.core.common.pal.system.disk.scxlvmutils");
        std::wstringstream       out;

        // Read the file, there should be at least one line, and the first
        // line should begin with ASCII (UTF-8) text in the form <major> ':' <minor>.
        std::vector< std::wstring > lines;

        try
        {
            SCXCoreLib::SCXStream::NLFs nlfs;

            m_extDepends->ReadAllLinesAsUTF8(path, lines, nlfs);
        }
        catch (SCXCoreLib::SCXException & e)
        {
            out.str(L"");

            out << L"An exception occurred while reading the file " << path.Get() << L": " << e.What();
            SCX_LOG(log, m_errorSuppressor.GetSeverity(out.str()), out.str());

            throw;
        }

        // There should only be exactly one line containting <major> ':' <minor>
        if (lines.size() != 1)
        {
            out.str(L"");

            if (lines.size() == 0)
            {
                out << L"The file " << path.Get() << L" is empty";
                SCX_LOG(log, m_errorSuppressor.GetSeverity(out.str()), out.str());

                throw SCXBadLVMDeviceException(path, out.str(), SCXSRCLOCATION);
            }

            // This is unexpected, but it can be ignored for now.
            out << L"After reading " << path.Get() << L", expected 1 line, but found " << lines.size();
            SCX_LOG(log, m_warningSuppressor.GetSeverity(out.str()), out.str());
        }

        std::wstringstream firstLine(lines[0]);
        int                majorFromFile;
        int                minorFromFile;
        wchar_t          colon;

        firstLine >> majorFromFile >> colon >> minorFromFile;

        if ((static_cast< unsigned int >(majorFromFile) == major) &&
            (static_cast< unsigned int >(minorFromFile) == minor))
        {
            result = true;
        }

        return result;
    }
    /**
       Discover physical disks.

       Logical disks are identified by the /etc/mnttab file (by design). Physical
       disks discovered will be those "hosting" the logical disks found. If ever
       seen in that file, the disk will be discovered. If the disk is removed it
       will be marked as offline.

       How to identify physical disks from logical disks:

       Linux --

       Logical disks are named things like /dev/hda0, /dev/hda1 and so on.
       The numeric value in the end of the name is the partition/logical ID
       of the physical disk. In the example above both logical disks are on
       physical disk /dev/hda.

       For LVM partitions on Linux have two entries for the same device.  An LVM
       device entry is stored in the /dev/mapper directory with a name in the form
       <logical-volume-group>-<logical-volume>.  There is also a device mapper (dm)
       device entry stored in the /dev directory in the form dm-<id>.  This is a
       1-to-1 relationship and the <id> is equal to the device minor ID that both
       device entries have in common.  Discovery of the physical device(s) that
       contain the LVM partition is done by mapping the LVM device to the dm device,
       then looking at the dm devices slave entries in Sysfs, and then finally
       performing the same conversion from a logical Linux partition name to a
       physical drive name that is done for all other partitions.

       Solaris --

       Logical disks are named things like /dev/dsk/c1t0d0s0, /dev/dsk/c1t0d0s1
       and so on. The last letter/numeric pair is the partition/logical ID
       of the physical disk. In the example above both logical disks are on
       physical disk /dev/dsk/c1t0d0.

       HPUX --

       Logical disks are logical volumes with names like /dev/vg00/lvol3.
       /dev/vg00 in the example name is the volume group name. Using the /etc/lvmtab
       file the volume group can be translated to a partition named /dev/disk/disk3_p2
       (or /dev/dsk/c2t0d0s2 using a naming standard deprecated as of HPUX 11.3). The
       old naming standard works like the one for solaris while the new one identifies
       the physical disk as /dev/disk/disk3 in the example above.

       AIX --

       TODO: Document how disks are enumerated on AIX.
    */
    void StatisticalPhysicalDiskEnumeration::FindPhysicalDisks()
    {
        for (EntityIterator iter=Begin(); iter!=End(); iter++)
        {
            SCXCoreLib::SCXHandle<StatisticalPhysicalDiskInstance> disk = *iter;
            disk->m_online = false;
        }

        m_deps->RefreshMNTTab();
        for (std::vector<MntTabEntry>::const_iterator it = m_deps->GetMNTTab().begin();
             it != m_deps->GetMNTTab().end(); it++)
        {
            if ( ! m_deps->FileSystemIgnored(it->fileSystem) &&
                 ! m_deps->DeviceIgnored(it->device) &&
                 m_deps->LinkToPhysicalExists(it->fileSystem, it->device, it->mountPoint) )
            {
                std::map<std::wstring, std::wstring> devices = m_deps->GetPhysicalDevices(it->device);
                if (devices.size() == 0)
                {
                    static SCXCoreLib::LogSuppressor suppressor(SCXCoreLib::eError, SCXCoreLib::eTrace);
                    std::wstringstream               out;

                    out << L"Unable to locate physical devices for: " << it->device;
                    SCX_LOG(m_log, suppressor.GetSeverity(out.str()), out.str());
                    continue;
                }
                for (std::map<std::wstring, std::wstring>::const_iterator dev_it = devices.begin();
                     dev_it != devices.end(); dev_it++)
                {
                    SCXCoreLib::SCXHandle<StatisticalPhysicalDiskInstance> disk = AddDiskInstance(dev_it->first, dev_it->second);
#if defined(hpux)
                    if (0 != disk)
                    {
                        if (m_pathToRdev.end() == m_pathToRdev.find(disk->m_device))
                        {
                            SCXCoreLib::SCXFilePath fp(disk->m_device);
                            fp.SetFilename(L"");
                            UpdatePathToRdev(fp.Get());
                        }
                        SCXASSERT(m_pathToRdev.end() != m_pathToRdev.find(disk->m_device));

                        scxlong diskInfoIndex = disk->FindDiskInfoByID(m_pathToRdev.find(disk->m_device)->second);
                        m_deps->AddDeviceInstance(disk->m_device, L"", diskInfoIndex, m_pathToRdev.find(disk->m_device)->second);
                    }
#endif
                }
            }
        }

#if defined(sun)
        this->UpdateSolarisHelper();
#endif

    }
Beispiel #3
0
    /**
       Uses stat to get the major/minor device ID for the given path.

       /param[in]     path   the path to a device to stat.

       /param[in/out] major  the value from stat will be compared with this
                             input value, and then this is set to the stat
                             value.

       /param[in/out] minor  the value from stat will be compared with this
                             input value, and then this is set to the stat
                             value.

       \return This method will return true if the input major/minor match
               the values read.

       \throws SCXFileSystemException  if file cannot be stat'ed.
     */
    bool SCXLVMUtils::StatPathId(const std::wstring & path, unsigned int & major, unsigned int & minor)
    {
        bool result = false;

        SCXCoreLib::SCXLogHandle log = SCXCoreLib::SCXLogHandleFactory::GetLogHandle(L"scx.core.common.pal.system.disk.scxlvmutils");
        std::wstringstream       out;

        SCXCoreLib::SCXFileSystem::SCXStatStruct stat;
        memset(&stat, 0, sizeof(stat));

        try
        {
            m_extDepends->Stat(path, &stat);
        }
        catch (SCXCoreLib::SCXException & e)
        {
            out.str(L"");

            out << L"An exception occurred while getting the file status for " << path << L": " << e.What();
            SCX_LOG(log, m_errorSuppressor.GetSeverity(out.str()), out.str());

            throw;
        }

        unsigned int majorFromStat = major(stat.st_rdev);
        unsigned int minorFromStat = minor(stat.st_rdev);

        if ((majorFromStat == major) && (minorFromStat == minor))
        {
            result = true;
        }

        // return the values read from stat
        major = majorFromStat;
        minor = minorFromStat;

        return result;
    }
    /**
       Update the enumeration.

       \param updateInstances If true (default) all instances will be updated.
                              Otherwise only the content of the enumeration will be updated.

    */
    void StaticPhysicalDiskEnumeration::Update(bool updateInstances/*=true*/)
    {
        for (EntityIterator iter=Begin(); iter!=End(); iter++)
        {
            SCXCoreLib::SCXHandle<StaticPhysicalDiskInstance> disk = *iter;
            disk->m_online = false;
        }

#if defined(linux)
        // First detect optical devices so later we can just skip the mount points which we determined are optical disks.
        // Various projects may or may not require optical drive detection. We check if iso9660 file system is to be
        // ignored to determine if optical drives should be detected.
        std::vector<std::wstring> drives;
        if ( ! m_deps->FileSystemIgnored(L"iso9660") )
        {
            // Get CD-ROM and DVD drives directly from the kernel interface in /proc.
            SCXCoreLib::SCXHandle<std::wistream> cdStrm = m_deps->GetWIStream("/proc/sys/dev/cdrom/info");
            std::vector<std::wstring> cdStrmLines;
            SCXCoreLib::SCXStream::NLFs nlfs;
            // /proc/sys/dev/cdrom/info format:
            // CD-ROM information, Id: cdrom.c 3.20 2003/12/17
            //
            // drive name:             sr0      hdc
            // drive speed:            0        0
            // drive # of slots:       1        1
            // ...
            SCXCoreLib::SCXStream::ReadAllLines(*cdStrm, cdStrmLines, nlfs);
            std::wstring lineID(L"drive name:");
            size_t i;
            for (i = 0; i < cdStrmLines.size(); i++)
            {
                if (cdStrmLines[i].substr(0, lineID.size()) == lineID)
                {
                    std::wstring drivesLine = cdStrmLines[i].substr(lineID.size(), std::wstring::npos);
                    SCXCoreLib::StrTokenize(drivesLine, drives, L" \t");
                    size_t d;
                    for (d = 0; d < drives.size(); d++)
                    {
                        AddDiskInstance(L"/dev/" + drives[d], L"/dev/" + drives[d], true);
                    }                
                    break;
                }
            }
        }
#endif

        m_deps->RefreshMNTTab();
        for (std::vector<MntTabEntry>::const_iterator it = m_deps->GetMNTTab().begin(); 
             it != m_deps->GetMNTTab().end(); it++)
        {
#if defined(linux)
            // It this is an optical device just skip it. We already processed it.
            size_t d;
            bool found = false;
            for (d = 0; d < drives.size(); d++)
            {
                if ((L"/dev/" + drives[d]) == it->device)
                {
                    found = true;
                    break;
                }
            }
            if (found)
            {
                continue;
            }
#endif
            if ( ! m_deps->FileSystemIgnored(it->fileSystem) &&
                 ! m_deps->DeviceIgnored(it->device) &&
                 m_deps->LinkToPhysicalExists(it->fileSystem, it->device, it->mountPoint) )
            {
                std::map<std::wstring, std::wstring> devices = m_deps->GetPhysicalDevices(it->device);
                if (devices.size() == 0)
                {
                    static SCXCoreLib::LogSuppressor suppressor(SCXCoreLib::eError, SCXCoreLib::eTrace);
                    std::wstringstream                  out;

                    out << L"Unable to locate physical devices for: " << it->device;
                    SCX_LOG(m_log, suppressor.GetSeverity(out.str()), out.str());
                    continue;
                }
                for (std::map<std::wstring, std::wstring>::const_iterator dev_it = devices.begin();
                     dev_it != devices.end(); dev_it++)
                {
                    SCXCoreLib::SCXHandle<StaticPhysicalDiskInstance> disk = AddDiskInstance(dev_it->first, dev_it->second);
                }
            }
        }
#if defined(sun)
        this->UpdateSolarisHelper();
#endif

        if (updateInstances)
        {
            UpdateInstances();
        }
    }
    /**
       Discover logical disks.
    
       Logical disks are identified by the /etc/mnttab file (by design). If ever
       seen in that file, the disk will be discovered. If the disk is removed it 
       will be marked as offline.
    
    */
    void StatisticalLogicalDiskEnumeration::FindLogicalDisks()
    {
        for (EntityIterator iter=Begin(); iter!=End(); iter++)
        {
            SCXCoreLib::SCXHandle<StatisticalLogicalDiskInstance> disk = *iter;
            disk->m_online = false;
        }

        m_deps->RefreshMNTTab();
        for (std::vector<MntTabEntry>::const_iterator it = m_deps->GetMNTTab().begin(); 
             it != m_deps->GetMNTTab().end(); it++)
        {
            if ( ! m_deps->FileSystemIgnored(it->fileSystem) && ! m_deps->DeviceIgnored(it->device))
            {
                SCXCoreLib::SCXHandle<StatisticalLogicalDiskInstance> disk = FindDiskByDevice(it->device);
                if (0 == disk)
                {
                    disk = new StatisticalLogicalDiskInstance(m_deps);
                    disk->m_device = it->device;
                    disk->m_mountPoint = it->mountPoint;
                    disk->m_fsType = it->fileSystem;
                    disk->SetId(disk->m_mountPoint);

#if defined(linux)
                    static SCXLVMUtils lvmUtils;

                    if (lvmUtils.IsDMDevice(it->device))
                    {
                    try
                    {
                            // Try to convert the potential LVM device path into its matching
                            // device mapper (dm) device path.
                            std::wstring dmDevice = lvmUtils.GetDMDevice(it->device);

                            SCXASSERT(!dmDevice.empty());
                            disk->m_samplerDevices.push_back(dmDevice);
                    }
                        catch (SCXCoreLib::SCXException& e)
                    {
                            static SCXCoreLib::LogSuppressor suppressor(SCXCoreLib::eWarning, SCXCoreLib::eTrace);
                            std::wstringstream               out;

                            out << L"An exception occurred resolving the dm device that represents the LVM partition " << it->device
                                << L" : " << e.What();
                            SCX_LOG(m_log, suppressor.GetSeverity(out.str()), out.str());
                        }
                    }
                    // no else required; device was not an LVM device
#endif

                    AddInstance(disk);

#if defined(hpux)
                    if (m_pathToRdev.end() == m_pathToRdev.find(disk->m_device))
                    {
                        SCXCoreLib::SCXFilePath fp(disk->m_device);
                        fp.SetFilename(L"");
                        UpdatePathToRdev(fp.Get());
                    }
                    SCXASSERT(m_pathToRdev.end() != m_pathToRdev.find(disk->m_device));

                    m_deps->AddDeviceInstance(disk->m_device, L"", disk->FindLVInfoByID(m_pathToRdev.find(disk->m_device)->second), m_pathToRdev.find(disk->m_device)->second);
#endif
                }
                disk->m_online = true;
            }
        }
    }
Beispiel #6
0
    /**
       Get the devicemapper (dm) device that contains the given LVM device.

       \param[in] lvmDevice the path to an LVM partition.

       \return This method will return the path to the containing dm device if
               \c lvmDevice is an actual LVM device; otherwise it will return
               an empty string.

       \throws This method will re-throw any of several exceptions that can
               occur while trying to access the devices on the file system.
               Any of these exceptions indicate that the path in lvmDevice
               is not valid for *any* device.  If the path is just not an
               LVM/dm device path, no exception is thrown; the method simply
               returns an empty string.

       note The dm device and the LVM device are just two different file paths
             for the same underlying device.  If the right map is found, both
             paths will `stat' the same.
     */
    std::wstring SCXLVMUtils::GetDMDevice(const std::wstring & lvmDevice)
    {
        std::wstring result;

        SCXCoreLib::SCXLogHandle log = SCXCoreLib::SCXLogHandleFactory::GetLogHandle(L"scx.core.common.pal.system.disk.scxlvmutils");
        std::wstringstream       out;

        SCX_LOGHYSTERICAL(log, SCXCoreLib::StrAppend(L"Looking for LVM device: ", lvmDevice));

        // WI 891635: Debian 8 DiskDrive reports weird file system
        //
        // Turns out that Debian 8 LVM is set up slightly differently, such that
        // the raw DM device (i.e. /dev/dm-0) is mounted directly. Just return
        // the device if that's what we got.
        if (SCXCoreLib::StrIsPrefix(lvmDevice, L"/dev/dm-"))
        {
            SCX_LOGHYSTERICAL(log, SCXCoreLib::StrAppend(SCXCoreLib::StrAppend(L"Device ", lvmDevice), L" is already DM device, returning it"));
            return lvmDevice;
        }

        // All LVM devices are in the /dev/mapper directory.
        if (IsDMDevice(lvmDevice))
        {
            SCX_LOGHYSTERICAL(log, SCXCoreLib::StrAppend(SCXCoreLib::StrAppend(L"Device: ", lvmDevice), L" IsDMDevice ..."));

            // Stat the LVM device.  Its minor ID number will indicate which
            // dm device it maps to.
            unsigned int major = 0;
            unsigned int minor = 0;

            // Any exceptions here are unexpected, so just let them move up-stack
            StatPathId(lvmDevice, major, minor);

            // On some systems, the device /dev/dm-<minor>, where <minor> is
            // minor device ID from the lvmDevice stat can be used as a quick
            // reference to the dm device name.
            std::wstringstream dmDevice;
            unsigned int       dmMajor = major;
            unsigned int       dmMinor = minor;
            bool               isMatch = false;

            dmDevice << L"/dev/dm-" << minor;

            try
            {
                isMatch = StatPathId(dmDevice.str(), dmMajor, dmMinor);
                SCX_LOGHYSTERICAL(log, SCXCoreLib::StrAppend(
                                           SCXCoreLib::StrAppend(
                                               SCXCoreLib::StrAppend(L"  Stat of ", dmDevice.str()),
                                               L" succeeded, isMatch: "),
                                           SCXCoreLib::StrFrom(isMatch)));
            }
            catch (SCXCoreLib::SCXFilePathNotFoundException & e)
            {
                // Unfortunately some systems don't have this handy way of
                // doing things, but there is a more convoluted way of
                // verifying that dm-<minor> is a valid dm device.
                out.str(L"");

                out << L"The device \"" << dmDevice.str() << L"\" does not exist, attempting secondary confirmation strategy";
                SCX_LOG(log, m_infoSuppressor.GetSeverity(out.str()), out.str());

                // Failed to find the device /dev/dm-<minor>.  If the file
                // /sys/block/dm-<minor>/dev contains the same major/minor
                // versions then return "dm-<minor>"
                //
                // Note: This won't be an absolute path to a real device.  It
                //       will be a string that can be found in /proc/diskstats,
                //       so it should get stats, but there may be unforseen
                //       consequences.
                dmDevice.str(L"");
                dmDevice << L"dm-" << minor;

                std::wstring dmDeviceName = dmDevice.str();

                dmDevice.str(L"");
                dmDevice << L"/sys/block/" << dmDeviceName << L"/dev";

                // Any exceptions can just move up-stack
                if (MatchIdInFile(dmDevice.str(), major, minor))
                {
                    // Again, in this case, just the name of the dm device is
                    // returned; not a valid device path.
                    return dmDeviceName;
                }
                else
                {
                    out.str(L"");

                    out << L"The device \"" << lvmDevice << L"\" does not map to \"" << dmDeviceName << L"\"";
                    SCX_LOG(log, m_errorSuppressor.GetSeverity(out.str()), out.str());

                    throw SCXBadLVMDeviceException(lvmDevice, out.str(), SCXSRCLOCATION);
                }
            }
            // let all other exceptions move up-stack

            if (!isMatch)
            {
                // /dev/dm-<minor>, but with non-matching device IDs
                out.str(L"");

                out << L"The the LVM device \"" << lvmDevice << L"\" and the dm device \"" << dmDevice.str() << L"\" do not have matching device IDs";
                SCX_LOG(log, m_errorSuppressor.GetSeverity(out.str()), out.str());

                throw SCXBadLVMDeviceException(lvmDevice, out.str(), SCXSRCLOCATION);
            }

            // done, found best match 100% confidence

            SCX_LOGHYSTERICAL(log, SCXCoreLib::StrAppend(L"Returning Device: ", dmDevice.str()));
            return dmDevice.str();
        }
        else
        {
            out.str(L"");

            out << L"The device \"" << lvmDevice << L"\" is not in the path \"/dev/mapper\"";
            SCX_LOGHYSTERICAL(log, out.str());
        }

        SCX_LOGHYSTERICAL(log, SCXCoreLib::StrAppend(L"Returning result: ", result));
        return result;
    }
Beispiel #7
0
    /**
       Get the slave devices that contain the given devicemapper (dm) device.

       \param[in] dmDevice the path to a dm device.

       \return This method will return a vector of paths to the devices that
               contain the given dm device.  If an error occurs the vecore is
               empty.

       \throws SCXFileSystemException if the slaves associated with this
               device cannot be located in /dev because of a failure in the
               assumptions about the content of /c dmDevice/slaves.

       \throws This method will also re-throw any of several exceptions that
               can occur while trying to access the devices on the file system.
               Any of these exceptions indicate that the LVM LVM/dm device's
               physical volumes cannot be resolved.
     */
    std::vector< std::wstring > SCXLVMUtils::GetDMSlaves(const std::wstring& dmDevice)
    {
        std::vector< std::wstring > result;

        SCXCoreLib::SCXLogHandle log = SCXCoreLib::SCXLogHandleFactory::GetLogHandle(L"scx.core.common.pal.system.disk.scxlvmutils");
        std::wstringstream       out;

        // First look in Sysfs to find out what partitions the dm device
        // maps onto.  This is done by listing the slaves entries for the
        // given dm device.
        //
        // Note: The entries in slaves are links and it is the link name
        //       that is important for this, *not* the resolved link name.
        std::wstringstream sysfsPath;

        sysfsPath << L"/sys/block/" << ((SCXCoreLib::SCXFilePath) dmDevice).GetFilename() << L"/slaves/";

        std::vector< SCXCoreLib::SCXFilePath > slaves;
        try
        {
            slaves = m_extDepends->GetFileSystemEntries(sysfsPath.str());
        }
        catch (SCXCoreLib::SCXException & e)
        {
#if defined(linux) &&                                          \
           ( ( defined(PF_DISTRO_SUSE)   && (PF_MAJOR<=9) ) || \
             ( defined(PF_DISTRO_REDHAT) && (PF_MAJOR<=4) ) )
            // LVM support on RHEL4 and SLES9 is limited by things like the
            // distribution update, what packages are installed, and even the
            // kernel version.  There are too many variables to try to determine
            // whether or not full LVM support is expected and this is an error
            // or when LVM support is minimal and this can be ignored.  In most
            // cases, this can be ignored, and the warning is logged for the
            // remaining few.
            out.str(L"");
            out << L"Support for LVM on "
#   if defined(PF_DISTRO_SUSE)
                << L"SuSE Linux Enterprise Server 9 "
#   else
                << L"Red Hat Enterprise Linux 4 "
#   endif
                << L"is limited to logical disk metrics.";

            SCX_LOG(log, m_warningSuppressor.GetSeverity(L"SCXLVMUtils::LegacyLvmWarnOneTime"), out.str());

            out.str(L"");
            out << L"Missing LVM support in SysFS; the path " << sysfsPath.str() << L" does not exist.";
            SCX_LOGHYSTERICAL(log, out.str());

            return result;
#endif

            out.str(L"");
            out << L"An exception occurred while getting the slave devices for \"" << dmDevice << L"\": " << e.What();
            SCX_LOG(log, m_errorSuppressor.GetSeverity(out.str()), out.str());

            throw;
        }

        if (slaves.size() == 0)
        {
#if defined(linux) &&                                          \
           ( ( defined(PF_DISTRO_SUSE)   && (PF_MAJOR<=9) ) || \
             ( defined(PF_DISTRO_REDHAT) && (PF_MAJOR<=4) ) )
            // LVM support on RHEL4 and SLES9 is limited by things like the
            // distribution update, what packages are installed, and even the
            // kernel version.  There are too many variables to try to determine
            // whether or not full LVM support is expected and this is an error
            // or when LVM support is minimal and this can be ignored.  In most
            // cases, this can be ignored, and the warning is logged for the
            // remaining few.
            out.str(L"");
            out << L"Support for LVM on "
#   if defined(PF_DISTRO_SUSE)
                << L"SuSE Linux Enterprise Server 9 "
#   else
                << L"Red Hat Enterprise Linux 4 "
#   endif
                << L"is limited to logical disk metrics.";

            SCX_LOG(log, m_warningSuppressor.GetSeverity(L"SCXLVMUtils::LegacyLvmWarnOneTime"), out.str());

            out.str(L"");
            out << L"Incomplete LVM support in SysFS; the path " << sysfsPath.str() << L" is empty.";
            SCX_LOGHYSTERICAL(log, out.str());

            return result;
#endif

            out.str(L"");

            out << L"There are no slave entries for the device \"" << dmDevice  << L"\"";
            SCX_LOG(log, m_errorSuppressor.GetSeverity(out.str()), out.str());

            throw SCXBadLVMDeviceException(dmDevice, out.str(), SCXSRCLOCATION);
        }

        // Each slave entry should be the name of a block device in /dev.
        for (std::vector< SCXCoreLib::SCXFilePath >::const_iterator iter = slaves.begin();
             iter != slaves.end(); iter++)
        {
            std::wstring dirpath    = iter->GetDirectory();
            size_t       dirPathLen = dirpath.length();
            size_t       pos        = dirpath.rfind(L'/', dirPathLen - 2) + 1;
            size_t       count      = dirPathLen - pos - 1;

            if ((dirPathLen <= 2) || (dirpath[dirPathLen - 1] != L'/') || (pos == std::wstring::npos) || (count == 0))
            {
                out.str(L"");

                out << L"The slave device entry \"" << dirpath << L"\" could not be parsed and will be ignored";
                SCX_LOG(log, m_warningSuppressor.GetSeverity(dirpath), out.str());
                continue;
            }
            std::wstring dirname = dirpath.substr(pos, count);

            // replace all '!' with '/' if special file is in a subdirectory of the /dev directory
            std::replace(dirname.begin(), dirname.end(), '!', '/');

            unsigned int       major = 0;
            unsigned int       minor = 0;
            std::wstringstream devPath;

            devPath << L"/dev/" << dirname;

            // Any exceptions here are unexpected, so just let them move up-stack
            StatPathId(devPath.str(), major, minor);

            // There are some pretty big assumptions being made about paths
            // here.  The assumptions are normally safe, but to be certain
            // that the device at devPath is the same device referenced by
            // /sys... ...slaves/, it is a good idea to match the device
            // major/minor ID.  The major/minor ID values are stored in the
            // dev file within the individual slave entry directories.
            SCXCoreLib::SCXFilePath slaveDevFilePath;

            slaveDevFilePath.SetDirectory(iter->Get());
            slaveDevFilePath.SetFilename(L"dev");

            if (MatchIdInFile(slaveDevFilePath, major, minor))
            {
                // Good!  The device named in /sys... ...slaves/ is the device
                // with the same name in /dev, so add it to the results.
                result.push_back(devPath.str());
            }
            else
            {
                // This is very suspicious.  Reaching this point means that
                // there is either a bad assumption or a half-installed/
                // half-uninstalled LVM partition.
                out.str(L"");

                out << L"The slave device " << iter->Get() << L" does not map to the expected device path " << devPath.str();
                SCX_LOG(log, m_warningSuppressor.GetSeverity(out.str()), out.str());
            }
        }

        return result;
    }
Beispiel #8
0
/**
   Find out operating system name and version

   This method caches system information in m_osName, m_osVersion and m_osAlias.
   It requires m_unameInfo to be set prior to call.

*/
void SCXOSTypeInfo::Init()  // private
{
    m_osVersion = L"";
    m_osName = L"Unknown";

    assert(m_unameIsValid);

#if defined(hpux) || defined(sun)

    if (m_unameIsValid)
    {
        m_osName = StrFromUTF8(m_unameInfo.sysname);
        m_osVersion = StrFromUTF8(m_unameInfo.release);
    }
#if defined(hpux)
    m_osAlias = L"HPUX";
    m_osManufacturer = L"Hewlett-Packard Company";
#elif defined(sun)
    m_osAlias = L"Solaris";
    m_osManufacturer = L"Oracle Corporation";
#endif
#elif defined(aix)

    if (m_unameIsValid)
    {
        m_osName = StrFromUTF8(m_unameInfo.sysname);

        // To get "5.3" we must read "5" and "3" from different fields.
        string ver(m_unameInfo.version);
        ver.append(".");
        ver.append(m_unameInfo.release);
        m_osVersion = StrFromUTF8(ver);
    }
    m_osAlias = L"AIX";
    m_osManufacturer = L"International Business Machines Corporation";
#elif defined(linux)
    vector<wstring> lines;
    SCXStream::NLFs nlfs;
#if defined(PF_DISTRO_SUSE)
    static const string relFileName = "/etc/SuSE-release";
    wifstream relfile(relFileName.c_str());
    wstring version(L"");
    wstring patchlevel(L"");

    SCXStream::ReadAllLines(relfile, lines, nlfs);

    if (!lines.empty()) {
        m_osName = ExtractOSName(lines[0]);
    }

    // Set the Linux Caption (get first line of the /etc/SuSE-release file)
    m_linuxDistroCaption = lines[0];
    if (0 == m_linuxDistroCaption.length())
    {
        // Fallback - should not normally happen
        m_linuxDistroCaption = L"SuSE";
    }

    // File contains one or more lines looking like this:
    // SUSE Linux Enterprise Server 10 (i586)
    // VERSION = 10
    // PATCHLEVEL = 1
    for (size_t i = 0; i<lines.size(); i++)
    {
        if (StrIsPrefix(StrTrim(lines[i]), L"VERSION", true))
        {
            wstring::size_type n = lines[i].find_first_of(L"=");
            if (n != wstring::npos)
            {
                version = StrTrim(lines[i].substr(n+1));
            }
        }
        else if (StrIsPrefix(StrTrim(lines[i]), L"PATCHLEVEL", true))
        {
            wstring::size_type n = lines[i].find_first_of(L"=");
            if (n != wstring::npos)
            {
                patchlevel = StrTrim(lines[i].substr(n+1));
            }
        }
    }

    if (version.length() > 0)
    {
        m_osVersion = version;

        if (patchlevel.length() > 0)
        {
            m_osVersion = version.append(L".").append(patchlevel);
        }
    }

    if (std::wstring::npos != m_osName.find(L"Desktop"))
    {
        m_osAlias = L"SLED";
    }
    else
    {   // Assume server.
        m_osAlias = L"SLES";
    }
    m_osManufacturer = L"SUSE GmbH";
#elif defined(PF_DISTRO_REDHAT)
    static const string relFileName = "/etc/redhat-release";
    wifstream relfile(relFileName.c_str());

    SCXStream::ReadAllLines(relfile, lines, nlfs);

    if (!lines.empty()) {
        m_osName = ExtractOSName(lines[0]);
    }

    // Set the Linux Caption (get first line of the /etc/redhat-release file)
    m_linuxDistroCaption = lines[0];
    if (0 == m_linuxDistroCaption.length())
    {
        // Fallback - should not normally happen
        m_linuxDistroCaption = L"Red Hat";
    }

    // File should contain one line that looks like this:
    // Red Hat Enterprise Linux Server release 5.1 (Tikanga)
    if (lines.size() > 0)
    {
        wstring::size_type n = lines[0].find_first_of(L"0123456789");
        if (n != wstring::npos)
        {
            wstring::size_type n2 = lines[0].substr(n).find_first_of(L" \t\n\t");
            m_osVersion = StrTrim(lines[0].substr(n,n2));
        }
    }

    if ((std::wstring::npos != m_osName.find(L"Client")) // RHED5
            || (std::wstring::npos != m_osName.find(L"Desktop"))) // RHED4
    {
        m_osAlias = L"RHED";
    }
    else
    {   // Assume server.
        m_osAlias = L"RHEL";
    }
    m_osManufacturer = L"Red Hat, Inc.";

#elif defined(PF_DISTRO_ULINUX)
    // The release file is created at agent start time by init.d startup script
    // This is done to insure that we can write to the appropriate directory at
    // the time (since, at agent run-time, we may not have root privileges).
    //
    // If we CAN create the file here (if we're running as root), then we'll
    // do so here. But in the normal case, this shouldn't be necessary.  Only
    // in "weird" cases (i.e. starting omiserver by hand, for example).

    // Create the release file by running GetLinuxOS.sh script
    // (if we have root privileges)

    try
    {
        if ( !SCXFile::Exists(m_deps->getReleasePath()) &&
                SCXFile::Exists(m_deps->getScriptPath()) &&
                m_deps->isReleasePathWritable() )
        {
            std::istringstream in;
            std::ostringstream out;
            std::ostringstream err;

            int ret = SCXCoreLib::SCXProcess::Run(m_deps->getScriptPath().c_str(), in, out, err, 10000);

            if ( ret || out.str().length() || err.str().length() )
            {
                wostringstream sout;
                sout << L"Unexpected errors running script: " << m_deps->getScriptPath().c_str()
                     << L", return code: " << ret
                     << L", stdout: " << StrFromUTF8(out.str())
                     << L", stderr: " << StrFromUTF8(err.str());

                SCX_LOGERROR(m_log, sout.str() );
            }
        }
    }
    catch(SCXCoreLib::SCXInterruptedProcessException &e)
    {
        wstring msg;
        msg = L"Timeout running script \"" + m_deps->getScriptPath() +
              L"\", " + e.Where() + L'.';
        SCX_LOGERROR(m_log, msg );
    };

    // Look in release file for O/S information

    string sFile = StrToUTF8(m_deps->getReleasePath());
    wifstream fin(sFile.c_str());
    SCXStream::ReadAllLines(fin, lines, nlfs);

    if (!lines.empty())
    {
        ExtractToken(L"OSName",     lines, m_osName);
        ExtractToken(L"OSVersion",  lines, m_osVersion);
        ExtractToken(L"OSFullName", lines, m_linuxDistroCaption);
        ExtractToken(L"OSAlias",    lines, m_osAlias);
        ExtractToken(L"OSManufacturer", lines, m_osManufacturer);
    }
    else
    {
        m_osAlias = L"Universal";
    }

    // Behavior for m_osCompatName (method GetOSName) should be as follows:
    //   PostInstall scripts will first look for SCX-RELEASE file (only on universal kits)
    //   If found, add "ORIGINAL_KIT_TYPE=Universal" to scxconfig.conf file,
    //      else   add "ORIGINAL_KIT_TYPE=!Universal" to scxconfig.conf file.
    //   After that is set up, the SCX-RELEASE file is created.
    //
    //   A RHEL system should of OSAlias of "RHEL, SLES system should have "SuSE" (in scx-release)
    //
    //   We need to mimic return values for RHEL and SLES on universal kits that did not
    //   have a universal kit installed previously, but only for RHEL and SLES kits.  In
    //   all other cases, continue to return "Linux Distribution".

    wstring configFilename(m_deps->getConfigPath());
    SCXConfigFile configFile(configFilename);

    try
    {
        configFile.LoadConfig();
    }
    catch(SCXFilePathNotFoundException &e)
    {
        // Something's whacky with postinstall, so we can't follow algorithm
        static SCXCoreLib::LogSuppressor suppressor(SCXCoreLib::eError, SCXCoreLib::eTrace);
        wstring logMessage(L"Unable to load configuration file " + configFilename);
        SCX_LOG(m_log, suppressor.GetSeverity(logMessage), logMessage);

        m_osCompatName = L"Unknown Linux Distribution";
    }

    if ( m_osCompatName.empty() )
    {
        wstring kitType;
        if ( configFile.GetValue(L"ORIGINAL_KIT_TYPE", kitType) )
        {
            if ( L"!Universal" == kitType )
            {
                if ( L"RHEL" == m_osAlias )
                {
                    m_osCompatName = L"Red Hat Distribution";
                }
                else if ( L"SLES" == m_osAlias )
                {
                    m_osCompatName = L"SuSE Distribution";
                }
            }
        }

        if ( m_osCompatName.empty() )
        {
            m_osCompatName = L"Linux Distribution";
        }
    }
#else
#error "Linux Platform not supported";
#endif

#elif defined(macos)
    m_osAlias = L"MacOS";
    m_osManufacturer = L"Apple Inc.";
    if (m_unameIsValid)
    {
        // MacOS is called "Darwin" in uname info, so we hard-code here
        m_osName = L"Mac OS";

        // This value we could read dynamically from the xml file
        // /System/Library/CoreServices/SystemVersion.plist, but that
        // file may be named differently based on client/server, and
        // reading the plist file would require framework stuff.
        //
        // Rather than using the plist, we'll use Gestalt, which is an
        // API designed to figure out versions of anything and everything.
        // Note that use of Gestalt requires the use of framework stuff
        // as well, so the Makefiles for MacOS are modified for that.

        SInt32 major, minor, bugfix;
        if (0 != Gestalt(gestaltSystemVersionMajor, &major)
                || 0 != Gestalt(gestaltSystemVersionMinor, &minor)
                || 0 != Gestalt(gestaltSystemVersionBugFix, &bugfix))
        {
            throw SCXCoreLib::SCXErrnoException(L"Gestalt", errno, SCXSRCLOCATION);
        }

        wostringstream sout;
        sout << major << L"." << minor << L"." << bugfix;
        m_osVersion = sout.str();
    }

#else
#error "Platform not supported"
#endif
}
    bool StaticDiskPartitionInstance::GetBootDrivePath(wstring& bootpathStr)
    {
        SCX_LOGTRACE(m_log, L"DiskPartition::GetBootDrivePath():: Entering . . .");

        bootpathStr.clear();

        // buffer to store lines read from process output
        wstring curLine;
        wstring bootInterfacePath;

        // Determine Solaris boot disk using 'prtconf' and 'ls /dev/dsk'
        // cmdString stores the current process we are running via SCXProcess::Run()
#if defined(sparc)
#if PF_MAJOR == 5 && (PF_MINOR  == 9 || PF_MINOR == 10)
        wstring cmdPrtString = L"/usr/sbin/prtconf -pv"; 
#elif PF_MAJOR == 5 && PF_MINOR  == 11
        wstring cmdPrtString = L"/sbin/prtconf -pv"; 
#else
#error "Platform not supported"
#endif
#else// sparc
        wstring cmdPrtString = L"/usr/bin/grep bootpath /boot/solaris/bootenv.rc"; 
#endif

        std::string prtconfResult;
        std::string finalResult;
        std::istringstream processInputPrt;
        std::ostringstream processOutputPrt;
        std::ostringstream processErrPrt;

        try 
        {
            int retCode = m_deps->Run(cmdPrtString, processInputPrt, processOutputPrt, processErrPrt, 15000);
            if (retCode)
            {
                SCX_LOGERROR(m_log, L"Error returned from prtconf, unable to determine boot partition. Error code=" + StrFrom(retCode));
                return false;
            }
            prtconfResult = processOutputPrt.str();
            SCX_LOGTRACE(m_log, L"  Got this output from " + cmdPrtString + L" : " + StrFromUTF8(prtconfResult) );
            size_t lengthCaptured = prtconfResult.length();

            // Truncate trailing newline if there in captured output                  
            if (lengthCaptured > 0)
            {
                if (prtconfResult[lengthCaptured - 1] == '\n')
                {
                    prtconfResult[lengthCaptured - 1] = '\0';
                }
            }

        }
        catch(SCXCoreLib::SCXException &e)
        {
            SCX_LOGERROR(m_log, L"Unable to determine boot partition using prtconf ..." + e.What());
            return false;
        }

        SCXRegexPtr solPrtconfPatternPtr(NULL);

        std::vector<wstring> matchingVector;

        // Let's build our RegEx:
        try
        {
            SCX_LOGTRACE(m_log, L"  Using this regex on PrtConf output: " + c_SolPrtconfPattern );
            solPrtconfPatternPtr = new SCXCoreLib::SCXRegex(c_SolPrtconfPattern);
        }
        catch(SCXCoreLib::SCXInvalidRegexException &e)
        {
            SCX_LOGERROR(m_log, L"Exception caught in compiling regex: " + e.What());
            return false;
        }

        std::istringstream stringStrmPrtconf(prtconfResult);
        vector<wstring>  allLines;                       // all lines read from prtconf output
        allLines.clear();
        SCXStream::NLFs nlfs;
        SCXCoreLib::SCXStream::ReadAllLinesAsUTF8(stringStrmPrtconf, allLines, nlfs);

        for(vector<wstring>::iterator it = allLines.begin(); it != allLines.end(); it++)
        {
            curLine.assign(*it);
            matchingVector.clear();

            // Let's get the Boot partition interface and drive letter from prtconf
            if (solPrtconfPatternPtr->ReturnMatch(curLine, matchingVector, 0))
            {
                bootInterfacePath = matchingVector[1];
                SCX_LOGTRACE(m_log, L"Found match of PrtConfPattern : " + bootInterfacePath);
                break;
            }
        }

        if (bootInterfacePath.size() == 0)
        {
            std::wstringstream warningMsg;
            if (matchingVector.size() > 0)
            {
                warningMsg << L"Couldn't find Boot Partition, regular expression error message was: " << matchingVector[0];
            }
            else 
            {
                warningMsg << L"Couldn't find Boot Partition.";
            }
            SCX_LOG(m_log, suppressor.GetSeverity(warningMsg.str()), warningMsg.str());
            return false;
        }

        // Replace "disk" by "disk" or "sd" to normalize the boot interface path
        wstring from(L"disk");
        size_t start_pos = bootInterfacePath.find(from);
        if(start_pos != std::string::npos)
        {
            bootInterfacePath.replace(start_pos, from.length(), L"(disk|sd)");
        }

        wstring solLsPattern(c_SolLsPatternBeg);
        solLsPattern += bootInterfacePath;

        // Now we need to build up our pattern to find the bootdisk, using our results from above:
        SCXRegexPtr solLsPatternPtr(NULL);

        //Let's build our RegEx:
        try
        {
            SCX_LOGTRACE(m_log, L"  Using this regex on ls -l /dev/dsk output: " + solLsPattern );
            solLsPatternPtr = new SCXCoreLib::SCXRegex(solLsPattern);
        }
        catch(SCXCoreLib::SCXInvalidRegexException &e)
        {
            SCX_LOGERROR(m_log, L"Exception caught in compiling LS Pattern regex: " + e.What());
            return false;
        }

        // Retrieve the bootdrive using the bootInterface and driveLetter
        wstring cmdStringLs = L"/usr/bin/ls -l /dev/dsk";
        std::string devDskResult;

        std::istringstream processInputLs;
        std::ostringstream processOutputLs;
        std::ostringstream processErrLs;
        curLine.clear();

        try 
        {
            SCXCoreLib::SCXProcess::Run(cmdStringLs, processInputLs, processOutputLs, processErrLs, 15000);
            devDskResult = processOutputLs.str();
            SCX_LOGTRACE(m_log, L"  Got this output from " + cmdStringLs + L" : " + StrFromUTF8(devDskResult) );

            size_t lengthCaptured = devDskResult.length();

            // Truncate trailing newline if there in captured output
            if (lengthCaptured > 0)
            {
                if (devDskResult[lengthCaptured - 1] == '\n')
                {
                    devDskResult[lengthCaptured - 1] = '\0';
                }
            }

        }
        catch(SCXCoreLib::SCXException &e)
        {
            SCX_LOGERROR(m_log, L"Unable to determine boot partition..." + e.What());
            return false;
        }

        std::istringstream stringStrmDevDsk(devDskResult);
        allLines.clear();
        SCXCoreLib::SCXStream::ReadAllLinesAsUTF8(stringStrmDevDsk, allLines, nlfs);

        wstring bootDisk(L"");
        for(vector<wstring>::iterator it = allLines.begin(); it != allLines.end(); it++)
        {
            curLine.assign(*it);
            curLine.push_back('\n');
            matchingVector.clear();

            // Let's get the boot drive
            if (solLsPatternPtr->ReturnMatch(curLine, matchingVector, 0))
            {
                bootDisk = matchingVector[1];  //e.g. "c1t0d0s0"
                break;
            }
        }

        //Check the results
        if (bootDisk.size() == 0)
        {
            std::wstringstream warningMsg;
            if (matchingVector.size() > 0)
            {
                warningMsg << L"Couldn't find Boot Drive, regular expression error message was: " << matchingVector[0];
            }
            else
            {
                warningMsg << L"Couldn't find Boot Drive.";
            }
            SCX_LOG(m_log, suppressor.GetSeverity(warningMsg.str()), warningMsg.str());
            return false;
        }     

        bootpathStr = L"/dev/dsk/" + bootDisk; //e.g. "/dev/dsk/c1t0d0s0"

        return true;
    }