1

Description

As part of a an open-source project, I am writing a script that will allow to host Jenkins agent as a service.

Jenkins is running on Java, so I had to call Java.exe.

What I Tried

For this, I need to make sure that when the script is being terminated, the java process is being terminated as well.

I found that using CMD /c or Start-Process is starting a new process, and because of this, stopping the PowerShell script does not stop the java.exe as well. The only thing that worked was the call operator, &.

However, as you see below it treats the infomration returned from JAVA as an exception. IT IS FALSE-POSITIVE.

What I tried and Need

I need to make sure the actual information is being sent to output as usual, not hidden.

This rules out $ErrorActionPreference = 'SilentlyContinue'

Also tried 2>&1 and *>&1 but same result

Any ideas will be great.

The Code

& "$Java\java.exe" -jar "$JenkinsPath\agent.jar" -url "$JenkinsURL/" -secret $Secret -name $env:computername -workDir "$JenkinsPath" *>>"$JenkinsPath\agent.log"

The Error

java.exe : Aug 04, 2024 5:14:14 PM org.jenkinsci.remoting.engine.WorkDirManager initializeWorkDir
At line:1 char:1
+ & "$Java\java.exe" -jar "$JenkinsPath\agent.jar" -url "$JenkinsURL/"  ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (Aug 04, 2024 5:...itializeWorkDir:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
 
INFO: Using D:\Jenkins\remoting as a remoting work directory
Aug 04, 2024 5:14:14 PM org.jenkinsci.remoting.engine.WorkDirManager setupLogging
INFO: Both error and output logs will be printed to D:\Jenkins\remoting
Aug 04, 2024 5:14:14 PM hudson.remoting.Launcher createEngine

Thanks in advance.

3
  • #1 If you are working with open source, is it mandatory windows? #2 is powershell mandatory? How are you creating your windows service? Commented Aug 4, 2024 at 16:11
  • 1. Yes 2. I can always fallback to C# within Powershell 3. For now working on the EXE itself, the service will be utilized at a later point. Commented Aug 4, 2024 at 16:35
  • As an aside: Calling jave.exe via cmd /C isn't necessary here, but with respect to terminating the PowerShell process running the script there should be no difference. By contrast, aStart-Process-launched process is an independent process (not a child process of the caller) that by default runs asynchronously and in a new window. Only in unusual situations is Start-Process called for; direct invocation (via & or just by name / path, if it isn't quoted and contains no variables) is almost always the better solution. Commented Aug 4, 2024 at 20:14

2 Answers 2

1

Your problem is the unexpected rendering of stderr lines, which inappropriately render as if they were errors.[1] Do the following to avoid the problem:

  • Apply redirection 2>&1 directly to the java.exe call.

    • This merges java.exe's stderr output into PowerShell's success output stream (PowerShell's stdout analog), allowing both stdout and stderr output to be processed in a PowerShell pipeline.
  • Pipe the result to a ForEach-Object call that calls the .ToString() method on each resulting object (% is a built-in alias of ForEach-Object)

    • This stringification is necessary, because PowerShell wraps each stderr line in a System.Management.Automation.ErrorRecord instance, which renders as if it were a PowerShell error, which causes the unexpected output you saw. Stringification via .ToString() yields the original stderr text.[2]
  • Apply the redirection to the log file (>>"$JenkinsPath\agent.log") to the latter; since all java.exe output is at that point routed via the success output stream >> is sufficient (i.e. no need for *>> in order to capture all streams).

# java.exe arguments omitted for brevity
& "$Java\java.exe" ... 2>&1 | % ToString >>"$JenkinsPath\agent.log"

Simpler alternative, via Add-Content:

# java.exe arguments omitted for brevity
& "$Java\java.exe" ... 2>&1 | Add-Content "$JenkinsPath\agent.log"
  • Add-Content, like Set-Content, implicitly stringifies its input objects via .ToString().

  • Character-encoding caveat:

    • In PowerShell (Core) 7, both solutions are equivalent and create BOM-less UTF-8 files, given that this encoding is the consistent default in this PowerShell edition.

    • By contrast, in Windows PowerShell (the legacy, ships-with-Windows edition whose latest and last version is 5.1.x), the redirection operators > / >> are in effect aliases of Out-File, which defaults to UTF-16LE with BOM ("Unicode"), whereas Add-Content / Set-Content use the system's active legacy ANSI code page by default; use the -Encoding parameter as needed, but note that -Encoding utf8 invariably creates a UTF-8 file with BOM.


[1] Specifically, it is the first stderr line output by an external program such as java.exe that is rendered this way when a 2> or *> redirection (or a >> variant) is applied to an external-program call. In the console (terminal), subsequent stderr lines render by their text only, but are printed in red too. When redirecting such output to a file, such as in your case, coloring is lost, so it is only the first, error-like representation that stands out. A simple test command:
cmd /c 'echo stderr1 >&2 & echo stdout1 & echo stderr2 >&2' 2>&1
Note that the original output order among the stdout and stderr streams isn't always maintained - see GitHub issue #5424 for a discussion.

[2] By contrast, use of > / >> / Out-File results in implicit stringification that uses the rich for-display representations you also see when outputting to the PowerShell console, via PowerShell's output-formatting system - except when stderr output from an external program isn't captured, i.e. prints directly to the display, in which case stderr lines print normally, as uncolored strings.

Sign up to request clarification or add additional context in comments.

1 Comment

Glad to hear it, @EliorMachlev.
0

Summary

Write one or several scripts able to ge the pid (process id) of the started process or somehow query the os to locate the unique process (jenkins in your case). Then use the pid to kill the process.


Foreground vs background

If you achieve to start the process in a background mode, it will be easy to stop later with windows service or similar even on another os (mac, linux)

I suggest you to read about this topic. Basically definition a background process is when you run by shell but the shell is not blocked. Foreground process is when the shell remains blocked after run it. In this case you need to press ctrl+c to exit the application and the shell becomes usable again.

More details here. Also it works on windows.

Two scripts: start and stop

No matter the os (windows, linux, mac) if you achieve to create these scripts, the process will start and stop easily

On linux, we can get the pid of process of a recently started process. Using that pid I can kill the app from another shell or script. I don't have a windows but basically the algorithm is:

start.bat

stop.bat

nssm

Some years ago, I used this tool http://nssm.cc/download to create a windows service from a java. It was easy. The stop worked as expected (java process exited)

enter image description here

If I'm not wrong, it was not required a background process. I just set the location of bat scripts (start.bat/stop.bat). The the start/stop using windows service worked as expected

enter image description here

Bat or powershell

I wrote this kind of scripts on windows and linux. It is a pain the usage of bat or powershell. It is like they were created to not be used to automate tasks. Simple string algorithms are so crazy and hard to understand

With linux is so easy to create this kind of scripts

Docker

With docker it would be easy. Even the windows service would not be required because you can configure that a container starts when computer starts. Anyway with docker you only need:

  • docker start jenkins
  • docker stop jenkins

These commands could be registered in your windows service and it will work without errors

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.