profesorfalken / jPowerShell

Simple Java API to interact with PowerShell console
Apache License 2.0
218 stars 84 forks source link

"$pid" command failing on initialize #56

Open mreedmdev opened 5 years ago

mreedmdev commented 5 years ago

As a user of earlier versions of jpowershell, I've upgraded to 3.0.1, but cannot seem to get off the ground. When attempting to openSession(), at line 168 the PowerShell.inialize method fails whenh parsing the result of the $pid command. In my case it is always an empty string and throws NumberFormatException.

//Get and store the PID of the process
this.pid = Long.valueOf(executeCommand("$pid").getCommandOutput());

I've tried configuring my own powershell (powershell-core) and the default installation of PowerShell in my environment. I've adjusted timeouts to give powershell more time to start since it appears the resultant "" is because PowerShell never gets started and cannot execute executeCommand("$pid") on line 168. This results in a NumberFormatException.

Used jpowershell.properties to set maxWait so that openSession is effected. maxWait:30000 and greater values.

Have tried nearly every variant of simply calling openSession and it fails regardless as described.

  try(PowerShell powerShell = PowerShell.openSession()) {
    PowerShellResponse response = powerShell.
        executeScript(pathToScript);
    log.info("{}", response);
  }catch(Exception e) {
    log.error("{}", e.getMessage());
  }

It seems the cause is that:

I've ensured that powershell is available either as the default 'powershell.exe' or a custom powershell installation. No matter which configuration I try it seems Powershell is not started. Any Ideas on my problme?

As a secondary item, in working my problem I think the line in bullet 3 should be changed to handle this situation better?

Any help would be appreciated. Thanks

jflefebvre06 commented 5 years ago

Hello,

Same problem.

To reproduce, add a file "%USERPROFILE%\documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1"

with the content :

#$HOME\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1

Write-Host "Loaded."
profesorfalken commented 5 years ago

Hello, Weird. Thanks for reporting it. I will check this.

Best regards

profesorfalken commented 5 years ago

Hello,

It should be OK if you try the 3.0.2 version.

Nevertheless I would like to understand why this happens, because I am not able to reproduce it.

mreedmdev commented 5 years ago

profesorfalken, It does NOT occur when I issue the $PID command from a console. I get a normal response of the process id. This only occurs when using jpowershell as described in my original post(if it is clear, i hope).

Windows Powershell v5.0.10586.117 and Powershell CORE v6.1.0

Thanks for your attention to this! I am happy to provide any more useful information. I have already update to your latest fix, but have not tested it yet. I will let you know.

Thanks!

mreedmdev commented 5 years ago

After getting the latest code I am now getting something other than "" empty string. Exception Message -> For input string: "Running MicrosoftPowerShell_profile.ps1... 30468"

In fact openSession() is throwing the exception and now seems to be trying to parse what you see in the exception message above "Running MicrosoftPowerShell_profile.ps1..." is prefixing the returned PID and infact instead of just a blank string. After several runs I see multiple powershell processes(started as a result of the test run) in task manager hung up after multiple subsequent runs.

I'll let you know some more information tomorrow. I incrementally rolled back to v2.0 and seemingly get the same message on all these versions. Although I had to change my code a bit since in v2.0 AutoCloseable is not implemented, as expected. So this let me know I was running against the correct version(s).

So.. I am going to pull latest again tomorrow and ensure I have made no mistakes in pulling, building and running the latest code for 3.0.2, but this is what I discovered today.

Thanks

jflefebvre06 commented 5 years ago

@profesorfalken works fine with 3.0.2 thank you.

profesorfalken commented 5 years ago

@mreedmdev Thank you for your answer. Now I see the problem. It is very interesting. I think I know how to fix it. I will try to do it today so you will be able to use the 3.x version which includes the proper autocloseable.

Best regards

profesorfalken commented 5 years ago

A new 3.0.3 version is available. Can you check @mreedmdev ?

Thanks

mreedmdev commented 5 years ago

@profesorfalken - Yes, I will check immediately and let you know the result ASAP.

Thank You !

mreedmdev commented 5 years ago

@profesorfalken, 3.0.3 seems to exhibit similar results. 1)It seems that the result of starting the session is loading the profile which results in the message "Running MicrosoftPowerShell_profile.ps1... and this is getting picked up in the outputstream being parsed when the $pid command is issued. 2)When I manually run the exact command getting ran by openSession() in a console, this is the first message output to the console before a prompt is presented. 3)Then I run the $pid command manually and get expected pid.

As I think you have already figured out the buffer is not getting cleared prior to the $pid command getting issued and NumberFormatException is thrown trying to parse the profile load message concatenated with the pid from the $pid command ""Running MicrosoftPowerShell_profile.ps1...23432"

This is the powershell prompt input/output from manually taking the same steps the code takes.

`C:>powershell.exe Windows PowerShell Copyright (C) 2015 Microsoft Corporation. All rights reserved.

Running MicrosoftPowerShell_profile.ps1... C:\mreed

$pid 17276 C:\mreed `

I think you are on the right track clearing the buffer prior to the $pid being issued, but my test of 3.0.3 show that the entire "Running MicrosoftPowerShell_profile.ps1...23432" is still being parsed and throwing.

Mark

mreedmdev commented 5 years ago

@profesorfalken

1)I have found that the profile that is loading has a line in the script Write-Host "Running MicrosoftPowerShell_profile.ps1..." So this is where that is coming from. @jflefebvre06 pointed this out in his earlier post.

2)If I remove the line from the profile the $PID command is executed and parsed correctly!

A couple of thoughts: 1) I am using the jpowershell.properties file to create longer waits since some configurations take longer to load powershell than others. 2)If the loading of the session does timeout there is still an NumberFormatException that is thrown because the result of the $PID command is "" empty string on a timeout and this still seems to occur on 3.0.3 3)Not sure if any users may have any Write-Host commands in any profile scripts, but this can cause the problem of the output being picked up in the buffer of the $PID command output and reproduces the NumberFormatException.

So at this point if I exclude the Write-Host and avoid an openSession() timeout, I am past the reported issue with 3.0.3. Mark

wortner commented 5 years ago

3.0.3 is working. I checked the power shell folder, but it is empty so I don't need to take care about Write-Host. So far couple of times I have got closeAndWait WARNING: Powershell process cannot be closed. Session seems to be blocked. It seems to be connected with some timeout setting and now I'm looking for way to set it.

jflefebvre06 commented 5 years ago

@profesorfalken No way to execute command excluding powershell profile ? perhaps -NoProfile option ?

Fix proposal :

PowerShellCommandProcessor.java :

pb = new ProcessBuilder("cmd.exe", "/c", "chcp", codePage, ">", "NUL", "&", powerShellExecutablePath,
                    "-ExecutionPolicy", "Bypass", "-NoExit", "-Command", "-");
pb = new ProcessBuilder("cmd.exe", "/c", "chcp", codePage, ">", "NUL", "&", powerShellExecutablePath,
                    "-ExecutionPolicy", "Bypass", "-NoExit", "-NoProfile", "-Command", "-");

PowerShell.java : Remove commandOutput.replaceAll("\\D", ""); from getPID() method.

profesorfalken commented 5 years ago

@mreedmdev

Hi Mark, This is exactly how I tested the fix for 3.0.3:

-First, I created a profile file at USER\Documents\WindowsPowerShell called Microsoft.PowerShell_profile.ps1 -I have written inside: Write-Host "Running MicrosoftPowerShell_profile.ps1..." -After that, I have launch one test and I have seen that executeCommand("$pid").getCommandOutput() returned: Running MicrosoftPowerShell_profile.ps1... 10562 -So I have added the clearOutput() method that clears the initial buffer before launching the first command. -And after that, I had the good result: 10562

So the problem of write-host on profile should be totally fixed in 3.0.3.

I still have the profile file set in my system and all the tests pass without a single problem. I cannot understand how that continued to generate a NumberFormatException in your case with the 3.0.3 version.

2)If the loading of the session does timeout there is still an NumberFormatException that is thrown because the result of the $PID command is "" empty string on a timeout and this still seems to occur on 3.0.3

I cannot understand how il can return an emty string. Event in 3.0.2 that should be fixed. Are you sure you are using the 3.0.3 version?

Best regards

profesorfalken commented 5 years ago

@wortner

Thanks to confirm that it works.

The closeAndWait WARNING: Powershell process cannot be closed. Session seems to be blocked. happens sometimes when the PowerShell console hangs (for example, if it has not finished to execute the command). This is why I have to take the PID at the initialization. In this cases I use this PID to kill the process and avoid 'zombie' processes in the background. Maybe it happens that your command is a little bit long, so in that case you can simply give a little more of time. By default it waits 10 secs.

If you want to give more time:

//Init
Map<String, String> myConfig = new HashMap<>();
myConfig.put("maxWait", "30000");
response = powerShell.configuration(myConfig).executeCommand("Get-WmiObject Win32_BIOS");
//Close

Best regards

profesorfalken commented 5 years ago

@jflefebvre06

Thats a great idea!. Do you want to make me a Pull Request yourself or do you prefer me to change it?

Best regards

jflefebvre06 commented 5 years ago

@profesorfalken Im out of office, could you please make the change

profesorfalken commented 5 years ago

v3.0.4 is published

DipteshSavla commented 5 years ago

Getting same issue on v3.1.0 for Powershell version Major Minor Build Revision


5 1 14393 1884

Best Regards

dsagomonov commented 4 years ago

Getting same issue on v3.1.0 java.lang.NumberFormatException: For input string: "3784" at java.lang.NumberFormatException.forInputString(Unknown Source) at java.lang.Long.parseLong(Unknown Source) at java.lang.Long.valueOf(Unknown Source) at com.profesorfalken.jpowershell.PowerShell.initalize(PowerShell.java:168) at com.profesorfalken.jpowershell.PowerShell.openSession(PowerShell.java:129) at com.profesorfalken.jpowershell.PowerShell.openSession(PowerShell.java:108) at com.profesorfalken.wmi4java.WMIPowerShell.executeCommand(WMIPowerShell.java:42) at com.profesorfalken.wmi4java.WMIPowerShell.queryObject(WMIPowerShell.java:122) at com.profesorfalken.wmi4java.WMI4Java.getWMIObjectList(WMI4Java.java:332)

jenskreidler commented 1 year ago

@wortner

Thanks to confirm that it works.

The closeAndWait WARNING: Powershell process cannot be closed. Session seems to be blocked. happens sometimes when the PowerShell console hangs (for example, if it has not finished to execute the command). This is why I have to take the PID at the initialization. In this cases I use this PID to kill the process and avoid 'zombie' processes in the background. Maybe it happens that your command is a little bit long, so in that case you can simply give a little more of time. By default it waits 10 secs.

If you want to give more time:

//Init
Map<String, String> myConfig = new HashMap<>();
myConfig.put("maxWait", "30000");
response = powerShell.configuration(myConfig).executeCommand("Get-WmiObject Win32_BIOS");
//Close

Best regards

Since Java 9+ we could get the PID directly from the [java.lang.Process#pid()](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Process.html#pid()). We could get rid of the extra-Powershell call.