# Copyright 2026 SigmaSRC, Inc. All Rights Reserved.

"""Gather the free and available disk space on local filesystems.
Note that we can hang if we query remote filesystems that are down, so
we don't try to do that."""

if __name__ == "__main__":
    import os
    import sys
    try:
        import sigma
    except ImportError:
        sys.path.append(os.path.join(os.pardir, os.pardir))
    del sys
    del os

import os
import platform
import re
import logging

logger = logging.getLogger("gather.gather_df")

basicos = platform.uname()[0]
if basicos in [ "Microsoft", "Microsoft Windows" ]:
    basicos = "Windows"
# The "l" option to df limits it to local filesystems.
# The "k" option gives us output in kilobytes, rather than some other
# unspecified unit.
if basicos in ("SunOS", "HP-UX"):
    _command = "/usr/bin/df -kl"
elif basicos == "Darwin":
    _command = "/bin/df -kl"
elif basicos == "Linux":
    _command = "/bin/df -klP"
elif basicos == "AIX":
    # AIX has no -l option; use grep to only report local mounts
    # The -I formats it right for our pattern
    _command = "/usr/bin/df -kI | /usr/bin/grep '^/'"
elif basicos != "Windows":
    raise Exception("Can't compute free disk on this operating system: %r" %
                    basicos)

# Real local filesystems have a device that starts with /dev.  I'll
# leave it to the server to filter for this and tally up the relevant
# columns.
_line = re.compile(r"^(?P<device>[^\s]+)\s+(?P<total>\d+)\s+(?P<used>\d+)\s+(?P<avail>\d+)")
assert _line.match("/dev/hda5             66761260   4840244  "
                        "59207956   8% /"), "_linuxline"

def Gather(collector):
    t = collector.table("df", ["device", "total", "used", "free"])
    if basicos == "Windows":
        from win32com.client import GetObject
        try:
            objItems = GetObject("WinMgMts://./root/cimv2").InstancesOf("Win32_LogicalDisk")
            for item in objItems:
                total = item.Properties_("Size").Value
                # We want disk space for local disks only
                dtype = item.Properties_("DriveType").Value
                if total == None or  dtype != 3: continue
                device = item.Properties_("Name").Value
                free = item.Properties_("FreeSpace").Value
                used = int(total) - int(free)
                t.add([device, total, used, free])
        except:
            logger.exception("df gather failed")
    elif basicos == "HP-UX":
        device = total = used = free = None
        file = os.popen(_command + " 2>&1", "r")
        K = 1024
        try:
            for line in file:
                if line.startswith("/"):
                    if device:
                        t.add([device, total, used, free])
                    device = total = used = free = None
                    m = re.match(
                     r"\S+\s+\((/dev/\S+)\s*\)\s+:\s*(\d+) total allocated Kb",
                     line)
                    if m:
                        device = m.group(1)
                        total = int(m.group(2)) * K
                else:
                    m = re.match(r"\s+(\d+) (free|used) allocated Kb", line)
                    if m:
                        bytes = int(m.group(1)) * K
                        if m.group(2) == "free":
                            free = bytes
                        else:
                            used = bytes
            if device:
                t.add([device, total, used, free])
        finally:
            file.close()
    else:
        file = os.popen(_command + " 2>&1", "r")
        try:
            for line in file:
                match = _line.match(line)
                if match:
                    (device, total, used, free) = \
                             match.group("device", "total", "used", "avail")
                    k = 1024
                    t.add([device, int(total) * k, int(used) * k, int(free) * k])
        finally:
            file.close()

def _test():
    from sigma.gather import gathering
    gathering.test(Gather)

if __name__ == "__main__":
    _test()
