barmstrong-daemon-spawn 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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