Class: Daemon

Inherits:
Object
  • Object
show all
Defined in:
lib/daemon.rb

Defined Under Namespace

Classes: LockError

Class Method Summary collapse

Class Method Details

.daemonize(pid_file, log_file = nil, sync = true) ⇒ Object



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
# File 'lib/daemon.rb', line 8

def self.daemonize(pid_file, log_file = nil, sync = true)
  if block_given?
    spawn do |send_ok, send_error|
      log = begin
        # try to lock before we kill stdin/out
        lock(pid_file)

        # close I/O
        disconnect(log_file, sync)
      rescue => error
        send_error.call(error)
      end

      send_ok.call

      yield log
    end # => pid, wait
  else
    # become new process group leader
    fence

    # try to lock before we kill stdin/out
    lock(pid_file)

    # close I/O
    disconnect(log_file, sync) # => log
  end
end

.disconnect(log_file = nil, sync = true) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/daemon.rb', line 96

def self.disconnect(log_file = nil, sync = true)
  if log_file
    log = File.open(log_file, File::WRONLY | File::CREAT | File::APPEND | File::BINARY)
    log.sync = sync
  else
    # don't raise on STDOUT/STDERR write
    log = File.new('/dev/null', File::WRONLY | File::BINARY)
  end

  # disconnect
  STDIN.close # raise IOError on STDIN.read
  STDOUT.reopen log
  STDERR.reopen log

  # provide log IO
  return log
end

.fenceObject



72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/daemon.rb', line 72

def self.fence
  if block_given?
    fork do
      Process.setsid # become new session leader
      # now in child
      yield
    end # => pid
  else
    exit! 0 if fork
    Process.setsid # become new session leader
    # now in child
  end
end

.lock(pid_file) ⇒ Object

Raises:



86
87
88
89
90
91
92
93
94
# File 'lib/daemon.rb', line 86

def self.lock(pid_file)
  pf = File.open(pid_file, File::RDWR | File::CREAT)
  raise LockError, pf unless pf.flock(File::LOCK_EX | File::LOCK_NB)
  pf.truncate(0)
  pf.write(Process.pid.to_s + "\n")
  pf.flush

  @pf = pf # keep it open and locked until process exits
end

.spawnObject



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
# File 'lib/daemon.rb', line 37

def self.spawn
  r, w = IO.pipe

  pid = fence do
    r.close

    yield(
      ->{
        w.write Marshal.dump(nil) # send OK to parent
        w.close
      },
      ->(error){
        w.write Marshal.dump(error) # send error to parent
        w.close
        exit 42
      }
    )
  end
  thr = Process.detach(pid)

  w.close
  data = r.read
  data.empty? and fail 'ok/error handler not called!'

  if error = Marshal.load(data)
    thr.join # wait for child to die
    raise error
  end

  return pid, thr
ensure
  w.close unless w.closed?
  r.close
end