be9-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,98 @@
1
+ = daemon-spawn
2
+
3
+ * http://github.com/alexvollmer/daemon-spawn
4
+
5
+ == DESCRIPTION:
6
+
7
+ Daemon launching and management made dead simple.
8
+
9
+ With daemon-spawn you can start, stop and restart processes that run
10
+ in the background. Processed are tracked by a simple PID file written
11
+ to disk.
12
+
13
+ In addition, you can choose to either execute ruby in your daemonized
14
+ process or 'exec' another process altogether (handy for wrapping other
15
+ services).
16
+
17
+ == SYNOPSIS:
18
+
19
+ === WRITING A DAEMON:
20
+
21
+ To create a new spawner, write a class that extends <tt>DaemonSpawn::Base</tt>
22
+ and provides +start+ and +stop+ methods. For example:
23
+
24
+ class MyServer < DaemonSpawn::Base
25
+
26
+ def start(args)
27
+ # process command-line args
28
+ # start your bad self
29
+ end
30
+
31
+ def stop
32
+ # stop your bad self
33
+ end
34
+ end
35
+
36
+ MyServer.spawn!(:log_file => '/var/log/echo_server.log',
37
+ :pid_file => '/var/run/echo_server.pid',
38
+ :sync_log => true,
39
+ :working_dir => File.dirname(__FILE__))
40
+
41
+ If you need command-line parameters, any arguments passed after one of
42
+ the commands (start, stop, status or restart) will be passed to the
43
+ +start+ method.
44
+
45
+ The +spawn!+ method takes a hash of symbolized keys. At a minimum you
46
+ _must_ specify the <tt>:working_dir</tt> option. You can also override
47
+ the default locations for the log and PID files.
48
+
49
+ See the <tt>examples</tt> directory for working examples.
50
+
51
+ === RUNNING A DAEMON:
52
+
53
+ Let's say that you have the example script listed above in
54
+ <tt>bin/my_server</tt>. Here are the commands for starting, querying
55
+ the status, restarting and stopping the daemon:
56
+
57
+ bin/my_server start
58
+ bin/my_server status
59
+ bin/my_server restart
60
+ bin/my_server stop
61
+
62
+ Note that if any additional arguments are passed to either
63
+ <tt>start</tt> or <tt>restart</tt> those will be passed to the +start+
64
+ method of an instance of your daemon class.
65
+
66
+
67
+ == REQUIREMENTS:
68
+
69
+ None!
70
+
71
+ == INSTALL:
72
+
73
+ * sudo gem install daemon-spawn
74
+
75
+ == LICENSE:
76
+
77
+ (The MIT License)
78
+
79
+ Copyright (c) 2009 Evri, Inc.
80
+
81
+ Permission is hereby granted, free of charge, to any person obtaining
82
+ a copy of this software and associated documentation files (the
83
+ 'Software'), to deal in the Software without restriction, including
84
+ without limitation the rights to use, copy, modify, merge, publish,
85
+ distribute, sublicense, and/or sell copies of the Software, and to
86
+ permit persons to whom the Software is furnished to do so, subject to
87
+ the following conditions:
88
+
89
+ The above copyright notice and this permission notice shall be
90
+ included in all copies or substantial portions of the Software.
91
+
92
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
93
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
94
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
95
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
96
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
97
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
98
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/daemon-spawn.rb'
6
+
7
+ Hoe.new('daemon-spawn', DaemonSpawn::VERSION) do |p|
8
+ p.developer('Alex Vollmer', 'alex@evri.com')
9
+ end
10
+
11
+ # vim: syntax=Ruby
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "socket"
4
+ require "rubygems"
5
+ gem "daemon-spawn", "=0.1.1"
6
+ require "daemon-spawn"
7
+
8
+ class EchoServer < DaemonSpawn::Base
9
+
10
+ attr_accessor :server_socket
11
+
12
+ def start(args)
13
+ port = args.empty? ? 0 : args.first.to_i
14
+ self.server_socket = TCPServer.new('127.0.0.1', port)
15
+ port = self.server_socket.addr[1]
16
+ puts "EchoServer started on port #{port}"
17
+ loop do
18
+ begin
19
+ client = self.server_socket.accept
20
+ while str = client.gets
21
+ client.write(str)
22
+ end
23
+ rescue Errno::ECONNRESET => e
24
+ STDERR.puts "Client reset connection"
25
+ end
26
+ end
27
+ end
28
+
29
+ def stop
30
+ puts "Stopping EchoServer..."
31
+ self.server_socket.close if self.server_socket
32
+ end
33
+ end
34
+
35
+ EchoServer.spawn!(:working_dir => File.join(File.dirname(__FILE__), '..'),
36
+ :log_file => '/tmp/echo_server.log',
37
+ :pid_file => '/tmp/echo_server.pid',
38
+ :sync_log => true,
39
+ :singleton => true)
@@ -0,0 +1,157 @@
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 0
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
+ end
60
+ rescue Errno::ESRCH
61
+ puts "Pid file found, but process was not running. The daemon may have died."
62
+ end
63
+
64
+ def self.status(daemon) #:nodoc:
65
+ if daemon.alive?
66
+ puts "#{daemon.classname} is running (PID #{PidFile.recall(daemon)})"
67
+ else
68
+ puts "#{daemon.classname} is NOT running"
69
+ end
70
+ end
71
+
72
+ class Base
73
+ attr_accessor :log_file, :pid_file, :sync_log, :working_dir, :singleton
74
+
75
+ def initialize(opts={ })
76
+ raise "You must specify a :working_dir" unless opts[:working_dir]
77
+ self.working_dir = opts[:working_dir]
78
+
79
+ self.log_file = opts[:log_file] || File.join(self.working_dir, 'logs', self.classname + '.log')
80
+ self.pid_file = opts[:pid_file] || File.join(self.working_dir, 'run', self.classname + '.pid')
81
+ self.sync_log = opts[:sync_log]
82
+ self.singleton = opts[:singleton] || false
83
+ end
84
+
85
+ def classname #:nodoc:
86
+ self.class.to_s.split('::').last
87
+ end
88
+
89
+ # Provide your implementation. These are provided as a reminder
90
+ # only and will raise an error if invoked. When started, this
91
+ # method will be invoked with the remaining command-line arguments.
92
+ def start(args)
93
+ raise "You must implement a 'start' method in your class!"
94
+ end
95
+
96
+ # Provide your implementation. These are provided as a reminder
97
+ # only and will raise an error if invoked.
98
+ def stop
99
+ raise "You must implement a 'stop' method in your class!"
100
+ end
101
+
102
+ def alive? #:nodoc:
103
+ if File.file?(self.pid_file)
104
+ begin
105
+ Process.kill(0, self.pid)
106
+ rescue Errno::ESRCH, ::Exception
107
+ false
108
+ end
109
+ else
110
+ false
111
+ end
112
+ end
113
+
114
+ def pid #:nodoc:
115
+ IO.read(self.pid_file).to_i rescue nil
116
+ end
117
+
118
+ # Invoke this method to process command-line args and dispatch
119
+ # appropriately. Valid options include the following _symbols_:
120
+ # - <tt>:working_dir</tt> -- the working directory (required)
121
+ # - <tt>:log_file</tt> -- path to the log file
122
+ # - <tt>:pid_file</tt> -- path to the pid file
123
+ # - <tt>:sync_log</tt> -- indicate whether or not to sync log IO
124
+ # - <tt>:singleton</tt> -- If set to true, only one instance is
125
+ # allowed to start
126
+ # args must begin with 'start', 'stop', 'status', or 'restart'.
127
+ # The first token will be removed and any remaining arguments
128
+ # passed to the daemon's start method.
129
+ def self.spawn!(opts={ }, args=ARGV)
130
+ case args.size > 0 && args.shift
131
+ when 'start'
132
+ daemon = self.new(opts)
133
+ DaemonSpawn.start(daemon, args)
134
+ when 'stop'
135
+ daemon = self.new(opts)
136
+ DaemonSpawn.stop(daemon)
137
+ when 'status'
138
+ daemon = self.new(opts)
139
+ if daemon.alive?
140
+ puts "#{daemon.classname} is running (#{daemon.pid})"
141
+ else
142
+ puts "#{daemon.classname} is NOT running"
143
+ end
144
+ when 'restart'
145
+ daemon = self.new(opts)
146
+ DaemonSpawn.stop(daemon)
147
+ DaemonSpawn.start(daemon, args)
148
+ when '-h', '--help', 'help'
149
+ DaemonSpawn.usage
150
+ exit
151
+ else
152
+ DaemonSpawn.usage "Invalid command"
153
+ exit 1
154
+ end
155
+ end
156
+ end
157
+ end
File without changes
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: be9-daemon-spawn
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Alex Vollmer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-04 00:00:00 -07: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: 1.12.2
24
+ version:
25
+ description: Daemon launching and management made dead simple. With daemon-spawn you can start, stop and restart processes that run in the background. Processed are tracked by a simple PID file written to disk. In addition, you can choose to either execute ruby in your daemonized process or 'exec' another process altogether (handy for wrapping other services).
26
+ email:
27
+ - alex@evri.com
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/test_daemon-spawn.rb
43
+ - examples/echo_server.rb
44
+ has_rdoc: false
45
+ homepage: http://github.com/alexvollmer/daemon-spawn
46
+ licenses:
47
+ post_install_message:
48
+ rdoc_options:
49
+ - --main
50
+ - README.txt
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ requirements: []
66
+
67
+ rubyforge_project: daemon-spawn
68
+ rubygems_version: 1.3.5
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: Daemon launching and management made dead simple
72
+ test_files:
73
+ - test/test_daemon-spawn.rb