Skip to content

Conversation

SteveL-MSFT
Copy link
Member

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

@iSazonov
Copy link
Collaborator

iSazonov commented Mar 28, 2017

@iSazonov
Copy link
Collaborator

We can use Path.GetTempPath

@SteveL-MSFT
Copy link
Member Author

I'll update it to use GetTempPath()

@SteveL-MSFT SteveL-MSFT requested a review from iSazonov March 28, 2017 16:47
/// <summary>
/// Get a temporary directory to use, needs to be unique to avoid collision
/// </summary>
public static string GetTemporaryDirectory()
Copy link
Contributor

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?

Copy link
Member Author

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());
Copy link
Contributor

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.

Copy link
Member Author

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);
Copy link
Contributor

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.

Copy link
Member Author

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");
Copy link
Collaborator

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?

Copy link
Member Author

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

Copy link
Collaborator

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()
Copy link
Collaborator

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree

Copy link
Collaborator

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));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Infinite loop can happen.

Copy link
Member Author

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe string.Empty?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agree

Copy link
Collaborator

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); }
Copy link
Collaborator

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.

Copy link
Member Author

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

Copy link
Collaborator

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
Copy link
Collaborator

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?).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, updated

Copy link
Collaborator

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));
Copy link
Collaborator

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.

Copy link
Member Author

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.

Copy link
Collaborator

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
Copy link
Collaborator

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?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

Copy link
Collaborator

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());
Copy link
Contributor

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

Copy link
Member Author

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()?

Copy link
Collaborator

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?

Copy link
Member Author

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

Copy link
Collaborator

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.

Copy link
Member Author

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
Copy link
Collaborator

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"?

Copy link
Member Author

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

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.

Copy link
Collaborator

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());
Copy link
Collaborator

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.

@SteveL-MSFT
Copy link
Member Author

@iSazonov @lzybkr any other feedback?

@iSazonov
Copy link
Collaborator

LGTM.

@SteveL-MSFT
Copy link
Member Author

@iSazonov can you mark your review as Approved? thanks

@iSazonov
Copy link
Collaborator

iSazonov commented Apr 1, 2017

Approved.

Copy link
Contributor

@lzybkr lzybkr left a 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.

@SteveL-MSFT
Copy link
Member Author

@lzybkr clean up added

@iSazonov
Copy link
Collaborator

iSazonov commented Apr 2, 2017

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).
Added clean-up is 👍

@lzybkr
Copy link
Contributor

lzybkr commented Apr 3, 2017

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.

@lzybkr
Copy link
Contributor

lzybkr commented Apr 3, 2017

@SteveL-MSFT - I've approved the code, but would you mind squashing and improving the commit message?

@SteveL-MSFT
Copy link
Member Author

@lzybkr I'll take care of it next week, at Whistler this week without my laptop

@SteveL-MSFT
Copy link
Member Author

@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.
@lzybkr lzybkr merged commit 753b196 into PowerShell:master Apr 9, 2017
@SteveL-MSFT SteveL-MSFT deleted the nohome branch June 19, 2017 21:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants