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.
Files changed (58) hide show
  1. data/Gemfile.lock +0 -8
  2. data/README.rdoc +75 -14
  3. data/bin/fire_forget +45 -22
  4. data/examples/long_task +27 -16
  5. data/fire_and_forget.gemspec +41 -4
  6. data/lib/fire_and_forget/client.rb +1 -1
  7. data/lib/fire_and_forget/command/fire.rb +23 -4
  8. data/lib/fire_and_forget/command/get_pid.rb +20 -0
  9. data/lib/fire_and_forget/command/set_pid.rb +0 -2
  10. data/lib/fire_and_forget/command/set_status.rb +1 -1
  11. data/lib/fire_and_forget/command.rb +11 -0
  12. data/lib/fire_and_forget/config.rb +3 -8
  13. data/lib/fire_and_forget/daemon.rb +14 -23
  14. data/lib/fire_and_forget/errors.rb +8 -0
  15. data/lib/fire_and_forget/launcher.rb +69 -6
  16. data/lib/fire_and_forget/server.rb +5 -1
  17. data/lib/fire_and_forget/task_description.rb +11 -0
  18. data/lib/fire_and_forget/utilities.rb +4 -4
  19. data/lib/fire_and_forget/version.rb +1 -1
  20. data/lib/fire_and_forget.rb +6 -2
  21. data/test/test_fire_and_forget.rb +59 -26
  22. data/vendor/daemons-1.1.0/LICENSE +29 -0
  23. data/vendor/daemons-1.1.0/README +224 -0
  24. data/vendor/daemons-1.1.0/Rakefile +88 -0
  25. data/vendor/daemons-1.1.0/Releases +152 -0
  26. data/vendor/daemons-1.1.0/TODO +2 -0
  27. data/vendor/daemons-1.1.0/lib/daemons/application.rb +468 -0
  28. data/vendor/daemons-1.1.0/lib/daemons/application_group.rb +194 -0
  29. data/vendor/daemons-1.1.0/lib/daemons/change_privilege.rb +19 -0
  30. data/vendor/daemons-1.1.0/lib/daemons/cmdline.rb +124 -0
  31. data/vendor/daemons-1.1.0/lib/daemons/controller.rb +140 -0
  32. data/vendor/daemons-1.1.0/lib/daemons/daemonize.rb +271 -0
  33. data/vendor/daemons-1.1.0/lib/daemons/etc_extension.rb +12 -0
  34. data/vendor/daemons-1.1.0/lib/daemons/exceptions.rb +28 -0
  35. data/vendor/daemons-1.1.0/lib/daemons/monitor.rb +138 -0
  36. data/vendor/daemons-1.1.0/lib/daemons/pid.rb +109 -0
  37. data/vendor/daemons-1.1.0/lib/daemons/pidfile.rb +116 -0
  38. data/vendor/daemons-1.1.0/lib/daemons/pidmem.rb +19 -0
  39. data/vendor/daemons-1.1.0/lib/daemons.rb +288 -0
  40. data/vendor/daemons-1.1.0/setup.rb +1360 -0
  41. data/vendor/json-1.5.0/COPYING +58 -0
  42. data/vendor/json-1.5.0/GPL +340 -0
  43. data/vendor/json-1.5.0/README +356 -0
  44. data/vendor/json-1.5.0/README-json-jruby.markdown +33 -0
  45. data/vendor/json-1.5.0/Rakefile +397 -0
  46. data/vendor/json-1.5.0/TODO +1 -0
  47. data/vendor/json-1.5.0/VERSION +1 -0
  48. data/vendor/json-1.5.0/lib/json/add/core.rb +147 -0
  49. data/vendor/json-1.5.0/lib/json/add/rails.rb +8 -0
  50. data/vendor/json-1.5.0/lib/json/common.rb +419 -0
  51. data/vendor/json-1.5.0/lib/json/editor.rb +1369 -0
  52. data/vendor/json-1.5.0/lib/json/pure/generator.rb +441 -0
  53. data/vendor/json-1.5.0/lib/json/pure/parser.rb +320 -0
  54. data/vendor/json-1.5.0/lib/json/pure.rb +15 -0
  55. data/vendor/json-1.5.0/lib/json/version.rb +8 -0
  56. data/vendor/json-1.5.0/lib/json.rb +10 -0
  57. metadata +41 -4
  58. 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 TCP socket server so before we want to use it we have to start this server.
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
- $ faf
24
- FAF process 16235 listening on 127.0.0.1:3001...
26
+ $ fire_forget
27
+ FAF process 16235 listening on /tmp/fire_and_forget.sock ...
25
28
 
26
- The service defaults to listening to port 3001 on localhost. You can change these values thus:
29
+ To change the path of the socket file use the '-s' parameter:
27
30
 
28
- $ faf -a 192.168.1.22 -p 9090
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 both the web-app and the tasks to use the right values using
34
+ If you do this you will need to set the web-app to use the right values:
31
35
 
32
- FireAndForget.port = 9090
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 task and the options
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 comms methods and map this task to the :long_running_task label
64
- # used in the calling script
65
- include FireAndForget::Daemon[:long_running_task]
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 and by default only binds to localhost to stop external access. But to stop unintended access to system commands, only scripts belonging to the same user as the F&F server process are executed. This might not be enough so in the future I might add a limit on the locations of runnable scripts.
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
- require 'rubygems' unless defined?(Gem)
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.bind_address = "127.0.0.1"
18
- options.port = FAF::DEFAULT_PORT
24
+ options.socket = FireAndForget::DEFAULT_SOCKET
25
+ options.gid = Process.egid
19
26
 
20
27
  OptionParser.new do |opts|
21
- opts.on("-a", "--bind-address ADDRESS", "Bind Address") { |v| options.bind_address = v }
22
- opts.on("-p", "--port PORT", Integer, "Port") { |p| options.port = p }
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
- server = TCPServer.new(options.bind_address, options.port)
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 bind to #{options.bind_address}:#{options.port}"
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
- while run and (session = server.accept)
38
- request = response = ""
39
-
40
- while l = session.gets
41
- request << l
42
- end
43
- session.close_read
44
-
45
- begin
46
- response = FAF::Server.parse(request)
47
- rescue => e
48
- response = "ERROR #{e}"
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
- session.write(response)
51
- session.close
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.bind_address}:#{options.port}..."
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
- $:.unshift(File.dirname(__FILE__) + "/../lib")
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
- require 'rubygems'
8
- require 'bundler'
10
+ $:.unshift(File.dirname(__FILE__) + "/../lib")
9
11
 
10
- begin
11
- Bundler.setup(:default)
12
- rescue Bundler::BundlerError => e
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
- require 'fire_and_forget'
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
- include FAF::Daemon[:publish]
24
+ require 'fire_and_forget'
21
25
 
26
+ include FAF::Daemon
22
27
 
23
- File.open(File.join(File.dirname(__FILE__), "../../long.out"), 'w') do |file|
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
- 60.times do |i|
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
- set_status(i)
42
+ set_task_status(i)
32
43
  rescue Exception => e
33
44
  file.write(e.to_s + "\n")
34
45
  end
@@ -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.1.2"
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{2010-12-15}
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/task.rb",
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 = TCPSocket.open(FireAndForget.bind_address, FireAndForget.port)
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 Errno::EACCES.new("'#{binary}' does not belong to user '#{ENV["USER"]}'") unless File.owned?(binary)
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 Errno::ENOENT.new("'#{binary}'") unless File.exists?(binary)
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
- pid
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
+
@@ -15,5 +15,3 @@ module FireAndForget
15
15
  end
16
16
  end
17
17
 
18
-
19
-
@@ -9,7 +9,7 @@ module FireAndForget
9
9
 
10
10
  def run
11
11
  FireAndForget::Server.set_pid(@task_name, @pid)
12
- FireAndForget::Server.status[@task_name] = @status_value
12
+ FireAndForget::Server.status[@task_name] = @status_value.to_s
13
13
  end
14
14
  end
15
15
  end
@@ -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 :port
4
- attr_accessor :bind_address
3
+ attr_accessor :socket
5
4
 
6
- def port
7
- @port ||= FireAndForget::DEFAULT_PORT
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 # need better name!
2
+ module Daemon
3
3
 
4
- def self.[](task_name)
5
- m = Module.new do
6
- def self.included(klass)
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
- def self.task_name=(task_name)
13
- @@task_name = task_name
14
- end
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
- def set_task_status(status)
21
- FireAndForget.set_status(@@task_name, status)
22
- rescue Errno::ECONNREFUSED
23
- # server isn't running but we don't want this to stop our script
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
+
@@ -0,0 +1,8 @@
1
+
2
+
3
+ module FireAndForget
4
+ class Error < ::StandardError; end
5
+ class PermissionsError < Error; end
6
+ class FileNotFoundError < Error; end
7
+ end
8
+