pidfromfile Read a PID from a file, return the PID if a process of the PID exists and is older than the file. If the process does not exist or is younger than the mtime of the file, exit with error and give no output. Documentation as blog platform Written in October of 2019: In these days of systemd, PID files are not as often encountered as before, but they are very much still with us. In one of my private projects, a system writes a PID file and I wanted to use that information in another script. The start of this story is my wanting to add some integrity checking to interpreting the PID file, as the process in question might have exited and the PID has been recycled. One common way of doing that check is accessing the proc file system and inspecting the binary and/or command line and see if the running process matches the expectation. I wanted to go a simpler route, if all I want to ensure is the PID not being recycled, just checking whether the running process of the given PID is older than the PID file is sufficient. And that is where my troubles began. To summarize, to ensure the validity of the PID in a PID file, I wanted to find (1) the start time of a process, and (2) the last modification time of the PID file. Because it is for one of my hobby projects, I wanted the solution to be cleanly portable between Linux and FreeBSD. Starting with the age of the process, my first impulse was diving into good, old /proc//stat. It is also the common starting place for code checking the name of the binary, but on Linux, the field starttime will contain the start time of the process expressed in ticks after boot. The configured length of a tick for the system available through sysconf(_SC_CLK_TCK) and the boot time of the system is available in /proc/stat in the field btime. A little roundabout, but now we have the process start time. Unless we want the code to work on FreeBSD, where none if this holds at all. These are all non-standard extensions of /proc and linuxisms. So, back to the drawing board. ps is the other common way to look at a process, so perhaps we can use something like “ps -o lstart= ”? It gives a pretty nasty RFC-822 style output, but GNU date can read that and convert it to something more useful. Also but, depending on the GNU tool chain on the BSD side is a very bad idea. So, parsing the so-called human readable data is a no go. On the other hand, the man page for ps claims it honors the environment variable LC_TIME, so perhaps manipulating the locale is a solution to avoid overly fancy string manipulation? Now, the ps implementation on Fedora 30 is buggy, and ignores LC_TIME (the FreeBSD ps honored the locale just fine). In other words, using lstart was stopped by a bug on the Linux side. The next attempt at looking at the process was “ps -o etimes= ”. The field etimes is “elapsed time since the process was started, in seconds”. Nice and straightforward, except now it is also necessary to sample the current time, and in a shell script edge cases are too often the common case, so since a second is a long while, I ended simply sampling the time before and after sampling etimes and continuing till all three measurements happened within the same second. I have still to see that loop ever run more than once. Also, the etimes field and the ps syntax are identical on FreeBSD. Finally, a solution for reading the process start time. Now, getting file modification time, that should be trivial. Just use stat and you should be good to go. For instance, “stat -c %Y ” should do exactly what is needed. And it does. On Linux. The FreeBSD stat command is totally incompatible. The other obvious tool is of course ls, but with ls one ends up in the same rabbit hole as date. The GNU version is quite different from the BSD version, and it is quite problematic to configure the output properly. As always, anything regarding presentations of date and time ends up being quite nasty. In this case I was getting quite irritated, and simply cheated. I use zsh and that shell has its own perfectly fine zstat which makes fetching mtime exactly as trivial as it should be. Sometimes, it is easier using a piece of well-established middleware than trying to figure out the common subset of even quite similar systems. Then, finally I had figured out how to get to very basic pieces of information in a portable fashion. How long did my script end up being? When I started, I expected something like five lines. Below [refer to pidfromfile in this repo] is the current version, and even though I normally would have said feedback is welcome, it is not now. I do not want to touch this problem again before something breaks badly. And that was today's installment of Yak Shaving for Fun and Profit. Licence for the software Copyright 2019, 2020 Steinar Knutsen Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission – subsequent versions of the EUPL (the “Licence”); You may not use this work except in compliance with the Licence. You may obtain a copy of the Licence at: https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 Unless required by applicable law or agreed to in writing, software distributed under the Licence is distributed on an “AS IS” basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licence for the specific language governing permissions and limitations under the Licence.