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 <tt>spawn!</tt> 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,12 @@
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
+ end
11
+
12
+ # vim: syntax=Ruby
@@ -0,0 +1,48 @@
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
+ port = self.server_socket.addr[1]
25
+ puts "EchoServer started on port #{port}"
26
+ loop do
27
+ begin
28
+ client = self.server_socket.accept
29
+ while str = client.gets
30
+ client.write(str)
31
+ end
32
+ rescue Errno::ECONNRESET => e
33
+ STDERR.puts "Client reset connection"
34
+ end
35
+ end
36
+ end
37
+
38
+ def stop
39
+ puts "Stopping EchoServer..."
40
+ self.server_socket.close if self.server_socket
41
+ end
42
+ end
43
+
44
+ EchoServer.spawn!(:working_dir => File.join(File.dirname(__FILE__), '..'),
45
+ :log_file => '/tmp/echo_server.log',
46
+ :pid_file => '/tmp/echo_server.pid',
47
+ :sync_log => true,
48
+ :singleton => true)
@@ -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)
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
File without changes
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: 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-11-01 01: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: 2.3.3
24
+ version:
25
+ description: |-
26
+ Daemon launching and management made dead simple.
27
+
28
+ With daemon-spawn you can start, stop and restart processes that run
29
+ in the background. Processed are tracked by a simple PID file written
30
+ to disk.
31
+
32
+ In addition, you can choose to either execute ruby in your daemonized
33
+ process or 'exec' another process altogether (handy for wrapping other
34
+ services).
35
+ email:
36
+ - alex.vollmer@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - History.txt
43
+ - Manifest.txt
44
+ - README.txt
45
+ files:
46
+ - History.txt
47
+ - Manifest.txt
48
+ - README.txt
49
+ - Rakefile
50
+ - lib/daemon-spawn.rb
51
+ - test/test_daemon-spawn.rb
52
+ - examples/echo_server.rb
53
+ has_rdoc: true
54
+ homepage: http://github.com/alexvollmer/daemon-spawn
55
+ licenses: []
56
+
57
+ post_install_message:
58
+ rdoc_options:
59
+ - --main
60
+ - README.txt
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: "0"
68
+ version:
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: "0"
74
+ version:
75
+ requirements: []
76
+
77
+ rubyforge_project: daemon-spawn
78
+ rubygems_version: 1.3.5
79
+ signing_key:
80
+ specification_version: 3
81
+ summary: Daemon launching and management made dead simple
82
+ test_files:
83
+ - test/test_daemon-spawn.rb