Module: Daemon
- Defined in:
- lib/vagrant-listen-server/daemon.rb
Class Method Summary collapse
-
.daemonize!(out = '/dev/null', err = '/dev/null') ⇒ Object
Wasted way too long trying to use Process.daemon.
- .start(vm) ⇒ Object
- .status(config) ⇒ Object
- .status_code(config) ⇒ Object
- .stop(vm) ⇒ Object
Class Method Details
.daemonize!(out = '/dev/null', err = '/dev/null') ⇒ Object
Wasted way too long trying to use Process.daemon. Doing it by hand. Code courtesy of: gist.github.com/mynameisrufus/1372491/b76b60fb1842bf0507f47869ab19ad50a045b214 See also:
* http://stackoverflow.com/questions/1740308/create-a-daemon-with-double-fork-in-ruby
* http://allenlsy.com/working-with-unix-process-in-ruby/
Related: Process.detach isn’t what you think. It creates a process to monitor the child so that this can exit without creating a zombie. Because we want to remove parents, this actually makes life harder than just doing a double fork.
Vagrant-notify uses fork as well, but that doesn’t work on windows:
https://github.com/fgrehm/vagrant-notify/blob/master/lib/vagrant-notify/server.rb
Only real option is to switch to Process.spawn? I need a windows machine to test it out on… Vagrant::Util::Subprocess allows spawning an executable, but I don’t know how to make that launch the included ruby binary and make sure it’s running the right file.
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/vagrant-listen-server/daemon.rb', line 61 def self.daemonize! out='/dev/null', err='/dev/null' raise 'First fork failed' if (pid = fork) == -1 return nil unless pid.nil? Process.setsid raise 'Second fork failed' if (pid = fork) == -1 exit unless pid.nil? # Dir.chdir '/' # File.umask 0000 $stdin.reopen '/dev/null' $stdout.reopen File.new(out, "a") $stderr.reopen File.new(err, "a") $stdout.sync = $stderr.sync = true Process.pid end |
.start(vm) ⇒ Object
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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'lib/vagrant-listen-server/daemon.rb', line 80 def self.start vm config = vm.config.listen_server log "Starting listen server - #{config.ip}:#{config.port}" status = self.status_code config if status == :running log 'Server already running.' return 1 end File.delete config.pid_file if status == :stale # TODO: use machine name in log file name. # TODO: use machine name in PID file and drop it from config. pid = daemonize! '/tmp/listen.log', '/tmp/listen.log' # Usually a daemon wants the parent to exit here, but we need vagrant to # keep going with its init process. return unless pid $0 = "vagrant-listen-server - #{vm.name}" File.write config.pid_file, pid log "Listen server started on PID #{pid}" clients = [] begin server = TCPServer.new config.ip, config.port rescue Errno::EADDRINUSE log "Can't start server - Port in use" exit 1 end send_to_clients = Proc.new do |type, data| bad_clients = [] log "Sending #{type} to #{clients.count} clients." clients.each do |client| begin client.puts ({type: type, data: data}).to_json rescue Errno::EPIPE log "Connection broke! #{client}" # Don't want to change the list of threads as we iterate. bad_clients.push client end end bad_clients.each do |client| clients.delete client end end callback = Proc.new do |modified, added, removed| log "Listen fired" send_to_clients.call :listen, [modified, added, removed] end folders = array_wrap config.folders # There is a recurring bug that keeps popping up in listen where only the # first directory is watched. Create a new listen object for each folder as # a workaround. # https://github.com/guard/listen/issues/243 listeners = folders.map do |folder| Listen.to(folder, &callback) end listeners.each &:start # server.accept is blocking - we need it in its own thread so we can # continue to have listener callbacks fired, and so we can sleep and catch # any interrupts. Thread.new do loop do Thread.fork(server.accept) do |client| log "New connection - #{client}" clients.push client loop do line = client.gets.chomp # Currently, the only message type is a ping. Don't bother checking # type for now. data = JSON.parse line log 'ping received' client.puts ({type: 'pong', data: data['message']}).to_json end end end end exiting = false Signal.trap 'INT' do log 'SIGINT caught' exiting = true end while not exiting # Original idea was to have the server ping. This requires more work on # the client for a basic implementation though, so now the client pings. # send_to_clients.call :echo, 'ping' sleep 0.5 end listeners.each &:stop log "Listen sleep finished" end |
.status(config) ⇒ Object
32 33 34 35 36 37 38 39 40 41 |
# File 'lib/vagrant-listen-server/daemon.rb', line 32 def self.status config code = self.status_code config case code when Fixnum then log "Running - PID: #{code}" when :stopped then log 'Stopped' when :stale then log 'Stopped. Stale PID file.' else log "Unknown status - #{code}" end end |
.status_code(config) ⇒ Object
19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/vagrant-listen-server/daemon.rb', line 19 def self.status_code config return :stopped unless File.exists? config.pid_file pid = File.read config.pid_file begin Process.getpgid pid.to_i return pid.to_i rescue Errno::ESRCH return :stale end :stopped end |
.stop(vm) ⇒ Object
188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/vagrant-listen-server/daemon.rb', line 188 def self.stop vm log 'Killing listen server' config = vm.config.listen_server status = self.status_code config unless status.is_a? Fixnum log 'Server is not running.' return 1 end pid = File.read config.pid_file Process.kill 'INT', pid.to_i File.delete config.pid_file end |