On OpenBSD just prefix the command with -s and it will only be allowed to run once from cron.
Thus finding the correct answer.
There comes a time in every tool’s life where someone will use it outside its intended scope.
At that time, reinvent the wheel to work for your needs. In this case, reinvent cron (either new forks, new implementations, or new tools altogether.
Like OpenBSD’s cron.
Is lockf not available on the target systems? It has similar behaviour to the tool described here, except that it uses a local filesystem lock.
This is a creative use of sockets, but I’d be curious to hear the tradeoffs between this approach and a more traditional lock file in tmpfs. Right off the bat, the author’s socket method sounds like it would involve fewer logistical steps than a file would (e.g., locate a tmpfs mount, create a unique file name, set permissions), although I believe there are standardized facilities to handle them. Are there other advantages? I would think the persistence semantics between the sockets and temporary files would at least be approximately the same.
On Linux you can just use the /dev/shm/ tmpfs, which I think is universally available. Or /var/run/user/$UID/.
I’ve used a similar trick in Python for a long time using Linux-specific abstract domain sockets: https://stackoverflow.com/questions/788411/check-to-see-if-python-script-is-running
One downside of such an approach is you don’t actually know what the PID or PGID of the cron job is, sometimes it’s useful to know if you need to kill the process or its process group.
lockrun is an alternative that uses a lockfile instead of ports. It can be told to wait or simply exit.
(I also just learned this tool I have used repeatedly over the past decade+ was created by a recent close collaborator, thanks Steve!)
Why IPv6? Just because there are so many?
Given it’s locking by port, I don’t think IPv4 v. IPv6 would make a difference on that front.
And unrelatedly, it’s worth noting that systemd timers explicitly support “only run one at a time”, so if you’re on a system that’s using systemd anyway, you can just avoid this problem.
Systemd timers solve a ton of problems with cron. I use them exclusively, especially with the per-user systemd (~/.config/systemd/user).
For people unfamiliar, an example from one of my personal servers (runs an hour after boot and every hour after the previous completes):
1 │ [Service]
2 │ Type=oneshot
3 │ ExecStart=/usr/bin/tclsh gc_files.tcl
1 │ [Timer]
2 │ OnActiveSec=1h
3 │ OnUnitInactiveSec=1h
5 │ [Install]
6 │ WantedBy=default.target
The systemd.timer documentation is superb.
Probably because there are so many loopback addresses in IPv4 — an entire 127.0.0.0/8 subnet, while in IPv6 it’s just ::1/128. Besides, why wouldn’t someone default to IPv6 these days unless for backwards compatibility?