File: sd_notify.rb

package info (click to toggle)
ruby-sd-notify 0.1.1-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 72 kB
  • sloc: ruby: 62; makefile: 4
file content (122 lines) | stat: -rw-r--r-- 3,267 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# frozen_string_literal: true

require "socket"

# SdNotify is a pure-Ruby implementation of sd_notify(3). It can be used to
# notify systemd about state changes. Methods of this package are no-op on
# non-systemd systems (eg. Darwin).
#
# The API maps closely to the original implementation of sd_notify(3),
# therefore be sure to check the official man pages prior to using SdNotify.
#
# @see https://www.freedesktop.org/software/systemd/man/sd_notify.html
module SdNotify
  # Exception raised when there's an error writing to the notification socket
  class NotifyError < RuntimeError; end

  READY     = "READY=1"
  RELOADING = "RELOADING=1"
  STOPPING  = "STOPPING=1"
  STATUS    = "STATUS="
  ERRNO     = "ERRNO="
  MAINPID   = "MAINPID="
  WATCHDOG  = "WATCHDOG=1"
  FDSTORE   = "FDSTORE=1"

  def self.ready(unset_env=false)
    notify(READY, unset_env)
  end

  def self.reloading(unset_env=false)
    notify(RELOADING, unset_env)
  end

  def self.stopping(unset_env=false)
    notify(STOPPING, unset_env)
  end

  # @param status [String] a custom status string that describes the current
  #   state of the service
  def self.status(status, unset_env=false)
    notify("#{STATUS}#{status}", unset_env)
  end

  # @param errno [Integer]
  def self.errno(errno, unset_env=false)
    notify("#{ERRNO}#{errno}", unset_env)
  end

  # @param pid [Integer]
  def self.mainpid(pid, unset_env=false)
    notify("#{MAINPID}#{pid}", unset_env)
  end

  def self.watchdog(unset_env=false)
    notify(WATCHDOG, unset_env)
  end

  def self.fdstore(unset_env=false)
    notify(FDSTORE, unset_env)
  end

  # @param [Boolean] true if the service manager expects watchdog keep-alive
  #   notification messages to be sent from this process.
  #
  # If the $WATCHDOG_USEC environment variable is set,
  # and the $WATCHDOG_PID variable is unset or set to the PID of the current
  # process
  #
  # @note Unlike sd_watchdog_enabled(3), this method does not mutate the
  #   environment.
  def self.watchdog?
    wd_usec = ENV["WATCHDOG_USEC"]
    wd_pid = ENV["WATCHDOG_PID"]

    return false if !wd_usec

    begin
      wd_usec = Integer(wd_usec)
    rescue
      return false
    end

    return false if wd_usec <= 0
    return true if !wd_pid || wd_pid == $$.to_s

    false
  end

  # Notify systemd with the provided state, via the notification socket, if
  # any.
  #
  # Generally this method will be used indirectly through the other methods
  # of the library.
  #
  # @param state [String]
  # @param unset_env [Boolean]
  #
  # @return [Fixnum, nil] the number of bytes written to the notification
  #   socket or nil if there was no socket to report to (eg. the program wasn't
  #   started by systemd)
  #
  # @raise [NotifyError] if there was an error communicating with the systemd
  #   socket
  #
  # @see https://www.freedesktop.org/software/systemd/man/sd_notify.html
  def self.notify(state, unset_env=false)
    sock = ENV["NOTIFY_SOCKET"]

    return nil if !sock

    ENV.delete("NOTIFY_SOCKET") if unset_env

    begin
      Addrinfo.unix(sock, :DGRAM).connect do |s|
        s.close_on_exec = true
        s.write(state)
      end
    rescue StandardError => e
      raise NotifyError, "#{e.class}: #{e.message}", e.backtrace
    end
  end
end