Last active
February 8, 2017 15:48
-
-
Save zimbatm/9f1bc26446af0ee3e5c5 to your computer and use it in GitHub Desktop.
A pure ruby implementation of the sd-daemon library distributed with SystemD
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# A pure ruby implementation of the sd-daemon library distributed with SystemD. | |
# | |
# Includes: | |
# * File descriptor passing for socket-based activation | |
# * Daemon ready notifications | |
# | |
# Missing: | |
# * Watchdog notifications | |
# * Support for logging with log levels on stderr | |
# * Detection of systemd boots | |
# * Detection of FD types | |
# | |
# More details: http://www.freedesktop.org/software/systemd/man/sd-daemon.html | |
# | |
module SdDaemon extend self | |
class NullSocket | |
def noop(*) end | |
alias sendmsg noop | |
alias close_on_exec= noop | |
end | |
# MSG_NOSIGNAL doesn't exist on OSX | |
# It's used to avoid SIGPIPE on the process if the other end disappears | |
MSG_NOSIGNAL = Socket.const_defined?(:MSG_NOSIGNAL) ? Socket::MSG_NOSIGNAL : 0 | |
LISTEN_FDS_START = 3 | |
def notify_ready | |
notify "READY=1" | |
end | |
# Returns an array of IO if LISTEN_FDS is set. | |
# | |
# The crank_compat flag turns off the LISTEN_PID check when true. | |
def listen_fds(crank_compat = true) | |
fds = [] | |
if (crank_compat || ENV['LISTEN_PID'].to_i == Process.pid) && | |
(fd_count = ENV['LISTEN_FDS'].to_i) > 0 | |
ENV.delete('LISTEN_PID') | |
ENV.delete('LISTEN_FDS') | |
fds = fd_count.times | |
.map{|i| IO.new(LISTEN_FDS_START + i)} | |
.each{|io| io.close_on_exec = true } | |
end | |
memoize(:listen_fds, fds) | |
end | |
protected | |
# Sends a message to the supervisor if LISTEN_FD/LISTEN_SOCKET is set. | |
# Otherwise it is a noop. | |
# | |
# LISTEN_FD is not part of the original spec and an extension for crank. | |
def notify(msg) | |
notify_socket.sendmsg msg, MSG_NOSIGNAL | |
end | |
def notify_socket | |
socket = if ((socket_path = ENV.delete('NOTIFY_SOCKET'))) | |
UNIXSocket.open(socket_path) | |
# This is our own extension | |
elsif ((fd = ENV['NOTIFY_FD'])) # && ENV['NOTIFY_PID'].to_i == Process.pid) | |
UNIXSocket.for_fd fd.to_i | |
else | |
NullSocket.new | |
end | |
socket.close_on_exec = true | |
memoize(:notify_socket, socket) | |
end | |
def memoize(method, value=nil) | |
value = yield if block_given? | |
singleton_class.send(:define_method, method) { value } | |
value | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This didn't entirely work for me because it's opening the UNIXSocket as a stream, not a datagram. I had to do: