From 86299552a14c09de033065a8a8c04f414ca0e71e Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Thu, 7 Jul 2016 09:31:08 -0700 Subject: [PATCH 01/34] Absolute filepath resolution for followsymlink --- src/libpsl-native/src/followsymlink.cpp | 129 +++++++++++++++++------- 1 file changed, 91 insertions(+), 38 deletions(-) diff --git a/src/libpsl-native/src/followsymlink.cpp b/src/libpsl-native/src/followsymlink.cpp index 40e12b9b7..eac83f0d4 100644 --- a/src/libpsl-native/src/followsymlink.cpp +++ b/src/libpsl-native/src/followsymlink.cpp @@ -45,46 +45,99 @@ char* FollowSymLink(const char* fileName) return NULL; } - char buffer[PATH_MAX]; - ssize_t sz = readlink(fileName, buffer, PATH_MAX); + char actualpath[PATH_MAX+1]; + char* realPath = realpath(fileName, actualpath); + - if (sz == -1) + /* if realPath does not return a value, continue with previous implementation of symlink*/ + if (realPath == NULL) { - switch(errno) + char buffer[PATH_MAX]; + ssize_t sz = readlink(fileName, buffer, PATH_MAX); + + if (sz == -1) { - case EACCES: - errno = ERROR_ACCESS_DENIED; - break; - case EFAULT: - errno = ERROR_INVALID_ADDRESS; - break; - case EINVAL: - errno = ERROR_INVALID_NAME; - break; - case EIO: - errno = ERROR_GEN_FAILURE; - break; - case ELOOP: - errno = ERROR_STOPPED_ON_SYMLINK; - break; - case ENAMETOOLONG: - errno = ERROR_BAD_PATH_NAME; - break; - case ENOENT: - errno = ERROR_FILE_NOT_FOUND; - break; - case ENOMEM: - errno = ERROR_OUTOFMEMORY; - break; - case ENOTDIR: - errno = ERROR_BAD_PATH_NAME; - break; - default: - errno = ERROR_INVALID_FUNCTION; - } - return NULL; - } - buffer[sz] = '\0'; - return strndup(buffer, sz + 1); + switch(errno) + + { + case EACCES: + errno = ERROR_ACCESS_DENIED; + break; + case EFAULT: + errno = ERROR_INVALID_ADDRESS; + break; + case EINVAL: + errno = ERROR_INVALID_NAME; + break; + case EIO: + errno = ERROR_GEN_FAILURE; + break; + case ELOOP: + errno = ERROR_STOPPED_ON_SYMLINK; + break; + case ENAMETOOLONG: + errno = ERROR_BAD_PATH_NAME; + break; + case ENOENT: + errno = ERROR_FILE_NOT_FOUND; + break; + case ENOMEM: + errno = ERROR_OUTOFMEMORY; + break; + case ENOTDIR: + errno = ERROR_BAD_PATH_NAME; + break; + default: + errno = ERROR_INVALID_FUNCTION; + } + return NULL; + } + + buffer[sz] = '\0'; + return strndup(buffer, sz + 1); + } + +/*else realpath returned a valid resolved path*/ + else + { + if (sizeof(realPath) == -1) + { + switch(errno) + { + case EACCES: + errno = ERROR_ACCESS_DENIED; + break; + case EFAULT: + errno = ERROR_INVALID_ADDRESS; + break; + case EINVAL: + errno = ERROR_INVALID_NAME; + break; + case EIO: + errno = ERROR_GEN_FAILURE; + break; + case ELOOP: + errno = ERROR_STOPPED_ON_SYMLINK; + break; + case ENAMETOOLONG: + errno = ERROR_BAD_PATH_NAME; + break; + case ENOENT: + errno = ERROR_FILE_NOT_FOUND; + break; + case ENOMEM: + errno = ERROR_OUTOFMEMORY; + break; + case ENOTDIR: + errno = ERROR_BAD_PATH_NAME; + break; + default: + errno = ERROR_INVALID_FUNCTION; + } + return NULL; + } + + return strndup(realPath, strlen(realPath) + 1 ); + } } From 64496d7bcc64159a9f001c48f4cff2fa46e8f762 Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Thu, 7 Jul 2016 12:12:16 -0700 Subject: [PATCH 02/34] adding check for symlink to followsymlink --- src/libpsl-native/src/followsymlink.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/libpsl-native/src/followsymlink.cpp b/src/libpsl-native/src/followsymlink.cpp index eac83f0d4..d4308f2df 100644 --- a/src/libpsl-native/src/followsymlink.cpp +++ b/src/libpsl-native/src/followsymlink.cpp @@ -7,7 +7,7 @@ #include #include #include "followsymlink.h" - +#include "issymlink.h" //! @brief Followsymlink determines target path of a sym link //! //! Followsymlink @@ -48,16 +48,14 @@ char* FollowSymLink(const char* fileName) char actualpath[PATH_MAX+1]; char* realPath = realpath(fileName, actualpath); - /* if realPath does not return a value, continue with previous implementation of symlink*/ - if (realPath == NULL) + if (IsSymLink(fileName)) { char buffer[PATH_MAX]; - ssize_t sz = readlink(fileName, buffer, PATH_MAX); + ssize_t sz = readlink(fileName, buffer, PATH_MAX); if (sz == -1) { - switch(errno) { From 2a6b32ce5b342becee227b724efd2719ddb58dd1 Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Thu, 7 Jul 2016 14:54:47 -0700 Subject: [PATCH 03/34] Refactored followsymlink --- src/libpsl-native/src/followsymlink.cpp | 35 +++++++++++++++++-------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/libpsl-native/src/followsymlink.cpp b/src/libpsl-native/src/followsymlink.cpp index d4308f2df..24a544664 100644 --- a/src/libpsl-native/src/followsymlink.cpp +++ b/src/libpsl-native/src/followsymlink.cpp @@ -8,6 +8,7 @@ #include #include "followsymlink.h" #include "issymlink.h" + //! @brief Followsymlink determines target path of a sym link //! //! Followsymlink @@ -38,26 +39,30 @@ char* FollowSymLink(const char* fileName) { errno = 0; - // Check parameters + // if filename is null, return null value if (!fileName) { errno = ERROR_INVALID_PARAMETER; return NULL; } - char actualpath[PATH_MAX+1]; - char* realPath = realpath(fileName, actualpath); - - /* if realPath does not return a value, continue with previous implementation of symlink*/ - if (IsSymLink(fileName)) + //if lstat in IsSymLink returns -1, path does not exist + if (IsSymLink(fileName) == -1) { + printf("-1 %s", fileName); + return NULL; + } + + /*If the path is a symlink, the function return the absolute filepath if valid. if not valid, return null*/ + if (IsSymLink(fileName) == 1) + { + printf("readlink %s", fileName); char buffer[PATH_MAX]; ssize_t sz = readlink(fileName, buffer, PATH_MAX); if (sz == -1) { switch(errno) - { case EACCES: errno = ERROR_ACCESS_DENIED; @@ -89,16 +94,23 @@ char* FollowSymLink(const char* fileName) default: errno = ERROR_INVALID_FUNCTION; } + return NULL; } buffer[sz] = '\0'; return strndup(buffer, sz + 1); - } - -/*else realpath returned a valid resolved path*/ + + } + + /*else the path is not a symlink - but attempt to resolve*/ else { + printf("realpath %s", fileName); + //Attempt to resolve with the absolute filepath + char actualpath[PATH_MAX+1]; + char* realPath = realpath(fileName, actualpath); + if (sizeof(realPath) == -1) { switch(errno) @@ -133,9 +145,10 @@ char* FollowSymLink(const char* fileName) default: errno = ERROR_INVALID_FUNCTION; } + return NULL; } - return strndup(realPath, strlen(realPath) + 1 ); + return strndup(realPath, strlen(realPath) + 1 ); } } From 0dc248b6f929781deb31e0834c4dd16ac3da1be5 Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Thu, 7 Jul 2016 15:02:23 -0700 Subject: [PATCH 04/34] changes to logic in followsymlink --- src/libpsl-native/src/followsymlink.cpp | 103 ++++++++++++------------ 1 file changed, 50 insertions(+), 53 deletions(-) diff --git a/src/libpsl-native/src/followsymlink.cpp b/src/libpsl-native/src/followsymlink.cpp index 24a544664..1540037eb 100644 --- a/src/libpsl-native/src/followsymlink.cpp +++ b/src/libpsl-native/src/followsymlink.cpp @@ -49,64 +49,13 @@ char* FollowSymLink(const char* fileName) //if lstat in IsSymLink returns -1, path does not exist if (IsSymLink(fileName) == -1) { - printf("-1 %s", fileName); return NULL; } /*If the path is a symlink, the function return the absolute filepath if valid. if not valid, return null*/ - if (IsSymLink(fileName) == 1) + if (IsSymLink(fileName) == 0) { - printf("readlink %s", fileName); - char buffer[PATH_MAX]; - ssize_t sz = readlink(fileName, buffer, PATH_MAX); - if (sz == -1) - { - switch(errno) - { - case EACCES: - errno = ERROR_ACCESS_DENIED; - break; - case EFAULT: - errno = ERROR_INVALID_ADDRESS; - break; - case EINVAL: - errno = ERROR_INVALID_NAME; - break; - case EIO: - errno = ERROR_GEN_FAILURE; - break; - case ELOOP: - errno = ERROR_STOPPED_ON_SYMLINK; - break; - case ENAMETOOLONG: - errno = ERROR_BAD_PATH_NAME; - break; - case ENOENT: - errno = ERROR_FILE_NOT_FOUND; - break; - case ENOMEM: - errno = ERROR_OUTOFMEMORY; - break; - case ENOTDIR: - errno = ERROR_BAD_PATH_NAME; - break; - default: - errno = ERROR_INVALID_FUNCTION; - } - - return NULL; - } - - buffer[sz] = '\0'; - return strndup(buffer, sz + 1); - - } - - /*else the path is not a symlink - but attempt to resolve*/ - else - { - printf("realpath %s", fileName); //Attempt to resolve with the absolute filepath char actualpath[PATH_MAX+1]; char* realPath = realpath(fileName, actualpath); @@ -150,5 +99,53 @@ char* FollowSymLink(const char* fileName) } return strndup(realPath, strlen(realPath) + 1 ); - } + + } + + /*else the path is not a symlink - but attempt to resolve*/ + else + { + char buffer[PATH_MAX]; + ssize_t sz = readlink(fileName, buffer, PATH_MAX); + + if (sz == -1) + { + switch(errno) + { + case EACCES: + errno = ERROR_ACCESS_DENIED; + break; + case EFAULT: + errno = ERROR_INVALID_ADDRESS; + break; + case EINVAL: + errno = ERROR_INVALID_NAME; + case EIO: + errno = ERROR_GEN_FAILURE; + break; + case ELOOP: + errno = ERROR_STOPPED_ON_SYMLINK; + break; + case ENAMETOOLONG: + errno = ERROR_BAD_PATH_NAME; + break; + case ENOENT: + errno = ERROR_FILE_NOT_FOUND; + break; + case ENOMEM: + errno = ERROR_OUTOFMEMORY; + break; + case ENOTDIR: + errno = ERROR_BAD_PATH_NAME; + break; + default: + errno = ERROR_INVALID_FUNCTION; + } + + return NULL; + } + + buffer[sz] = '\0'; + return strndup(buffer, sz + 1); +} } From e8386bd4b9e13c493e76f2493705f9ea0feea881 Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Thu, 7 Jul 2016 15:46:58 -0700 Subject: [PATCH 05/34] changing logic around realpath in followsymlink --- src/libpsl-native/src/followsymlink.cpp | 165 +++++++++++++----------- 1 file changed, 87 insertions(+), 78 deletions(-) diff --git a/src/libpsl-native/src/followsymlink.cpp b/src/libpsl-native/src/followsymlink.cpp index 1540037eb..6a77bcc52 100644 --- a/src/libpsl-native/src/followsymlink.cpp +++ b/src/libpsl-native/src/followsymlink.cpp @@ -53,99 +53,108 @@ char* FollowSymLink(const char* fileName) } /*If the path is a symlink, the function return the absolute filepath if valid. if not valid, return null*/ - if (IsSymLink(fileName) == 0) + if (IsSymLink(fileName)) { - //Attempt to resolve with the absolute filepath char actualpath[PATH_MAX+1]; char* realPath = realpath(fileName, actualpath); - - if (sizeof(realPath) == -1) - { - switch(errno) + + //if realPath is null, go onto the readlink implementation + if (realPath == NULL) + { + char buffer[PATH_MAX]; + ssize_t sz = readlink(fileName, buffer, PATH_MAX); + + if (sz == -1) { - case EACCES: - errno = ERROR_ACCESS_DENIED; - break; - case EFAULT: - errno = ERROR_INVALID_ADDRESS; - break; - case EINVAL: - errno = ERROR_INVALID_NAME; - break; - case EIO: - errno = ERROR_GEN_FAILURE; - break; - case ELOOP: - errno = ERROR_STOPPED_ON_SYMLINK; - break; - case ENAMETOOLONG: - errno = ERROR_BAD_PATH_NAME; - break; - case ENOENT: - errno = ERROR_FILE_NOT_FOUND; - break; - case ENOMEM: - errno = ERROR_OUTOFMEMORY; - break; - case ENOTDIR: - errno = ERROR_BAD_PATH_NAME; - break; - default: - errno = ERROR_INVALID_FUNCTION; + switch(errno) + { + case EACCES: + errno = ERROR_ACCESS_DENIED; + break; + case EFAULT: + errno = ERROR_INVALID_ADDRESS; /*If the path is a symlink, the function return the absolute filepath if valid. if not valid, return null*/ + break; + case EINVAL: + errno = ERROR_INVALID_NAME; + case EIO: + errno = ERROR_GEN_FAILURE; + break; + case ELOOP: + errno = ERROR_STOPPED_ON_SYMLINK; + break; + case ENAMETOOLONG: + errno = ERROR_BAD_PATH_NAME; + break; + case ENOENT: + errno = ERROR_FILE_NOT_FOUND; + break; + case ENOMEM: + errno = ERROR_OUTOFMEMORY; + break; + case ENOTDIR: + errno = ERROR_BAD_PATH_NAME; + break; + default: + errno = ERROR_INVALID_FUNCTION; } return NULL; + } + + buffer[sz] = '\0'; + return strndup(buffer, sz + 1); } - - return strndup(realPath, strlen(realPath) + 1 ); + else + { + return strndup(realPath, strlen(realPath) + 1 ); + } + } - } - - /*else the path is not a symlink - but attempt to resolve*/ - else + else { - char buffer[PATH_MAX]; - ssize_t sz = readlink(fileName, buffer, PATH_MAX); + char buffer[PATH_MAX]; + ssize_t sz = readlink(fileName, buffer, PATH_MAX); - if (sz == -1) - { - switch(errno) + if (sz == -1) { - case EACCES: - errno = ERROR_ACCESS_DENIED; - break; - case EFAULT: - errno = ERROR_INVALID_ADDRESS; - break; - case EINVAL: - errno = ERROR_INVALID_NAME; - case EIO: - errno = ERROR_GEN_FAILURE; - break; - case ELOOP: - errno = ERROR_STOPPED_ON_SYMLINK; - break; - case ENAMETOOLONG: - errno = ERROR_BAD_PATH_NAME; - break; - case ENOENT: - errno = ERROR_FILE_NOT_FOUND; - break; - case ENOMEM: - errno = ERROR_OUTOFMEMORY; - break; - case ENOTDIR: - errno = ERROR_BAD_PATH_NAME; - break; - default: - errno = ERROR_INVALID_FUNCTION; + switch(errno) + { + case EACCES: + errno = ERROR_ACCESS_DENIED; + break; + case EFAULT: + errno = ERROR_INVALID_ADDRESS; /*If the path is a symlink, the function return the absolute filepath if valid. if not valid, return null*/ + break; + case EINVAL: + errno = ERROR_INVALID_NAME; + case EIO: + errno = ERROR_GEN_FAILURE; + break; + case ELOOP: + errno = ERROR_STOPPED_ON_SYMLINK; + break; + case ENAMETOOLONG: + errno = ERROR_BAD_PATH_NAME; + break; + case ENOENT: + errno = ERROR_FILE_NOT_FOUND; + break; + case ENOMEM: + errno = ERROR_OUTOFMEMORY; + break; + case ENOTDIR: + errno = ERROR_BAD_PATH_NAME; + break; + default: + errno = ERROR_INVALID_FUNCTION; } return NULL; - } + } - buffer[sz] = '\0'; - return strndup(buffer, sz + 1); -} + buffer[sz] = '\0'; + return strndup(buffer, sz + 1); + + } } From 4a4df0f0801fea0b0d07e0b2d195096dcf066c0e Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Mon, 11 Jul 2016 20:56:34 -0700 Subject: [PATCH 06/34] Editing for OSX passing symbolic links --- .../New-Item.Tests.ps1 | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 index 570b42565..3955e788b 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 @@ -36,7 +36,7 @@ Describe "New-Item" { It "Should create a file without error" { New-Item -Name $testfile -Path $tmpDirectory -ItemType file - Test-Path $FullyQualifiedFile | Should Be $true + Test-Path $FullyQualifiedFile | Should Be $true $fileInfo = Get-ChildItem $FullyQualifiedFile $fileInfo.Target | Should Be $null @@ -105,14 +105,15 @@ Describe "New-Item" { } It "Should create a symbolic link of a file without error" { - New-Item -Name $testfile -Path $tmpDirectory -ItemType file - Test-Path $FullyQualifiedFile | Should Be $true + New-Item -Name $testfile -Path $tmpDirectory -ItemType file + Test-Path $FullyQualifiedFile | Should Be $true - New-Item -ItemType SymbolicLink -Target $FullyQualifiedFile -Name $testlink -Path $tmpDirectory - Test-Path $FullyQualifiedLink | Should Be $true + New-Item -ItemType SymbolicLink -Target $FullyQualifiedFile -Name $testlink -Path $tmpDirectory + Test-Path $FullyQualifiedLink | Should Be $true + $target = $FullyQualifiedFile $fileInfo = Get-ChildItem $FullyQualifiedLink - $fileInfo.Target | Should Be $FullyQualifiedFile + $fileInfo.Target | Should Be $target $fileInfo.LinkType | Should Be "SymbolicLink" } @@ -128,18 +129,18 @@ Describe "New-Item" { } It "Should create a symbolic link from directory without error" { - New-Item -Name $testFolder -Path $tmpDirectory -ItemType directory - Test-Path $FullyQualifiedFolder | Should Be $true + New-Item -Name $testFolder -Path $tmpDirectory -ItemType directory + Test-Path $FullyQualifiedFolder | Should Be $true - New-Item -ItemType SymbolicLink -Target $FullyQualifiedFolder -Name $testlink -Path $tmpDirectory - Test-Path $FullyQualifiedLink | Should Be $true + New-Item -ItemType SymbolicLink -Target $FullyQualifiedFolder -Name $testlink -Path $tmpDirectory + Test-Path $FullyQualifiedLink | Should Be $true $fileInfo = Get-ChildItem $FullyQualifiedLink $fileInfo.Target | Should Be $FullyQualifiedFolder $fileInfo.LinkType | Should Be "SymbolicLink" - # Remove the link explicitly to avoid broken symlink issue - Remove-Item $FullyQualifiedLink -Force + # Remove the link explicitly to avoid broken symlink issue + Remove-Item $FullyQualifiedLink -Force } It "Should create a hard link of a file without error" { @@ -154,6 +155,4 @@ Describe "New-Item" { $fileInfo.LinkType | Should Be "HardLink" } - - } From 7c6a0ce2f1ce7af6533af058b68ada2c9329d9f9 Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Mon, 11 Jul 2016 22:22:45 -0700 Subject: [PATCH 07/34] New-Item tests to use Should Match --- .../Microsoft.PowerShell.Management/New-Item.Tests.ps1 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 index 3955e788b..2b51c50f4 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 @@ -111,9 +111,8 @@ Describe "New-Item" { New-Item -ItemType SymbolicLink -Target $FullyQualifiedFile -Name $testlink -Path $tmpDirectory Test-Path $FullyQualifiedLink | Should Be $true - $target = $FullyQualifiedFile $fileInfo = Get-ChildItem $FullyQualifiedLink - $fileInfo.Target | Should Be $target + $fileInfo.Target | Should Match $FullyQualifiedFile $fileInfo.LinkType | Should Be "SymbolicLink" } @@ -136,7 +135,7 @@ Describe "New-Item" { Test-Path $FullyQualifiedLink | Should Be $true $fileInfo = Get-ChildItem $FullyQualifiedLink - $fileInfo.Target | Should Be $FullyQualifiedFolder + $fileInfo.Target | Should Match $FullyQualifiedFolder $fileInfo.LinkType | Should Be "SymbolicLink" # Remove the link explicitly to avoid broken symlink issue From 05b350961cd29aec597c156298bd378cdc0e91ba Mon Sep 17 00:00:00 2001 From: "Raghu Shantha [MSFT]" Date: Tue, 12 Jul 2016 15:37:40 -0700 Subject: [PATCH 08/34] Add ability to perform SxS install in MSI package manifest --- build.psm1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/build.psm1 b/build.psm1 index e868767f3..41021dde6 100644 --- a/build.psm1 +++ b/build.psm1 @@ -556,7 +556,10 @@ Built upon .NET Core, it is also a C# REPL. Write-Verbose "Packaging $Source" if ($IsWindows) { - $msiPackagePath = New-MSIPackage -ProductSourcePath $Source -ProductVersion $Version -AssetsPath "$PSScriptRoot\Assets" + + # Product Guid needs to be unique for every PowerShell version to allow SxS install + $productGuid = [guid]::NewGuid() + $msiPackagePath = New-MSIPackage -ProductSourcePath $Source -ProductVersion $Version -AssetsPath "$PSScriptRoot\Assets" -ProductGuid $productGuid $appxPackagePath = New-AppxPackage -PackageVersion $Version -SourcePath $Source -AssetsPath "$PSScriptRoot\Assets" $packages = @($msiPackagePath, $appxPackagePath) From a4146d258639aaedf849b458feba8a44a6ff89e3 Mon Sep 17 00:00:00 2001 From: joeyaiello Date: Tue, 12 Jul 2016 15:51:03 -0700 Subject: [PATCH 09/34] add value to PSGet demo --- demos/1-PowerShellGet.ps1 | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/demos/1-PowerShellGet.ps1 b/demos/1-PowerShellGet.ps1 index 8a3a70495..769fa6f44 100644 --- a/demos/1-PowerShellGet.ps1 +++ b/demos/1-PowerShellGet.ps1 @@ -4,11 +4,13 @@ Get-PackageProvider #(should show 2 providers - NuGet,PowerShellGet) ## Using PowerShellGet find and install other demos +# Value: equivalent of pypi +# Look for all the modules we'll be demoing today +Find-Module -Tag 'Open PowerShell','Demos' +# Pipe this to Install-Module to install them Find-Module -Tag 'Open PowerShell','Demos' | Install-Module -Verbose Get-Module - -# Find and Download node.js from nuget.org -Find-Package -Name node.js -ProviderName NuGet -Verbose -Source http://nuget.org/api/v2 +# Note that ScriptAnalyzer gets installed because the VSCode demo is dependent upon it # Register trusted endpoints Register-PackageSource -Name NuGet -Location http://nuget.org/api/v2 -Trusted -ProviderName NuGet From 90defe681dcfd6fd63b80571a859cc2fa93ed644 Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Tue, 12 Jul 2016 15:58:21 -0700 Subject: [PATCH 10/34] Changing childitem to cope with regex --- .../Microsoft.PowerShell.Management/New-Item.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 index 2b51c50f4..593f892ad 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/New-Item.Tests.ps1 @@ -112,7 +112,7 @@ Describe "New-Item" { Test-Path $FullyQualifiedLink | Should Be $true $fileInfo = Get-ChildItem $FullyQualifiedLink - $fileInfo.Target | Should Match $FullyQualifiedFile + $fileInfo.Target | Should Match ([regex]::Escape($FullyQualifiedFile)) $fileInfo.LinkType | Should Be "SymbolicLink" } @@ -135,7 +135,7 @@ Describe "New-Item" { Test-Path $FullyQualifiedLink | Should Be $true $fileInfo = Get-ChildItem $FullyQualifiedLink - $fileInfo.Target | Should Match $FullyQualifiedFolder + $fileInfo.Target | Should Match ([regex]::Escape($FullyQualifiedFolder)) $fileInfo.LinkType | Should Be "SymbolicLink" # Remove the link explicitly to avoid broken symlink issue From 0c38f29d68ecb606223232d632ce99c09434c519 Mon Sep 17 00:00:00 2001 From: joeyaiello Date: Tue, 12 Jul 2016 16:00:13 -0700 Subject: [PATCH 11/34] add comment-level demos We've signed off on these demos conceptually. The next step is for them to be implemented and all blocking engineering work needs to be enumerated and solved. --- demos/apache.ps1 | 14 ++++++++++++++ demos/crontab.ps1 | 9 +++++++++ demos/dsc.ps1 | 11 +++++++++++ demos/json.ps1 | 11 +++++++++++ demos/remoting.ps1 | 2 ++ demos/rest.ps1 | 7 +++++++ demos/xml.ps1 | 42 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 96 insertions(+) create mode 100644 demos/apache.ps1 create mode 100644 demos/crontab.ps1 create mode 100644 demos/dsc.ps1 create mode 100644 demos/json.ps1 create mode 100644 demos/remoting.ps1 create mode 100644 demos/rest.ps1 create mode 100644 demos/xml.ps1 diff --git a/demos/apache.ps1 b/demos/apache.ps1 new file mode 100644 index 000000000..e9f3cd2bc --- /dev/null +++ b/demos/apache.ps1 @@ -0,0 +1,14 @@ +# Get-ApacheVHosts +# Enumerate configured virtual hosts (web sites) on an apache server + +# New-ApacheVHost +# Given defined parameters, create a $ServerName.conf virtual host config file at $VHostsDirectory + +# Get-ApacheModules +# List installed Apache Modules + +# Demo execution: +# Run Get-ApacheVHosts and enumerate configured virtual hosts +# Run Get-ApacheModules to show installed Apache modules +# Run New-ApacheVHost to create a new virtual host and restart Apache +# Run Get-ApacheVhosts to show the newly created virtual host diff --git a/demos/crontab.ps1 b/demos/crontab.ps1 new file mode 100644 index 000000000..d90e2dbdb --- /dev/null +++ b/demos/crontab.ps1 @@ -0,0 +1,9 @@ +# Use the crontab module available at ./modules/CronTab + +# Get the existing cron jobs + +# Sort them by data + +# Create a new one using a DateTime object + +# Show in bash that the new cron job exists diff --git a/demos/dsc.ps1 b/demos/dsc.ps1 new file mode 100644 index 000000000..c1fdd6903 --- /dev/null +++ b/demos/dsc.ps1 @@ -0,0 +1,11 @@ +# DSC MOF Compilation +# DSC Configuration() script that: +# Defines base configuration users, groups, settings +# Uses PS function to set package configuration (ensure=Present) for an array of packages +# Probes for the existence of a package (Apache or MySQL) and conditionally configures the workload. I.e., if Apache is installed, configure Apache settings + +# Demo execution: +# Show the .ps1 +# Run the .ps1 to generate a MOF +# Apply the MOF locally with Start-DSCConfiguration +# Show the newly configured state \ No newline at end of file diff --git a/demos/json.ps1 b/demos/json.ps1 new file mode 100644 index 000000000..4804ffc69 --- /dev/null +++ b/demos/json.ps1 @@ -0,0 +1,11 @@ +# Get-Mongostat +# Get mongodb statistics with parsed JSON + +# journalctl is a native tool to emit SystemD's journal, which includes diagnostic info pertaining to SystemD services +# Show-JournalCtl +# Given journalctl parameters, run journalctl with a -o json to emit JSON, e.g. journalctl $Parameters -o json --no-pager +# Return structured data from Journal + +# Demo execution +# Show journatcl example +# Using Where & Sort, filter journal output to drill into a specific problem (e.g. service failed to start) \ No newline at end of file diff --git a/demos/remoting.ps1 b/demos/remoting.ps1 new file mode 100644 index 000000000..fe909db1b --- /dev/null +++ b/demos/remoting.ps1 @@ -0,0 +1,2 @@ +# Show PSRP working over OpenSSH +# Includes Windows -> Linux and Linux -> Windows \ No newline at end of file diff --git a/demos/rest.ps1 b/demos/rest.ps1 new file mode 100644 index 000000000..a7b8c799f --- /dev/null +++ b/demos/rest.ps1 @@ -0,0 +1,7 @@ +# Get all repositories from the GitHub API, sort by most pull requests, most stars, most forks, etc. + +# Get repo info (json) from the github api +# Convert json to PS object +# Set the private status to false +# Convert back to json +# Push the new json back to the github api \ No newline at end of file diff --git a/demos/xml.ps1 b/demos/xml.ps1 new file mode 100644 index 000000000..151ee628f --- /dev/null +++ b/demos/xml.ps1 @@ -0,0 +1,42 @@ +Dev: XML legacy [ETS?] +What are some xml workloads we can simplify with XML parsing? +Maybe cracking open some repository metadata? +JEE servers use XML pretty extensively for configuration. Perhaps a script to adjust JEE server settings? Obviously, parsing XML in bash is fraught with danger... + +PS /etc/tomcat> $tomcatserverfile="/usr/share/tomcat/conf/server.xml" +PS /etc/tomcat> [xml]$tomcatconfig=Get-Content $tomcatserverfile +PS /etc/tomcat> $tomcatconfig.Server + + +port : 8005 +shutdown : SHUTDOWN +#comment : { Security listener. Documentation at /docs/config/listeners.html + + , APR library loader. Documentation at /docs/apr.html , Initialize Jasper prior to webapps are loaded. Documentation at + /docs/jasper-howto.html , Prevent memory leaks due to use of particular java/javax APIs...} +Listener : {Listener, Listener, Listener, Listener...} +GlobalNamingResources : GlobalNamingResources +Service : Service + + + + +PS /etc/tomcat> $tomcatconfig.Server.port +8005 +PS /etc/tomcat> $tomcatconfig.Server.Listener + +className SSLEngine +--------- --------- +org.apache.catalina.core.AprLifecycleListener on +org.apache.catalina.core.JasperListener +org.apache.catalina.core.JreMemoryLeakPreventionListener +org.apache.catalina.mbeans.GlobalResourcesLifecycleListener +org.apache.catalina.core.ThreadLocalLeakPreventionListener + + +PS /etc/tomcat> $tomcatconfig.Server.Service + +name #comment +---- -------- +Catalina {The connectors can use a shared executor, you can define one or more named thread pools, ... + From 73dc2fd4d48bb2002b70f38e6041f81f1307cd87 Mon Sep 17 00:00:00 2001 From: joeyaiello Date: Tue, 12 Jul 2016 16:54:08 -0700 Subject: [PATCH 12/34] add Samples folder --- .../ApacheHTTPServer/ApacheVirtualHosts.psm1 | 173 ++++++++++++++++ samples/modules/CronTab/CronTab.ps1xml | 69 +++++++ samples/modules/CronTab/CronTab.psd1 | Bin 0 -> 3520 bytes samples/modules/CronTab/CronTab.psm1 | 189 ++++++++++++++++++ samples/modules/README.md | 1 + 5 files changed, 432 insertions(+) create mode 100644 samples/modules/ApacheHTTPServer/ApacheVirtualHosts.psm1 create mode 100644 samples/modules/CronTab/CronTab.ps1xml create mode 100644 samples/modules/CronTab/CronTab.psd1 create mode 100644 samples/modules/CronTab/CronTab.psm1 create mode 100644 samples/modules/README.md diff --git a/samples/modules/ApacheHTTPServer/ApacheVirtualHosts.psm1 b/samples/modules/ApacheHTTPServer/ApacheVirtualHosts.psm1 new file mode 100644 index 000000000..94710c813 --- /dev/null +++ b/samples/modules/ApacheHTTPServer/ApacheVirtualHosts.psm1 @@ -0,0 +1,173 @@ +$moduleRoot = Split-Path -Path $MyInvocation.MyCommand.Path + + +Function New-ApacheVHost { + [CmdletBinding()] + param ( + [parameter (Mandatory = $true)][string]$ServerName, + [string]$VHostsDirectory, + [parameter (Mandatory = $true)][string]$DocumentRoot, + [parameter (Mandatory = $true)][string]$VirtualHostIPAddress, + [string[]]$ServerAliases, + [parameter (Mandatory = $true)][string]$VirtualHostPort, + [string]$ServerAdmin, + [string]$CustomLogPath, + [string]$ErrorLogPath + ) + + if ($VHostsDirectory -notlike "*/*"){ + #Try to find default + if (Test-Path "/etc/httpd/conf.d"){ + $VHostsDirectory = "/etc/httpd/conf.d/" + } + if (Test-Path "/etc/apache2/sites-enabled"){ + $VHostsDirectory = "/etc/apache2/sites-enabled" + } + } + + if (!(Test-Path $VHostsDirectory)){ + Write-Error "Specified virtual hosts directory does not exist: $VHostsDirectory" + exit 1 + } + + $vHostDef = ".*:[0-9]*)(?.*)\((?.*)\)" + $VHostProps = $matches + $vhobject |Add-Member -type NoteProperty -name "ServerName" -value $matches.ServerName.Trim() + $vhobject |Add-Member -type NoteProperty -name "ListenAddress" -value $matches.Listen.Trim() + $vhobject |Add-Member -type NoteProperty -name "ConfFile" -value $matches.ConfFile.Trim() + $Vhosts += $vhobject + } + + } + + Return $Vhosts + +} + +Function Restart-ApacheHTTPServer{ + [CmdletBinding()] + Param( + [boolean]$Graceful + ) + + if ($Graceful -eq $null){$Graceful = $fase} + + $cmd = $null + if (Test-Path "/usr/sbin/apache2ctl"){ + $cmd = "/usr/sbin/apache2ctl" + }elseif(Test-Path "/usr/sbin/httpd"){ + $cmd = "/usr/sbin/httpd" + } + + if ($cmd -eq $null){ + Write-Error "Unable to find httpd/apache2ctl command" + exit -1 + } + + if ($Graceful){ + $cmd = $cmd += " -k graceful" + }else{ + $cmd = $cmd += " -k restart" + } + + Invoke-Expression $Cmd +} + +Function Get-ApacheModules{ + $cmd = $null + if (Test-Path "/usr/sbin/apache2ctl"){ + $cmd = "/usr/sbin/apache2ctl" + }elseif(Test-Path "/usr/sbin/httpd"){ + $cmd = "/usr/sbin/httpd" + } + + if ($cmd -eq $null){ + Write-Error "Unable to find httpd/apache2ctl command" + exit -1 + } + + $cmd = $cmd += " -M |grep -v Loaded" + + $ApacheModules = @() + function New-ModuleObj { + New-Object PSObject -Property @{} + } + + + $Results = Invoke-Expression $cmd + Foreach ($mod in $Results){ + $modObj = New-ModuleObj + $modObj | Add-Member -type NoteProperty -name "Module" -value $mod.trim() + $ApacheModules += $modObj + } + + Return $ApacheModules + +} diff --git a/samples/modules/CronTab/CronTab.ps1xml b/samples/modules/CronTab/CronTab.ps1xml new file mode 100644 index 000000000..1d1f1f90d --- /dev/null +++ b/samples/modules/CronTab/CronTab.ps1xml @@ -0,0 +1,69 @@ + + + + + + Default + + CronJob + + + + + + 10 + Left + + + + 10 + Left + + + + 10 + Left + + + + 10 + Left + + + + 10 + Left + + + + Left + + + + + + + Minute + + + Hour + + + DayOfMonth + + + Month + + + DayOfWeek + + + Command + + + + + + + + \ No newline at end of file diff --git a/samples/modules/CronTab/CronTab.psd1 b/samples/modules/CronTab/CronTab.psd1 new file mode 100644 index 0000000000000000000000000000000000000000..23d9d95f051b15feb3b1c0ca41ca273156cd41b4 GIT binary patch literal 3520 zcmbuBTTc{05QXd6#Q!jf4`L!J#_NNLL{KqE2p8XXVHZZ(nRRy&jp47W=j)>F>Dd|7 z7_!WztE*0(sy;P8f4sEscGGTz-z_U_-;Qi%rA=+2r-{{eZ0B}nBR$nxDQu|K$_Bds zwq!BZn%fR+uD@E|O4%;7|5(-+dRo}2?)YEIf*1q&D{U;Bx!&IkSoXBH*3*0aPY7Pf z{{@#cv~wu?srF55O)JezKFHrpD`l`PWPdKJVR%a};}(h(#}PCmw|zN|2a3I> zAJ+%1==+K`)gPS2?03cPrm}$pM>P+KU&(4BTX-sk5C*$!3nT7JTT!fSS(4FL-QUUb z-m(o@ohx(ad?D{V!|`WbNAgeQN+B8PEiuR8O|vG64LE&chvt4g^T*2fnUD_cxjohH z)!^%){H@v}t@zzQ8I5$mD(f|M0#8S}rv}e-_q4^?y3m2a`|P?3fho6kTWDs7T+Y{t^5lfk@qRw= zLNyBEb|n4v07Ki8Gq9rk@5sl)!1)v1Epu{DZ`aiYXSyaVoW+?i&^I%KlVkODex^3U zdQymZwoK#}Nqx^61pwY+=xp$Wr3^j=>~&v!rYKUD-G=Xw=+bcGp+ zbI#0(>~aOkb9^1xXOGvy6%BS)#0Oflrc*ajM0oboMMoEUQ)?He%C+uPca`3k;dE0| z7;yyswQa_qOXF&30h}Gl8=4wBEzcoG*3O92pXz}R$30ReQKP6EaVB9ksdG|-yIP5y zk}ZZHS}PnD`=PiaF*|YN$QGz`o!Ssr-bQ44A(jL zz#2O0ord?BJiFFKwFfI+Smg@3<&N!U$mhcAR(E@iHZvx@H+K}_Lh2VQ^-i&dI#GA^ z+Y4AZ320`Q#T%_GI@X#qj?O6M!xaSnxEm-VuP-yjd&ZyXIaZP0X6!_eD{-`z8pAV%^8a4 zc1CC^yAvO29=hJ_3dM!2W{M0asG-+4=QN?Brp>RVLeKo=*!!%a^)2l<5VDi2zULbO zb!^V+Zf%IXx8uj_ZHKHAnb??K|D6qVwEA}UK?_Vj)99Ir&!&iovP9HK@h9gI8T^?GdR zm@`F>426*S)s508+DEOTu>H)ribuQu!--xpSNtsb+y;mDhJVvtH)r}$Ze98O7kp?L z-GkPQY)2;xuF%l*T@b(Z(=_R;XTLXB)#aLADiZbF6e~;-kD8y$Wlf1X+PAx@tVsp> l{ea+YU}CCo + [CmdletBinding(SupportsShouldProcess=$true,ConfirmImpact="High")] + param ( + [Alias("u")][Parameter(Mandatory=$false)][String] $UserName, + [Alias("j")][Parameter(Mandatory=$true,ValueFromPipeline=$true)][PSTypeName("CronJob")] $Job #TODO use CronJob type + ) + process { + + [string[]] $crontab = Get-CronTab -user $UserName + [string[]] $newcrontab = $null + $found = $false + + foreach ($line in $crontab) { + $cronjob = ConvertTo-CronJob -crontab $line + if ((Compare-object $cronjob.psobject.properties $Job.psobject.properties) -eq $null) { + $found = $true + } else { + $newcrontab += $line + } + } + + if (-not $found) { + $e = New-Object System.Exception -ArgumentList "Job not found" + throw $e + } + if ($pscmdlet.ShouldProcess($Job.Command,"Remove")) { + Import-CronTab -user $UserName -crontab $newcrontab + } + } +} + +function New-CronJob { +<# +.SYNOPSIS + Create a new cron job +.DESCRIPTION + Create a new job in the cron table. Date and time parameters can be specified + as ranges such as 10-30, as a list: 5,6,7, or combined 1-5,10-15. An asterisk + means 'first through last' (the entire allowed range). Step values can be used + with ranges or with an asterisk. Every 2 hours can be specified as either + 0-23/2 or */2. +.EXAMPLE + New-CronJob -Minute 10-30 -Hour 10-20/2 -DayOfMonth */2 -Command "/bin/bash -c 'echo hello' > ~/hello" +.RETURNVALUE + If successful, an object representing the cron job is returned +.PARAMETER UserName + Optional parameter to specify a specific user's cron table +.PARAMETER Minute + Valid values are 0 to 59. If not specified, defaults to *. +.PARAMETER Hour + Valid values are 0-23. If not specified, defaults to *. +.PARAMETER DayOfMonth + Valid values are 1-31. If not specified, defaults to *. +.PARAMETER Month + Valid values are 1-12. If not specified, defaults to *. +.PARAMETER DayOfWeek + Valid values are 0-7. 0 and 7 are both Sunday. If not specified, defaults to *. +.PARAMETER Command + Command to execute at the scheduled time and day. +#> + [CmdletBinding()] + param ( + [Alias("u")][Parameter(Mandatory=$false)][String] $UserName, + [Alias("mi")][Parameter(Position=1)][String[]] $Minute = "*", + [Alias("h")][Parameter(Position=2)][String[]] $Hour = "*", + [Alias("dm")][Parameter(Position=3)][String[]] $DayOfMonth = "*", + [Alias("mo")][Parameter(Position=4)][String[]] $Month = "*", + [Alias("dw")][Parameter(Position=5)][String[]] $DayOfWeek = "*", + [Alias("c")][Parameter(Mandatory=$true,Position=6)][String] $Command + ) + process { + # TODO: validate parameters, note that different versions of crontab support different capabilities + $line = "{0} {1} {2} {3} {4} {5}" -f [String]::Join(",",$Minute), [String]::Join(",",$Hour), + [String]::Join(",",$DayOfMonth), [String]::Join(",",$Month), [String]::Join(",",$DayOfWeek), $Command + [string[]] $crontab = Get-CronTab -user $UserName + $crontab += $line + Import-CronTab -User $UserName -crontab $crontab + ConvertTo-CronJob -crontab $line + } +} + +function Get-CronJob { +<# +.SYNOPSIS + Returns the current cron jobs from the cron table +.DESCRIPTION + Returns the current cron jobs from the cron table +.EXAMPLE + Get-CronJob -UserName Steve +.RETURNVALUE + CronJob objects +.PARAMETER UserName + Optional parameter to specify a specific user's cron table +#> + [CmdletBinding()] + [OutputType([PSObject])] + param ( + [Alias("u")][Parameter(Mandatory=$false)][String] $UserName + ) + process { + $crontab = Get-CronTab -user $UserName + ForEach ($line in $crontab) { + if ($line.Trim().Length -gt 0) + { + ConvertTo-CronJob -crontab $line + } + } + } +} diff --git a/samples/modules/README.md b/samples/modules/README.md new file mode 100644 index 000000000..9a7f330f3 --- /dev/null +++ b/samples/modules/README.md @@ -0,0 +1 @@ +# Samples From 46199198725c79824c31a6394803f1b20b0cb7d3 Mon Sep 17 00:00:00 2001 From: joeyaiello Date: Tue, 12 Jul 2016 17:09:55 -0700 Subject: [PATCH 13/34] move sample modules into demos --- .../ApacheHTTPServer/ApacheVirtualHosts.psm1 | 0 {samples => demos}/modules/CronTab/CronTab.ps1xml | 0 {samples => demos}/modules/CronTab/CronTab.psd1 | Bin {samples => demos}/modules/CronTab/CronTab.psm1 | 0 {samples => demos}/modules/README.md | 0 5 files changed, 0 insertions(+), 0 deletions(-) rename {samples => demos}/modules/ApacheHTTPServer/ApacheVirtualHosts.psm1 (100%) rename {samples => demos}/modules/CronTab/CronTab.ps1xml (100%) rename {samples => demos}/modules/CronTab/CronTab.psd1 (100%) rename {samples => demos}/modules/CronTab/CronTab.psm1 (100%) rename {samples => demos}/modules/README.md (100%) diff --git a/samples/modules/ApacheHTTPServer/ApacheVirtualHosts.psm1 b/demos/modules/ApacheHTTPServer/ApacheVirtualHosts.psm1 similarity index 100% rename from samples/modules/ApacheHTTPServer/ApacheVirtualHosts.psm1 rename to demos/modules/ApacheHTTPServer/ApacheVirtualHosts.psm1 diff --git a/samples/modules/CronTab/CronTab.ps1xml b/demos/modules/CronTab/CronTab.ps1xml similarity index 100% rename from samples/modules/CronTab/CronTab.ps1xml rename to demos/modules/CronTab/CronTab.ps1xml diff --git a/samples/modules/CronTab/CronTab.psd1 b/demos/modules/CronTab/CronTab.psd1 similarity index 100% rename from samples/modules/CronTab/CronTab.psd1 rename to demos/modules/CronTab/CronTab.psd1 diff --git a/samples/modules/CronTab/CronTab.psm1 b/demos/modules/CronTab/CronTab.psm1 similarity index 100% rename from samples/modules/CronTab/CronTab.psm1 rename to demos/modules/CronTab/CronTab.psm1 diff --git a/samples/modules/README.md b/demos/modules/README.md similarity index 100% rename from samples/modules/README.md rename to demos/modules/README.md From dd2f97c0aa9535dad986ab9e5aec73ee67638f9b Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 13:56:25 -0700 Subject: [PATCH 14/34] Enable caching of dotnet and NuGet packages on CI --- .travis.yml | 4 ++++ appveyor.yml | 3 +++ 2 files changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index 698b30efa..70c605a7c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,10 @@ matrix: - os: osx osx_image: xcode8 language: generic +cache: + directories: + - $HOME/.dotnet + - $HOME/.nuget addons: artifacts: s3_region: "us-west-2" diff --git a/appveyor.yml b/appveyor.yml index f743073eb..6ef542b11 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,8 @@ version: 0.5.0.{build} +cache: + - '%LocalAppData%\Microsoft\dotnet' + nuget: project_feed: true From 281c7cf17a7f7147d39929918b3fd27fc7a29aa8 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 13:56:39 -0700 Subject: [PATCH 15/34] Fix AppVeyor version --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 6ef542b11..477221cf7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 0.5.0.{build} +version: 0.6.0.{build} cache: - '%LocalAppData%\Microsoft\dotnet' From df271c48cc9a1e780e45d803c8c5b6e21170a032 Mon Sep 17 00:00:00 2001 From: Manikyam Bavandla Date: Thu, 7 Jul 2016 18:06:20 -0700 Subject: [PATCH 16/34] PowerShellGet changes for Linux --- .../Shared/PowerShellGet/PSGet.Resource.psd1 | Bin 78998 -> 78920 bytes .../Shared/PowerShellGet/PSModule.psm1 | 131 +++++++++++------- 2 files changed, 82 insertions(+), 49 deletions(-) diff --git a/src/Modules/Shared/PowerShellGet/PSGet.Resource.psd1 b/src/Modules/Shared/PowerShellGet/PSGet.Resource.psd1 index 6dfd08222a2a507b315dee74819f83970a894f7c..5fc17b600e3eaabd4f23e4fb60444702931817f7 100644 GIT binary patch delta 307 zcmbRClI6q;mWC~it2o#d7!n!s7*Zw&vWv4iGvqTAO=eV5-+qpRah2%wJI0K&CMzfk zO#UFtvwemfqYBsLs5YMM30jPHtn!Ww3JkssnGDGcML=!EK%B-I{3Q$-K#NL%ddh)x@pMNuM)Aom@?!kSK#h4otvO($N+uUNN>0D0 z%P7Hb#8Ati4pg58Ha~ZIp$?p6MNGjB?W#7&Edl>oF)yPK*_wd~ONH^gFtY zVv{%AVzI0QYARw#1v;b@Xe!9cOrQ-4Kz=0P(j~VT^-&yv2}Fh#3IJ`%n=8 delta 288 zcmX@{f@RuEmWC~it2ic`Nr_E1P~_YGg@bXG==3agMv=)MWO=rS$TO;NO`3 zd2-=t(aAlZ_@-abXJnhcMjPnPm=dnZK1-w~E3mLl4lop(9M#6Ny+xDJj&=Ga9mcZh z2V@zIU^;}Rcj+=#O%6~Ln=WR=D8QWwbe96qL-`CP(^d5tgD0~oLHuoI$SA}N0K#ui AW&i*H diff --git a/src/Modules/Shared/PowerShellGet/PSModule.psm1 b/src/Modules/Shared/PowerShellGet/PSModule.psm1 index 6f53550ef..c3d986e34 100644 --- a/src/Modules/Shared/PowerShellGet/PSModule.psm1 +++ b/src/Modules/Shared/PowerShellGet/PSModule.psm1 @@ -14,25 +14,40 @@ Microsoft.PowerShell.Core\Set-StrictMode -Version Latest # Check if this is nano server. [System.Runtime.Loader.AssemblyLoadContext] is only available on NanoServer $script:isNanoServer = $null -ne ('System.Runtime.Loader.AssemblyLoadContext' -as [Type]) -try + +if($IsWindows) { - $script:MyDocumentsFolderPath = [Environment]::GetFolderPath("MyDocuments") + $script:ProgramFilesPSPath = Microsoft.PowerShell.Management\Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell" } -catch +else { - $script:MyDocumentsFolderPath = $null + $script:ProgramFilesPSPath = $PSHome } -$script:ProgramFilesPSPath = Microsoft.PowerShell.Management\Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell" +if($IsWindows) +{ + try + { + $script:MyDocumentsFolderPath = [Environment]::GetFolderPath("MyDocuments") + } + catch + { + $script:MyDocumentsFolderPath = $null + } -$script:MyDocumentsPSPath = if($script:MyDocumentsFolderPath) - { - Microsoft.PowerShell.Management\Join-Path -Path $script:MyDocumentsFolderPath -ChildPath "WindowsPowerShell" - } - else - { - Microsoft.PowerShell.Management\Join-Path -Path $env:USERPROFILE -ChildPath "Documents\WindowsPowerShell" - } + $script:MyDocumentsPSPath = if($script:MyDocumentsFolderPath) + { + Microsoft.PowerShell.Management\Join-Path -Path $script:MyDocumentsFolderPath -ChildPath "WindowsPowerShell" + } + else + { + Microsoft.PowerShell.Management\Join-Path -Path $env:USERPROFILE -ChildPath "Documents\WindowsPowerShell" + } +} +else +{ + $script:MyDocumentsPSPath = Microsoft.PowerShell.Management\Join-Path -Path $HOME -ChildPath ".local/share/PowerShell" +} $script:ProgramFilesModulesPath = Microsoft.PowerShell.Management\Join-Path -Path $script:ProgramFilesPSPath -ChildPath "Modules" $script:MyDocumentsModulesPath = Microsoft.PowerShell.Management\Join-Path -Path $script:MyDocumentsPSPath -ChildPath "Modules" @@ -43,8 +58,18 @@ $script:MyDocumentsScriptsPath = Microsoft.PowerShell.Management\Join-Path -Path $script:TempPath = ([System.IO.DirectoryInfo]$env:TEMP).FullName $script:PSGetItemInfoFileName = "PSGetModuleInfo.xml" -$script:PSGetProgramDataPath = Microsoft.PowerShell.Management\Join-Path -Path $env:ProgramData -ChildPath 'Microsoft\Windows\PowerShell\PowerShellGet\' -$script:PSGetAppLocalPath = Microsoft.PowerShell.Management\Join-Path -Path $env:LOCALAPPDATA -ChildPath 'Microsoft\Windows\PowerShell\PowerShellGet\' + +if($IsWindows) +{ + $script:PSGetProgramDataPath = Microsoft.PowerShell.Management\Join-Path -Path $env:ProgramData -ChildPath 'Microsoft\Windows\PowerShell\PowerShellGet\' + $script:PSGetAppLocalPath = Microsoft.PowerShell.Management\Join-Path -Path $env:LOCALAPPDATA -ChildPath 'Microsoft\Windows\PowerShell\PowerShellGet\' +} +else +{ + $script:PSGetProgramDataPath = "$HOME/.config/powershell/PowerShellGet" #TODO: Get $env:ProgramData equivalent + $script:PSGetAppLocalPath = "$HOME/.config/powershell/PowerShellGet" +} + $script:PSGetModuleSourcesFilePath = Microsoft.PowerShell.Management\Join-Path -Path $script:PSGetAppLocalPath -ChildPath "PSRepositories.xml" $script:PSGetModuleSources = $null $script:PSGetInstalledModules = $null @@ -89,8 +114,8 @@ $script:NuGetProviderVersion = [Version]'2.8.5.201' $script:SupportsPSModulesFeatureName="supports-powershell-modules" $script:FastPackRefHastable = @{} -$script:NuGetBinaryProgramDataPath="$env:ProgramFiles\PackageManagement\ProviderAssemblies" -$script:NuGetBinaryLocalAppDataPath="$env:LOCALAPPDATA\PackageManagement\ProviderAssemblies" +$script:NuGetBinaryProgramDataPath=if($IsWindows) {"$env:ProgramFiles\PackageManagement\ProviderAssemblies"} +$script:NuGetBinaryLocalAppDataPath=if($IsWindows) {"$env:LOCALAPPDATA\PackageManagement\ProviderAssemblies"} # go fwlink for 'https://nuget.org/nuget.exe' $script:NuGetClientSourceURL = 'http://go.microsoft.com/fwlink/?LinkID=690216&clcid=0x409' $script:NuGetExeName = 'NuGet.exe' @@ -577,7 +602,7 @@ catch # Ignore the error and try adding the type below } -if(-not $script:TelemetryEnabled) +if(-not $script:TelemetryEnabled -and $IsWindows) { try { @@ -682,7 +707,7 @@ function Publish-Module Begin { - if($script:isNanoServer) { + if($script:isNanoServer -or $IsCore) { $message = $LocalizedData.PublishPSArtifactUnsupportedOnNano -f "Module" ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $message ` @@ -1808,10 +1833,6 @@ function Update-Module [PSCredential] $ProxyCredential, - [Parameter()] - [switch] - $SkipPublisherCheck, - [Parameter()] [Switch] $Force @@ -2485,7 +2506,7 @@ function Publish-Script Begin { - if($script:isNanoServer) { + if($script:isNanoServer -or $IsCore) { $message = $LocalizedData.PublishPSArtifactUnsupportedOnNano -f "Script" ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $message ` @@ -6129,7 +6150,7 @@ function Ping-Endpoint $results = @{} $WebProxy = $null - if($Proxy) + if($Proxy -and $IsWindows) { $ProxyNetworkCredential = $null if($ProxyCredential) @@ -6359,6 +6380,11 @@ function ValidateAndSet-PATHVariableIfUserAccepts $Request ) + if(-not $IsWindows) + { + return + } + Set-PSGetSettingsVariable # Check and add the scope path to PATH environment variable if USER accepts the prompt. @@ -7154,9 +7180,10 @@ function Install-NuGetClientBinaries $Force ) - if($script:NuGetProvider -and - (-not $BootstrapNuGetExe -or - ($script:NuGetExePath -and (Microsoft.PowerShell.Management\Test-Path -Path $script:NuGetExePath)))) + if(-not $IsWindows -or + ($script:NuGetProvider -and + (-not $BootstrapNuGetExe -or + ($script:NuGetExePath -and (Microsoft.PowerShell.Management\Test-Path -Path $script:NuGetExePath))))) { return } @@ -7256,7 +7283,7 @@ function Install-NuGetClientBinaries } # On Nano server we don't need NuGet.exe - if(-not $bootstrapNuGetProvider -and ($script:isNanoServer -or -not $BootstrapNuGetExe)) + if(-not $bootstrapNuGetProvider -and ($script:isNanoServer -or $IsCore -or -not $BootstrapNuGetExe)) { return } @@ -7313,7 +7340,7 @@ function Install-NuGetClientBinaries } } - if($BootstrapNuGetExe -and -not $script:isNanoServer) + if($BootstrapNuGetExe -and -not $script:isNanoServer -and -not $IsCore) { Write-Verbose -Message $LocalizedData.DownloadingNugetExe @@ -7397,10 +7424,19 @@ function Test-RunningAsElevated [OutputType([bool])] Param() - $wid=[System.Security.Principal.WindowsIdentity]::GetCurrent() - $prp=new-object System.Security.Principal.WindowsPrincipal($wid) - $adm=[System.Security.Principal.WindowsBuiltInRole]::Administrator - return $prp.IsInRole($adm) + if($IsWindows) + { + $wid=[System.Security.Principal.WindowsIdentity]::GetCurrent() + $prp=new-object System.Security.Principal.WindowsPrincipal($wid) + $adm=[System.Security.Principal.WindowsBuiltInRole]::Administrator + return $prp.IsInRole($adm) + } + elseif($IsLinux) + { + return ($env:SUDO_UID -eq '1000') + } + + return $false } function Get-EscapedString @@ -13638,6 +13674,10 @@ function Validate-ModuleAuthenticodeSignature [string] $InstallLocation, + [Parameter()] + [Switch] + $IsUpdateOperation, + [Parameter()] [Switch] $SkipPublisherCheck @@ -13652,21 +13692,13 @@ function Validate-ModuleAuthenticodeSignature } # Skip the publisher check when -SkipPublisherCheck is specified and - # previously-installed module is not signed by Microsoft - if($SkipPublisherCheck) + # it is not an update operation. + if(-not $IsUpdateOperation -and $SkipPublisherCheck) { - if($InstalledModuleDetails -and $InstalledModuleDetails.IsMicrosoftCertificate) - { - $Message = $LocalizedData.ProceedingWithPublisherCheckForMicrosoftModules -f ($CurrentModuleInfo.Name) - Write-Verbose -Message $message - } - else - { - $Message = $LocalizedData.SkippingPublisherCheck -f ($CurrentModuleInfo.Version, $CurrentModuleInfo.Name) - Write-Verbose -Message $message + $Message = $LocalizedData.SkippingPublisherCheck -f ($CurrentModuleInfo.Version, $CurrentModuleInfo.Name) + Write-Verbose -Message $message - return $true - } + return $true } # Validate the catalog signature for the current module being installed. @@ -13744,7 +13776,7 @@ function Validate-ModuleAuthenticodeSignature } else { - $Message = $LocalizedData.PublishersMismatch -f ($CurrentModuleAuthenticodePublisher, $CurrentModuleInfo.Name, $CurrentModuleInfo.Version, $InstalledModuleAuthenticodePublisher, $InstalledModuleInfo.Name, $InstalledModuleVersion) + $Message = $LocalizedData.PublishersMismatch -f ($InstalledModuleInfo.Name, $InstalledModuleVersion, $CurrentModuleInfo.Name, $CurrentModuleAuthenticodePublisher, $CurrentModuleInfo.Version) ThrowError -ExceptionName 'System.InvalidOperationException' ` -ExceptionMessage $message ` -ErrorId 'PublishersMismatch' ` @@ -14130,10 +14162,11 @@ function Test-ValidManifestModule -CallerPSCmdlet $PSCmdlet ` -ErrorCategory InvalidOperation } - else + elseif($IsWindows) { $ValidationResult = Validate-ModuleAuthenticodeSignature -CurrentModuleInfo $PSModuleInfo ` -InstallLocation $InstallLocation ` + -IsUpdateOperation:$IsUpdateOperation ` -SkipPublisherCheck:$SkipPublisherCheck if($ValidationResult) From 076ad7f8e9d8db34f85d1d46bcad461b56f01a71 Mon Sep 17 00:00:00 2001 From: Manikyam Bavandla Date: Fri, 8 Jul 2016 13:36:14 -0700 Subject: [PATCH 17/34] Added mapping for $env:TEMP path in Linux. Added mapping for $env:TEMP path in Linux. --- src/Modules/Shared/PowerShellGet/PSModule.psm1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Modules/Shared/PowerShellGet/PSModule.psm1 b/src/Modules/Shared/PowerShellGet/PSModule.psm1 index c3d986e34..ebf8b7b11 100644 --- a/src/Modules/Shared/PowerShellGet/PSModule.psm1 +++ b/src/Modules/Shared/PowerShellGet/PSModule.psm1 @@ -56,7 +56,7 @@ $script:ProgramFilesScriptsPath = Microsoft.PowerShell.Management\Join-Path -Pat $script:MyDocumentsScriptsPath = Microsoft.PowerShell.Management\Join-Path -Path $script:MyDocumentsPSPath -ChildPath "Scripts" -$script:TempPath = ([System.IO.DirectoryInfo]$env:TEMP).FullName +$script:TempPath = if($IsWindows){ ([System.IO.DirectoryInfo]$env:TEMP).FullName } else { '/tmp' } $script:PSGetItemInfoFileName = "PSGetModuleInfo.xml" if($IsWindows) @@ -4963,7 +4963,7 @@ function New-ScriptFileInfo $ScriptMetadataString += $ScriptCommentHelpInfoString $ScriptMetadataString += "Param()`r`n`r`n" - $tempScriptFilePath = Microsoft.PowerShell.Management\Join-Path -Path $env:TEMP -ChildPath "$(Get-Random).ps1" + $tempScriptFilePath = Microsoft.PowerShell.Management\Join-Path -Path $script:TempPath -ChildPath "$(Get-Random).ps1" try { @@ -5324,7 +5324,7 @@ function Update-ScriptFileInfo return } - $tempScriptFilePath = Microsoft.PowerShell.Management\Join-Path -Path $env:TEMP -ChildPath "$(Get-Random).ps1" + $tempScriptFilePath = Microsoft.PowerShell.Management\Join-Path -Path $script:TempPath -ChildPath "$(Get-Random).ps1" try { From 87db56d17f6d1ad20655f4b72251975974a221fd Mon Sep 17 00:00:00 2001 From: Manikyam Bavandla Date: Tue, 12 Jul 2016 14:07:42 -0700 Subject: [PATCH 18/34] Changes for Get-ChildItem, Select-Object, IsWindows, IsCore and IsLinux Changes for Get-ChildItem, Select-Object, IsWindows, IsCore and IsLinux --- .../Shared/PowerShellGet/PSModule.psm1 | 144 ++++++++++-------- 1 file changed, 78 insertions(+), 66 deletions(-) diff --git a/src/Modules/Shared/PowerShellGet/PSModule.psm1 b/src/Modules/Shared/PowerShellGet/PSModule.psm1 index ebf8b7b11..33f01dbcc 100644 --- a/src/Modules/Shared/PowerShellGet/PSModule.psm1 +++ b/src/Modules/Shared/PowerShellGet/PSModule.psm1 @@ -14,8 +14,11 @@ Microsoft.PowerShell.Core\Set-StrictMode -Version Latest # Check if this is nano server. [System.Runtime.Loader.AssemblyLoadContext] is only available on NanoServer $script:isNanoServer = $null -ne ('System.Runtime.Loader.AssemblyLoadContext' -as [Type]) +function IsWindows { $PSVarable = Get-Variable -Name IsWindows -ErrorAction Ignore; return (-not $PSVarable -or $PSVarable.Value) } +function IsLinux { $PSVarable = Get-Variable -Name IsLinux -ErrorAction Ignore; return ($PSVarable -and $PSVarable.Value) } +function IsCore { $PSVarable = Get-Variable -Name IsCore -ErrorAction Ignore; return ($PSVarable -and $PSVarable.Value) } -if($IsWindows) +if(IsWindows) { $script:ProgramFilesPSPath = Microsoft.PowerShell.Management\Join-Path -Path $env:ProgramFiles -ChildPath "WindowsPowerShell" } @@ -24,7 +27,7 @@ else $script:ProgramFilesPSPath = $PSHome } -if($IsWindows) +if(IsWindows) { try { @@ -46,7 +49,7 @@ if($IsWindows) } else { - $script:MyDocumentsPSPath = Microsoft.PowerShell.Management\Join-Path -Path $HOME -ChildPath ".local/share/PowerShell" + $script:MyDocumentsPSPath = Microsoft.PowerShell.Management\Join-Path -Path $HOME -ChildPath ".local/share/powershell" } $script:ProgramFilesModulesPath = Microsoft.PowerShell.Management\Join-Path -Path $script:ProgramFilesPSPath -ChildPath "Modules" @@ -56,18 +59,18 @@ $script:ProgramFilesScriptsPath = Microsoft.PowerShell.Management\Join-Path -Pat $script:MyDocumentsScriptsPath = Microsoft.PowerShell.Management\Join-Path -Path $script:MyDocumentsPSPath -ChildPath "Scripts" -$script:TempPath = if($IsWindows){ ([System.IO.DirectoryInfo]$env:TEMP).FullName } else { '/tmp' } +$script:TempPath = if(IsWindows){ ([System.IO.DirectoryInfo]$env:TEMP).FullName } else { '/tmp' } $script:PSGetItemInfoFileName = "PSGetModuleInfo.xml" -if($IsWindows) +if(IsWindows) { $script:PSGetProgramDataPath = Microsoft.PowerShell.Management\Join-Path -Path $env:ProgramData -ChildPath 'Microsoft\Windows\PowerShell\PowerShellGet\' $script:PSGetAppLocalPath = Microsoft.PowerShell.Management\Join-Path -Path $env:LOCALAPPDATA -ChildPath 'Microsoft\Windows\PowerShell\PowerShellGet\' } else { - $script:PSGetProgramDataPath = "$HOME/.config/powershell/PowerShellGet" #TODO: Get $env:ProgramData equivalent - $script:PSGetAppLocalPath = "$HOME/.config/powershell/PowerShellGet" + $script:PSGetProgramDataPath = "$HOME/.config/powershell/powershellget" #TODO: Get $env:ProgramData equivalent + $script:PSGetAppLocalPath = "$HOME/.config/powershell/powershellget" } $script:PSGetModuleSourcesFilePath = Microsoft.PowerShell.Management\Join-Path -Path $script:PSGetAppLocalPath -ChildPath "PSRepositories.xml" @@ -114,8 +117,8 @@ $script:NuGetProviderVersion = [Version]'2.8.5.201' $script:SupportsPSModulesFeatureName="supports-powershell-modules" $script:FastPackRefHastable = @{} -$script:NuGetBinaryProgramDataPath=if($IsWindows) {"$env:ProgramFiles\PackageManagement\ProviderAssemblies"} -$script:NuGetBinaryLocalAppDataPath=if($IsWindows) {"$env:LOCALAPPDATA\PackageManagement\ProviderAssemblies"} +$script:NuGetBinaryProgramDataPath=if(IsWindows) {"$env:ProgramFiles\PackageManagement\ProviderAssemblies"} +$script:NuGetBinaryLocalAppDataPath=if(IsWindows) {"$env:LOCALAPPDATA\PackageManagement\ProviderAssemblies"} # go fwlink for 'https://nuget.org/nuget.exe' $script:NuGetClientSourceURL = 'http://go.microsoft.com/fwlink/?LinkID=690216&clcid=0x409' $script:NuGetExeName = 'NuGet.exe' @@ -602,7 +605,7 @@ catch # Ignore the error and try adding the type below } -if(-not $script:TelemetryEnabled -and $IsWindows) +if(-not $script:TelemetryEnabled -and (IsWindows)) { try { @@ -707,7 +710,7 @@ function Publish-Module Begin { - if($script:isNanoServer -or $IsCore) { + if($script:isNanoServer -or (IsCore)) { $message = $LocalizedData.PublishPSArtifactUnsupportedOnNano -f "Module" ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $message ` @@ -869,7 +872,7 @@ function Publish-Module } else { - $resolvedPath = Resolve-PathHelper -Path $Path -CallerPSCmdlet $PSCmdlet | Microsoft.PowerShell.Utility\Select-Object -First 1 + $resolvedPath = Resolve-PathHelper -Path $Path -CallerPSCmdlet $PSCmdlet | Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore if(-not $resolvedPath -or -not (Microsoft.PowerShell.Management\Test-Path -Path $resolvedPath -PathType Container)) @@ -1059,7 +1062,7 @@ function Publish-Module # Use Find-Script to check if that name is already used as scriptname $scriptPSGetItemInfo = Find-Script @FindParameters | Microsoft.PowerShell.Core\Where-Object {$_.Name -eq $moduleName} | - Microsoft.PowerShell.Utility\Select-Object -Last 1 + Microsoft.PowerShell.Utility\Select-Object -Last 1 -ErrorAction Ignore if($scriptPSGetItemInfo) { $message = $LocalizedData.SpecifiedNameIsAlearyUsed -f ($moduleName, $Repository, 'Find-Script') @@ -1074,7 +1077,7 @@ function Publish-Module $null = $FindParameters.Remove('Tag') $currentPSGetItemInfo = Find-Module @FindParameters | Microsoft.PowerShell.Core\Where-Object {$_.Name -eq $moduleInfo.Name} | - Microsoft.PowerShell.Utility\Select-Object -Last 1 + Microsoft.PowerShell.Utility\Select-Object -Last 1 -ErrorAction Ignore if($currentPSGetItemInfo) { @@ -1412,7 +1415,7 @@ function Save-Module { if($Path) { - $destinationPath = Resolve-PathHelper -Path $Path -CallerPSCmdlet $PSCmdlet | Microsoft.PowerShell.Utility\Select-Object -First 1 + $destinationPath = Resolve-PathHelper -Path $Path -CallerPSCmdlet $PSCmdlet | Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore if(-not $destinationPath -or -not (Microsoft.PowerShell.Management\Test-path $destinationPath)) { @@ -1429,7 +1432,7 @@ function Save-Module } else { - $destinationPath = Resolve-PathHelper -Path $LiteralPath -IsLiteralPath -CallerPSCmdlet $PSCmdlet | Microsoft.PowerShell.Utility\Select-Object -First 1 + $destinationPath = Resolve-PathHelper -Path $LiteralPath -IsLiteralPath -CallerPSCmdlet $PSCmdlet | Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore if(-not $destinationPath -or -not (Microsoft.PowerShell.Management\Test-Path -LiteralPath $destinationPath)) { @@ -1878,7 +1881,7 @@ function Update-Module if(-not $installedPackages -and -not (Test-WildcardPattern -Name $moduleName)) { - $availableModules = Get-Module -ListAvailable $moduleName -Verbose:$false | Microsoft.PowerShell.Utility\Select-Object -Unique + $availableModules = Get-Module -ListAvailable $moduleName -Verbose:$false | Microsoft.PowerShell.Utility\Select-Object -Unique -ErrorAction Ignore if(-not $availableModules) { @@ -2506,7 +2509,7 @@ function Publish-Script Begin { - if($script:isNanoServer -or $IsCore) { + if($script:isNanoServer -or (IsCore)) { $message = $LocalizedData.PublishPSArtifactUnsupportedOnNano -f "Script" ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $message ` @@ -2527,7 +2530,7 @@ function Publish-Script if($Path) { $scriptFilePath = Resolve-PathHelper -Path $Path -CallerPSCmdlet $PSCmdlet | - Microsoft.PowerShell.Utility\Select-Object -First 1 + Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore if(-not $scriptFilePath -or -not (Microsoft.PowerShell.Management\Test-Path -Path $scriptFilePath -PathType Leaf)) @@ -2544,7 +2547,7 @@ function Publish-Script else { $scriptFilePath = Resolve-PathHelper -Path $LiteralPath -IsLiteralPath -CallerPSCmdlet $PSCmdlet | - Microsoft.PowerShell.Utility\Select-Object -First 1 + Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore if(-not $scriptFilePath -or -not (Microsoft.PowerShell.Management\Test-Path -LiteralPath $scriptFilePath -PathType Leaf)) @@ -2698,7 +2701,7 @@ function Publish-Script # Use Find-Module to check if that name is already used as module name $modulePSGetItemInfo = Find-Module @FindParameters | Microsoft.PowerShell.Core\Where-Object {$_.Name -eq $scriptName} | - Microsoft.PowerShell.Utility\Select-Object -Last 1 + Microsoft.PowerShell.Utility\Select-Object -Last 1 -ErrorAction Ignore if($modulePSGetItemInfo) { $message = $LocalizedData.SpecifiedNameIsAlearyUsed -f ($scriptName, $Repository, 'Find-Module') @@ -2715,7 +2718,7 @@ function Publish-Script $currentPSGetItemInfo = $null $currentPSGetItemInfo = Find-Script @FindParameters | Microsoft.PowerShell.Core\Where-Object {$_.Name -eq $scriptName} | - Microsoft.PowerShell.Utility\Select-Object -Last 1 + Microsoft.PowerShell.Utility\Select-Object -Last 1 -ErrorAction Ignore if($currentPSGetItemInfo) { @@ -3074,7 +3077,7 @@ function Save-Script if($Path) { $destinationPath = Resolve-PathHelper -Path $Path -CallerPSCmdlet $PSCmdlet | - Microsoft.PowerShell.Utility\Select-Object -First 1 + Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore if(-not $destinationPath -or -not (Microsoft.PowerShell.Management\Test-path $destinationPath)) { @@ -3092,7 +3095,7 @@ function Save-Script else { $destinationPath = Resolve-PathHelper -Path $LiteralPath -IsLiteralPath -CallerPSCmdlet $PSCmdlet | - Microsoft.PowerShell.Utility\Select-Object -First 1 + Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore if(-not $destinationPath -or -not (Microsoft.PowerShell.Management\Test-Path -LiteralPath $destinationPath)) { @@ -4459,7 +4462,7 @@ function Test-ScriptFileInfo $scriptFilePath = $null if($Path) { - $scriptFilePath = Resolve-PathHelper -Path $Path -CallerPSCmdlet $PSCmdlet | Microsoft.PowerShell.Utility\Select-Object -First 1 + $scriptFilePath = Resolve-PathHelper -Path $Path -CallerPSCmdlet $PSCmdlet | Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore if(-not $scriptFilePath -or -not (Microsoft.PowerShell.Management\Test-Path -Path $scriptFilePath -PathType Leaf)) { @@ -4475,7 +4478,7 @@ function Test-ScriptFileInfo } else { - $scriptFilePath = Resolve-PathHelper -Path $LiteralPath -IsLiteralPath -CallerPSCmdlet $PSCmdlet | Microsoft.PowerShell.Utility\Select-Object -First 1 + $scriptFilePath = Resolve-PathHelper -Path $LiteralPath -IsLiteralPath -CallerPSCmdlet $PSCmdlet | Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore if(-not $scriptFilePath -or -not (Microsoft.PowerShell.Management\Test-Path -LiteralPath $scriptFilePath -PathType Leaf)) { @@ -4532,7 +4535,7 @@ function Test-ScriptFileInfo $psscriptInfoComments = $CommentTokens | Microsoft.PowerShell.Core\Where-Object { $_.Extent.Text -match "<#PSScriptInfo" } | - Microsoft.PowerShell.Utility\Select-Object -First 1 + Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore if(-not $psscriptInfoComments) { @@ -4721,20 +4724,20 @@ function Test-ScriptFileInfo if($allCommands) { - $allCommandNames = $allCommands | ForEach-Object {$_.Name} | Select-Object -Unique + $allCommandNames = $allCommands | ForEach-Object {$_.Name} | Select-Object -Unique -ErrorAction Ignore ValidateAndAdd-PSScriptInfoEntry -PSScriptInfo $PSScriptInfo ` -PropertyName $script:DefinedCommands ` -PropertyValue $allCommandNames ` -CallerPSCmdlet $PSCmdlet - $allFunctionNames = $allCommands | Where-Object {-not $_.IsWorkflow} | ForEach-Object {$_.Name} | Select-Object -Unique + $allFunctionNames = $allCommands | Where-Object {-not $_.IsWorkflow} | ForEach-Object {$_.Name} | Select-Object -Unique -ErrorAction Ignore ValidateAndAdd-PSScriptInfoEntry -PSScriptInfo $PSScriptInfo ` -PropertyName $script:DefinedFunctions ` -PropertyValue $allFunctionNames ` -CallerPSCmdlet $PSCmdlet - $allWorkflowNames = $allCommands | Where-Object {$_.IsWorkflow} | ForEach-Object {$_.Name} | Select-Object -Unique + $allWorkflowNames = $allCommands | Where-Object {$_.IsWorkflow} | ForEach-Object {$_.Name} | Select-Object -Unique -ErrorAction Ignore ValidateAndAdd-PSScriptInfoEntry -PSScriptInfo $PSScriptInfo ` -PropertyName $script:DefinedWorkflows ` -PropertyValue $allWorkflowNames ` @@ -5110,7 +5113,7 @@ function Update-ScriptFileInfo if($Path) { $scriptFilePath = Resolve-PathHelper -Path $Path -CallerPSCmdlet $PSCmdlet | - Microsoft.PowerShell.Utility\Select-Object -First 1 + Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore if(-not $scriptFilePath -or -not (Microsoft.PowerShell.Management\Test-Path -Path $scriptFilePath -PathType Leaf)) @@ -5127,7 +5130,7 @@ function Update-ScriptFileInfo else { $scriptFilePath = Resolve-PathHelper -Path $LiteralPath -IsLiteralPath -CallerPSCmdlet $PSCmdlet | - Microsoft.PowerShell.Utility\Select-Object -First 1 + Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore if(-not $scriptFilePath -or -not (Microsoft.PowerShell.Management\Test-Path -LiteralPath $scriptFilePath -PathType Leaf)) @@ -5380,7 +5383,7 @@ function Update-ScriptFileInfo $psscriptInfoComments = $CommentTokens | Microsoft.PowerShell.Core\Where-Object { $_.Extent.Text -match "<#PSScriptInfo" } | - Microsoft.PowerShell.Utility\Select-Object -First 1 + Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore if(-not $psscriptInfoComments) { @@ -6150,7 +6153,7 @@ function Ping-Endpoint $results = @{} $WebProxy = $null - if($Proxy -and $IsWindows) + if($Proxy -and (IsWindows)) { $ProxyNetworkCredential = $null if($ProxyCredential) @@ -6380,7 +6383,7 @@ function ValidateAndSet-PATHVariableIfUserAccepts $Request ) - if(-not $IsWindows) + if(-not (IsWindows)) { return } @@ -7180,7 +7183,7 @@ function Install-NuGetClientBinaries $Force ) - if(-not $IsWindows -or + if(-not (IsWindows) -or ($script:NuGetProvider -and (-not $BootstrapNuGetExe -or ($script:NuGetExePath -and (Microsoft.PowerShell.Management\Test-Path -Path $script:NuGetExePath))))) @@ -7267,7 +7270,7 @@ function Install-NuGetClientBinaries $_.Path -and ((Microsoft.PowerShell.Management\Split-Path -Path $_.Path -Leaf) -eq $script:NuGetExeName) -and (-not $_.Path.StartsWith($env:windir, [System.StringComparison]::OrdinalIgnoreCase)) - } | Microsoft.PowerShell.Utility\Select-Object -First 1 + } | Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore if($nugetCmd -and $nugetCmd.Path) { @@ -7283,7 +7286,7 @@ function Install-NuGetClientBinaries } # On Nano server we don't need NuGet.exe - if(-not $bootstrapNuGetProvider -and ($script:isNanoServer -or $IsCore -or -not $BootstrapNuGetExe)) + if(-not $bootstrapNuGetProvider -and ($script:isNanoServer -or (IsCore) -or -not $BootstrapNuGetExe)) { return } @@ -7340,7 +7343,7 @@ function Install-NuGetClientBinaries } } - if($BootstrapNuGetExe -and -not $script:isNanoServer -and -not $IsCore) + if($BootstrapNuGetExe -and -not $script:isNanoServer -and -not (IsCore)) { Write-Verbose -Message $LocalizedData.DownloadingNugetExe @@ -7424,14 +7427,14 @@ function Test-RunningAsElevated [OutputType([bool])] Param() - if($IsWindows) + if(IsWindows) { $wid=[System.Security.Principal.WindowsIdentity]::GetCurrent() $prp=new-object System.Security.Principal.WindowsPrincipal($wid) $adm=[System.Security.Principal.WindowsBuiltInRole]::Administrator return $prp.IsInRole($adm) } - elseif($IsLinux) + elseif(IsLinux) { return ($env:SUDO_UID -eq '1000') } @@ -7522,7 +7525,7 @@ function ValidateAndGet-ScriptDependencies $psgetItemInfo = Find-Module @FindModuleArguments | Microsoft.PowerShell.Core\Where-Object {$_.Name -eq $ModuleName} | - Microsoft.PowerShell.Utility\Select-Object -Last 1 + Microsoft.PowerShell.Utility\Select-Object -Last 1 -ErrorAction Ignore if(-not $psgetItemInfo) { @@ -7565,7 +7568,7 @@ function ValidateAndGet-ScriptDependencies $psgetItemInfo = Find-Script @FindScriptArguments | Microsoft.PowerShell.Core\Where-Object {$_.Name -eq $requiredScript} | - Microsoft.PowerShell.Utility\Select-Object -Last 1 + Microsoft.PowerShell.Utility\Select-Object -Last 1 -ErrorAction Ignore if(-not $psgetItemInfo) { @@ -7692,7 +7695,7 @@ function ValidateAndGet-RequiredModuleDetails $psgetItemInfo = Find-Module @FindModuleArguments | Microsoft.PowerShell.Core\Where-Object {$_.Name -eq $ModuleName} | - Microsoft.PowerShell.Utility\Select-Object -Last 1 + Microsoft.PowerShell.Utility\Select-Object -Last 1 -ErrorAction Ignore if(-not $psgetItemInfo) { @@ -7736,7 +7739,7 @@ function ValidateAndGet-RequiredModuleDetails $psgetItemInfo = Find-Module @FindModuleArguments | Microsoft.PowerShell.Core\Where-Object {$_.Name -eq $ModuleName} | - Microsoft.PowerShell.Utility\Select-Object -Last 1 + Microsoft.PowerShell.Utility\Select-Object -Last 1 -ErrorAction Ignore if(-not $psgetItemInfo) { @@ -9518,7 +9521,7 @@ function Find-Package if($options.ContainsKey($script:Tag)) { - $userSpecifiedTags = $options[$script:Tag] | Microsoft.PowerShell.Utility\Select-Object -Unique + $userSpecifiedTags = $options[$script:Tag] | Microsoft.PowerShell.Utility\Select-Object -Unique -ErrorAction Ignore } else { @@ -9529,7 +9532,7 @@ function Find-Package if($options.ContainsKey('DscResource')) { $specifiedDscResources = $options['DscResource'] | - Microsoft.PowerShell.Utility\Select-Object -Unique | + Microsoft.PowerShell.Utility\Select-Object -Unique -ErrorAction Ignore | Microsoft.PowerShell.Core\ForEach-Object {"$($script:DscResource)_$_"} } @@ -9537,7 +9540,7 @@ function Find-Package if($options.ContainsKey('RoleCapability')) { $specifiedRoleCapabilities = $options['RoleCapability'] | - Microsoft.PowerShell.Utility\Select-Object -Unique | + Microsoft.PowerShell.Utility\Select-Object -Unique -ErrorAction Ignore | Microsoft.PowerShell.Core\ForEach-Object {"$($script:RoleCapability)_$_"} } @@ -9545,7 +9548,7 @@ function Find-Package if($options.ContainsKey('Command')) { $specifiedCommands = $options['Command'] | - Microsoft.PowerShell.Utility\Select-Object -Unique | + Microsoft.PowerShell.Utility\Select-Object -Unique -ErrorAction Ignore | Microsoft.PowerShell.Core\ForEach-Object {"$($script:Command)_$_"} } @@ -9553,7 +9556,7 @@ function Find-Package if($options.ContainsKey('Includes')) { $includes = $options['Includes'] | - Microsoft.PowerShell.Utility\Select-Object -Unique | + Microsoft.PowerShell.Utility\Select-Object -Unique -ErrorAction Ignore | Microsoft.PowerShell.Core\ForEach-Object {"$($script:Includes)_$_"} # Add PSIncludes_DscResource to $specifiedIncludes iff -DscResource names are not specified @@ -10468,7 +10471,7 @@ function Install-PackageUtility } } - $InstalledItemsList | Select-Object -Unique + $InstalledItemsList | Select-Object -Unique -ErrorAction Ignore if($Debug) { @@ -10554,11 +10557,11 @@ function Install-PackageUtility $InstalledItemDetails = $null if($packageType -eq $script:PSArtifactTypeModule) { - $InstalledItemDetails = Get-InstalledModuleDetails -Name $pkg.Name | Select-Object -Last 1 + $InstalledItemDetails = Get-InstalledModuleDetails -Name $pkg.Name | Select-Object -Last 1 -ErrorAction Ignore } elseif($packageType -eq $script:PSArtifactTypeScript) { - $InstalledItemDetails = Get-InstalledScriptDetails -Name $pkg.Name | Select-Object -Last 1 + $InstalledItemDetails = Get-InstalledScriptDetails -Name $pkg.Name | Select-Object -Last 1 -ErrorAction Ignore } if($InstalledItemDetails -and @@ -10983,7 +10986,7 @@ function Uninstall-Package if(-not $Force -and $dependentModules) { - $message = $LocalizedData.UnableToUninstallAsOtherModulesNeedThisModule -f ($moduleName, $version, $moduleBase, $(($dependentModules.Name | Select-Object -Unique) -join ','), $moduleName) + $message = $LocalizedData.UnableToUninstallAsOtherModulesNeedThisModule -f ($moduleName, $version, $moduleBase, $(($dependentModules.Name | Select-Object -Unique -ErrorAction Ignore) -join ','), $moduleName) ThrowError -ExceptionName "System.InvalidOperationException" ` -ExceptionMessage $message ` @@ -11129,7 +11132,7 @@ function Uninstall-Package ($scriptName, $version, $scriptBase, - $(($dependentScriptNames | Select-Object -Unique) -join ','), + $(($dependentScriptNames | Select-Object -Unique -ErrorAction Ignore) -join ','), $scriptName) ThrowError -ExceptionName 'System.InvalidOperationException' ` @@ -11674,11 +11677,20 @@ function Set-InstalledModulesVariable foreach ($location in $modulePaths) { # find all modules installed using PowerShellGet - $moduleBases = Get-ChildItem $location -Recurse ` - -Attributes Hidden -Filter $script:PSGetItemInfoFileName ` - -ErrorAction SilentlyContinue ` - -WarningAction SilentlyContinue ` - | Foreach-Object { $_.Directory } + $GetChildItemParams = @{ + Path = $location + Recurse = $true + Filter = $script:PSGetItemInfoFileName + ErrorAction = 'SilentlyContinue' + WarningAction = 'SilentlyContinue' + } + + if(IsWindows) + { + $GetChildItemParams['Attributes'] = 'Hidden' + } + + $moduleBases = Get-ChildItem @GetChildItemParams | Foreach-Object { $_.Directory } foreach ($moduleBase in $moduleBases) @@ -12206,7 +12218,7 @@ function Test-ModuleInstalled # Check if module is already installed $availableModule = Microsoft.PowerShell.Core\Get-Module -ListAvailable -Name $Name -Verbose:$false | Microsoft.PowerShell.Core\Where-Object {-not (Test-ModuleSxSVersionSupport) -or -not $RequiredVersion -or ($RequiredVersion -eq $_.Version)} | - Microsoft.PowerShell.Utility\Select-Object -Unique + Microsoft.PowerShell.Utility\Select-Object -Unique -ErrorAction Ignore return $availableModule } @@ -12267,7 +12279,7 @@ function Test-ScriptInstalled $scriptInfo = $scriptInfos | Microsoft.PowerShell.Core\Where-Object { (-not $RequiredVersion) -or ($RequiredVersion -eq $_.Version) - } | Microsoft.PowerShell.Utility\Select-Object -First 1 + } | Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore return $scriptInfo } @@ -13860,7 +13872,7 @@ function Validate-ModuleCommandAlreadyAvailable -WarningAction SilentlyContinue | Microsoft.PowerShell.Core\Where-Object { ($CommandNames -contains $_.Name) -and ($_.Source -ne $CurrentModuleInfo.Name) } | - Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction SilentlyContinue + Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore if($AvailableCommand) { $message = $LocalizedData.ModuleCommandAlreadyAvailable -f ($AvailableCommand.Name, $CurrentModuleInfo.Name) @@ -14035,11 +14047,11 @@ function Get-InstalledModuleAuthenticodeSignature $SourceModule = $AvailableModules | Microsoft.PowerShell.Core\Where-Object { $_.ModuleBase.StartsWith($InstallLocation, [System.StringComparison]::OrdinalIgnoreCase) - } | Microsoft.PowerShell.Utility\Select-Object -First 1 + } | Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore if(-not $SourceModule) { - $SourceModule = $AvailableModules | Microsoft.PowerShell.Utility\Select-Object -First 1 + $SourceModule = $AvailableModules | Microsoft.PowerShell.Utility\Select-Object -First 1 -ErrorAction Ignore } else { @@ -14162,7 +14174,7 @@ function Test-ValidManifestModule -CallerPSCmdlet $PSCmdlet ` -ErrorCategory InvalidOperation } - elseif($IsWindows) + elseif(IsWindows) { $ValidationResult = Validate-ModuleAuthenticodeSignature -CurrentModuleInfo $PSModuleInfo ` -InstallLocation $InstallLocation ` From bb4eadba1d55eaf890ed9928da2fb4ce5e787f09 Mon Sep 17 00:00:00 2001 From: Manikyam Bavandla Date: Wed, 13 Jul 2016 12:57:05 -0700 Subject: [PATCH 19/34] Corrected a typo in $PSVariable Corrected a typo in $PSVariable --- src/Modules/Shared/PowerShellGet/PSModule.psm1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Modules/Shared/PowerShellGet/PSModule.psm1 b/src/Modules/Shared/PowerShellGet/PSModule.psm1 index 33f01dbcc..5d1804eb6 100644 --- a/src/Modules/Shared/PowerShellGet/PSModule.psm1 +++ b/src/Modules/Shared/PowerShellGet/PSModule.psm1 @@ -14,9 +14,9 @@ Microsoft.PowerShell.Core\Set-StrictMode -Version Latest # Check if this is nano server. [System.Runtime.Loader.AssemblyLoadContext] is only available on NanoServer $script:isNanoServer = $null -ne ('System.Runtime.Loader.AssemblyLoadContext' -as [Type]) -function IsWindows { $PSVarable = Get-Variable -Name IsWindows -ErrorAction Ignore; return (-not $PSVarable -or $PSVarable.Value) } -function IsLinux { $PSVarable = Get-Variable -Name IsLinux -ErrorAction Ignore; return ($PSVarable -and $PSVarable.Value) } -function IsCore { $PSVarable = Get-Variable -Name IsCore -ErrorAction Ignore; return ($PSVarable -and $PSVarable.Value) } +function IsWindows { $PSVariable = Get-Variable -Name IsWindows -ErrorAction Ignore; return (-not $PSVariable -or $PSVariable.Value) } +function IsLinux { $PSVariable = Get-Variable -Name IsLinux -ErrorAction Ignore; return ($PSVariable -and $PSVariable.Value) } +function IsCore { $PSVariable = Get-Variable -Name IsCore -ErrorAction Ignore; return ($PSVariable -and $PSVariable.Value) } if(IsWindows) { From 1f9343ece26b77a821a352e077d192f53667b692 Mon Sep 17 00:00:00 2001 From: Indhu Sivaramakrishnan Date: Wed, 13 Jul 2016 14:03:05 -0700 Subject: [PATCH 20/34] Updated Microsoft.PowerShell.ScheduledJob to have version 3.0.0.0 --- src/Microsoft.PowerShell.ScheduledJob/AssemblyInfo.cs | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/Microsoft.PowerShell.ScheduledJob/AssemblyInfo.cs diff --git a/src/Microsoft.PowerShell.ScheduledJob/AssemblyInfo.cs b/src/Microsoft.PowerShell.ScheduledJob/AssemblyInfo.cs new file mode 100644 index 000000000..8be322935 --- /dev/null +++ b/src/Microsoft.PowerShell.ScheduledJob/AssemblyInfo.cs @@ -0,0 +1,8 @@ +using System.Reflection; +using System.Resources; + +[assembly:AssemblyFileVersionAttribute("3.0.0.0")] +[assembly:AssemblyVersion("3.0.0.0")] + +[assembly:AssemblyCulture("")] +[assembly:NeutralResourcesLanguage("en-US")] From c17244eed26e5118a5bb20c927ad445eb57dbc48 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Mon, 11 Jul 2016 12:26:32 -0700 Subject: [PATCH 21/34] Fix IsAdministrator for Unix --- .../engine/Utils.cs | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index a9d4f2aaf..8dd8f96be 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -960,21 +960,20 @@ namespace System.Management.Automation internal static bool IsAdministrator() { - // Porting note: only Windows supports the - // SecurityPrincipal API of .net for now assume Linux - // users have no concept of "Administrator" for now and - // specifically do not map to a check for root user - if (Platform.IsWindows) - { - System.Security.Principal.WindowsIdentity currentIdentity = System.Security.Principal.WindowsIdentity.GetCurrent(); - System.Security.Principal.WindowsPrincipal principal = new System.Security.Principal.WindowsPrincipal(currentIdentity); + // Porting note: only Windows supports the SecurityPrincipal API of .NET. Due to + // advanced privilege models, the correct approach on Unix is to assume the user has + // permissions, attempt the task, and error gracefully if the task fails due to + // permissions. To fit into PowerShell's existing model of pre-emptively checking + // permissions (which cannot be assumed on Unix), we "assume" the user is an + // administrator by returning true, thus nullifying this check on Unix. +#if LINUX + return true; +#else + System.Security.Principal.WindowsIdentity currentIdentity = System.Security.Principal.WindowsIdentity.GetCurrent(); + System.Security.Principal.WindowsPrincipal principal = new System.Security.Principal.WindowsPrincipal(currentIdentity); - return principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator); - } - else - { - return false; - } + return principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator); +#endif } internal static bool NativeItemExists(string path) From 0b114a3b71fc7dba2031842a84572d9541d44124 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Tue, 12 Jul 2016 14:55:45 -0700 Subject: [PATCH 22/34] Add GetStat Wraps stat() with mapped errno. --- src/libpsl-native/src/CMakeLists.txt | 1 + src/libpsl-native/src/getstat.cpp | 96 ++++++++++++++++++++++++++++ src/libpsl-native/src/getstat.h | 10 +++ 3 files changed, 107 insertions(+) create mode 100644 src/libpsl-native/src/getstat.cpp create mode 100644 src/libpsl-native/src/getstat.h diff --git a/src/libpsl-native/src/CMakeLists.txt b/src/libpsl-native/src/CMakeLists.txt index 5b2ec2419..6d47622d3 100644 --- a/src/libpsl-native/src/CMakeLists.txt +++ b/src/libpsl-native/src/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(psl-native SHARED + getstat.cpp getcurrentprocessorid.cpp getusername.cpp getcomputername.cpp diff --git a/src/libpsl-native/src/getstat.cpp b/src/libpsl-native/src/getstat.cpp new file mode 100644 index 000000000..3e89d1a6e --- /dev/null +++ b/src/libpsl-native/src/getstat.cpp @@ -0,0 +1,96 @@ +//! @file getstat.cpp +//! @author Andrew Schwartzmeyer +//! @brief returns the stat of a file + +#include +#include +#include +#include +#include +#include +#include "getstat.h" + +//! @brief GetStat returns the stat of a file. This simply delegates to the +//! stat() system call and maps errno to the expected values for GetLastError. +//! +//! GetStat +//! +//! @param[in] path +//! @parblock +//! A pointer to the buffer that contains the file name +//! +//! char* is marshaled as an LPStr, which on Linux is UTF-8. +//! @endparblock +//! +//! @param[in] stat +//! @parblock +//! A pointer to the buffer in which to place the stat information +//! @endparblock +//! +//! @exception errno Passes these errors via errno to GetLastError: +//! - ERROR_INVALID_PARAMETER: parameter is not valid +//! - ERROR_FILE_NOT_FOUND: file does not exist +//! - ERROR_ACCESS_DENIED: access is denied +//! - ERROR_INVALID_ADDRESS: attempt to access invalid address +//! - ERROR_STOPPED_ON_SYMLINK: too many symbolic links +//! - ERROR_GEN_FAILURE: I/O error occurred +//! - ERROR_INVALID_NAME: file provided is not a symbolic link +//! - ERROR_INVALID_FUNCTION: incorrect function +//! - ERROR_BAD_PATH_NAME: pathname is too long +//! - ERROR_OUTOFMEMORY insufficient kernel memory +//! +//! @retval 0 if successful +//! @retval -1 if failed +//! + +int32_t GetStat(const char* path, struct stat* buf) +{ + errno = 0; + + if (!path) + { + errno = ERROR_INVALID_PARAMETER; + return -1; + } + + int32_t ret = stat(path, buf); + + if (ret != 0) + { + switch(errno) + { + case EACCES: + errno = ERROR_ACCESS_DENIED; + break; + case EBADF: + errno = ERROR_FILE_NOT_FOUND; + break; + case EFAULT: + errno = ERROR_INVALID_ADDRESS; + break; + case ELOOP: + errno = ERROR_STOPPED_ON_SYMLINK; + break; + case ENAMETOOLONG: + errno = ERROR_GEN_FAILURE; + break; + case ENOENT: + errno = ERROR_FILE_NOT_FOUND; + break; + case ENOMEM: + errno = ERROR_NO_SUCH_USER; + break; + case ENOTDIR: + errno = ERROR_INVALID_NAME; + break; + case EOVERFLOW: + errno = ERROR_BUFFER_OVERFLOW; + break; + default: + errno = ERROR_INVALID_FUNCTION; + } + return -1; + } + + return 0; +} diff --git a/src/libpsl-native/src/getstat.h b/src/libpsl-native/src/getstat.h new file mode 100644 index 000000000..00bc26e9f --- /dev/null +++ b/src/libpsl-native/src/getstat.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include "pal.h" + +PAL_BEGIN_EXTERNC + +int32_t GetStat(const char* path, struct stat* buf); + +PAL_END_EXTERNC From 13a82a59eeeaf8d39bbc57ca2603a59fbcd57641 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Tue, 12 Jul 2016 14:57:06 -0700 Subject: [PATCH 23/34] Add GetFileOwner --- src/libpsl-native/src/CMakeLists.txt | 1 + src/libpsl-native/src/getfileowner.cpp | 105 +++++++++++++++++++++++++ src/libpsl-native/src/getfileowner.h | 9 +++ 3 files changed, 115 insertions(+) create mode 100644 src/libpsl-native/src/getfileowner.cpp create mode 100644 src/libpsl-native/src/getfileowner.h diff --git a/src/libpsl-native/src/CMakeLists.txt b/src/libpsl-native/src/CMakeLists.txt index 6d47622d3..6840ceb45 100644 --- a/src/libpsl-native/src/CMakeLists.txt +++ b/src/libpsl-native/src/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(psl-native SHARED getstat.cpp + getfileowner.cpp getcurrentprocessorid.cpp getusername.cpp getcomputername.cpp diff --git a/src/libpsl-native/src/getfileowner.cpp b/src/libpsl-native/src/getfileowner.cpp new file mode 100644 index 000000000..53afdb08e --- /dev/null +++ b/src/libpsl-native/src/getfileowner.cpp @@ -0,0 +1,105 @@ +//! @file getfileowner.cpp +//! @author Andrew Schwartzmeyer +//! @brief returns the owner of a file + +#include +#include +#include +#include +#include +#include +#include "getstat.h" +#include "getfileowner.h" + +//! @brief GetFileOwner returns the owner of a file +//! +//! GetFileOwner +//! +//! @param[in] fileName +//! @parblock +//! A pointer to the buffer that contains the file name +//! +//! char* is marshaled as an LPStr, which on Linux is UTF-8. +//! @endparblock +//! +//! @exception errno Passes these errors via errno to GetLastError: +//! - ERROR_INVALID_PARAMETER: parameter is not valid +//! - ERROR_FILE_NOT_FOUND: file does not exist +//! - ERROR_ACCESS_DENIED: access is denied +//! - ERROR_INVALID_ADDRESS: attempt to access invalid address +//! - ERROR_STOPPED_ON_SYMLINK: too many symbolic links +//! - ERROR_GEN_FAILURE: I/O error occurred +//! - ERROR_INVALID_NAME: file provided is not a symbolic link +//! - ERROR_INVALID_FUNCTION: incorrect function +//! - ERROR_BAD_PATH_NAME: pathname is too long +//! - ERROR_OUTOFMEMORY insufficient kernal memory +//! +//! @retval file owner, or NULL if unsuccessful +//! +char* GetFileOwner(const char* fileName) +{ + int32_t ret = 0; + errno = 0; + + if (!fileName) + { + errno = ERROR_INVALID_PARAMETER; + return NULL; + } + + struct stat fileStat; + ret = GetStat(fileName, &fileStat); + if (ret != 0) + { + return NULL; + } + + struct passwd pwd; + struct passwd* result = NULL; + char* buf; + + int buflen = sysconf(_SC_GETPW_R_SIZE_MAX); + if (buflen < 1) + { + buflen = 2048; + } + +allocate: + buf = (char*)calloc(buflen, sizeof(char)); + + ret = getpwuid_r(fileStat.st_uid, &pwd, buf, buflen, &result); + + if (ret != 0) + { + switch(errno) + { + case ERANGE: + free(buf); + buflen *= 2; + goto allocate; + case ENOENT: + case ESRCH: + case EBADF: + case EPERM: + errno = ERROR_NO_SUCH_USER; + break; + case ENOMEM: + errno = ERROR_OUTOFMEMORY; + break; + default: + errno = ERROR_GEN_FAILURE; + } + return NULL; + } + + // no result + if (result == NULL) + { + return NULL; + } + + size_t userlen = strnlen(pwd.pw_name, buflen); + char* username = strndup(pwd.pw_name, userlen); + free(buf); + return username; +} diff --git a/src/libpsl-native/src/getfileowner.h b/src/libpsl-native/src/getfileowner.h new file mode 100644 index 000000000..7da83dccc --- /dev/null +++ b/src/libpsl-native/src/getfileowner.h @@ -0,0 +1,9 @@ +#pragma once + +#include "pal.h" + +PAL_BEGIN_EXTERNC + +char* GetFileOwner(const char* fileName); + +PAL_END_EXTERNC From e6d3cbcce04bba15c1cd55b74a099d752fc8c793 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Tue, 12 Jul 2016 15:27:11 -0700 Subject: [PATCH 24/34] Implement Get-Process -IncludeUserName for Linux So much plumbing. The correct way to identify the owner of a process on Linux is to get the owner of the /proc/ file. However, .NET Core has no API to get this information, thus it had to be plumbed through to the native library. --- .../commands/management/Process.cs | 4 ++++ .../CoreCLR/CorePsPlatform.cs | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs index 073c1e5fe..13aabd84d 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs @@ -866,6 +866,9 @@ namespace Microsoft.PowerShell.Commands private static string RetrieveProcessUserName(Process process, Cmdlet cmdlet) { string userName = null; +#if LINUX + userName = Platform.NonWindowsGetFileOwner("/proc/" + process.Id); +#else IntPtr tokenUserInfo = IntPtr.Zero; IntPtr processTokenHandler = IntPtr.Zero; @@ -955,6 +958,7 @@ namespace Microsoft.PowerShell.Commands } } +#endif return userName; } diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 063ba49dd..fc735c470 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -354,6 +354,18 @@ namespace System.Management.Automation } } + internal static string NonWindowsGetFileOwner(string path) + { + if (!IsWindows) + { + return LinuxPlatform.GetFileOwner(path); + } + else + { + throw new PlatformNotSupportedException(); + } + } + #if CORECLR internal static string NonWindowsGetFolderPath(SpecialFolder folder) { @@ -740,6 +752,11 @@ namespace System.Management.Automation return Native.FollowSymLink(path); } + public static string GetFileOwner(string path) + { + return Native.GetFileOwner(path); + } + [StructLayout(LayoutKind.Sequential)] internal class SetDateInfoInternal { @@ -809,6 +826,9 @@ namespace System.Management.Automation [return: MarshalAs(UnmanagedType.LPStr)] internal static extern string FollowSymLink([MarshalAs(UnmanagedType.LPStr)]string filePath); + [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] + [return: MarshalAs(UnmanagedType.LPStr)] + internal static extern string GetFileOwner([MarshalAs(UnmanagedType.LPStr)]string filePath); } } From f3328fec5c49707fea1e551a6295a82c0c9549dc Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Tue, 12 Jul 2016 16:03:08 -0700 Subject: [PATCH 25/34] Add tests for native GetFileOwner --- src/libpsl-native/test/CMakeLists.txt | 1 + src/libpsl-native/test/test-getfileowner.cpp | 32 ++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/libpsl-native/test/test-getfileowner.cpp diff --git a/src/libpsl-native/test/CMakeLists.txt b/src/libpsl-native/test/CMakeLists.txt index 4ddc53836..1ab2bfd3e 100644 --- a/src/libpsl-native/test/CMakeLists.txt +++ b/src/libpsl-native/test/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(googletest) add_executable(psl-native-test + test-getfileowner.cpp test-locale.cpp test-getcurrentprocessid.cpp test-getusername.cpp diff --git a/src/libpsl-native/test/test-getfileowner.cpp b/src/libpsl-native/test/test-getfileowner.cpp new file mode 100644 index 000000000..77f1e2fc8 --- /dev/null +++ b/src/libpsl-native/test/test-getfileowner.cpp @@ -0,0 +1,32 @@ +//! @file test-getfileowner.cpp +//! @author Andrew Schwartzmeyer +//! @brief Tests GetFileOwner + +#include +#include +#include +#include "getfileowner.h" + +using namespace std; + +//! Test fixture for GetFileOwner +class GetFileOwnerTest : public ::testing::Test +{ +}; + +TEST_F(GetFileOwnerTest, CanGetOwnerOfRoot) +{ + ASSERT_STREQ(GetFileOwner("/"), "root"); +} + +TEST_F(GetFileOwnerTest, CannotGetOwnerOfFakeFile) +{ + EXPECT_STREQ(GetFileOwner("SomeMadeUpFileNameThatDoesNotExist"), NULL); + EXPECT_EQ(errno, ERROR_FILE_NOT_FOUND); +} + +TEST_F(GetFileOwnerTest, ReturnsNullForNullInput) +{ + EXPECT_STREQ(GetFileOwner(NULL), NULL); + EXPECT_EQ(errno, ERROR_INVALID_PARAMETER); +} From 6f938bd8604f66f0f169b339c78aa98f2011edb6 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Tue, 12 Jul 2016 16:14:37 -0700 Subject: [PATCH 26/34] Add regression test for Get-Process -IncludeUserName --- .../Microsoft.PowerShell.Management/Get-Process.Tests.ps1 | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-Process.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-Process.Tests.ps1 index d2ac52def..64a9f7fe4 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Get-Process.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Get-Process.Tests.ps1 @@ -1,4 +1,8 @@ Describe "Get-Process" { + It "Should support -IncludeUserName" { + (Get-Process powershell -IncludeUserName | Select-Object -First 1).UserName | Should Match $env:USERNAME + } + # These tests are no good, please replace! It "Should return a type of Object[] for Get-Process cmdlet" -Pending:$IsOSX { (Get-Process).GetType().BaseType | Should Be 'array' From 8ac64286e134619e9a9c3e2b7965e8c613e98323 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Tue, 12 Jul 2016 19:25:58 -0700 Subject: [PATCH 27/34] Refactor libpsl to use GetPwUid() --- src/libpsl-native/src/CMakeLists.txt | 1 + src/libpsl-native/src/getfileowner.cpp | 65 ++------------------- src/libpsl-native/src/getpwuid.cpp | 81 ++++++++++++++++++++++++++ src/libpsl-native/src/getpwuid.h | 10 ++++ src/libpsl-native/src/getusername.cpp | 52 +---------------- 5 files changed, 99 insertions(+), 110 deletions(-) create mode 100644 src/libpsl-native/src/getpwuid.cpp create mode 100644 src/libpsl-native/src/getpwuid.h diff --git a/src/libpsl-native/src/CMakeLists.txt b/src/libpsl-native/src/CMakeLists.txt index 6840ceb45..8b5279f8e 100644 --- a/src/libpsl-native/src/CMakeLists.txt +++ b/src/libpsl-native/src/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(psl-native SHARED getstat.cpp + getpwuid.cpp getfileowner.cpp getcurrentprocessorid.cpp getusername.cpp diff --git a/src/libpsl-native/src/getfileowner.cpp b/src/libpsl-native/src/getfileowner.cpp index 53afdb08e..c1bd5b69f 100644 --- a/src/libpsl-native/src/getfileowner.cpp +++ b/src/libpsl-native/src/getfileowner.cpp @@ -9,6 +9,7 @@ #include #include #include "getstat.h" +#include "getpwuid.h" #include "getfileowner.h" //! @brief GetFileOwner returns the owner of a file @@ -22,17 +23,8 @@ //! char* is marshaled as an LPStr, which on Linux is UTF-8. //! @endparblock //! -//! @exception errno Passes these errors via errno to GetLastError: +//! @exception errno Passes this error via errno to GetLastError: //! - ERROR_INVALID_PARAMETER: parameter is not valid -//! - ERROR_FILE_NOT_FOUND: file does not exist -//! - ERROR_ACCESS_DENIED: access is denied -//! - ERROR_INVALID_ADDRESS: attempt to access invalid address -//! - ERROR_STOPPED_ON_SYMLINK: too many symbolic links -//! - ERROR_GEN_FAILURE: I/O error occurred -//! - ERROR_INVALID_NAME: file provided is not a symbolic link -//! - ERROR_INVALID_FUNCTION: incorrect function -//! - ERROR_BAD_PATH_NAME: pathname is too long -//! - ERROR_OUTOFMEMORY insufficient kernal memory //! //! @retval file owner, or NULL if unsuccessful //! @@ -47,59 +39,12 @@ char* GetFileOwner(const char* fileName) return NULL; } - struct stat fileStat; - ret = GetStat(fileName, &fileStat); + struct stat buf; + ret = GetStat(fileName, &buf); if (ret != 0) { return NULL; } - struct passwd pwd; - struct passwd* result = NULL; - char* buf; - - int buflen = sysconf(_SC_GETPW_R_SIZE_MAX); - if (buflen < 1) - { - buflen = 2048; - } - -allocate: - buf = (char*)calloc(buflen, sizeof(char)); - - ret = getpwuid_r(fileStat.st_uid, &pwd, buf, buflen, &result); - - if (ret != 0) - { - switch(errno) - { - case ERANGE: - free(buf); - buflen *= 2; - goto allocate; - case ENOENT: - case ESRCH: - case EBADF: - case EPERM: - errno = ERROR_NO_SUCH_USER; - break; - case ENOMEM: - errno = ERROR_OUTOFMEMORY; - break; - default: - errno = ERROR_GEN_FAILURE; - } - return NULL; - } - - // no result - if (result == NULL) - { - return NULL; - } - - size_t userlen = strnlen(pwd.pw_name, buflen); - char* username = strndup(pwd.pw_name, userlen); - free(buf); - return username; + return GetPwUid(buf.st_uid); } diff --git a/src/libpsl-native/src/getpwuid.cpp b/src/libpsl-native/src/getpwuid.cpp new file mode 100644 index 000000000..62263067e --- /dev/null +++ b/src/libpsl-native/src/getpwuid.cpp @@ -0,0 +1,81 @@ +//! @file getpwuid.cpp +//! @author Andrew Schwartzmeyer +//! @brief returns the username for a uid + +#include +#include +#include +#include +#include +#include +#include "getpwuid.h" + +//! @brief GetPwUid returns the username for a uid +//! +//! GetPwUid +//! +//! @param[in] uid +//! @parblock +//! The user identifier to lookup. +//! @endparblock +//! +//! @exception errno Passes these errors via errno to GetLastError: +//! - ERROR_NO_SUCH_USER: user lookup unsuccessful +//! - ERROR_OUTOFMEMORY insufficient kernel memory +//! - ERROR_GEN_FAILURE: anything else +//! +//! @retval username as UTF-8 string, or NULL if unsuccessful +//! +char* GetPwUid(uid_t uid) +{ + int32_t ret = 0; + struct passwd pwd; + struct passwd* result = NULL; + char* buf; + + int buflen = sysconf(_SC_GETPW_R_SIZE_MAX); + if (buflen < 1) + { + buflen = 2048; + } + +allocate: + buf = (char*)calloc(buflen, sizeof(char)); + + ret = getpwuid_r(uid, &pwd, buf, buflen, &result); + + if (ret != 0) + { + switch(errno) + { + case ERANGE: + free(buf); + buflen *= 2; + goto allocate; + case ENOENT: + case ESRCH: + case EBADF: + case EPERM: + errno = ERROR_NO_SUCH_USER; + break; + case ENOMEM: + errno = ERROR_OUTOFMEMORY; + break; + default: + errno = ERROR_GEN_FAILURE; + } + return NULL; + } + + // no result + if (result == NULL) + { + return NULL; + } + + // allocate copy on heap so CLR can free it + size_t userlen = strnlen(pwd.pw_name, buflen); + char* username = strndup(pwd.pw_name, userlen); + free(buf); + return username; +} diff --git a/src/libpsl-native/src/getpwuid.h b/src/libpsl-native/src/getpwuid.h new file mode 100644 index 000000000..cbf2cca91 --- /dev/null +++ b/src/libpsl-native/src/getpwuid.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include "pal.h" + +PAL_BEGIN_EXTERNC + +char* GetPwUid(uid_t uid); + +PAL_END_EXTERNC diff --git a/src/libpsl-native/src/getusername.cpp b/src/libpsl-native/src/getusername.cpp index 0efab9668..56c3d6d08 100644 --- a/src/libpsl-native/src/getusername.cpp +++ b/src/libpsl-native/src/getusername.cpp @@ -2,64 +2,16 @@ //! @author Andrew Schwartzmeyer //! @brief Implements GetUserName for Linux -#include -#include #include -#include -#include +#include "getpwuid.h" #include "getusername.h" //! @brief GetUserName retrieves the name of the user associated with //! the current thread. //! -//! @exception errno Passes these errors via errno to GetLastError: -//! - ERROR_INVALID_PARAMETER: parameter is not valid -//! - ERROR_NO_SUCH_USER: there was no corresponding user -//! - ERROR_GEN_FAILURE: sysconf() or getpwuid() failed for unknown reasons -//! //! @retval username as UTF-8 string, or null if unsuccessful char* GetUserName() { - errno = 0; - - struct passwd pwd; - struct passwd* result; - // gets the initial suggested size for buf - int buflen = sysconf(_SC_GETPW_R_SIZE_MAX); - if (buflen == -1) - { - errno = ERROR_GEN_FAILURE; - return NULL; - } - std::string buf(buflen, 0); - // geteuid() gets the effective user ID of the calling process, and is always successful - int ret = getpwuid_r(geteuid(), &pwd, &buf[0], buflen, &result); - - // Map errno to Win32 Error Codes - if (ret) - { - switch (errno) - { - case ENOENT: - case ESRCH: - case EBADF: - case EPERM: - errno = ERROR_NO_SUCH_USER; - break; - default: - errno = ERROR_GEN_FAILURE; - } - return NULL; - } - - // Check if no user matched - if (result == NULL) - { - errno = ERROR_NO_SUCH_USER; - return NULL; - } - - // allocate copy on heap so CLR can free it - return strdup(result->pw_name); + return GetPwUid(geteuid()); } From 900b46d28139ff8c4fcc889c648b78a63cc16f3f Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Tue, 12 Jul 2016 20:02:10 -0700 Subject: [PATCH 28/34] Add GetUserFromPid() to libpsl And clean up some tests. --- src/libpsl-native/src/CMakeLists.txt | 1 + src/libpsl-native/src/getuserfrompid.cpp | 47 +++++++++++++++++++ src/libpsl-native/src/getuserfrompid.h | 9 ++++ src/libpsl-native/test/CMakeLists.txt | 1 + src/libpsl-native/test/test-getfileowner.cpp | 15 ++---- .../test/test-getuserfrompid.cpp | 13 +++++ src/libpsl-native/test/test-getusername.cpp | 6 +-- 7 files changed, 76 insertions(+), 16 deletions(-) create mode 100644 src/libpsl-native/src/getuserfrompid.cpp create mode 100644 src/libpsl-native/src/getuserfrompid.h create mode 100644 src/libpsl-native/test/test-getuserfrompid.cpp diff --git a/src/libpsl-native/src/CMakeLists.txt b/src/libpsl-native/src/CMakeLists.txt index 8b5279f8e..8b3d0f19a 100644 --- a/src/libpsl-native/src/CMakeLists.txt +++ b/src/libpsl-native/src/CMakeLists.txt @@ -1,6 +1,7 @@ add_library(psl-native SHARED getstat.cpp getpwuid.cpp + getuserfrompid.cpp getfileowner.cpp getcurrentprocessorid.cpp getusername.cpp diff --git a/src/libpsl-native/src/getuserfrompid.cpp b/src/libpsl-native/src/getuserfrompid.cpp new file mode 100644 index 000000000..fd148a9ff --- /dev/null +++ b/src/libpsl-native/src/getuserfrompid.cpp @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include "pal.h" +#include "getfileowner.h" +#include "getpwuid.h" +#include "getuserfrompid.h" + +char* GetUserFromPid(pid_t pid) +{ + +#if defined(__linux__) + + // Get effective owner of pid from procfs + std::stringstream ss; + ss << "/proc/" << pid; + std::string path; + ss >> path; + + return GetFileOwner(path.c_str()); + +#elif defined(__APPLE__) && defined(__MACH__) + + // Get effective owner of pid from sysctl + struct kinfo_proc oldp; + size_t oldlenp = sizeof(oldp); + int name[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; + u_int namelen = sizeof(name)/sizeof(int); + + // Read-only query + int ret = sysctl(name, namelen, &oldp, &oldlenp, NULL, 0); + if (ret != 0 || oldlenp == 0) + { + errno = ERROR_GEN_FAILURE; + return NULL; + } + + return GetPwUid(oldp.kp_eproc.e_ucred.cr_uid); + +#else + + return NULL; + +#endif + +} diff --git a/src/libpsl-native/src/getuserfrompid.h b/src/libpsl-native/src/getuserfrompid.h new file mode 100644 index 000000000..c019f475f --- /dev/null +++ b/src/libpsl-native/src/getuserfrompid.h @@ -0,0 +1,9 @@ +#pragma once + +#include "pal.h" + +PAL_BEGIN_EXTERNC + +char* GetUserFromPid(pid_t pid); + +PAL_END_EXTERNC diff --git a/src/libpsl-native/test/CMakeLists.txt b/src/libpsl-native/test/CMakeLists.txt index 1ab2bfd3e..ed4440d2a 100644 --- a/src/libpsl-native/test/CMakeLists.txt +++ b/src/libpsl-native/test/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(googletest) add_executable(psl-native-test test-getfileowner.cpp test-locale.cpp + test-getuserfrompid.cpp test-getcurrentprocessid.cpp test-getusername.cpp test-getcomputername.cpp diff --git a/src/libpsl-native/test/test-getfileowner.cpp b/src/libpsl-native/test/test-getfileowner.cpp index 77f1e2fc8..275c63ed1 100644 --- a/src/libpsl-native/test/test-getfileowner.cpp +++ b/src/libpsl-native/test/test-getfileowner.cpp @@ -7,25 +7,18 @@ #include #include "getfileowner.h" -using namespace std; - -//! Test fixture for GetFileOwner -class GetFileOwnerTest : public ::testing::Test +TEST(GetFileOwnerTest, CanGetOwnerOfRoot) { -}; - -TEST_F(GetFileOwnerTest, CanGetOwnerOfRoot) -{ - ASSERT_STREQ(GetFileOwner("/"), "root"); + EXPECT_STREQ(GetFileOwner("/"), "root"); } -TEST_F(GetFileOwnerTest, CannotGetOwnerOfFakeFile) +TEST(GetFileOwnerTest, CannotGetOwnerOfFakeFile) { EXPECT_STREQ(GetFileOwner("SomeMadeUpFileNameThatDoesNotExist"), NULL); EXPECT_EQ(errno, ERROR_FILE_NOT_FOUND); } -TEST_F(GetFileOwnerTest, ReturnsNullForNullInput) +TEST(GetFileOwnerTest, ReturnsNullForNullInput) { EXPECT_STREQ(GetFileOwner(NULL), NULL); EXPECT_EQ(errno, ERROR_INVALID_PARAMETER); diff --git a/src/libpsl-native/test/test-getuserfrompid.cpp b/src/libpsl-native/test/test-getuserfrompid.cpp new file mode 100644 index 000000000..ca6d866c6 --- /dev/null +++ b/src/libpsl-native/test/test-getuserfrompid.cpp @@ -0,0 +1,13 @@ +//! @file test-getuserfrompid.cpp +//! @author Andrew Schwartzmeyer +//! @brief Unit tests for GetUserFromPid + +#include +#include +#include "getuserfrompid.h" + +TEST(GetUserFromPid, Success) +{ + char* expected = getpwuid(geteuid())->pw_name; + EXPECT_STREQ(GetUserFromPid(getpid()), expected); +} diff --git a/src/libpsl-native/test/test-getusername.cpp b/src/libpsl-native/test/test-getusername.cpp index 2ccb29f68..3f4751276 100644 --- a/src/libpsl-native/test/test-getusername.cpp +++ b/src/libpsl-native/test/test-getusername.cpp @@ -2,9 +2,6 @@ //! @author Andrew Schwartzmeyer //! @brief Unit tests for GetUserName -#include -#include -#include #include #include #include "getusername.h" @@ -12,6 +9,5 @@ TEST(GetUserName, Success) { char* expected = getpwuid(geteuid())->pw_name; - ASSERT_TRUE(expected != NULL); - ASSERT_EQ(GetUserName(), std::string(expected)); + EXPECT_STREQ(GetUserName(), expected); } From 2abe4619922ed57a9b8a51e1bb4946566cf61f29 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Tue, 12 Jul 2016 20:05:11 -0700 Subject: [PATCH 29/34] Refactor to use GetUserFromPid --- .../commands/management/Process.cs | 2 +- .../CoreCLR/CorePsPlatform.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs index 13aabd84d..1a622ed28 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs @@ -867,7 +867,7 @@ namespace Microsoft.PowerShell.Commands { string userName = null; #if LINUX - userName = Platform.NonWindowsGetFileOwner("/proc/" + process.Id); + userName = Platform.NonWindowsGetUserFromPid(process.Id); #else IntPtr tokenUserInfo = IntPtr.Zero; IntPtr processTokenHandler = IntPtr.Zero; diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index fc735c470..eb9d3b262 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -354,11 +354,11 @@ namespace System.Management.Automation } } - internal static string NonWindowsGetFileOwner(string path) + internal static string NonWindowsGetUserFromPid(int path) { if (!IsWindows) { - return LinuxPlatform.GetFileOwner(path); + return LinuxPlatform.GetUserFromPid(path); } else { @@ -752,9 +752,9 @@ namespace System.Management.Automation return Native.FollowSymLink(path); } - public static string GetFileOwner(string path) + public static string GetUserFromPid(int pid) { - return Native.GetFileOwner(path); + return Native.GetUserFromPid(pid); } [StructLayout(LayoutKind.Sequential)] @@ -828,7 +828,7 @@ namespace System.Management.Automation [DllImport(psLib, CharSet = CharSet.Ansi, SetLastError = true)] [return: MarshalAs(UnmanagedType.LPStr)] - internal static extern string GetFileOwner([MarshalAs(UnmanagedType.LPStr)]string filePath); + internal static extern string GetUserFromPid(int pid); } } From 92495065e8043e7b2e4d59659926596cda73c982 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 00:08:06 -0700 Subject: [PATCH 30/34] Guard Linux native build steps So that non-zero exit codes fail the build, and the tests are run verbosely. --- build.psm1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.psm1 b/build.psm1 index 41021dde6..1599cce29 100644 --- a/build.psm1 +++ b/build.psm1 @@ -201,9 +201,9 @@ function Start-PSBuild { try { Push-Location $Native - cmake -DCMAKE_BUILD_TYPE=Debug . - make -j - make test + Start-NativeExecution { cmake -DCMAKE_BUILD_TYPE=Debug . } + Start-NativeExecution { make -j } + Start-NativeExecution { ctest --verbose } } finally { Pop-Location } From d44d4859381f10b6cd41a39bdec3de63d8bd3445 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 12:32:18 -0700 Subject: [PATCH 31/34] Fix FQDN test for OS X --- .../src/getfullyqualifiedname.cpp | 6 +-- .../test/test-getfullyqualifiedname.cpp | 39 +++++++++---------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/libpsl-native/src/getfullyqualifiedname.cpp b/src/libpsl-native/src/getfullyqualifiedname.cpp index 44178a0a5..bc4fe1b19 100644 --- a/src/libpsl-native/src/getfullyqualifiedname.cpp +++ b/src/libpsl-native/src/getfullyqualifiedname.cpp @@ -27,12 +27,12 @@ char *GetFullyQualifiedName() char *computerName = GetComputerName(); if (computerName == NULL) { - return NULL; + return NULL; } if (strchr(computerName, '.') != NULL) { - return computerName; + return computerName; } struct addrinfo hints, *info; @@ -51,7 +51,7 @@ char *GetFullyQualifiedName() if (getaddrinfo(computerName, "http", &hints, &info) != 0) { errno = ERROR_BAD_NET_NAME; - return NULL; + return NULL; } // info is actually a link-list. We'll just return the first full name diff --git a/src/libpsl-native/test/test-getfullyqualifiedname.cpp b/src/libpsl-native/test/test-getfullyqualifiedname.cpp index 7c0353cd1..94c2947d6 100644 --- a/src/libpsl-native/test/test-getfullyqualifiedname.cpp +++ b/src/libpsl-native/test/test-getfullyqualifiedname.cpp @@ -3,38 +3,35 @@ //! @brief Unit tests for GetFullyQualifiedName #include +#include "getcomputername.h" #include "getfullyqualifiedname.h" #include #include #include #include -//! Test fixture for GetComputerNameTest -class GetFullyQualifiedNameTest : public ::testing::Test -{ -}; - -TEST_F(GetFullyQualifiedNameTest, ValidateLinuxGetFullyQualifiedDomainName) +TEST(GetFullyQualifiedNameTest, ValidateLinuxGetFullyQualifiedDomainName) { std::string actual(GetFullyQualifiedName()); - std::string hostname(_POSIX_HOST_NAME_MAX, 0); - ASSERT_FALSE(gethostname(&hostname[0], hostname.length())); - // trim null characters from string - hostname = std::string(hostname.c_str()); + std::string hostname(GetComputerName()); - struct addrinfo hints, *info; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = AI_CANONNAME; - ASSERT_FALSE(getaddrinfo(hostname.c_str(), "http", &hints, &info)); + // FQDN starts with hostname (which may be fully qualified) + EXPECT_LE(hostname, actual); - // Compare hostname part of FQDN - ASSERT_EQ(hostname, actual.substr(0, hostname.length())); + // hostname did not include domain name, so test it separately + if (hostname < actual) + { + struct addrinfo hints, *info; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_CANONNAME; + EXPECT_FALSE(getaddrinfo(hostname.c_str(), "http", &hints, &info)); - // Compare canonical name to FQDN - ASSERT_EQ(info->ai_canonname, actual); + // Compare canonical name to FQDN + EXPECT_STREQ(info->ai_canonname, actual.c_str()); - freeaddrinfo(info); + freeaddrinfo(info); + } } From 85d9370c2f7cdcbe59c5cf7604d309116359a6b2 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 15:55:15 -0700 Subject: [PATCH 32/34] Clean up FollowSymlink --- src/libpsl-native/src/followsymlink.cpp | 150 ++++++++---------------- 1 file changed, 47 insertions(+), 103 deletions(-) diff --git a/src/libpsl-native/src/followsymlink.cpp b/src/libpsl-native/src/followsymlink.cpp index 6a77bcc52..46576256e 100644 --- a/src/libpsl-native/src/followsymlink.cpp +++ b/src/libpsl-native/src/followsymlink.cpp @@ -37,124 +37,68 @@ char* FollowSymLink(const char* fileName) { - errno = 0; + errno = 0; - // if filename is null, return null value if (!fileName) { errno = ERROR_INVALID_PARAMETER; return NULL; } - //if lstat in IsSymLink returns -1, path does not exist - if (IsSymLink(fileName) == -1) + // return null for non symlinks + if (!IsSymLink(fileName)) { return NULL; } - /*If the path is a symlink, the function return the absolute filepath if valid. if not valid, return null*/ - if (IsSymLink(fileName)) + // attempt to resolve with the absolute file path + char buffer[PATH_MAX]; + char* realPath = realpath(fileName, buffer); + + if (realPath) { - //Attempt to resolve with the absolute filepath - char actualpath[PATH_MAX+1]; - char* realPath = realpath(fileName, actualpath); - - //if realPath is null, go onto the readlink implementation - if (realPath == NULL) - { - char buffer[PATH_MAX]; - ssize_t sz = readlink(fileName, buffer, PATH_MAX); - - if (sz == -1) - { - switch(errno) - { - case EACCES: - errno = ERROR_ACCESS_DENIED; - break; - case EFAULT: - errno = ERROR_INVALID_ADDRESS; /*If the path is a symlink, the function return the absolute filepath if valid. if not valid, return null*/ - break; - case EINVAL: - errno = ERROR_INVALID_NAME; - case EIO: - errno = ERROR_GEN_FAILURE; - break; - case ELOOP: - errno = ERROR_STOPPED_ON_SYMLINK; - break; - case ENAMETOOLONG: - errno = ERROR_BAD_PATH_NAME; - break; - case ENOENT: - errno = ERROR_FILE_NOT_FOUND; - break; - case ENOMEM: - errno = ERROR_OUTOFMEMORY; - break; - case ENOTDIR: - errno = ERROR_BAD_PATH_NAME; - break; - default: - errno = ERROR_INVALID_FUNCTION; - } + return strndup(realPath, strlen(realPath) + 1); + } - return NULL; - } - - buffer[sz] = '\0'; - return strndup(buffer, sz + 1); - } - else + // if the path wasn't resolved, use readlink + ssize_t sz = readlink(fileName, buffer, PATH_MAX); + if (sz == -1) + { + switch(errno) { - return strndup(realPath, strlen(realPath) + 1 ); + case EACCES: + errno = ERROR_ACCESS_DENIED; + break; + case EFAULT: + errno = ERROR_INVALID_ADDRESS; + break; + case EINVAL: + errno = ERROR_INVALID_NAME; + case EIO: + errno = ERROR_GEN_FAILURE; + break; + case ELOOP: + errno = ERROR_STOPPED_ON_SYMLINK; + break; + case ENAMETOOLONG: + errno = ERROR_BAD_PATH_NAME; + break; + case ENOENT: + errno = ERROR_FILE_NOT_FOUND; + break; + case ENOMEM: + errno = ERROR_OUTOFMEMORY; + break; + case ENOTDIR: + errno = ERROR_BAD_PATH_NAME; + break; + default: + errno = ERROR_INVALID_FUNCTION; } + + return NULL; } - - else - { - char buffer[PATH_MAX]; - ssize_t sz = readlink(fileName, buffer, PATH_MAX); - - if (sz == -1) - { - switch(errno) - { - case EACCES: - errno = ERROR_ACCESS_DENIED; - break; - case EFAULT: - errno = ERROR_INVALID_ADDRESS; /*If the path is a symlink, the function return the absolute filepath if valid. if not valid, return null*/ - break; - case EINVAL: - errno = ERROR_INVALID_NAME; - case EIO: - errno = ERROR_GEN_FAILURE; - break; - case ELOOP: - errno = ERROR_STOPPED_ON_SYMLINK; - break; - case ENAMETOOLONG: - errno = ERROR_BAD_PATH_NAME; - break; - case ENOENT: - errno = ERROR_FILE_NOT_FOUND; - break; - case ENOMEM: - errno = ERROR_OUTOFMEMORY; - break; - case ENOTDIR: - errno = ERROR_BAD_PATH_NAME; - break; - default: - errno = ERROR_INVALID_FUNCTION; - } - return NULL; - } - - buffer[sz] = '\0'; - return strndup(buffer, sz + 1); - - } + buffer[sz] = '\0'; + return strndup(buffer, sz + 1); } From 2a790591f06b7124ef9e2cbeab9672c6ef851559 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 15:56:12 -0700 Subject: [PATCH 33/34] Clean up CreateSymLink tests --- src/libpsl-native/test/test-createsymlink.cpp | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/libpsl-native/test/test-createsymlink.cpp b/src/libpsl-native/test/test-createsymlink.cpp index abc0646c6..427221c24 100644 --- a/src/libpsl-native/test/test-createsymlink.cpp +++ b/src/libpsl-native/test/test-createsymlink.cpp @@ -32,19 +32,19 @@ protected: // First create a temp file int fd = mkstemp(fileTemplateBuf); EXPECT_TRUE(fd != -1); - file = fileTemplateBuf; + file = fileTemplateBuf; - // Create a temp directory - dir = mkdtemp(dirTemplateBuf); + // Create a temp directory + dir = mkdtemp(dirTemplateBuf); EXPECT_TRUE(dir != NULL); // Create symbolic link to file - int ret1 = CreateSymLink(fileSymLink.c_str(), file); - EXPECT_EQ(ret1, 1); - + int ret1 = CreateSymLink(fileSymLink.c_str(), file); + EXPECT_EQ(ret1, 1); + // Create symbolic link to directory - int ret2 = CreateSymLink(dirSymLink.c_str(), dir); - EXPECT_EQ(ret2, 1); + int ret2 = CreateSymLink(dirSymLink.c_str(), dir); + EXPECT_EQ(ret2, 1); } ~CreateSymLinkTest() @@ -52,16 +52,16 @@ protected: int ret; ret = unlink(fileSymLink.c_str()); - EXPECT_EQ(0, ret); + EXPECT_EQ(0, ret); ret = unlink(dirSymLink.c_str()); - EXPECT_EQ(0, ret); + EXPECT_EQ(0, ret); ret = unlink(file); - EXPECT_EQ(0, ret); + EXPECT_EQ(0, ret); - ret = rmdir(dir); - EXPECT_EQ(0, ret); + ret = rmdir(dir); + EXPECT_EQ(0, ret); } }; @@ -77,7 +77,7 @@ TEST_F(CreateSymLinkTest, FilePathNameDoesNotExist) std::string invalidFile = "/tmp/symlinktest_invalidFile"; std::string invalidLink = "/tmp/symlinktest_invalidLink"; - // make sure neither exists + // make sure neither exists unlink(invalidFile.c_str()); unlink(invalidLink.c_str()); From 894f8401a1749fe97e896c9d4f915a4c5c17d775 Mon Sep 17 00:00:00 2001 From: Andrew Schwartzmeyer Date: Wed, 13 Jul 2016 16:00:10 -0700 Subject: [PATCH 34/34] Fix CreateSymLink tests for OS X /tmp is symlinked to /private/tmp and since we're testing symlink resolution through FollowSymlink, we need to change what we expect to the realpath. --- src/libpsl-native/test/test-createsymlink.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libpsl-native/test/test-createsymlink.cpp b/src/libpsl-native/test/test-createsymlink.cpp index 427221c24..7bb9997b3 100644 --- a/src/libpsl-native/test/test-createsymlink.cpp +++ b/src/libpsl-native/test/test-createsymlink.cpp @@ -97,7 +97,9 @@ TEST_F(CreateSymLinkTest, SymLinkToFile) EXPECT_EQ(1, retVal); std::string target = FollowSymLink(fileSymLink.c_str()); - EXPECT_EQ(target, file); + char buffer[PATH_MAX]; + std::string expected = realpath(file, buffer); + EXPECT_EQ(target, expected); } TEST_F(CreateSymLinkTest, SymLinkToDirectory) @@ -106,7 +108,9 @@ TEST_F(CreateSymLinkTest, SymLinkToDirectory) EXPECT_EQ(1, retVal); std::string target = FollowSymLink(dirSymLink.c_str()); - EXPECT_EQ(target, dir); + char buffer[PATH_MAX]; + std::string expected = realpath(dir, buffer); + EXPECT_EQ(target, expected); } TEST_F(CreateSymLinkTest, SymLinkAgain)