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.
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
@@ -1,17 +1,44 @@
1
1
  module FireAndForget
2
2
  module Launcher
3
- def add_task(task_name, path_to_binary, default_params={}, niceness=0)
4
- if default_params.is_a?(Numeric)
5
- niceness = default_params
6
- default_params = {}
7
- end
8
- tasks[task_name] = Task.new(task_name, path_to_binary, default_params, niceness)
3
+ # Registers a task and makes it available for easy launching using #fire
4
+ #
5
+ # @param [Symbol] task_name
6
+ # the name for the task. This should be unique
7
+ #
8
+ # @param [String] path_to_binary
9
+ # the path to the executable that should be run when this task is launched
10
+ #
11
+ # @param [Fixnum] niceness
12
+ # the niceness value of the process >= 0. The higher this value the 'nicer' the launched
13
+ # process will be (a high nice value results in a low priority task).
14
+ # On UNIX systems the max, nicest, value is 20
15
+ #
16
+ # @param [Hash] default_params
17
+ # A Hash of parameters that should be passed to every invocation of the task.
18
+ # These will be converted to command line parameters
19
+ # { "setting" => "value", "output" => "destination"}
20
+ # gives the parameters
21
+ # --setting=value --output=destination
22
+ # @see FireAndForget::Utilities#to_arguments
23
+ #
24
+ # @param [Hash] env
25
+ # A Hash of values to add to the task's ENV settings
26
+ #
27
+ def add_task(task_name, path_to_binary, niceness=0, default_params={}, env={})
28
+ tasks[task_name] = TaskDescription.new(task_name, path_to_binary, niceness, default_params, env)
9
29
  end
10
30
 
31
+ # Returns the path to the binary for the given task
32
+ #
33
+ # @param [Symbol] task_name the name of the task
34
+ # @return [String] the path of the task's binary
11
35
  def binary(task_name)
12
36
  tasks[task_name].binary
13
37
  end
14
38
 
39
+ # Gets the TaskDescription of a task
40
+ #
41
+ # @param [Symbol] task_name the name of the task to get
15
42
  def [](task_name)
16
43
  tasks[task_name]
17
44
  end
@@ -20,35 +47,65 @@ module FireAndForget
20
47
  @tasks ||= {}
21
48
  end
22
49
 
50
+ # Launches the given task
51
+ #
52
+ # @param [Symbol] task_name the name of the task to launch
53
+ # @param [Hash] params parameters to pass to the executable
23
54
  def fire(task_name, params={})
24
55
  task = tasks[task_name]
25
56
  command = Command::Fire.new(task, params)
26
57
  Client.run(command)
27
58
  end
28
59
 
60
+ # Sets the status of the given task enabling simple interprocess communication through string messages
61
+ #
62
+ # @param [String] task_name the name of the task to set the status for
63
+ # @param [#to_s] status the setting for the given task's status
29
64
  def set_status(task_name, status)
30
65
  command = Command::SetStatus.new(task_name, status)
31
66
  Client.run(command)
32
67
  end
33
68
 
69
+ # Get the status for the given task
70
+ # @see #set_status
71
+ #
72
+ # @param [Symbol] task_name the name of the task
73
+ # @return [String] the current status of the task
34
74
  def get_status(task_name)
35
75
  command = Command::GetStatus.new(task_name)
36
76
  Client.run(command)
37
77
  end
38
78
 
79
+ # Used by the {FireAndForget::Daemon} module to set the correct PID for a given task
39
80
  def map_pid(task_name, pid)
40
81
  command = Command::SetPid.new(task_name, pid)
41
82
  Client.run(command)
42
83
  end
84
+ alias_method :set_pid, :map_pid
85
+
86
+ # Retrieve the PID of the running task given by task_name
87
+ def get_pid(task_name)
88
+ command = Command::GetPid.new(task_name)
89
+ Client.run(command)
90
+ end
91
+ alias_method :pid, :get_pid
43
92
 
93
+ # Sends a running task the TERM signal
44
94
  def term(task_name)
45
95
  kill(task_name, "TERM")
46
96
  end
47
97
 
98
+ # Sends a running task the INT signal
48
99
  def int(task_name)
49
100
  kill(task_name, "INT")
50
101
  end
51
102
 
103
+ # Sends a running task an arbitrary signal
104
+ #
105
+ # @param [Symbol] task_name the name of the task to send the signal
106
+ # @param [String] signal the signal to send
107
+ #
108
+ # @see Signal#list for a full list of signals available
52
109
  def kill(task_name, signal="TERM")
53
110
  command = Command::Kill.new(task_name, signal)
54
111
  Client.run(command)
@@ -56,6 +113,12 @@ module FireAndForget
56
113
 
57
114
  protected
58
115
 
116
+ # Catch method missing to enable launching of tasks by direct name
117
+ # e.g.
118
+ # FireAndForget.add_task(:process_things, "/usr/bin/process")
119
+ # launch this task:
120
+ # FireAndForget.process_things
121
+ #
59
122
  def method_missing(method, *args, &block)
60
123
  if tasks.key?(method)
61
124
  fire(method, *args, &block)
@@ -7,7 +7,11 @@ module FireAndForget
7
7
  end
8
8
 
9
9
  def self.run(cmd)
10
- cmd.run
10
+ if Command.allowed?(cmd)
11
+ cmd.run
12
+ else
13
+ raise PermissionsError, "'#{cmd.class}' is not an approved command"
14
+ end
11
15
  end
12
16
 
13
17
  def self.kill(task_name, signal="TERM")
@@ -0,0 +1,11 @@
1
+
2
+
3
+ module FireAndForget
4
+ class TaskDescription
5
+ attr_reader :name, :binary, :niceness, :params, :env
6
+
7
+ def initialize(name, path_to_binary, niceness=0, default_parameters={}, env={})
8
+ @name, @binary, @params, @niceness, @env = name, path_to_binary, default_parameters, niceness, env
9
+ end
10
+ end
11
+ end
@@ -4,16 +4,16 @@ module FireAndForget
4
4
  module Utilities
5
5
  def to_arguments(params={})
6
6
  params.keys.sort { |a, b| a.to_s <=> b.to_s }.map do |key|
7
- %(--#{key}=#{to_json(params[key])})
7
+ %(--#{key}=#{to_parameter(params[key])})
8
8
  end.join(" ")
9
9
  end
10
10
 
11
- def to_json(obj)
11
+ def to_parameter(obj)
12
12
  if obj.is_a?(String)
13
- obj.inspect
13
+ obj
14
14
  else
15
15
  JSON.generate(obj)
16
- end
16
+ end.inspect
17
17
  end
18
18
  end
19
19
  end
@@ -1,4 +1,4 @@
1
1
 
2
2
  module FireAndForget
3
- VERSION = "0.1.2"
3
+ VERSION = "0.2.0"
4
4
  end
@@ -1,16 +1,20 @@
1
1
 
2
2
  module FireAndForget
3
- DEFAULT_PORT = 3001
3
+ DEFAULT_SOCKET = "/tmp/fire_and_forget.sock"
4
+ ENV_SOCKET = "__FAF_SOCKET"
5
+ ENV_TASK_NAME = "__FAF_TASK_NAME"
4
6
 
5
7
  autoload :Config, "fire_and_forget/config"
6
8
  autoload :Utilities, "fire_and_forget/utilities"
7
9
  autoload :Launcher, "fire_and_forget/launcher"
8
- autoload :Task, "fire_and_forget/task"
10
+ autoload :TaskDescription, "fire_and_forget/task_description"
9
11
  autoload :Client, "fire_and_forget/client"
10
12
  autoload :Command, "fire_and_forget/command"
11
13
  autoload :Server, "fire_and_forget/server"
12
14
  autoload :Daemon, "fire_and_forget/daemon"
13
15
 
16
+ require "fire_and_forget/errors"
17
+
14
18
  extend Config
15
19
  extend Utilities
16
20
  extend Launcher
@@ -7,7 +7,7 @@ class TestFireAndForget < Test::Unit::TestCase
7
7
  :param1 => "value1",
8
8
  :param2 => "value2",
9
9
  :array => [1, 2, "3"]
10
- }).should == %(--array=[1,2,"3"] --param1="value1" --param2="value2")
10
+ }).should == %(--array="[1,2,\\"3\\"]" --param1="value1" --param2="value2")
11
11
  end
12
12
  end
13
13
  context "configuration" do
@@ -29,20 +29,24 @@ class TestFireAndForget < Test::Unit::TestCase
29
29
  FAF.publish(args)
30
30
  end
31
31
 
32
- should "enable setting of port for server" do
33
- FAF.port = 3007
34
- FAF.port.should == 3007
35
- end
32
+ should "enable setting of path to socket" do
33
+ FAF.socket = "/tmp/something"
34
+ FAF.socket.should == "/tmp/something"
35
+ end
36
+ # should "enable setting of port for server" do
37
+ # FAF.port = 3007
38
+ # FAF.port.should == 3007
39
+ # end
36
40
 
37
- should "enable setting an address for the server" do
38
- FAF.bind_address = "10.0.1.10"
39
- FAF.bind_address.should == "10.0.1.10"
40
- end
41
+ # should "enable setting an address for the server" do
42
+ # FAF.bind_address = "10.0.1.10"
43
+ # FAF.bind_address.should == "10.0.1.10"
44
+ # end
41
45
  end
42
46
 
43
47
  context "commands" do
44
48
  should "serialize and deserialize correctly" do
45
- task = FAF::Task.new(:publish, "/publish", {:param1 => "value1", :param2 => "value2"}, 9)
49
+ task = FAF::TaskDescription.new(:publish, "/publish", 9, {:param1 => "value1", :param2 => "value2"})
46
50
  cmd = FAF::Command::CommandBase.new(task, {"param2" => "newvalue2", :param3 => "value3"})
47
51
  cmd2 = FAF::Command.load(cmd.dump)
48
52
  task2 = cmd2.task
@@ -55,28 +59,56 @@ class TestFireAndForget < Test::Unit::TestCase
55
59
 
56
60
  context "actions" do
57
61
  setup do
58
- @task = FAF::Task.new(:publish, "/publish", {:param1 => "value1", :param2 => "value2"}, 9)
62
+ @task = FAF::TaskDescription.new(:publish, "/publish", 9, {:param1 => "value1", :param2 => "value2"})
59
63
  end
60
64
  should "set status for a task" do
61
65
  cmd = FAF::Command::SetStatus.new(:publish, :doing)
62
66
  FAF::Server.run(cmd)
63
67
  cmd = FAF::Command::GetStatus.new(:publish)
64
68
  status = FAF::Server.run(cmd)
65
- status.should == :doing
69
+ status.should == "doing"
66
70
  end
71
+
67
72
  should "only run scripts belonging to the same user as the ruby process" do
68
73
  stub(File).exist?("/publish") { true }
69
74
  stub(File).exists?("/publish") { true }
70
- stub(File).owned?("/publish") { false }
75
+ stat = Object.new
76
+ stub(stat).uid { Process.uid + 1 }
77
+ stub(File).stat("/publish") { stat }
71
78
  cmd = FAF::Command::Fire.new(@task)
72
- lambda { cmd.run }.should raise_error(Errno::EACCES)
79
+ lambda { cmd.run }.should raise_error(FAF::PermissionsError)
73
80
  end
81
+
82
+ should "not raise an error if the binary belongs to this process" do
83
+ stat = Object.new
84
+ stub(stat).uid { Process.uid }
85
+ stub(File).stat("/publish") { stat }
86
+ cmd = FAF::Command::Fire.new(@task)
87
+ lambda { cmd.permitted? }.should_not raise_error(FAF::PermissionsError)
88
+ end
89
+
90
+ should "raise an error if the binary belongs to this process" do
91
+ stat = Object.new
92
+ uid = Process.uid
93
+ stub(stat).uid { uid }
94
+ stub(File).stat("/publish") { stat }
95
+ stub(Process).euid { uid + 1 }
96
+ cmd = FAF::Command::Fire.new(@task)
97
+ lambda { cmd.permitted? }.should raise_error(FAF::PermissionsError)
98
+ end
99
+
74
100
  should "give error if binary doesn't exist" do
75
101
  stub(File).exist?("/publish") { false }
76
102
  stub(File).exists?("/publish") { false }
77
103
  stub(File).owned?("/publish") { true }
78
104
  cmd = FAF::Command::Fire.new(@task)
79
- lambda { cmd.run }.should raise_error(Errno::ENOENT )
105
+ lambda { cmd.run }.should raise_error(FAF::FileNotFoundError)
106
+ end
107
+
108
+ should "raise error if command isn't one of the approved list" do
109
+ cmd = Object.new
110
+ mock(cmd).run.times(0)
111
+ lambda { FAF::Server.run(cmd) }.should raise_error(FAF::PermissionsError)
80
112
  end
81
113
  end
82
114
 
@@ -92,9 +124,8 @@ class TestFireAndForget < Test::Unit::TestCase
92
124
  stub(connection).close_write
93
125
  mock(connection).read { "99999" }
94
126
  mock(connection).close
95
- FAF.bind_address = "10.0.1.10"
96
- FAF.port = 9007
97
- mock(TCPSocket).open("10.0.1.10", 9007) { connection }
127
+ FAF.socket = "/tmp/faf.sock"
128
+ mock(UNIXSocket).open("/tmp/faf.sock") { connection }
98
129
  pid = FAF.publish({:param2 => "value3"})
99
130
  pid.should == "99999"
100
131
  end
@@ -107,6 +138,7 @@ class TestFireAndForget < Test::Unit::TestCase
107
138
 
108
139
  should "run any command sent to it" do
109
140
  command = Object.new
141
+ stub(FAF::Command).allowed? { true }
110
142
  mock(FAF::Command).load(is_a(String)) { command }
111
143
  mock(command).run { "666" }
112
144
  result = FAF::Server.parse("object")
@@ -115,17 +147,18 @@ class TestFireAndForget < Test::Unit::TestCase
115
147
  end
116
148
  context "daemon methods" do
117
149
  setup do
118
- class ::TaskClass; end
150
+ class ::TaskDescriptionClass; end
119
151
  end
120
152
  teardown do
121
- Object.send(:remove_const, :TaskClass) rescue nil
153
+ Object.send(:remove_const, :TaskDescriptionClass) rescue nil
122
154
  end
123
155
 
124
- should "map a taskname to a pid when included" do
125
- mock(FAF::Client).run(satisfy { |cmd|
126
- (cmd.pid == $$) && (cmd.task_name == :tasking)
127
- })
128
- TaskClass.send(:include, FAF::Daemon[:tasking])
129
- end
156
+ ## not sure how to test this
157
+ # should "map a taskname to a pid when included" do
158
+ # mock(FAF::Client).run(satisfy { |cmd|
159
+ # (cmd.pid == $$) && (cmd.task_name == :tasking)
160
+ # })
161
+ # TaskDescriptionClass.send(:include, FAF::Daemon)
162
+ # end
130
163
  end
131
164
  end
@@ -0,0 +1,29 @@
1
+ Copyright (c) 2005-2007 Thomas Uehlinger
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ This license does not apply to daemonize.rb, which is was written by
25
+ Travis Whitton und published under the following license:
26
+
27
+ The Daemonize extension module is copywrited free software by Travis Whitton
28
+ <whitton@atlantic.net>. You can redistribute it under the terms specified in
29
+ the COPYING file of the Ruby distribution.
@@ -0,0 +1,224 @@
1
+ = Daemons Version 1.1.0
2
+
3
+ (See Releases for release-specific information)
4
+
5
+ == What is Daemons?
6
+
7
+ Daemons provides an easy way to wrap existing ruby scripts (for example a self-written server)
8
+ to be <i>run as a daemon</i> and to be <i>controlled by simple start/stop/restart commands</i>.
9
+
10
+ If you want, you can also use daemons to <i>run blocks of ruby code in a daemon process</i> and to control
11
+ these processes from the main application.
12
+
13
+ Besides this basic functionality, daemons offers many advanced features like <i>exception backtracing</i>
14
+ and logging (in case your ruby script crashes) and <i>monitoring</i> and automatic restarting of your processes
15
+ if they crash.
16
+
17
+ Daemons includes the <tt>daemonize.rb</tt> script written by <i>Travis Whitton</i> to do the daemonization
18
+ process.
19
+
20
+ == Basic Usage
21
+
22
+ You can use Daemons in four different ways:
23
+
24
+ === 1. Create wrapper scripts for your server scripts or applications
25
+
26
+ Layout: suppose you have your self-written server <tt>myserver.rb</tt>:
27
+
28
+ # this is myserver.rb
29
+ # it does nothing really useful at the moment
30
+
31
+ loop do
32
+ sleep(5)
33
+ end
34
+
35
+ To use <tt>myserver.rb</tt> in a production environment, you need to be able to
36
+ run <tt>myserver.rb</tt> in the _background_ (this means detach it from the console, fork it
37
+ in the background, release all directories and file descriptors).
38
+
39
+ Just create <tt>myserver_control.rb</tt> like this:
40
+
41
+ # this is myserver_control.rb
42
+
43
+ require 'rubygems' # if you use RubyGems
44
+ require 'daemons'
45
+
46
+ Daemons.run('myserver.rb')
47
+
48
+ And use it like this from the console:
49
+
50
+ $ ruby myserver_control.rb start
51
+ (myserver.rb is now running in the background)
52
+ $ ruby myserver_control.rb restart
53
+ (...)
54
+ $ ruby myserver_control.rb stop
55
+
56
+ For testing purposes you can even run <tt>myserver.rb</tt> <i>without forking</i> in the background:
57
+
58
+ $ ruby myserver_control.rb run
59
+
60
+ An additional nice feature of Daemons is that you can pass <i>additional arguments</i> to the script that
61
+ should be daemonized by seperating them by two _hyphens_:
62
+
63
+ $ ruby myserver_control.rb start -- --file=anyfile --a_switch another_argument
64
+
65
+
66
+ === 2. Create wrapper scripts that include your server procs
67
+
68
+ Layout: suppose you have some code you want to run in the background and control that background process
69
+ from a script:
70
+
71
+ # this is your code
72
+ # it does nothing really useful at the moment
73
+
74
+ loop do
75
+ sleep(5)
76
+ end
77
+
78
+ To run this code as a daemon create <tt>myproc_control.rb</tt> like this and include your code:
79
+
80
+ # this is myproc_control.rb
81
+
82
+ require 'rubygems' # if you use RubyGems
83
+ require 'daemons'
84
+
85
+ Daemons.run_proc('myproc.rb') do
86
+ loop do
87
+ sleep(5)
88
+ end
89
+ end
90
+
91
+ And use it like this from the console:
92
+
93
+ $ ruby myproc_control.rb start
94
+ (myproc.rb is now running in the background)
95
+ $ ruby myproc_control.rb restart
96
+ (...)
97
+ $ ruby myproc_control.rb stop
98
+
99
+ For testing purposes you can even run <tt>myproc.rb</tt> <i>without forking</i> in the background:
100
+
101
+ $ ruby myproc_control.rb run
102
+
103
+ === 3. Control a bunch of daemons from another application
104
+
105
+ Layout: you have an application <tt>my_app.rb</tt> that wants to run a bunch of
106
+ server tasks as daemon processes.
107
+
108
+ # this is my_app.rb
109
+
110
+ require 'rubygems' # if you use RubyGems
111
+ require 'daemons'
112
+
113
+ task1 = Daemons.call(:multiple => true) do
114
+ # first server task
115
+
116
+ loop {
117
+ conn = accept_conn()
118
+ serve(conn)
119
+ }
120
+ end
121
+
122
+ task2 = Daemons.call do
123
+ # second server task
124
+
125
+ loop {
126
+ something_different()
127
+ }
128
+ end
129
+
130
+ # the parent process continues to run
131
+
132
+ # we can even control our tasks, for example stop them
133
+ task1.stop
134
+ task2.stop
135
+
136
+ exit
137
+
138
+ === 4. Daemonize the currently running process
139
+
140
+ Layout: you have an application <tt>my_daemon.rb</tt> that wants to run as a daemon
141
+ (but without the ability to be controlled by daemons via start/stop commands)
142
+
143
+ # this is my_daemons.rb
144
+
145
+ require 'rubygems' # if you use RubyGems
146
+ require 'daemons'
147
+
148
+ # Initialize the app while we're not a daemon
149
+ init()
150
+
151
+ # Become a daemon
152
+ Daemons.daemonize
153
+
154
+ # The server loop
155
+ loop {
156
+ conn = accept_conn()
157
+ serve(conn)
158
+ }
159
+
160
+
161
+ <b>For further documentation, refer to the module documentation of Daemons.</b>
162
+
163
+
164
+ == Download and Installation
165
+
166
+ *Download*: just go to http://rubyforge.org/projects/daemons/
167
+
168
+ Installation *with* RubyGems:
169
+ $ su
170
+ # gem install daemons
171
+
172
+ Installation *without* RubyGems:
173
+ $ tar xfz daemons-x.x.x.tar.gz
174
+ $ cd daemons-x.x.x
175
+ $ su
176
+ # ruby setup.rb
177
+
178
+ == Documentation
179
+
180
+ For further documentation, refer to the module documentation of Daemons (click on Daemons).
181
+
182
+ The RDoc documentation is also online at http://daemons.rubyforge.org
183
+
184
+
185
+ == Author
186
+
187
+ Written 2005-2010 by Thomas Uehlinger <mailto:th.uehlinger@gmx.ch>.
188
+ Anonymous SVN checkout is available with "svn checkout http://daemons.rubyforge.org/svn/".
189
+
190
+ == License
191
+
192
+ Copyright (c) 2005-2010 Thomas Uehlinger
193
+
194
+ Permission is hereby granted, free of charge, to any person
195
+ obtaining a copy of this software and associated documentation
196
+ files (the "Software"), to deal in the Software without
197
+ restriction, including without limitation the rights to use,
198
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
199
+ copies of the Software, and to permit persons to whom the
200
+ Software is furnished to do so, subject to the following
201
+ conditions:
202
+
203
+ The above copyright notice and this permission notice shall be
204
+ included in all copies or substantial portions of the Software.
205
+
206
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
207
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
208
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
209
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
210
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
211
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
212
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
213
+ OTHER DEALINGS IN THE SOFTWARE.
214
+
215
+ This license does not apply to daemonize.rb, which is was written by
216
+ Travis Whitton und published under the following license:
217
+
218
+ The Daemonize extension module is copywrited free software by Travis Whitton
219
+ <whitton@atlantic.net>. You can redistribute it under the terms specified in
220
+ the COPYING file of the Ruby distribution.
221
+
222
+ == Feedback and other resources
223
+
224
+ At http://rubyforge.org/projects/daemons.