-
Notifications
You must be signed in to change notification settings - Fork 7.8k
PowerShell doesn't start if env var HOME is not defined #3437
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Can we use something like |
We can use Path.GetTempPath |
I'll update it to use |
/// <summary> | ||
/// Get a temporary directory to use, needs to be unique to avoid collision | ||
/// </summary> | ||
public static string GetTemporaryDirectory() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this need to be public?
And shouldn't it return the same directory every time it's called?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
making it internal. yeah, it should return the same directory per process
string tempDir = ""; | ||
do | ||
{ | ||
tempDir = Path.Combine(Path.GetTempPath(),System.Guid.NewGuid().ToString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't really matter here because the loop should only run once, but as a matter of style, you should move calls that always (or should always) return the same thing (like Path.GetTempPath
) outside of the loop.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will fix
|
||
try | ||
{ | ||
Directory.CreateDirectory(tempDir); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is still a race here - extremely unlikely, but another thread/process could create the same directory you're trying to create.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CreateDirectory
will return tempDir if that directory already exists
@@ -1044,6 +1044,11 @@ private static string InternalGetFolderPath(SpecialFolder folder) | |||
string folderPath = null; | |||
|
|||
#if UNIX | |||
string envHome = System.Environment.GetEnvironmentVariable("HOME"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we protect Windows too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the code where "USERPROFILE" env var is used, it appears to handle the case where it's null already
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see.
Closed.
/// <summary> | ||
/// Get a temporary directory to use, needs to be unique to avoid collision | ||
/// </summary> | ||
public static string GetTemporaryDirectory() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I open Issue to enhance New-TemporaryFile
cmdlet to return a temporary directory.
So maybe we move the code to PSUtils.cs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Closed.
{ | ||
tempDir = Path.Combine(Path.GetTempPath(),System.Guid.NewGuid().ToString()); | ||
} | ||
while (Directory.Exists(tempDir)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Infinite loop can happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure how we can be in an infinite loop realistically here
} | ||
catch (UnauthorizedAccessException) | ||
{ | ||
return ""; // will become current working directory |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe string.Empty?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Closed.
break; | ||
case SpecialFolder.LocalApplicationData: | ||
folderPath = System.IO.Path.Combine(System.Environment.GetEnvironmentVariable("HOME"), ".config"); | ||
folderPath = System.IO.Path.Combine(envHome, ".config"); | ||
if (!System.IO.Directory.Exists(folderPath)) { System.IO.Directory.CreateDirectory(folderPath); } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the worst case, here we can get UnauthorizedAccessException
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will wrap in try..catch
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Closed.
@@ -204,6 +206,20 @@ internal static class CommonEnvVariableNames | |||
#endif | |||
} | |||
|
|||
/// <summary> | |||
/// Get a temporary directory to use, needs to be unique to avoid collision |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe the comment not clear enough (unique for current process?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, updated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Closed.
{ | ||
tempDir = Path.Combine(tempPath,System.Guid.NewGuid().ToString()); | ||
} | ||
while (Directory.Exists(tempDir)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe remove the cycle? If we believe that it cannot be infinite that means we believe that NewGuid
is always globally unique and then the cycle is generally not needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like the risk of an infinite loop is slightly lower than the risk of NewGuid() not being unique although realistically, I expect both to be practically not a real issue but having the loop seems very slightly safer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Closed.
} | ||
catch (UnauthorizedAccessException) | ||
{ | ||
// some scenarios run as accounts without filesystem access |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, sorry, I again don't understand the comment for the first time. Could you make it more clear?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Closed.
string tempPath = Path.GetTempPath(); | ||
do | ||
{ | ||
tempDir = Path.Combine(tempPath,System.Guid.NewGuid().ToString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming this scenario is common (no HOME), we'll eventually create a ton of these directories.
I think we should either:
- Use a reproducible name
- Remove the directory before the process exits
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't know how common this will be, but cleaning up makes sense. Putting it in ConsoleHost.Dispose()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is a common scenario, then perhaps we should require to specify a temporary directory using one of the possible ways?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov although not discoverable, for advanced uses you can define XDG_CONFIG_HOME, XDG_CACHE_HOME, and XDG_DATA_HOME env vars which PowerShell will use (on non-Windows). This was really only to support simple cases (like Puppet) to not put a barrier for adoption of PowerShell
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant that maybe we need to specify in the documentation that for proper PowerShell operation, users need to define (enumeration here) variables.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Created doc bug MicrosoftDocs/PowerShell-Docs#1126
I don't think users should be required to create those env vars, we should just work in most cases
@@ -195,7 +197,7 @@ internal static bool IsInbox | |||
/// Some common environment variables used in PS have different | |||
/// names in different OS platforms | |||
/// </summary> | |||
internal static class CommonEnvVariableNames | |||
public static class CommonEnvVariableNames |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should it really be "public"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking it would be useful for cmdlet authors rather than having to rely on HOME
for Unix and USERPROFILE
for Windows
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, perhaps we should keep it internal for now until https://github.com/PowerShell/PowerShell-RFC/blob/master/1-Draft/RFC0019-PowerShell-Core-Interop-Module.md gets resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's just a constant - makes no sense.
We would discuss this in the topic about creating platform unified scripts to get common solution and best practices. But we have not yet created such a discussion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You said the first :-)
string tempPath = Path.GetTempPath(); | ||
do | ||
{ | ||
tempDir = Path.Combine(tempPath,System.Guid.NewGuid().ToString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant that maybe we need to specify in the documentation that for proper PowerShell operation, users need to define (enumeration here) variables.
LGTM. |
@iSazonov can you mark your review as |
Approved. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good except there is no cleanup.
We don't really have an ideal place for engine wide cleanup. ConsoleHost.Dispose isn't it - that only applies to the console host.
One possibility is when closing the last runspace (LocalRunspace.Dispose or something like that.) This is somewhat clean because we track all of the runspaces for debugging, but it does require extra care because a host may create new runspaces after you've deleted the directory.
@lzybkr clean up added |
I'd not worried much about cleaning up temporary files and directories because the implication is that it's "trash", which can be removed by the user or by the system at any time. For example, Windows has Disk CleanUp Utility (I run it regularly on users computer by SCCM, GPO and scheduled tasks) and a group policy to create a temporary directory on the user session (%temp% is created on logon and removed on logoff). |
Cleanup is important because shell processes are sometimes created frequently, and degrading system performance due to too many directories in TMP is not something we want to do. Not everybody cleans up TMP, or maybe not often enough at any rate. |
@SteveL-MSFT - I've approved the code, but would you mind squashing and improving the commit message? |
@lzybkr I'll take care of it next week, at Whistler this week without my laptop |
@lzybkr better? |
used does not have a home directory. Updated PowerShell to use a process specific temporary directory if HOME, CONFIG, CACHE, and DATA directories are not availble. Temporary directory is cleaned up when last runspace is disposed.
In some environments (like Puppet), the HOME env var is not defined and this causes PowerShell to fail to start as it assumes HOME is available and tries to end up concatenating $null with a string. Fix is to return an empty string instead of $null. Side effect is that some working directories like
.cache
get created in the current working directory instead of HOME (which doesn't exist in this case) which seems acceptable.Addresses #1794