barmstrong-daemon-spawn 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/History.txt ADDED
@@ -0,0 +1,11 @@
1
+ === 0.2.0 / 2009-04-22
2
+
3
+ * Allow specification of args instead of ARGV so that scripts can
4
+ handle command line parsing and validation prior to spawning.
5
+
6
+ === 0.1.0 / 2009-01-26
7
+
8
+ * 1 major enhancement
9
+
10
+ * Happy Birthday!
11
+
data/Manifest.txt ADDED
@@ -0,0 +1,7 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ lib/daemon-spawn.rb
6
+ test/test_daemon-spawn.rb
7
+ examples/echo_server.rb
data/README.txt ADDED
@@ -0,0 +1,34 @@
1
+ A slight motification of the excellent daemon-spawn gem found here, to make it easier to run multiple worker.
2
+
3
+ * http://github.com/alexvollmer/daemon-spawn
4
+
5
+
6
+
7
+ == INSTALL:
8
+
9
+ * sudo gem install daemon-spawn
10
+
11
+ == LICENSE:
12
+
13
+ (The MIT License)
14
+
15
+ Copyright (c) 2009 Evri, Inc.
16
+
17
+ Permission is hereby granted, free of charge, to any person obtaining
18
+ a copy of this software and associated documentation files (the
19
+ 'Software'), to deal in the Software without restriction, including
20
+ without limitation the rights to use, copy, modify, merge, publish,
21
+ distribute, sublicense, and/or sell copies of the Software, and to
22
+ permit persons to whom the Software is furnished to do so, subject to
23
+ the following conditions:
24
+
25
+ The above copyright notice and this permission notice shall be
26
+ included in all copies or substantial portions of the Software.
27
+
28
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
29
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
31
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
32
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
33
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
34
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/daemon-spawn.rb'
6
+
7
+ Hoe.spec('daemon-spawn') do |p|
8
+ p.developer('Alex Vollmer', 'alex.vollmer@gmail.com')
9
+ p.rubyforge_name = 'daemon-spawn'
10
+ p.test_globs = "test/**/*_test.rb"
11
+ end
12
+
13
+ # vim: syntax=Ruby
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "socket"
4
+ require "rubygems"
5
+ require "daemon-spawn"
6
+
7
+ # An echo server using daemon-spawn. It starts up local TCP server
8
+ # socket and repeats each line it receives on the connection. To fire
9
+ # it up run:
10
+ # ./echo_server.rb start 12345
11
+ # Then connect to it using telnet to test it:
12
+ # telnet localhost 12345
13
+ # > howdy!
14
+ # howdy!
15
+ # to shut the daemon down, go back to the command-line and run:
16
+ # ./echo_server.rb stop
17
+ class EchoServer < DaemonSpawn::Base
18
+
19
+ attr_accessor :server_socket
20
+
21
+ def start(args)
22
+ port = args.empty? ? 0 : args.first.to_i
23
+ self.server_socket = TCPServer.new('127.0.0.1', port)
24
+ self.server_socket.setsockopt(Socket::SOL_SOCKET,
25
+ Socket::SO_REUSEADDR,
26
+ true)
27
+ port = self.server_socket.addr[1]
28
+ puts "EchoServer started on port #{port}"
29
+ loop do
30
+ begin
31
+ client = self.server_socket.accept
32
+ puts "Got a connection from #{client}"
33
+ while str = client.gets
34
+ client.write(str)
35
+ puts "Echoed '#{str}' to #{client}"
36
+ end
37
+ rescue Errno::ECONNRESET => e
38
+ STDERR.puts "Client reset connection"
39
+ end
40
+ end
41
+ end
42
+
43
+ def stop
44
+ puts "Stopping EchoServer..."
45
+ self.server_socket.close if self.server_socket
46
+ end
47
+ end
48
+
49
+ if $0 == __FILE__
50
+ EchoServer.spawn!(:working_dir => File.join(File.dirname(__FILE__), '..'),
51
+ :log_file => '/tmp/echo_server.log',
52
+ :pid_file => '/tmp/echo_server.pid',
53
+ :sync_log => true,
54
+ :singleton => true)
55
+ end
@@ -0,0 +1,158 @@
1
+ require "fileutils"
2
+
3
+ # Large portions of this were liberally stolen from the
4
+ # 'simple-daemon' project at http://simple-daemon.rubyforge.org/
5
+ module DaemonSpawn
6
+ VERSION = '0.2.0'
7
+
8
+ def self.usage(msg=nil) #:nodoc:
9
+ print "#{msg}, " if msg
10
+ puts "usage: #{$0} <command> [options]"
11
+ puts "Where <command> is one of start, stop, restart or status"
12
+ puts "[options] are additional options passed to the underlying process"
13
+ end
14
+
15
+ def self.start(daemon, args) #:nodoc:
16
+ if !File.writable?(File.dirname(daemon.log_file))
17
+ STDERR.puts "Unable to write log file to #{daemon.log_file}"
18
+ exit 1
19
+ end
20
+
21
+ if !File.writable?(File.dirname(daemon.pid_file))
22
+ STDERR.puts "Unable to write log file to #{daemon.pid_file}"
23
+ exit 1
24
+ end
25
+
26
+ if daemon.alive? && daemon.singleton
27
+ STDERR.puts "An instance of #{daemon.classname} is already " +
28
+ "running (PID #{daemon.pid})"
29
+ exit 1
30
+ end
31
+
32
+ fork do
33
+ Process.setsid
34
+ exit if fork
35
+ open(daemon.pid_file, 'w') { |f| f << Process.pid }
36
+ Dir.chdir daemon.working_dir
37
+ File.umask 0000
38
+ log = File.new(daemon.log_file, "a")
39
+ log.sync = daemon.sync_log
40
+ STDIN.reopen "/dev/null"
41
+ STDOUT.reopen log
42
+ STDERR.reopen STDOUT
43
+ trap("TERM") {daemon.stop; exit}
44
+ daemon.start(args)
45
+ end
46
+ puts "#{daemon.classname} started."
47
+ end
48
+
49
+ def self.stop(daemon) #:nodoc:
50
+ if pid = daemon.pid
51
+ FileUtils.rm(daemon.pid_file)
52
+ Process.kill("TERM", pid)
53
+ begin
54
+ Process.wait(pid)
55
+ rescue Errno::ECHILD
56
+ end
57
+ else
58
+ puts "Pid file not found. Is the daemon started?"
59
+ exit
60
+ end
61
+ rescue Errno::ESRCH
62
+ puts "Pid file found, but process was not running. The daemon may have died."
63
+ end
64
+
65
+ def self.status(daemon) #:nodoc:
66
+ if daemon.alive?
67
+ puts "#{daemon.classname} is running (PID #{PidFile.recall(daemon)})"
68
+ else
69
+ puts "#{daemon.classname} is NOT running"
70
+ end
71
+ end
72
+
73
+ class Base
74
+ attr_accessor :log_file, :pid_file, :sync_log, :working_dir, :singleton
75
+
76
+ def initialize(opts={ })
77
+ raise "You must specify a :working_dir" unless opts[:working_dir]
78
+ self.working_dir = opts[:working_dir]
79
+
80
+ self.log_file = opts[:log_file] || File.join(self.working_dir, 'logs', self.classname + '.log')
81
+ self.pid_file = opts[:pid_file] || File.join(self.working_dir, 'run', self.classname + '.pid')
82
+ self.sync_log = opts[:sync_log]
83
+ self.singleton = opts[:singleton] || false
84
+ end
85
+
86
+ def classname #:nodoc:
87
+ self.class.to_s.split('::').last
88
+ end
89
+
90
+ # Provide your implementation. These are provided as a reminder
91
+ # only and will raise an error if invoked. When started, this
92
+ # method will be invoked with the remaining command-line arguments.
93
+ def start(args)
94
+ raise "You must implement a 'start' method in your class!"
95
+ end
96
+
97
+ # Provide your implementation. These are provided as a reminder
98
+ # only and will raise an error if invoked.
99
+ def stop
100
+ raise "You must implement a 'stop' method in your class!"
101
+ end
102
+
103
+ def alive? #:nodoc:
104
+ if File.file?(self.pid_file)
105
+ begin
106
+ Process.kill(0, self.pid)
107
+ rescue Errno::ESRCH, ::Exception
108
+ false
109
+ end
110
+ else
111
+ false
112
+ end
113
+ end
114
+
115
+ def pid #:nodoc:
116
+ IO.read(self.pid_file).to_i rescue nil
117
+ end
118
+
119
+ # Invoke this method to process command-line args and dispatch
120
+ # appropriately. Valid options include the following _symbols_:
121
+ # - <tt>:working_dir</tt> -- the working directory (required)
122
+ # - <tt>:log_file</tt> -- path to the log file
123
+ # - <tt>:pid_file</tt> -- path to the pid file
124
+ # - <tt>:sync_log</tt> -- indicate whether or not to sync log IO
125
+ # - <tt>:singleton</tt> -- If set to true, only one instance is
126
+ # allowed to start
127
+ # args must begin with 'start', 'stop', 'status', or 'restart'.
128
+ # The first token will be removed and any remaining arguments
129
+ # passed to the daemon's start method.
130
+ def self.spawn!(opts={ }, args=ARGV.dup)
131
+ case args.size > 0 && args.shift
132
+ when 'start'
133
+ daemon = self.new(opts)
134
+ DaemonSpawn.start(daemon, args)
135
+ when 'stop'
136
+ daemon = self.new(opts)
137
+ DaemonSpawn.stop(daemon)
138
+ when 'status'
139
+ daemon = self.new(opts)
140
+ if daemon.alive?
141
+ puts "#{daemon.classname} is running (#{daemon.pid})"
142
+ else
143
+ puts "#{daemon.classname} is NOT running"
144
+ end
145
+ when 'restart'
146
+ daemon = self.new(opts)
147
+ DaemonSpawn.stop(daemon)
148
+ DaemonSpawn.start(daemon, args)
149
+ when '-h', '--help', 'help'
150
+ DaemonSpawn.usage
151
+ exit
152
+ else
153
+ DaemonSpawn.usage "Invalid command"
154
+ exit 1
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,59 @@
1
+ require "test/unit"
2
+ $:.unshift(File.join(File.dirname(__FILE__), "..", "examples"))
3
+ require "echo_server"
4
+
5
+ class DaemonSpawnTest < Test::Unit::TestCase
6
+
7
+ EXAMPLES = File.join(File.dirname(__FILE__), "..", "examples")
8
+
9
+ def while_running
10
+ Dir.chdir(EXAMPLES) do
11
+ `./echo_server.rb stop`
12
+ assert_match(/EchoServer started./, `./echo_server.rb start 5150`)
13
+ socket = TCPSocket.new('localhost', 5150)
14
+ socket.setsockopt(Socket::SOL_SOCKET,
15
+ Socket::SO_RCVTIMEO,
16
+ [1, 0].pack("l_2"))
17
+
18
+ yield(socket) if block_given?
19
+
20
+ socket.close
21
+ assert_match(//, `./echo_server.rb stop`)
22
+ assert_raises(Errno::ECONNREFUSED) { TCPSocket.new('localhost', 5150) }
23
+ end
24
+ end
25
+
26
+ def test_daemon_running
27
+ while_running do |socket|
28
+ socket << "foobar\n"
29
+ assert_equal "foobar\n", socket.readline
30
+ end
31
+ end
32
+
33
+ def test_status_running
34
+ while_running do |socket|
35
+ assert_match(/EchoServer is running/, `./echo_server.rb status`)
36
+ end
37
+ end
38
+
39
+ def test_status_not_running
40
+ Dir.chdir(EXAMPLES) do
41
+ assert_match(/EchoServer is NOT running/, `./echo_server.rb status`)
42
+ end
43
+ end
44
+
45
+ def test_start_after_started
46
+ while_running do |socket|
47
+ assert_match(/An instance of EchoServer is already running/,
48
+ `./echo_server.rb start 5150 2>&1`)
49
+ end
50
+ end
51
+
52
+ def test_stop_after_stopped
53
+ Dir.chdir(EXAMPLES) do
54
+ assert_match("Pid file not found. Is the daemon started?",
55
+ `./echo_server.rb stop`)
56
+ end
57
+ end
58
+
59
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: barmstrong-daemon-spawn
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Brian Armstrong
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-19 00:00:00 -02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.3.3
24
+ version:
25
+ description: Fork of the daemon-spawn gem by Alex Vollmer.
26
+ email:
27
+ - ...
28
+ executables: []
29
+
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - History.txt
34
+ - Manifest.txt
35
+ - README.txt
36
+ files:
37
+ - History.txt
38
+ - Manifest.txt
39
+ - README.txt
40
+ - Rakefile
41
+ - lib/daemon-spawn.rb
42
+ - test/daemon_spawn_test.rb
43
+ - examples/echo_server.rb
44
+ has_rdoc: true
45
+ homepage: http://github.com/alexvollmer/daemon-spawn
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --main
51
+ - README.txt
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ requirements: []
67
+
68
+ rubyforge_project: daemon-spawn
69
+ rubygems_version: 1.3.5
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: Daemon launching and management made dead simple
73
+ test_files:
74
+ - test/daemon_spawn_test.rb