Dictionaries/Computer.Dict.Linux/Computer.Dict.Linux.psm1

<#
 
  ###### ####### ## ## ######## ## ## ######## ######## ########
 ## ## ## ## ### ### ## ## ## ## ## ## ## ##
 ## ## ## #### #### ## ## ## ## ## ## ## ##
 ## ## ## ## ### ## ######## ## ## ## ###### ########
 ## ## ## ## ## ## ## ## ## ## ## ##
 ## ## ## ## ## ## ## ## ## ## ## ## ##
  ###### ####### ## ## ## ####### ## ######## ## ##
 
#>


# function Get-MyInvocation {
# [CmdletBinding()]
# [OutputType([String])]
# Param (
# )
# Begin {
# Write-EnterFunction
# }

# Process {
# $MyInvocation
# }

# End {
# Write-LeaveFunction
# }
# }

<#
.SYNOPSIS
Get the computer name
 
.DESCRIPTION
 
.PARAMETER localhost
request localhost's ComputerName
 
.EXAMPLE
$name = Get-ComputerName
 
.NOTES
This function comes from Dict.Unix module.
#>

function Get-ComputerName {
    [CmdletBinding()]Param (
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        # delete everything after 1st dot to get only hostname (e.g. without domainname)
        return (hostname) -replace "\..*"
    }

    End {
        Write-LeaveFunction
    }
}

function Get-ComputerDomain {
    hostname -d
}

function Get-ComputerManufacturer {
    [CmdletBinding()][OutputType([String])]Param (
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        return (New-HashtableFromCommand -Command "dmidecode | awk '/^System Information/,/^$/'" -sep ":" -SkipFirstLines 1).manufacturer
    }

    End {
        Write-LeaveFunction
    }
}

function Get-ComputerModel {
    [CmdletBinding()]Param (
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        return (dmidecode | awk '/^System Information/,/^$/' | Select-Object -Skip 1 | ConvertFrom-StringData -Delimiter ":")."product name"
        # return (New-HashtableFromCommand -Command "dmidecode | awk '/^System Information/,/^$/'" -sep ":" -SkipFirstLines 1)."product name"
    }

    End {
        Write-LeaveFunction
    }
}

function Get-ComputerSerialNumber {
    [CmdletBinding()]Param (
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        return (New-HashtableFromCommand -Command "dmidecode | awk '/^System Information/,/^$/'" -sep ":" -SkipFirstLines 1)."serial number"
    }

    End {
        Write-LeaveFunction
    }
}

<#
    .SYNOPSIS
    Know if computer was booted using old legacy BIOS mode or new UEFI.
 
    .DESCRIPTION
    To make next boot efficient, we need to know in what mode the computer booted to this stage.
    This function will tell us that.
    If every tests fail, or if we can't determined the boot mode, it defaults to "BIOS" mode.
 
    .EXAMPLE
    If (Get-ComputerFirmwareType -eq "UEFI") { # System is running UEFI firmware... }
 
    .EXAMPLE
    Switch (Get-ComputerFirmwareType) {
        "BIOS" { "Legacy BIOS" }
        "UEFI" { "UEFI" }
        Default { "Unknown" }
    }
 
    .OUTPUTS
    string "BIOS" or "UEFI"
 
    .NOTES
    General notes
#>

function Get-ComputerFirmwareType {
    [CmdletBinding()]
    [OutputType([String])]
    Param (
        # [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$string
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        $bootMode = "BIOS"
        if (Test-DirExist "/sys/firmware/efi") { $bootMode = "UEFI" }

        return $bootMode
    }

    End {
        Write-LeaveFunction
    }
}

<#
 
 ######## ######## ## ## ######## ######## ## ## ######## ########
 ## ## ## ## ## ## ## ### ## ## ##
 ## ## ## ## ## ## ## #### ## ## ##
 ###### ## ######### ###### ######## ## ## ## ###### ##
 ## ## ## ## ## ## ## ## #### ## ##
 ## ## ## ## ## ## ## ## ### ## ##
 ######## ## ## ## ######## ## ## ## ## ######## ##
 
#>


# Get-NetAdapter is not available on linux

# array of interfaces found
$Script:interfaces = $null

<#
.SYNOPSIS
Gather informations about network interfaces from registry.
 
.DESCRIPTION
Get-NetAdapter is not available on linux. That's why we try to
gather informations from the sysfs. Then we ask 'ip' for layer 3 data
 
.EXAMPLE
$interfaces = Get-ComputerNetAdapter
 
.NOTES
General notes
#>

function Get-ComputerNetAdapter {
    [CmdletBinding()]
    [OutputType([array])]
    Param (
        # [Parameter(Mandatory = $true, ValueFromPipeLine = $true)][string]$string
        # [switch]$Passthru,

        # Force a refresh, do not blindly return cache
        [Alias('Refresh')]
        [switch]$Force
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        if (($null -eq $Script:interfaces) -or $Force) {
            $Script:interfaces = @()
            $Path = "/sys/class/net"
            foreach ($item in (Get-ChildItem $Path)) {
                $interface = @{}
                $interface.name = $item.name
                foreach ($subitem in (Get-ChildItem $item.fullname -File)) {
                    $property = $subitem.name
                    try {
                        $interface.$property = (Get-Content $subitem -Raw) -replace "\n"
                    } catch {
                        Write-Warning "Unable to read $property property."
                    }
                }
                # we are missing IP properties.
                # try with 'ip' command
                $output = Execute-Command -exe ip -args "-o -4 addr show dev $($item.name)" -PassThru
                # $regex = "\d: $($item.name)\s+inet (?<cidr>\d+\.\d+\.\d+\.\d+/\d+) brd (?<broadcast>\d+\.\d+\.\d+\.\d+) scope (?<scope>[a-z]+) (dynamic (?<dynamic>[a-z]*))? .*"
                # $regex = "\d: (?<ifname>[a-z0-9]+)\s+inet (?<cidr>\d+\.\d+\.\d+\.\d+/\d+)?(?<ipaddress>\d+\.\d+\.\d+\.\d+)? (brd (?<broadcast>\d+\.\d+\.\d+\.\d+))?(peer (?<peer>\d+\.\d+\.\d+\.\d+/\d+))? scope .*"
                $regex = "\d: $($item.name)\s+inet (?<cidr>\d+\.\d+\.\d+\.\d+/\d+)?(?<ipaddress>\d+\.\d+\.\d+\.\d+)? (brd (?<broadcast>\d+\.\d+\.\d+\.\d+))?(peer (?<peer>\d+\.\d+\.\d+\.\d+/\d+))? scope .*"
                if ($output -match $regex) {
                    $interface.cidr = $Matches.cidr
                    $interface.ipaddress = (($interface.cidr) -split "/")[0]
                    $interface.broadcast = $Matches.broadcast
                    $interface.scope = $Matches.scope
                    $interface.dynamic = $Matches.dynamic
                }
                # Place here properties that we have to make common between all OS's
                $interface.macaddress = $interface.address
                $Script:interfaces += $interface
            }
        }
        return $Script:interfaces
    }

    End {
        Write-LeaveFunction
    }
}

<#
 
 ######## #### ###### ## ##
 ## ## ## ## ## ## ##
 ## ## ## ## ## ##
 ## ## ## ###### #####
 ## ## ## ## ## ##
 ## ## ## ## ## ## ##
 ######## #### ###### ## ##
 
#>


<#
.SYNOPSIS
List local disks
 
.DESCRIPTION
Fetch all informations available about local disks. Linux lacks the 'Get-Disk' of the Storage module available on Windows.
 
.EXAMPLE
An example
 
.NOTES
General notes
#>

function Get-ComputerDisk {
    [CmdletBinding(DefaultParameterSetName = "ALL")]
    [OutputType([hashtable])]
    Param (
        # Disk number
        [Parameter(Mandatory = $true, ParameterSetName = 'DISKNUMBER')][string]$DiskNumber,

        # Disk device name (without /dev)
        [Parameter(Mandatory = $true, ParameterSetName = 'DISKNAME')][string]$DiskName,

        # Full device address like in '/dev/sda'
        [Parameter(Mandatory = $true, ParameterSetName = 'DEVICE')][string]$Device,

        # Partition of the disk given its ID
        [Alias('UniqueId')]
        [Parameter(Mandatory = $true, ParameterSetName = 'DISKID')][string]$DiskId,

        # Return all $disks
        [Parameter(Mandatory = $false, ParameterSetName = 'ALL')][switch]$All

    )
    Begin {
        Write-EnterFunction
        $disks = ((lsblk --output-all --json --tree) | convertfrom-json).blockdevices | Where-Object { $_.type -eq "disk" }
        # comply with windows code
        foreach ($d in $disks) {
            foreach ($part in $d.children) {
                # foreach <item> is created by reference, not by copy. So we can modify it
                # add IsActive to bootable partition
                if ($part.partflags -eq "0x80") {
                    $part | Add-Member -Force -MemberType NoteProperty -Name IsActive -Value $true
                } else {
                    $part | Add-Member -Force -MemberType NoteProperty -Name IsActive -Value $false
                }
                if ($part.Mountpoints) { $part | Add-Member -Force -MemberType NoteProperty -Name "AccessPaths" -Value $part.Mountpoints }
                if ($part.UUID) { $part | Add-Member -Force -MemberType NoteProperty -Name "Guid" -Value $part.UUID }
                $part | Add-Member -Force -MemberType NoteProperty -Name "FileSystemLabel" -Value $part.label
            }
        }
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'DISKNUMBER' {
                $disks = $disks[$DiskNumber]
            }
            'DISKNAME' {
                $disks = $disks | Where-Object { $_.name -eq $DiskName }
            }
            'DEVICE' {
                $disks = $disks | Where-Object { $_.path -eq $Device }
            }
            'DISKID' {
                $disks = $disks | Where-Object { $_.ptuuid -eq $DiskId }
            }
            default {
            }
        }
        return $disks
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
List local partitions
 
.DESCRIPTION
Fetch all informations available about local partitions. Linux lacks the 'Get-Partition' of the Storage module available on Windows.
 
.EXAMPLE
An example
 
.NOTES
General notes
#>

function Get-ComputerDiskPartition {
    [CmdletBinding(DefaultParameterSetName = "ALL")]
    [OutputType([hashtable])]
    Param (
        # Partition object
        [Parameter(Mandatory = $true, ValueFromPipeLine = $true, ParameterSetName = 'OBJECT')]$InputObject,

        # Partition with UUID
        [Alias('UniqueId')]
        [Parameter(Mandatory = $true, ParameterSetName = 'PARTUUID')][string]$PartUUID,

        # Label of the partition to mount
        [Alias('Label', 'PartitionLabel')]
        [Parameter(Mandatory = $true, ParameterSetName = 'PARTLABEL')][string]$PartLabel,

        # Disk device name of the partition
        [Parameter(Mandatory = $true, ParameterSetName = 'DISKPART')][string]$Disk,

        # Partition number (can be several strings char since nvme disks. e.g. 'p1')
        [Alias('PartitionNumber')]
        [Parameter(Mandatory = $false, ParameterSetName = 'DISKPART')][string]$PartNum,

        # Full device address like in '/dev/sda1'
        [Parameter(Mandatory = $true, ParameterSetName = 'DEVICE')][string]$Device,

        # Mountpoint of a mounted partition
        [Alias('DriveLetter')]
        [Parameter(Mandatory = $true, ParameterSetName = 'MOUNTPOINT')][string]$Mountpoint,

        # Partition of the disk given its ID
        [Parameter(Mandatory = $true, ParameterSetName = 'DISKID')][string]$DiskId,

        # Return all partitions
        [Parameter(Mandatory = $false, ParameterSetName = 'ALL')][switch]$All

    )
    Begin {
        Write-EnterFunction
        $disks = Get-ComputerDisk
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'OBJECT' {
                $partitions = ($disks.children | Where-Object { $_.partuuid -eq $InputObject.UUID })
            }
            'PARTUUID' {
                $partitions = ($disks.children | Where-Object { $_.partuuid -eq "$PartUUID" })
            }
            'PARTLABEL' {
                $partitions = ($disks.children | Where-Object { $_.label -eq "$PartLabel" })
            }
            'DISKPART' {
                $partitions = ($disks | Where-Object { $_.name -eq $Disk }).children
                if ($PartNumber) {
                    $partitions = ($partitions | Where-Object { $_.name -eq "$Disk$PartNumber" })
                }
            }
            'DEVICE' {
                $partitions = ($disks.children | Where-Object { $_.path -like "$Device*" })
            }
            'MOUNTPOINT' {
                # $partitions = ($disks.children | Where-Object { $_.mountpoint -eq "$Mountpoint" })
                $partitions = ($disks.children | Where-Object { $Mountpoint -in $_.Mountpoints })
            }
            'DISKID' {
                $partitions = ($disks.children | Where-Object { $_.ptuuid -eq "$DiskId" })
            }
            default {
                $partitions = $disks.children
            }
        }
        return $partitions
    }

    End {
        Write-LeaveFunction
    }
}

<#
.SYNOPSIS
Mount a partition into running operating system
 
.DESCRIPTION
Mount specified partition
 
.EXAMPLE
An example
 
.NOTES
General notes
 
.OUTPUTS
[string] mountpoint
#>

function Mount-ComputerDiskPartition {
    [CmdletBinding()]
    [OutputType([String], [array])]
    Param (
        # Partition object
        [Parameter(Mandatory = $true, ValueFromPipeLine = $true, ParameterSetName = 'OBJECT')]$InputObject,

        # UUID of the partition to mount
        [Parameter(Mandatory = $true, ParameterSetName = 'PARTUUID')][string]$PartUuid,

        # Label of the partition to mount
        [Alias('Label', 'PartitionLabel')]
        [Parameter(Mandatory = $true, ParameterSetName = 'PARTLABEL')][string]$PartLabel,

        # Disk device name of the partition
        [Parameter(Mandatory = $true, ParameterSetName = 'DISKPART')][string]$Disk,

        # Partition number (can be several strings char since nvme disks. e.g. 'p1')
        [Parameter(Mandatory = $true, ParameterSetName = 'DISKPART')][string]$PartNum,

        # Full device address like in '/dev/sda1'
        [Parameter(Mandatory = $true, ParameterSetName = 'DEVICE')][string]$Device,

        # options to pass
        [Parameter(Mandatory = $false)][string]$Options,

        # Recurse through partition children
        [Parameter(Mandatory = $false)][switch]$Recurse,

        # Specify mounting to this mountpoint
        [Parameter(Mandatory = $false)][string]$Mountpoint,

        # If partition is already mounted, it will mount it to another mountpoint
        [Parameter(Mandatory = $false)][switch]$Force,

        # if specified, return the whole partition object,
        # else return only mountpoint
        [switch]$Passthru
    )
    Begin {
        Write-EnterFunction
    }

    Process {
        switch ($PSCmdlet.ParameterSetName) {
            'OBJECT' {
                $partition = $InputObject
            }
            'PARTUUID' {
                $partition = Get-ComputerDiskPartition -PartUUID $PartUuid
            }
            'PARTLABEL' {
                $partition = Get-ComputerDiskPartition -PartLabel $PartLabel
            }
            'DISKPART' {
                $partition = Get-ComputerDiskPartition -Disk $Disk -PartNum $PartNum
            }
            'DEVICE' {
                $partition = Get-ComputerDiskPartition -Device $Device
            }
        }
        # handle -Bind first because if it already mounted we do not have to remount it twice
        if ($Force) {
            $Mountpoint = "/var/lib/pwsh.fw.computer/mountpoints/$($partition.Guid)"
            $partition = Mount-ComputerDiskPartition -InputObject $partition -Mountpoint $Mountpoint -Passthru
        }
        # partition is already mounted ? return mountpoint
        # 1st case -> $Mountpoint not specified and Partition already mounted -> return
        # 2nd case -> $Mountpoit specified and partition already mounted -> check if partition is mounted on this specific mountpoit
        if ((!$Mountpoint) -and $partition.mountpoint) {
            Write-Debug "Partition $($partition.name) already mounted at '$Mountpoint'"
            if ($Passthru) {
                return Get-ComputerDiskPartition -InputObject $partition
            } else {
                return $Mountpoint
            }
        }
        if ($Mountpoint -in $partition.mountpoints) {
            Write-Debug "Partition $($partition.name) already mounted at '$Mountpoint'"
            if ($Passthru) {
                return Get-ComputerDiskPartition -InputObject $partition
            } else {
                return $Mountpoint
            }
        }
        if ($partition.children) {
            if ($Recurse) {
                $Mountpoints = @()
                foreach ($c in $partition.children) {
                    $Mountpoints += Mount-ComputerDiskPartition -InputObject $c -Recurse
                }
                if ($Passthru) {
                    return Get-ComputerDiskPartition -InputObject $partition
                } else {
                    return $Mountpoint
                }
            } else {
                Write-Warning "Partition $($partition.name) have children (subvolumes). You may want to mount those volumes instead."
                Write-Warning "Use -Recurse to mount subvolumes automatically."
                return $null
            }
        }
        if (!$Mountpoint) {
            # $Mountpoint = "/mnt/$($partition.name)"
            $Mountpoint = "/var/lib/pwsh.fw.computer/mountpoints/$($partition.Guid)"
        }
        $rc = New-Item $Mountpoint -ItemType Directory -Force
        Write-Debug "Mount partition $($partition.name) in $Mountpoint"
        # options is an array of comma separated values
        $aOptions = $Options -split ","
        switch ($partition.fstype) {
            'ntfs' {
                # probe for mountability
                $rc = Execute-Command -exe ntfs-3g.probe -args "--readwrite $($partition.path)" -AsBool
                rc=$?
                switch (${rc}) {
                    14 {
                        Write-Warning "Windows is hibernating, ntfs-3g will refuse to mount it."
                        Write-Warning "We'll try to mount removing hiberfile"
                        # if ${options} is defined, then OPTIONS="${options},ro" else, OPTIONS="ro"
                        $aOptions += "remove_hiberfile"
                    }
                }
                # Make sure the dirty flag is removed. If not, mounting the NTFS partition is impossible.
                $rc = Execute-Command -exe ntfsfix -args "-d $($partition.path)"
                # if ${options} is defined, then OPTIONS="-o ${options}" else, OPTIONS=""
                $rc = Execute-Command -exe ntfs-3g -rags "$($partition.path) $MountPoint $($aOptions -join ",")"
            }
            'crypto_LUKS' {
                $passPhrase = Write-Question -Prompt "Please enter pass-phrase to decypher partition $($partition.name)"
                $rc = Execute-Command -exe "cryptsetup" -args "luksOpen $($partition.path) $($partition.name) - <<< ${passPhrase}"
                # if [ -e /dev/mapper/${part} ]; then
                # $mountPoint=$(MountPartition /dev/mapper/${part})
                # return $(true)
                # else
                # CLog "ERR" "${FUNCNAME}(): failed to unlock luks partition /dev/${part}"
                # return $(false)
                # fi
            }
            'swap' {
                # Do nothing, we do not mount swap.
                Write-Warning "Partition $($partition.name) is a SWAP partition. Skip it."
            }
            Default {
                # if partition already have a mountpoint, use another mount syntax
                if ($Force) {
                    $rc = Execute-Command -exe mount -args "--bind $($partition.mountpoint) $MountPoint $($aOptions -join ",")"
                } else {
                    $rc = Execute-Command -exe mount -args "-t auto $($partition.path) $MountPoint $($aOptions -join ",")"
                }
            }
        }
        if ($Passthru) {
            return Get-ComputerDiskPartition -InputObject $partition
        } else {
            return $Mountpoint
        }
    }

    End {
        Write-LeaveFunction
    }
}