forker 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. data/README.md +57 -0
  2. data/lib/forker.rb +124 -0
  3. metadata +82 -0
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # Goals
2
+
3
+ * Simplest code possible to daemonize code, redirect output and manage the
4
+ pidfile.
5
+
6
+ ## Usage
7
+
8
+ require 'forker'
9
+ Forker.fork(
10
+ :log => "/dev/null", # Default value
11
+ :pid => "/var/run/#{File.basename($0)}.pid", # Default value
12
+ :chdir => false, # Default value
13
+ :umask => false # Default value
14
+ )
15
+
16
+ # Do stuff in daemonized proc...
17
+
18
+ ## Or for a simple start/stop script
19
+
20
+ #!/usr/bin/env ruby
21
+ # Example usage from command line:
22
+ # ./bin/myscript start --logfile LOGFILE --pidfile PIDFILE
23
+ # ./bin/myscript stop --logfile LOGFILE --pidfile PIDFILE
24
+
25
+ require 'forker'
26
+ Forker::CLI.run(ARGV)
27
+
28
+ ## Example bin/worker script for Resque
29
+
30
+ #!/usr/bin/env ruby
31
+
32
+ # Default to production, this script can't be passed env vars on the prod servers
33
+ ENV['RAILS_ENV'] ||= 'production'
34
+
35
+ # Simulate calling rake environment
36
+ $rails_rake_task = true
37
+
38
+ # Load rails env
39
+ require File.expand_path("../config/environment", File.dirname(__FILE__))
40
+
41
+ require 'forker'
42
+ Forker::CLI.run(ARGV)
43
+
44
+ worker = nil
45
+ queues = (ENV['QUEUES'] || ENV['QUEUE'] || '*').to_s.split(',')
46
+
47
+ begin
48
+ worker = Resque::Worker.new(*queues)
49
+ worker.verbose = ENV['LOGGING'] || ENV['VERBOSE']
50
+ worker.very_verbose = ENV['VVERBOSE']
51
+ rescue Resque::NoQueueError
52
+ abort "set QUEUE env var, e.g. $ QUEUE=critical,high,low rake resque:work"
53
+ end
54
+
55
+ puts "*** Starting worker in #{ENV['RAILS_ENV']} environment: #{worker}"
56
+
57
+ worker.work(ENV['INTERVAL'] || 5) # interval, will block
data/lib/forker.rb ADDED
@@ -0,0 +1,124 @@
1
+ # This gem follows best practices for daemonizing processes in a UNIX
2
+ # environment with as little fluff as possible. Forks once, calls `setsid`,
3
+ # and forks again. STDIN, STDOUT, STDERR are all redirected to /dev/null by
4
+ # default. We'll manage the pidfile also.
5
+ #
6
+ # `chdir` and `umask` are optional precautions:
7
+ #
8
+ # * `chdir` is a safeguard against problems that would occur is you tried to
9
+ # launch a daemon process on a mounted filesystem
10
+ # * `umask` gives your daemon process more control over file creation
11
+ # permissions
12
+ #
13
+ # Notes
14
+ # $$ = Process.pid
15
+
16
+ require 'system_timer'
17
+
18
+ module Forker
19
+ module CLI
20
+ # You can use this if your daemon is simple enough or write your own and
21
+ # use Forker#fork! directly
22
+ def self.run(argv)
23
+ require 'optparse'
24
+ options = {}
25
+ opts = OptionParser.new do |opts|
26
+ opts.banner = "Usage: #{File.basename($0)} [start|stop] -p /path/to/pidfile.pid"
27
+
28
+ opts.on("-l", "--logfile LOGFILE", "redirect output to this location") do |logfile|
29
+ options[:log] = logfile
30
+ end
31
+
32
+ opts.on("-p", "--pidfile PIDFILE", "save pidfile to this location") do |pidfile|
33
+ options[:pid] = pidfile
34
+ end
35
+ end
36
+ opts.parse!(argv)
37
+
38
+ if options[:pid].nil?
39
+ puts opts
40
+ exit
41
+ end
42
+
43
+ case argv.first
44
+ when "start"
45
+ Forker.fork!(options)
46
+ when "stop"
47
+ Forker.kill(options)
48
+ exit
49
+ else
50
+ puts opts
51
+ exit
52
+ end
53
+ end
54
+ end
55
+
56
+ def self.fork!(opts={})
57
+ opts = { :log => "/dev/null", :pid => "/var/run/#{File.basename($0)}.pid" }.merge(opts)
58
+
59
+ $stdout.sync = $stderr.sync = true
60
+ $stdin.reopen("/dev/null")
61
+
62
+ exit if fork
63
+
64
+ Process.setsid
65
+
66
+ exit if fork
67
+
68
+ Dir.chdir("/") if opts[:chdir]
69
+ File.umask(0000) if opts[:umask]
70
+
71
+ if File.exist?(opts[:pid])
72
+ begin
73
+ existing_pid = File.read(opts[:pid]).to_i
74
+ Process.kill(0, existing_pid) # See if proc exists
75
+ abort "error: existing process #{existing_pid} using this pidfile, exiting"
76
+ rescue Errno::ESRCH
77
+ puts "warning: removing stale pidfile with pid #{existing_pid}"
78
+ end
79
+ end
80
+
81
+ File.open(opts[:pid], 'w') { |f| f.write($$) }
82
+
83
+ at_exit do
84
+ ( File.read(opts[:pid]).to_i == $$ and File.unlink(opts[:pid]) ) rescue nil
85
+ end
86
+
87
+ puts "forked process is #{$$}"
88
+ puts "output redirected to #{opts[:log]}"
89
+
90
+ $stdout.reopen(opts[:log], 'a')
91
+ $stderr.reopen(opts[:log], 'a')
92
+ $stdout.sync = $stderr.sync = true
93
+ end
94
+
95
+ def self.kill(opts={})
96
+ begin
97
+ pid = File.read(opts[:pid]).to_i
98
+ sec = 60 # Seconds to wait before force killing
99
+ Process.kill("TERM", pid)
100
+
101
+ begin
102
+ SystemTimer.timeout(sec) do
103
+ loop do
104
+ puts "waiting #{sec} seconds for #{pid} before sending KILL"
105
+ Process.kill(0, pid) # See if proc exists
106
+
107
+ sec -= 1
108
+ sleep 1
109
+ end
110
+ end
111
+ rescue Errno::ESRCH
112
+ puts "killed process #{pid}"
113
+ rescue Timeout::Error
114
+ Process.kill("KILL", pid)
115
+ puts "force killed process #{pid}"
116
+ end
117
+
118
+ rescue Errno::ENOENT
119
+ puts "warning: pidfile #{opts[:pid]} does not exist"
120
+ rescue Errno::ESRCH
121
+ puts "warning: process #{pid} does not exist"
122
+ end
123
+ end
124
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: forker
3
+ version: !ruby/object:Gem::Version
4
+ hash: 21
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 1
10
+ version: 1.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Ben Marini
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-09-27 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: SystemTimer
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 11
30
+ segments:
31
+ - 1
32
+ - 2
33
+ version: "1.2"
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ description: Fork your ruby code with confidence
37
+ email: bmarini@gmail.com
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files: []
43
+
44
+ files:
45
+ - lib/forker.rb
46
+ - README.md
47
+ has_rdoc: true
48
+ homepage: http://github.com/bmarini/forker
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options: []
53
+
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.3.7
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Fork your ruby code with confidence
81
+ test_files: []
82
+