fire_and_forget 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/Gemfile.lock
CHANGED
@@ -1,10 +1,3 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
fire_and_forget (0.1.0)
|
5
|
-
daemons (~> 1.1.0)
|
6
|
-
json (~> 1.4.6)
|
7
|
-
|
8
1
|
GEM
|
9
2
|
remote: http://rubygems.org/
|
10
3
|
specs:
|
@@ -26,7 +19,6 @@ PLATFORMS
|
|
26
19
|
DEPENDENCIES
|
27
20
|
bundler (~> 1.0.0)
|
28
21
|
daemons (~> 1.1.0)
|
29
|
-
fire_and_forget!
|
30
22
|
jeweler (~> 1.5.1)
|
31
23
|
jnunemaker-matchy (~> 0.4)
|
32
24
|
json (~> 1.4.6)
|
data/README.rdoc
CHANGED
@@ -14,23 +14,26 @@ What it doesn't:
|
|
14
14
|
|
15
15
|
- Have any kind of queue mechanism. If you fire the same task twice then that task will be running twice
|
16
16
|
- Have any persistant state
|
17
|
+
- Work in non-UNIX environments
|
17
18
|
|
18
19
|
== Quick Start
|
19
20
|
|
21
|
+
gem install fire_and_forget
|
20
22
|
|
21
|
-
FAF works by running a simple
|
23
|
+
FAF works by running a simple UNIX socket server so before we want to use it we
|
24
|
+
have to start this server.
|
22
25
|
|
23
|
-
$
|
24
|
-
FAF process 16235 listening on
|
26
|
+
$ fire_forget
|
27
|
+
FAF process 16235 listening on /tmp/fire_and_forget.sock ...
|
25
28
|
|
26
|
-
|
29
|
+
To change the path of the socket file use the '-s' parameter:
|
27
30
|
|
28
|
-
$
|
31
|
+
$ fire_forget -s /path/to/socket
|
32
|
+
FAF process 16240 listening on /path/to/socket ...
|
29
33
|
|
30
|
-
If you do this you will need to set
|
34
|
+
If you do this you will need to set the web-app to use the right values:
|
31
35
|
|
32
|
-
FireAndForget.
|
33
|
-
FireAndForget.bind_address = "192.168.1.22"
|
36
|
+
FireAndForget.socket = "/path/to/socket"
|
34
37
|
|
35
38
|
Now inside our Ruby app:
|
36
39
|
|
@@ -39,13 +42,16 @@ Now inside our Ruby app:
|
|
39
42
|
|
40
43
|
# First set register our task by giving it a name and a path to a script file
|
41
44
|
FireAndForget.add_task(:long_running_task, "/path/to/script")
|
45
|
+
# => #<FireAndForget::TaskDescription @name=:long_running_task ...>
|
42
46
|
|
43
|
-
# when we want to call the script we simple call #fire passing the name of the
|
44
|
-
# we want to pass
|
47
|
+
# when we want to call the script we simple call #fire passing the name of the
|
48
|
+
# task and the options we want to pass
|
45
49
|
FireAndForget.fire(:long_running_task, {:param3 => "value3"})
|
50
|
+
# => "OK"
|
46
51
|
|
47
52
|
# Or, use the name as a method:
|
48
53
|
FireAndForget.long_running_task({:param3 => "value3"})
|
54
|
+
# => "OK"
|
49
55
|
|
50
56
|
This will result in the following command being exec'd in an independent process:
|
51
57
|
|
@@ -53,6 +59,38 @@ This will result in the following command being exec'd in an independent process
|
|
53
59
|
|
54
60
|
It up to the script to parse and deal with the command line options.
|
55
61
|
|
62
|
+
The options for the #add_task call are as follows:
|
63
|
+
|
64
|
+
task_name: A symbol giving the (unique) name for this task
|
65
|
+
path_to_binary: The path on disk of the executable to run when this task is launched
|
66
|
+
niceness: an integer in the range 0-20 giving the 'niceness' of the task process.
|
67
|
+
default_params: Command line parameters to pass to each invocation of this task
|
68
|
+
env: settings to add to this task's environment
|
69
|
+
|
70
|
+
The higher the nice value, the lower the priority of the task. Setting a high 'nice'
|
71
|
+
allows you to launch intensive background tasks without affecting the
|
72
|
+
performance of your front line HTTP services.
|
73
|
+
|
74
|
+
For example:
|
75
|
+
|
76
|
+
FireAndForget.add_task(:long_running_task, "/path/to/script", 12,
|
77
|
+
{:param1 => "value1", :param2 => "value2", :param3 => "value3"},
|
78
|
+
{"ENVIRONMENT_SETTING" => "Something"}
|
79
|
+
)
|
80
|
+
|
81
|
+
this will add some default parameters to all invocations of the task, so now the call
|
82
|
+
|
83
|
+
FireAndForget.fire(:long_running_task, {:param3 => "newvalue3"})
|
84
|
+
|
85
|
+
will issue the following command in the background
|
86
|
+
|
87
|
+
/path/to/script --param1="value1" --param2="value2" --param3="newvalue3"
|
88
|
+
|
89
|
+
as well as setting it's nice value to 12 and updating ENV with the extra parameter "ENVIRONMENT_SETTING".
|
90
|
+
|
91
|
+
Parameter values are JSON encoded so you can pass arrays or hashes. Again, it is
|
92
|
+
up to the script to decode these values.
|
93
|
+
|
56
94
|
Interprocess communication is relatively easy. Take the following as the source of the script "/path/to/script"
|
57
95
|
|
58
96
|
#!/usr/bin/env ruby
|
@@ -60,9 +98,9 @@ Interprocess communication is relatively easy. Take the following as the source
|
|
60
98
|
require 'rubygems'
|
61
99
|
require 'fire_and_forget'
|
62
100
|
|
63
|
-
# this will mix in the
|
64
|
-
#
|
65
|
-
include FireAndForget::Daemon
|
101
|
+
# this will mix in the F&F status methods
|
102
|
+
# the F&F configuration (socket and task-name) is calculated automatically from ENV parameters
|
103
|
+
include FireAndForget::Daemon
|
66
104
|
|
67
105
|
30.times do |i|
|
68
106
|
# update our status. What you put here is up to you, but it should be a String
|
@@ -83,10 +121,33 @@ If we decided we've had enough then we can kill it:
|
|
83
121
|
# Or send any signal (see the Process.kill documentation)
|
84
122
|
FireAndForget.kill(:long_running_task, "HUP")
|
85
123
|
|
124
|
+
== Supporting multiple users
|
125
|
+
|
126
|
+
Tasks will attempt to run as the same user as the originating process. To enable
|
127
|
+
this, and hence enable a single F&F server to launch tasks for any number of
|
128
|
+
separate web-apps, simply run the F&F server as root. See the following section on
|
129
|
+
Security for the implications of this and how to stop unlimited access to the service.
|
130
|
+
|
86
131
|
== Security
|
87
132
|
|
88
|
-
F&F is intended to be run in a relatively trusted environment
|
133
|
+
F&F is intended to be run in a relatively trusted environment. You the UNIX
|
134
|
+
socket file has its permissions set so that only the process owner and a defined
|
135
|
+
group can write to the file. To change the UNIX group that has access pass the
|
136
|
+
group name as a parameter to the server:
|
137
|
+
|
138
|
+
$ fire_forget -s /path/to/socket -g webapp_group
|
139
|
+
|
140
|
+
Only members of the 'webapp_group' will be able to launch processes through
|
141
|
+
the server. Additionally, only scripts belonging to the same user as the F&F task description
|
142
|
+
are executed.
|
143
|
+
|
144
|
+
In the meantime, as I say, this is defined to be run in a trusted environment, for instance on a
|
145
|
+
single non-shared server (potentially running many sites).
|
146
|
+
|
147
|
+
== TODO
|
89
148
|
|
149
|
+
- Improve security restrictions (perhaps by limiting the tasks that each user can launch)
|
150
|
+
- Improve the tests so they are closer to testing the full stack
|
90
151
|
|
91
152
|
== Contributing to fire_and_forget
|
92
153
|
|
data/bin/fire_forget
CHANGED
@@ -6,49 +6,72 @@ Dir.chdir(File.join(File.dirname(__FILE__), '..'))
|
|
6
6
|
faf_lib_path = File.expand_path(File.dirname(__FILE__) + "/../lib")
|
7
7
|
$:.unshift(faf_lib_path) unless $:.include?(faf_lib_path)
|
8
8
|
|
9
|
-
|
9
|
+
# include dependencies without using rubygems (to save memory)
|
10
|
+
$:.unshift('vendor/json-1.5.0/lib')
|
11
|
+
$:.unshift('vendor/daemons-1.1.0/lib')
|
12
|
+
|
13
|
+
require 'vendor/json-1.5.0/lib/json/pure'
|
14
|
+
require 'vendor/daemons-1.1.0/lib/daemons'
|
15
|
+
|
10
16
|
|
11
17
|
require 'ostruct'
|
12
18
|
require 'optparse'
|
13
19
|
require 'socket'
|
20
|
+
require 'etc'
|
14
21
|
require 'fire_and_forget'
|
15
22
|
|
16
23
|
options = OpenStruct.new
|
17
|
-
options.
|
18
|
-
options.
|
24
|
+
options.socket = FireAndForget::DEFAULT_SOCKET
|
25
|
+
options.gid = Process.egid
|
19
26
|
|
20
27
|
OptionParser.new do |opts|
|
21
|
-
opts.on("-
|
22
|
-
opts.on("-
|
28
|
+
opts.on("-s", "--socket SOCKET", "Socket") { |v| options.socket = v }
|
29
|
+
opts.on("-g", "--socket-group GROUPNAME", "Socket owning group") { |v| options.gid = nil; options.group_name = v }
|
23
30
|
end.parse!
|
24
31
|
|
32
|
+
raise ArgumentError, "You must specify a socket file using -s" unless options.socket
|
33
|
+
unless options.gid
|
34
|
+
if options.group_name
|
35
|
+
options.gid = Etc.getgrnam(options.group_name).gid
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
25
39
|
server = nil
|
26
40
|
|
27
41
|
begin
|
28
|
-
|
42
|
+
raise Errno::EADDRINUSE if File.exists?(options.socket) and File.socket?(options.socket)
|
43
|
+
server = UNIXServer.new(options.socket)
|
44
|
+
File.chown(nil, options.gid, options.socket)
|
45
|
+
# make the socket group writable
|
46
|
+
File.chmod(0770, options.socket)
|
29
47
|
rescue Errno::EADDRINUSE
|
30
|
-
puts "FAF unable to
|
48
|
+
puts "FAF unable to create socket #{options.socket}: file exists"
|
31
49
|
exit(1)
|
32
50
|
end
|
33
51
|
|
34
52
|
run = true
|
35
53
|
|
36
54
|
server_thread = Thread.new do
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
55
|
+
begin
|
56
|
+
while run and (session = server.accept)
|
57
|
+
request = response = ""
|
58
|
+
|
59
|
+
while l = session.gets
|
60
|
+
request << l
|
61
|
+
end
|
62
|
+
session.close_read
|
63
|
+
|
64
|
+
begin
|
65
|
+
response = FAF::Server.parse(request)
|
66
|
+
rescue => e
|
67
|
+
response = "ERROR #{e}"
|
68
|
+
end
|
69
|
+
session.write(response)
|
70
|
+
session.close
|
49
71
|
end
|
50
|
-
|
51
|
-
|
72
|
+
ensure
|
73
|
+
server.close
|
74
|
+
File.unlink(options.socket) if File.exists?(options.socket)
|
52
75
|
end
|
53
76
|
end
|
54
77
|
|
@@ -59,7 +82,7 @@ end
|
|
59
82
|
end
|
60
83
|
end
|
61
84
|
|
62
|
-
puts "Fire&Forget process #{$$} listening on #{options.
|
85
|
+
puts "Fire&Forget process #{$$} listening on #{options.socket} ..."
|
63
86
|
|
64
87
|
server_thread.join
|
65
88
|
|
data/examples/long_task
CHANGED
@@ -2,33 +2,44 @@
|
|
2
2
|
|
3
3
|
Dir.chdir(File.join(File.dirname(__FILE__), '..'))
|
4
4
|
|
5
|
-
|
5
|
+
File.open(File.join(File.dirname(__FILE__), "../../long.out"), 'w+') do |file|
|
6
|
+
file.sync = true
|
7
|
+
STDOUT.reopen(file)
|
8
|
+
STDERR.reopen(file)
|
6
9
|
|
7
|
-
|
8
|
-
require 'bundler'
|
10
|
+
$:.unshift(File.dirname(__FILE__) + "/../lib")
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
$stderr.puts e.message
|
14
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
15
|
-
exit e.status_code
|
16
|
-
end
|
12
|
+
require 'etc'
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler'
|
17
15
|
|
18
|
-
|
16
|
+
begin
|
17
|
+
Bundler.setup(:default)
|
18
|
+
rescue Bundler::BundlerError => e
|
19
|
+
$stderr.puts e.message
|
20
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
21
|
+
exit e.status_code
|
22
|
+
end
|
19
23
|
|
20
|
-
|
24
|
+
require 'fire_and_forget'
|
21
25
|
|
26
|
+
include FAF::Daemon
|
22
27
|
|
23
|
-
|
24
|
-
file.sync = true
|
28
|
+
file.write(("="*40)+"\n")
|
25
29
|
file.write("PID: #{$$}\n")
|
30
|
+
file.write("User: #{Etc.getlogin}\n")
|
31
|
+
file.write("UID: #{Process.uid}\n")
|
26
32
|
file.write(`ps -xO nice | grep '^#{$$}'`)
|
27
33
|
file.write("\n")
|
28
|
-
|
34
|
+
ENV.keys.sort.each do | k |
|
35
|
+
file.write(" #{k}: #{ENV[k].inspect}\n")
|
36
|
+
end
|
37
|
+
file.write("\n")
|
38
|
+
|
39
|
+
30.times do |i|
|
29
40
|
file.write("#{i}\n")
|
30
41
|
begin
|
31
|
-
|
42
|
+
set_task_status(i)
|
32
43
|
rescue Exception => e
|
33
44
|
file.write(e.to_s + "\n")
|
34
45
|
end
|
data/fire_and_forget.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{fire_and_forget}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Garry Hill"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2011-01-19}
|
13
13
|
s.default_executable = %q{fire_forget}
|
14
14
|
s.email = %q{garry@magnetised.info}
|
15
15
|
s.executables = ["fire_forget"]
|
@@ -31,19 +31,56 @@ Gem::Specification.new do |s|
|
|
31
31
|
"lib/fire_and_forget/client.rb",
|
32
32
|
"lib/fire_and_forget/command.rb",
|
33
33
|
"lib/fire_and_forget/command/fire.rb",
|
34
|
+
"lib/fire_and_forget/command/get_pid.rb",
|
34
35
|
"lib/fire_and_forget/command/get_status.rb",
|
35
36
|
"lib/fire_and_forget/command/kill.rb",
|
36
37
|
"lib/fire_and_forget/command/set_pid.rb",
|
37
38
|
"lib/fire_and_forget/command/set_status.rb",
|
38
39
|
"lib/fire_and_forget/config.rb",
|
39
40
|
"lib/fire_and_forget/daemon.rb",
|
41
|
+
"lib/fire_and_forget/errors.rb",
|
40
42
|
"lib/fire_and_forget/launcher.rb",
|
41
43
|
"lib/fire_and_forget/server.rb",
|
42
|
-
"lib/fire_and_forget/
|
44
|
+
"lib/fire_and_forget/task_description.rb",
|
43
45
|
"lib/fire_and_forget/utilities.rb",
|
44
46
|
"lib/fire_and_forget/version.rb",
|
45
47
|
"test/helper.rb",
|
46
|
-
"test/test_fire_and_forget.rb"
|
48
|
+
"test/test_fire_and_forget.rb",
|
49
|
+
"vendor/daemons-1.1.0/LICENSE",
|
50
|
+
"vendor/daemons-1.1.0/README",
|
51
|
+
"vendor/daemons-1.1.0/Rakefile",
|
52
|
+
"vendor/daemons-1.1.0/Releases",
|
53
|
+
"vendor/daemons-1.1.0/TODO",
|
54
|
+
"vendor/daemons-1.1.0/lib/daemons.rb",
|
55
|
+
"vendor/daemons-1.1.0/lib/daemons/application.rb",
|
56
|
+
"vendor/daemons-1.1.0/lib/daemons/application_group.rb",
|
57
|
+
"vendor/daemons-1.1.0/lib/daemons/change_privilege.rb",
|
58
|
+
"vendor/daemons-1.1.0/lib/daemons/cmdline.rb",
|
59
|
+
"vendor/daemons-1.1.0/lib/daemons/controller.rb",
|
60
|
+
"vendor/daemons-1.1.0/lib/daemons/daemonize.rb",
|
61
|
+
"vendor/daemons-1.1.0/lib/daemons/etc_extension.rb",
|
62
|
+
"vendor/daemons-1.1.0/lib/daemons/exceptions.rb",
|
63
|
+
"vendor/daemons-1.1.0/lib/daemons/monitor.rb",
|
64
|
+
"vendor/daemons-1.1.0/lib/daemons/pid.rb",
|
65
|
+
"vendor/daemons-1.1.0/lib/daemons/pidfile.rb",
|
66
|
+
"vendor/daemons-1.1.0/lib/daemons/pidmem.rb",
|
67
|
+
"vendor/daemons-1.1.0/setup.rb",
|
68
|
+
"vendor/json-1.5.0/COPYING",
|
69
|
+
"vendor/json-1.5.0/GPL",
|
70
|
+
"vendor/json-1.5.0/README",
|
71
|
+
"vendor/json-1.5.0/README-json-jruby.markdown",
|
72
|
+
"vendor/json-1.5.0/Rakefile",
|
73
|
+
"vendor/json-1.5.0/TODO",
|
74
|
+
"vendor/json-1.5.0/VERSION",
|
75
|
+
"vendor/json-1.5.0/lib/json.rb",
|
76
|
+
"vendor/json-1.5.0/lib/json/add/core.rb",
|
77
|
+
"vendor/json-1.5.0/lib/json/add/rails.rb",
|
78
|
+
"vendor/json-1.5.0/lib/json/common.rb",
|
79
|
+
"vendor/json-1.5.0/lib/json/editor.rb",
|
80
|
+
"vendor/json-1.5.0/lib/json/pure.rb",
|
81
|
+
"vendor/json-1.5.0/lib/json/pure/generator.rb",
|
82
|
+
"vendor/json-1.5.0/lib/json/pure/parser.rb",
|
83
|
+
"vendor/json-1.5.0/lib/json/version.rb"
|
47
84
|
]
|
48
85
|
s.homepage = %q{http://github.com/magnetised/fire_and_forget}
|
49
86
|
s.licenses = ["MIT"]
|
@@ -13,7 +13,7 @@ module FireAndForget
|
|
13
13
|
def open_connection
|
14
14
|
connection = result = nil
|
15
15
|
begin
|
16
|
-
connection =
|
16
|
+
connection = UNIXSocket.open(FireAndForget.socket)
|
17
17
|
yield(connection)
|
18
18
|
connection.flush
|
19
19
|
connection.close_write
|
@@ -4,7 +4,13 @@ require 'daemons'
|
|
4
4
|
module FireAndForget
|
5
5
|
module Command
|
6
6
|
class Fire < CommandBase
|
7
|
-
attr_reader :niceness
|
7
|
+
attr_reader :niceness, :task_uid, :task_gid
|
8
|
+
|
9
|
+
def initialize(task, params={})
|
10
|
+
super
|
11
|
+
@task_uid = Process.euid
|
12
|
+
@task_gid = Process.egid
|
13
|
+
end
|
8
14
|
|
9
15
|
def niceness
|
10
16
|
@task.niceness
|
@@ -23,24 +29,37 @@ module FireAndForget
|
|
23
29
|
end
|
24
30
|
|
25
31
|
def permitted?
|
26
|
-
raise
|
32
|
+
raise PermissionsError, "'#{binary}' does not belong to user '#{ENV["USER"]}'" unless File.stat(binary).uid == task_uid
|
27
33
|
true
|
28
34
|
end
|
29
35
|
|
30
36
|
def exists?
|
31
|
-
raise
|
37
|
+
raise FileNotFoundError, "'#{binary}'" unless File.exists?(binary)
|
32
38
|
true
|
33
39
|
end
|
34
40
|
|
41
|
+
def env
|
42
|
+
@task.env.merge({
|
43
|
+
FireAndForget::ENV_SOCKET => FireAndForget.socket,
|
44
|
+
FireAndForget::ENV_TASK_NAME => @task.name.to_s
|
45
|
+
})
|
46
|
+
end
|
47
|
+
|
35
48
|
def run
|
36
49
|
if valid?
|
37
50
|
pid = fork do
|
51
|
+
# set up the environment so that the task can access the F&F server
|
52
|
+
env.each { | k, v | ENV[k] = v }
|
38
53
|
Daemons.daemonize(:backtrace => true)
|
39
54
|
Process.setpriority(Process::PRIO_PROCESS, 0, niceness) if niceness > 0
|
55
|
+
# change to the UID of the originating thread if necessary
|
56
|
+
Process::UID.change_privilege(task_uid) unless Process.euid == task_uid
|
40
57
|
exec(cmd)
|
41
58
|
end
|
42
59
|
Process.detach(pid) if pid
|
43
|
-
|
60
|
+
# don't return the PID as it's actually wrong (the daemonize call forks again so our original
|
61
|
+
# PID is at least 1 out)
|
62
|
+
"OK"
|
44
63
|
end
|
45
64
|
end
|
46
65
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module FireAndForget
|
2
|
+
module Command
|
3
|
+
class GetPid < CommandBase
|
4
|
+
|
5
|
+
attr_reader :task_name, :pid
|
6
|
+
|
7
|
+
def initialize(task_name)
|
8
|
+
@task_name = task_name.to_sym
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
FireAndForget::Server.pids[@task_name]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
@@ -7,6 +7,16 @@ module FireAndForget
|
|
7
7
|
Marshal.load(command)
|
8
8
|
end
|
9
9
|
|
10
|
+
def self.allowed?(cmd)
|
11
|
+
allowed_commands.include?(cmd.class)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.allowed_commands
|
15
|
+
@allowed_commands ||= self.constants.map { |c| self.const_get(c) }.select do |k|
|
16
|
+
k.respond_to?(:ancestors) && k.ancestors.include?(CommandBase)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
10
20
|
class CommandBase
|
11
21
|
attr_reader :tag, :cmd, :params, :task
|
12
22
|
|
@@ -39,5 +49,6 @@ module FireAndForget
|
|
39
49
|
autoload :SetStatus, "fire_and_forget/command/set_status"
|
40
50
|
autoload :GetStatus, "fire_and_forget/command/get_status"
|
41
51
|
autoload :SetPid, "fire_and_forget/command/set_pid"
|
52
|
+
autoload :GetPid, "fire_and_forget/command/get_pid"
|
42
53
|
end
|
43
54
|
end
|
@@ -1,14 +1,9 @@
|
|
1
1
|
module FireAndForget
|
2
2
|
module Config
|
3
|
-
attr_accessor :
|
4
|
-
attr_accessor :bind_address
|
3
|
+
attr_accessor :socket
|
5
4
|
|
6
|
-
def
|
7
|
-
@
|
8
|
-
end
|
9
|
-
|
10
|
-
def bind_address
|
11
|
-
@bind_address ||= "127.0.0.1"
|
5
|
+
def socket
|
6
|
+
@socket ||= (ENV[FireAndForget::ENV_SOCKET] || FireAndForget::DEFAULT_SOCKET)
|
12
7
|
end
|
13
8
|
end
|
14
9
|
end
|
@@ -1,31 +1,22 @@
|
|
1
1
|
module FireAndForget
|
2
|
-
module Daemon
|
2
|
+
module Daemon
|
3
3
|
|
4
|
-
def self.
|
5
|
-
|
6
|
-
|
7
|
-
FireAndForget.map_pid(self.task_name, $$)
|
8
|
-
rescue Errno::ECONNREFUSED
|
9
|
-
# server isn't running but we don't want this to stop our script
|
10
|
-
end
|
4
|
+
def self.task_name
|
5
|
+
ENV[FireAndForget::ENV_TASK_NAME]
|
6
|
+
end
|
11
7
|
|
12
|
-
|
13
|
-
|
14
|
-
|
8
|
+
def self.included(klass)
|
9
|
+
FireAndForget.set_pid(self.task_name, $$)
|
10
|
+
rescue Errno::ECONNREFUSED
|
11
|
+
# server isn't running but we don't want this to stop our script
|
12
|
+
end
|
15
13
|
|
16
|
-
def self.task_name
|
17
|
-
@@task_name
|
18
|
-
end
|
19
14
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
26
|
-
m.task_name = task_name
|
27
|
-
m
|
15
|
+
def set_task_status(status)
|
16
|
+
FireAndForget.set_status(FireAndForget::Daemon.task_name, status)
|
17
|
+
rescue Errno::ECONNREFUSED
|
18
|
+
# server isn't running but we don't want this to stop our script
|
28
19
|
end
|
29
|
-
|
30
20
|
end
|
31
21
|
end
|
22
|
+
|