fire_and_forget 0.1.2 → 0.2.0
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.
- data/Gemfile.lock +0 -8
- data/README.rdoc +75 -14
- data/bin/fire_forget +45 -22
- data/examples/long_task +27 -16
- data/fire_and_forget.gemspec +41 -4
- data/lib/fire_and_forget/client.rb +1 -1
- data/lib/fire_and_forget/command/fire.rb +23 -4
- data/lib/fire_and_forget/command/get_pid.rb +20 -0
- data/lib/fire_and_forget/command/set_pid.rb +0 -2
- data/lib/fire_and_forget/command/set_status.rb +1 -1
- data/lib/fire_and_forget/command.rb +11 -0
- data/lib/fire_and_forget/config.rb +3 -8
- data/lib/fire_and_forget/daemon.rb +14 -23
- data/lib/fire_and_forget/errors.rb +8 -0
- data/lib/fire_and_forget/launcher.rb +69 -6
- data/lib/fire_and_forget/server.rb +5 -1
- data/lib/fire_and_forget/task_description.rb +11 -0
- data/lib/fire_and_forget/utilities.rb +4 -4
- data/lib/fire_and_forget/version.rb +1 -1
- data/lib/fire_and_forget.rb +6 -2
- data/test/test_fire_and_forget.rb +59 -26
- data/vendor/daemons-1.1.0/LICENSE +29 -0
- data/vendor/daemons-1.1.0/README +224 -0
- data/vendor/daemons-1.1.0/Rakefile +88 -0
- data/vendor/daemons-1.1.0/Releases +152 -0
- data/vendor/daemons-1.1.0/TODO +2 -0
- data/vendor/daemons-1.1.0/lib/daemons/application.rb +468 -0
- data/vendor/daemons-1.1.0/lib/daemons/application_group.rb +194 -0
- data/vendor/daemons-1.1.0/lib/daemons/change_privilege.rb +19 -0
- data/vendor/daemons-1.1.0/lib/daemons/cmdline.rb +124 -0
- data/vendor/daemons-1.1.0/lib/daemons/controller.rb +140 -0
- data/vendor/daemons-1.1.0/lib/daemons/daemonize.rb +271 -0
- data/vendor/daemons-1.1.0/lib/daemons/etc_extension.rb +12 -0
- data/vendor/daemons-1.1.0/lib/daemons/exceptions.rb +28 -0
- data/vendor/daemons-1.1.0/lib/daemons/monitor.rb +138 -0
- data/vendor/daemons-1.1.0/lib/daemons/pid.rb +109 -0
- data/vendor/daemons-1.1.0/lib/daemons/pidfile.rb +116 -0
- data/vendor/daemons-1.1.0/lib/daemons/pidmem.rb +19 -0
- data/vendor/daemons-1.1.0/lib/daemons.rb +288 -0
- data/vendor/daemons-1.1.0/setup.rb +1360 -0
- data/vendor/json-1.5.0/COPYING +58 -0
- data/vendor/json-1.5.0/GPL +340 -0
- data/vendor/json-1.5.0/README +356 -0
- data/vendor/json-1.5.0/README-json-jruby.markdown +33 -0
- data/vendor/json-1.5.0/Rakefile +397 -0
- data/vendor/json-1.5.0/TODO +1 -0
- data/vendor/json-1.5.0/VERSION +1 -0
- data/vendor/json-1.5.0/lib/json/add/core.rb +147 -0
- data/vendor/json-1.5.0/lib/json/add/rails.rb +8 -0
- data/vendor/json-1.5.0/lib/json/common.rb +419 -0
- data/vendor/json-1.5.0/lib/json/editor.rb +1369 -0
- data/vendor/json-1.5.0/lib/json/pure/generator.rb +441 -0
- data/vendor/json-1.5.0/lib/json/pure/parser.rb +320 -0
- data/vendor/json-1.5.0/lib/json/pure.rb +15 -0
- data/vendor/json-1.5.0/lib/json/version.rb +8 -0
- data/vendor/json-1.5.0/lib/json.rb +10 -0
- metadata +41 -4
- data/lib/fire_and_forget/task.rb +0 -11
@@ -0,0 +1,194 @@
|
|
1
|
+
|
2
|
+
module Daemons
|
3
|
+
class ApplicationGroup
|
4
|
+
|
5
|
+
attr_reader :app_name
|
6
|
+
attr_reader :script
|
7
|
+
|
8
|
+
attr_reader :monitor
|
9
|
+
|
10
|
+
#attr_reader :controller
|
11
|
+
|
12
|
+
attr_reader :options
|
13
|
+
|
14
|
+
attr_reader :applications
|
15
|
+
|
16
|
+
attr_accessor :controller_argv
|
17
|
+
attr_accessor :app_argv
|
18
|
+
|
19
|
+
attr_accessor :dir_mode
|
20
|
+
attr_accessor :dir
|
21
|
+
|
22
|
+
# true if the application is supposed to run in multiple instances
|
23
|
+
attr_reader :multiple
|
24
|
+
|
25
|
+
|
26
|
+
def initialize(app_name, options = {})
|
27
|
+
@app_name = app_name
|
28
|
+
@options = options
|
29
|
+
|
30
|
+
if options[:script]
|
31
|
+
@script = File.expand_path(options[:script])
|
32
|
+
end
|
33
|
+
|
34
|
+
#@controller = controller
|
35
|
+
@monitor = nil
|
36
|
+
|
37
|
+
#options = controller.options
|
38
|
+
|
39
|
+
@multiple = options[:multiple] || false
|
40
|
+
|
41
|
+
@dir_mode = options[:dir_mode] || :script
|
42
|
+
@dir = options[:dir] || ''
|
43
|
+
|
44
|
+
@keep_pid_files = options[:keep_pid_files] || false
|
45
|
+
@no_pidfiles = options[:no_pidfiles] || false
|
46
|
+
|
47
|
+
#@applications = find_applications(pidfile_dir())
|
48
|
+
@applications = []
|
49
|
+
end
|
50
|
+
|
51
|
+
# Setup the application group.
|
52
|
+
# Currently this functions calls <tt>find_applications</tt> which finds
|
53
|
+
# all running instances of the application and populates the application array.
|
54
|
+
#
|
55
|
+
def setup
|
56
|
+
@applications = find_applications(pidfile_dir())
|
57
|
+
end
|
58
|
+
|
59
|
+
def pidfile_dir
|
60
|
+
PidFile.dir(@dir_mode, @dir, script)
|
61
|
+
end
|
62
|
+
|
63
|
+
def find_applications(dir)
|
64
|
+
if @no_pidfiles
|
65
|
+
return find_applications_by_app_name(app_name)
|
66
|
+
else
|
67
|
+
return find_applications_by_pidfiles(dir)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# TODO: identifiy the monitor process
|
72
|
+
def find_applications_by_app_name(app_name)
|
73
|
+
pids = []
|
74
|
+
|
75
|
+
begin
|
76
|
+
x = `ps auxw | grep -v grep | awk '{print $2, $11, $12}' | grep #{app_name}`
|
77
|
+
if x && x.chomp!
|
78
|
+
processes = x.split(/\n/).compact
|
79
|
+
processes = processes.delete_if do |p|
|
80
|
+
pid, name, add = p.split(/\s/)
|
81
|
+
# We want to make sure that the first part of the process name matches
|
82
|
+
# so that app_name matches app_name_22
|
83
|
+
|
84
|
+
app_name != name[0..(app_name.length - 1)] and not add.include?(app_name)
|
85
|
+
end
|
86
|
+
pids = processes.map {|p| p.split(/\s/)[0].to_i}
|
87
|
+
end
|
88
|
+
rescue ::Exception
|
89
|
+
end
|
90
|
+
|
91
|
+
return pids.map {|f|
|
92
|
+
app = Application.new(self, {}, PidMem.existing(f))
|
93
|
+
setup_app(app)
|
94
|
+
app
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
def find_applications_by_pidfiles(dir)
|
99
|
+
pid_files = PidFile.find_files(dir, app_name, ! @keep_pid_files)
|
100
|
+
|
101
|
+
#pp pid_files
|
102
|
+
|
103
|
+
@monitor = Monitor.find(dir, app_name + '_monitor')
|
104
|
+
|
105
|
+
pid_files.reject! {|f| f =~ /_monitor.pid$/}
|
106
|
+
|
107
|
+
return pid_files.map {|f|
|
108
|
+
app = Application.new(self, {}, PidFile.existing(f))
|
109
|
+
setup_app(app)
|
110
|
+
app
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
def new_application(add_options = {})
|
115
|
+
if @applications.size > 0 and not @multiple
|
116
|
+
if options[:force]
|
117
|
+
@applications.delete_if {|a|
|
118
|
+
unless a.running?
|
119
|
+
a.zap
|
120
|
+
true
|
121
|
+
end
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
raise RuntimeException.new('there is already one or more instance(s) of the program running') unless @applications.empty?
|
126
|
+
end
|
127
|
+
|
128
|
+
app = Application.new(self, add_options)
|
129
|
+
|
130
|
+
setup_app(app)
|
131
|
+
|
132
|
+
@applications << app
|
133
|
+
|
134
|
+
return app
|
135
|
+
end
|
136
|
+
|
137
|
+
def setup_app(app)
|
138
|
+
app.controller_argv = @controller_argv
|
139
|
+
app.app_argv = @app_argv
|
140
|
+
end
|
141
|
+
private :setup_app
|
142
|
+
|
143
|
+
def create_monitor(an_app)
|
144
|
+
return if @monitor
|
145
|
+
|
146
|
+
if options[:monitor]
|
147
|
+
@monitor = Monitor.new(an_app)
|
148
|
+
|
149
|
+
@monitor.start(@applications)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def start_all
|
154
|
+
@monitor.stop if @monitor
|
155
|
+
@monitor = nil
|
156
|
+
|
157
|
+
@applications.each {|a|
|
158
|
+
fork {
|
159
|
+
a.start
|
160
|
+
}
|
161
|
+
}
|
162
|
+
end
|
163
|
+
|
164
|
+
def stop_all(no_wait = false)
|
165
|
+
@monitor.stop if @monitor
|
166
|
+
|
167
|
+
threads = []
|
168
|
+
|
169
|
+
@applications.each {|a|
|
170
|
+
threads << Thread.new do
|
171
|
+
a.stop(no_wait)
|
172
|
+
end
|
173
|
+
}
|
174
|
+
|
175
|
+
threads.each {|t| t.join}
|
176
|
+
end
|
177
|
+
|
178
|
+
def reload_all
|
179
|
+
@applications.each {|a| a.reload}
|
180
|
+
end
|
181
|
+
|
182
|
+
def zap_all
|
183
|
+
@monitor.stop if @monitor
|
184
|
+
|
185
|
+
@applications.each {|a| a.zap}
|
186
|
+
end
|
187
|
+
|
188
|
+
def show_status
|
189
|
+
@applications.each {|a| a.show_status}
|
190
|
+
end
|
191
|
+
|
192
|
+
end
|
193
|
+
|
194
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'daemons/etc_extension'
|
2
|
+
|
3
|
+
class CurrentProcess
|
4
|
+
def self.change_privilege(user, group=user)
|
5
|
+
puts "Changing process privilege to #{user}:#{group}"
|
6
|
+
|
7
|
+
uid, gid = Process.euid, Process.egid
|
8
|
+
target_uid = Etc.getpwnam(user).uid
|
9
|
+
target_gid = Etc.getgrnam(group).gid
|
10
|
+
|
11
|
+
if uid != target_uid || gid != target_gid
|
12
|
+
Process.initgroups(user, target_gid)
|
13
|
+
Process::GID.change_privilege(target_gid)
|
14
|
+
Process::UID.change_privilege(target_uid)
|
15
|
+
end
|
16
|
+
rescue Errno::EPERM => e
|
17
|
+
raise "Couldn't change user and group to #{user}:#{group}: #{e}"
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
|
2
|
+
module Daemons
|
3
|
+
|
4
|
+
class Optparse
|
5
|
+
|
6
|
+
attr_reader :usage
|
7
|
+
|
8
|
+
def initialize(controller)
|
9
|
+
@controller = controller
|
10
|
+
@options = {}
|
11
|
+
|
12
|
+
@opts = OptionParser.new do |opts|
|
13
|
+
#opts.banner = "Usage: example.rb [options]"
|
14
|
+
opts.banner = ""
|
15
|
+
|
16
|
+
# Boolean switch.
|
17
|
+
# opts.on("-v", "--[no-]verbose", "Run verbosely") do |v|
|
18
|
+
# @options[:verbose] = v
|
19
|
+
# end
|
20
|
+
|
21
|
+
opts.on("-t", "--ontop", "Stay on top (does not daemonize)") do |t|
|
22
|
+
@options[:ontop] = t
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on("-f", "--force", "Force operation") do |t|
|
26
|
+
@options[:force] = t
|
27
|
+
end
|
28
|
+
|
29
|
+
opts.on("-n", "--no_wait", "Do not wait for processes to stop") do |t|
|
30
|
+
@options[:no_wait] = t
|
31
|
+
end
|
32
|
+
|
33
|
+
#opts.separator ""
|
34
|
+
#opts.separator "Specific options:"
|
35
|
+
|
36
|
+
|
37
|
+
opts.separator ""
|
38
|
+
opts.separator "Common options:"
|
39
|
+
|
40
|
+
# No argument, shows at tail. This will print an options summary.
|
41
|
+
# Try it and see!
|
42
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
43
|
+
#puts opts
|
44
|
+
#@usage =
|
45
|
+
controller.print_usage()
|
46
|
+
|
47
|
+
exit
|
48
|
+
end
|
49
|
+
|
50
|
+
# Another typical switch to print the version.
|
51
|
+
opts.on_tail("--version", "Show version") do
|
52
|
+
puts "daemons version #{Daemons::VERSION}"
|
53
|
+
exit
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
begin
|
58
|
+
@usage = @opts.to_s
|
59
|
+
rescue ::Exception # work around a bug in ruby 1.9
|
60
|
+
@usage = <<END
|
61
|
+
-t, --ontop Stay on top (does not daemonize)
|
62
|
+
-f, --force Force operation
|
63
|
+
-n, --no_wait Do not wait for processes to stop
|
64
|
+
|
65
|
+
Common options:
|
66
|
+
-h, --help Show this message
|
67
|
+
--version Show version
|
68
|
+
END
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
#
|
74
|
+
# Return a hash describing the options.
|
75
|
+
#
|
76
|
+
def parse(args)
|
77
|
+
# The options specified on the command line will be collected in *options*.
|
78
|
+
# We set default values here.
|
79
|
+
#options = {}
|
80
|
+
|
81
|
+
|
82
|
+
##pp args
|
83
|
+
@opts.parse(args)
|
84
|
+
|
85
|
+
return @options
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
class Controller
|
92
|
+
|
93
|
+
def print_usage
|
94
|
+
puts "Usage: #{@app_name} <command> <options> -- <application options>"
|
95
|
+
puts
|
96
|
+
puts "* where <command> is one of:"
|
97
|
+
puts " start start an instance of the application"
|
98
|
+
puts " stop stop all instances of the application"
|
99
|
+
puts " restart stop all instances and restart them afterwards"
|
100
|
+
puts " reload send a SIGHUP to all instances of the application"
|
101
|
+
puts " run start the application and stay on top"
|
102
|
+
puts " zap set the application to a stopped state"
|
103
|
+
puts " status show status (PID) of application instances"
|
104
|
+
puts
|
105
|
+
puts "* and where <options> may contain several of the following:"
|
106
|
+
|
107
|
+
puts @optparse.usage
|
108
|
+
end
|
109
|
+
|
110
|
+
def catch_exceptions(&block)
|
111
|
+
begin
|
112
|
+
block.call
|
113
|
+
rescue CmdException, OptionParser::ParseError => e
|
114
|
+
puts "ERROR: #{e.to_s}"
|
115
|
+
puts
|
116
|
+
print_usage()
|
117
|
+
rescue RuntimeException => e
|
118
|
+
puts "ERROR: #{e.to_s}"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
|
2
|
+
module Daemons
|
3
|
+
class Controller
|
4
|
+
|
5
|
+
attr_reader :app_name
|
6
|
+
|
7
|
+
attr_reader :group
|
8
|
+
|
9
|
+
attr_reader :options
|
10
|
+
|
11
|
+
|
12
|
+
COMMANDS = [
|
13
|
+
'start',
|
14
|
+
'stop',
|
15
|
+
'restart',
|
16
|
+
'run',
|
17
|
+
'zap',
|
18
|
+
'reload',
|
19
|
+
'status'
|
20
|
+
]
|
21
|
+
|
22
|
+
def initialize(options = {}, argv = [])
|
23
|
+
@options = options
|
24
|
+
@argv = argv
|
25
|
+
|
26
|
+
# Allow an app_name to be specified. If not specified use the
|
27
|
+
# basename of the script.
|
28
|
+
@app_name = options[:app_name]
|
29
|
+
|
30
|
+
if options[:script]
|
31
|
+
@script = File.expand_path(options[:script])
|
32
|
+
|
33
|
+
@app_name ||= File.split(@script)[1]
|
34
|
+
end
|
35
|
+
|
36
|
+
@app_name ||= 'unknown_application'
|
37
|
+
|
38
|
+
@command, @controller_part, @app_part = Controller.split_argv(argv)
|
39
|
+
|
40
|
+
#@options[:dir_mode] ||= :script
|
41
|
+
|
42
|
+
@optparse = Optparse.new(self)
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
# This function is used to do a final update of the options passed to the application
|
47
|
+
# before they are really used.
|
48
|
+
#
|
49
|
+
# Note that this function should only update <tt>@options</tt> and no other variables.
|
50
|
+
#
|
51
|
+
def setup_options
|
52
|
+
#@options[:ontop] ||= true
|
53
|
+
end
|
54
|
+
|
55
|
+
def run
|
56
|
+
@options.update @optparse.parse(@controller_part).delete_if {|k,v| !v}
|
57
|
+
|
58
|
+
setup_options()
|
59
|
+
|
60
|
+
#pp @options
|
61
|
+
|
62
|
+
@group = ApplicationGroup.new(@app_name, @options)
|
63
|
+
@group.controller_argv = @controller_part
|
64
|
+
@group.app_argv = @app_part
|
65
|
+
|
66
|
+
@group.setup
|
67
|
+
|
68
|
+
case @command
|
69
|
+
when 'start'
|
70
|
+
@group.new_application.start
|
71
|
+
when 'run'
|
72
|
+
@options[:ontop] ||= true
|
73
|
+
@group.new_application.start
|
74
|
+
when 'stop'
|
75
|
+
@group.stop_all(@options[:no_wait])
|
76
|
+
when 'restart'
|
77
|
+
unless @group.applications.empty?
|
78
|
+
@group.stop_all
|
79
|
+
sleep(1)
|
80
|
+
@group.start_all
|
81
|
+
else
|
82
|
+
puts "Warning: no instances running. Starting..."
|
83
|
+
@group.new_application.start
|
84
|
+
end
|
85
|
+
when 'reload'
|
86
|
+
@group.reload_all
|
87
|
+
when 'zap'
|
88
|
+
@group.zap_all
|
89
|
+
when 'status'
|
90
|
+
unless @group.applications.empty?
|
91
|
+
@group.show_status
|
92
|
+
else
|
93
|
+
puts "#{@group.app_name}: no instances running"
|
94
|
+
end
|
95
|
+
when nil
|
96
|
+
raise CmdException.new('no command given')
|
97
|
+
#puts "ERROR: No command given"; puts
|
98
|
+
|
99
|
+
#print_usage()
|
100
|
+
#raise('usage function not implemented')
|
101
|
+
else
|
102
|
+
raise Error.new("command '#{@command}' not implemented")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
# Split an _argv_ array.
|
108
|
+
# +argv+ is assumed to be in the following format:
|
109
|
+
# ['command', 'controller option 1', 'controller option 2', ..., '--', 'app option 1', ...]
|
110
|
+
#
|
111
|
+
# <tt>command</tt> must be one of the commands listed in <tt>COMMANDS</tt>
|
112
|
+
#
|
113
|
+
# *Returns*: the command as a string, the controller options as an array, the appliation options
|
114
|
+
# as an array
|
115
|
+
#
|
116
|
+
def Controller.split_argv(argv)
|
117
|
+
argv = argv.dup
|
118
|
+
|
119
|
+
command = nil
|
120
|
+
controller_part = []
|
121
|
+
app_part = []
|
122
|
+
|
123
|
+
if COMMANDS.include? argv[0]
|
124
|
+
command = argv.shift
|
125
|
+
end
|
126
|
+
|
127
|
+
if i = argv.index('--')
|
128
|
+
# Handle the case where no controller options are given, just
|
129
|
+
# options after "--" as well (i == 0)
|
130
|
+
controller_part = (i == 0 ? [] : argv[0..i-1])
|
131
|
+
app_part = argv[i+1..-1]
|
132
|
+
else
|
133
|
+
controller_part = argv[0..-1]
|
134
|
+
end
|
135
|
+
|
136
|
+
return command, controller_part, app_part
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|