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
@@ -1,17 +1,44 @@
|
|
1
1
|
module FireAndForget
|
2
2
|
module Launcher
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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)
|
@@ -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}=#{
|
7
|
+
%(--#{key}=#{to_parameter(params[key])})
|
8
8
|
end.join(" ")
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
11
|
+
def to_parameter(obj)
|
12
12
|
if obj.is_a?(String)
|
13
|
-
obj
|
13
|
+
obj
|
14
14
|
else
|
15
15
|
JSON.generate(obj)
|
16
|
-
end
|
16
|
+
end.inspect
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
data/lib/fire_and_forget.rb
CHANGED
@@ -1,16 +1,20 @@
|
|
1
1
|
|
2
2
|
module FireAndForget
|
3
|
-
|
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 :
|
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
|
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
|
33
|
-
FAF.
|
34
|
-
FAF.
|
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
|
-
|
39
|
-
|
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::
|
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::
|
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 ==
|
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
|
-
|
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(
|
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(
|
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.
|
96
|
-
|
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 ::
|
150
|
+
class ::TaskDescriptionClass; end
|
119
151
|
end
|
120
152
|
teardown do
|
121
|
-
Object.send(:remove_const, :
|
153
|
+
Object.send(:remove_const, :TaskDescriptionClass) rescue nil
|
122
154
|
end
|
123
155
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
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.
|