Skip to content

Instantly share code, notes, and snippets.

@bender-the-greatest
Last active October 8, 2020 07:13
Show Gist options
  • Save bender-the-greatest/9b46c4b66b00e36176e20b7f44cec511 to your computer and use it in GitHub Desktop.
Save bender-the-greatest/9b46c4b66b00e36176e20b7f44cec511 to your computer and use it in GitHub Desktop.
Configure a new wine prefix. A bit convoluted since I wanted this to set up a shared Wine directory, but Wine doesn't support multiuser scenarios. Run Set-WineEnv, the other two functions are helpers used in that function.
function Get-CanWriteDirectory {
[CmdletBinding()]
Param(
[Parameter(Mandatory, Position=0)]
[ValidateScript(
{ Test-Path -PathType Container $_ },
ErrorMessage = "Could not find the path or it is not a directory: {0}"
)]
[string]$Path
)
# Make sure temp file doesn't already exist (we clean it up)
$index = 0
$tempFilePath = Join-Path $Path "$($MyInvocation.MyCommand -replace '-')Check$(( $index )).txt"
while( Test-Path -PathType Leaf $tempFilePath ) {
Write-Verbose "${tempFilePath} already exists. Incrementing index..."
$tempFilePath = Join-Path $Path "$($MyInvocation.MyCommand -replace '-')Check$(( ++$index )).txt"
}
# Write temp file to see if we can
$checkPermArgs = @{
Path = $tempFilePath
ErrorAction = 'Stop'
}
Write-Verbose "Writing file ${tempFilePath}"
try {
"$($MyInvocation.MyCommand) check $([DateTime]::Now)" | Out-File @checkPermArgs
} catch {
Write-Verbose "Error writing ""${tempFilePath}"": $($_.Exception.Message)"
return $false
}
# Clean up our messy handiwork
$removeItemArgs = @{
Path = $tempFilePath
ErrorAction = 'Stop'
Force = $true
}
Write-Verbose "Removing file ${tempFilePath}"
try {
Remove-Item @removeItemArgs
} catch {
Write-Warning "Error removing temp file ""${tempFilePath}"": $($_.Exception.Message)"
}
$true
}
function Invoke-Executable {
[CmdletBinding()]
Param(
[Parameter(Mandatory, Position=0)]
[string]$Command,
[Parameter(Position=1, ValueFromRemainingArguments)]
[string[]]$Arguments,
[int[]]$ExitCode = 0
)
try {
& $Command @Arguments
} catch [System.Management.Automation.CommandNotFoundException] {
$message = "Command ${Command} not found. If an argument is mistaken for a named parameter of this function, prefix positional arguments with --. ",
"`te.g. ``Invoke-Executable ping -- www.google.com -c 2``" -join [System.Environment]::NewLine
throw [System.Management.Automation.RuntimeException]::new([string]$message, $_.Exception)
}
if( $LASTEXITCODE -notin $ExitCode ) {
Write-Error "Command failed with exit code ${LASTEXITCODE}. Use -ExitCode to specify expected exit codes."
}
}
function Set-WineEnv {
[Alias('SetWine')]
[CmdletBinding()]
Param(
[ValidateSet('win32', 'win64')]
[Parameter(Position=0,Mandatory)]
[Alias('Arch')]
[string]$WineArch,
[Alias('Wine', 'Root', 'Prefix')]
[string]$WinePrefix,
[switch]$CreateIfMissing = $true,
[switch]$Configure = $false,
[switch]$SetACLs = $true,
[ValidateScript(
{ $_ -notmatch '\s' },
ErrorMessage = 'Whitespace not allowed in the WineGroup'
)]
[string]$WineGroup = 'wineusers',
[ValidateScript(
{ $_ -notmatch '\s' },
ErrorMessage = 'Whitespace not allowed in the WineUser'
)]
[string]$WineUser = $env:USER
)
if( !( Get-Command wine ) ){
throw "Wine executable is not on the path. Bailing."
}
# Set the wineprefix if not set
if( !$WinePrefix ) {
$useWinePrefix = switch( $WineArch ) {
'win32' { '/wineroot/win32'; break; }
'win64' { '/wineroot/win64'; break; }
default { throw "Invalid winarch detected while setting WINEPREFIX: ${WineArch}" }
}
} else {
$useWinePrefix = $WinePrefix
}
# Make sure prefix exists, create if missing and directed to
if( !( Test-Path -PathType Container -Path $useWinePrefix ) ) {
switch( $CreateIfMissing ) {
$false { throw "WINEPREFIX ""${useWinePrefix}"" doesn't exist, and CreateIfMissing set to false" }
default {
Write-Verbose "WINEPREFIX ""${useWinePrefix}"" doesn't exist, creating..."
if( Get-CanWriteDirectory -Path $useWinePrefix ) {
New-Item -ItemType Directory $useWinePrefix -ErrorAction Stop
} else {
Invoke-Executable sudo -- mkdir -p $useWinePrefix
}
}
}
}
Write-Verbose "Changing mode of to 2770"
Invoke-Executable -ErrorAction Stop sudo -- chmod 2770 $useWinePrefix
Write-Verbose "Adding group ""${WineGroup}"" if it doesn't already exist..."
Invoke-Executable -ErrorAction Stop sudo -- groupadd -f $WineGroup
Write-Verbose "Changing owner to ${WineUser}:${WineGroup}"
Invoke-Executable -ErrorAction Stop sudo -- chown -R "${WineUser}:${WineGroup}" $useWinePrefix
Write-Verbose "Adding current user $(whoami) to ${WineGroup}..."
Invoke-Executable -ErrorAction Stop sudo -- usermod -a -G $WineGroup ( whoami )
if( $SetACLs ) {
$prefixDevice = ( df -P $useWinePrefix | Select-Object -Skip 1 ) -Split '\s+' | Select-Object -First 1
Write-Warning 'Setting default ACLs and updating existing ACLs on WinePrefix and subdirectories'
Write-Warning 'It is up to you to ensure your filesystem is mounted with ACLs enabled'
Invoke-Executable -ErrorAction Stop sudo -- setfacl -dm "u:${WineUser}:rwx,g:${WineGroup}:rwx,o::-" $useWinePrefix
Invoke-Executable -ErrorAction Stop sudo -- setfacl -Rm "u:${WineUser}:rwx,g:${WineGroup}:rwx,o::-" $useWinePrefix
}
$env:WINEPREFIX = $useWinePrefix
$env:WINEARCH = $WineArch
if( $Configure ) {
Invoke-Executable winecfg
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment