Snippet 0x05: Windows .bat: Checking if process is running by PID file
Contents
Batch (.bat) files are an MS-DOS legacy technology and I don’t know a single person who loves writing them – and yet, to this day, many people have to do it. In order to run Java programs, start daemons or check if a process is running, batch files have to be used. For my open source file sync tool Syncany, I had to do just that:
The Syncany daemon runs in the background and is started by a batch script. To check if the daemon is already running, the batch script needs to read a PID file and determine if a process with this PID is running.
Since it took me an enormeous amount of time to figure out how to do that (I am a Linux guy!), I wanted to share that little script in this code snippet.
1. Full code
As always, the full working code (embedded in a my open source file sync software Syncany is available on GitHub. Feel free to use it, or to leave feedback in the comments.
2. Read PID file, and check if process running
Assuming that the application writes a PID file, the batch script has to do two things: First, it needs to check whether that PID file exists. If it doesn’t the application is obviously not running (or the PID file creation failed for some reason).
And second, check if the process ID in the PID file is actually running. If it is, the daemon/process is running. If it is not, the process has probably crashed or has forgotten to clean up after itself. In that case, we can consider the process not running.
Without further ado, here’s the .bat file:
Reading a PID file, and checking if PID is running
rem (..)
set APP_USER_DIR=%AppData%\Syncany
set APP_DAEMON_PIDFILE=%APP_USER_DIR%\daemon.pid
set APP_DAEMON_PIDFILE_TMP=%APP_USER_DIR%\daemon.pid.tmp
rem (..)
set RUNNING=0
set APID=-1
if exist %APP_DAEMON_PIDFILE% (
set /P APID=<%APP_DAEMON_PIDFILE%
)
if not "%APID%" == "-1" (
wmic process where "ProcessID = %APID%" get processid 2> %APP_DAEMON_PIDFILE_TMP% > NUL
set /P STDERR=<%APP_DAEMON_PIDFILE_TMP%
for /f %%i in ("%APP_DAEMON_PIDFILE_TMP%") do set size=%%~zi
if not defined size set size=0
del %APP_DAEMON_PIDFILE_TMP%
)
if not "%APID%" == "-1" (
if "%size%" == "0" set RUNNING=1
)
rem (..)
By default, the script is assumed to be not running, so only if the PID file exists (if exist ..), the file is read (set /P APID=<...). If this is successful, the APID variable contains the process ID and not -1.
Using the wonderful wmic process command, we can check if that PID is returned in the list of running processes. While a human being can easily determine if that is the case, it’s not so easy with batch files, because the find command is a bit unreliable. I resolved this by simply checking the error output (STDERR) instead. If wmic doesn’t find any process matching the given PID, it writes something to STDERR. If it does, STDERR is empty. To check if STDERR returned something we redirect the error output to a temporary file (2> ...) and check if that file has a size of zero (if not defined size set size=0).
In the last bit, we check if the process ID is not -1, and the size of the temporary error output file is zero (no error); and only then we set RUNNING=1.
3. Issues and justification
I most certainly realize that this solution is not great. I’d even go as far as to say it’s a dirty workaround. However, all the other solutions I’ve tried (tasklist .. | find and checking for the %ERRORLEVEL%) were terribly unreliable. In particular, the find errorlevel/exit code arbitraribly returned 0 (99%), 1 or 9009 – even if the state of the daemon/process did not change. The solution above works just fine, and I hope it’ll help you guys somehow.
A. About this post
I’m trying a new section for my blog. I call it Code Snippets. It’ll be very short, code-focused posts of things I recently discovered or find fascinating or helpful. I hope this helps.