Skip to content

Instantly share code, notes, and snippets.

@zimbatm
Last active February 8, 2017 15:48
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zimbatm/9f1bc26446af0ee3e5c5 to your computer and use it in GitHub Desktop.
Save zimbatm/9f1bc26446af0ee3e5c5 to your computer and use it in GitHub Desktop.
A pure ruby implementation of the sd-daemon library distributed with SystemD
# 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
@sj26
Copy link

sj26 commented Oct 21, 2016

This didn't entirely work for me because it's opening the UNIXSocket as a stream, not a datagram. I had to do:

def notify_socket
  @notify_socket ||= Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM, 0).tap do |socket|
    socket.connect(Socket.pack_sockaddr_un(ENV["NOTIFY_SOCKET"]))
    socket.close_on_exec = true
  end
end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment