alexvollmer-daemon-spawn 0.1.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,6 @@
1
+ === 0.1.0 / 2009-01-26
2
+
3
+ * 1 major enhancement
4
+
5
+ * Happy Birthday!
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,8 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/daemon-spawn
6
+ lib/daemon-spawn.rb
7
+ test/test_daemon-spawn.rb
8
+ 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
data/bin/daemon-spawn ADDED
File without changes
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'daemon-spawn'
4
+ require 'socket'
5
+
6
+ class EchoServer < DaemonSpawn::Base
7
+
8
+ attr_accessor :server_socket
9
+
10
+ def start(args)
11
+ port = args.empty? ? 0 : args.first.to_i
12
+ self.server_socket = TCPServer.new('127.0.0.1', port)
13
+ port = self.server_socket.addr[1]
14
+ puts "EchoServer started on port #{port}"
15
+ loop do
16
+ begin
17
+ client = self.server_socket.accept
18
+ while str = client.gets
19
+ client.write(str)
20
+ end
21
+ rescue Errno::ECONNRESET => e
22
+ STDERR.puts "Client reset connection"
23
+ end
24
+ end
25
+ end
26
+
27
+ def stop
28
+ puts "Stopping EchoServer..."
29
+ self.server_socket.close if self.server_socket
30
+ end
31
+ end
32
+
33
+ EchoServer.spawn!(:working_dir => File.join(File.dirname(__FILE__), '..'),
34
+ :log_file => '/tmp/echo_server.log',
35
+ :pid_file => '/tmp/echo_server.pid',
36
+ :sync_log => true,
37
+ :singleton => true)
@@ -0,0 +1,151 @@
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.1.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
+ else
54
+ puts "Pid file not found. Is the daemon started?"
55
+ exit
56
+ end
57
+ rescue Errno::ESRCH
58
+ puts "Pid file found, but process was not running. The daemon may have died."
59
+ end
60
+
61
+ def self.status(daemon) #:nodoc:
62
+ if daemon.alive?
63
+ puts "#{daemon.classname} is running (PID #{PidFile.recall(daemon)})"
64
+ else
65
+ puts "#{daemon.classname} is NOT running"
66
+ end
67
+ end
68
+
69
+ class Base
70
+ attr_accessor :log_file, :pid_file, :sync_log, :working_dir, :singleton
71
+
72
+ def initialize(opts={ })
73
+ raise "You must specify a :working_dir" unless opts[:working_dir]
74
+ self.working_dir = opts[:working_dir]
75
+
76
+ self.log_file = opts[:log_file] || File.join(self.working_dir, 'logs', self.classname + '.log')
77
+ self.pid_file = opts[:pid_file] || File.join(self.working_dir, 'run', self.classname + '.pid')
78
+ self.sync_log = opts[:sync_log]
79
+ self.singleton = opts[:singleton] || false
80
+ end
81
+
82
+ def classname #:nodoc:
83
+ self.class.to_s.split('::').last
84
+ end
85
+
86
+ # Provide your implementation. These are provided as a reminder
87
+ # only and will raise an error if invoked. When started, this
88
+ # method will be invoked with the remaining command-line arguments.
89
+ def start(args)
90
+ raise "You must implement a 'start' method in your class!"
91
+ end
92
+
93
+ # Provide your implementation. These are provided as a reminder
94
+ # only and will raise an error if invoked.
95
+ def stop
96
+ raise "You must implement a 'stop' method in your class!"
97
+ end
98
+
99
+ def alive? #:nodoc:
100
+ if File.file?(self.pid_file)
101
+ begin
102
+ Process.kill(0, self.pid)
103
+ rescue Errno::ESRCH, ::Exception
104
+ false
105
+ end
106
+ else
107
+ false
108
+ end
109
+ end
110
+
111
+ def pid #:nodoc:
112
+ IO.read(self.pid_file).to_i rescue nil
113
+ end
114
+
115
+ # Invoke this method to process command-line args and dispatch
116
+ # appropriately. Valid options include the following _symbols_:
117
+ # - <tt>:working_dir</tt> -- the working directory (required)
118
+ # - <tt>:log_file</tt> -- path to the log file
119
+ # - <tt>:pid_file</tt> -- path to the pid file
120
+ # - <tt>:sync_log</tt> -- indicate whether or not to sync log IO
121
+ # - <tt>:singleton</tt> -- If set to true, only one instance is
122
+ # allowed to start
123
+ def self.spawn!(opts={ })
124
+ case ARGV.size > 0 && ARGV.shift
125
+ when 'start'
126
+ daemon = self.new(opts)
127
+ DaemonSpawn.start(daemon, ARGV)
128
+ when 'stop'
129
+ daemon = self.new(opts)
130
+ DaemonSpawn.stop(daemon)
131
+ when 'status'
132
+ daemon = self.new(opts)
133
+ if daemon.alive?
134
+ puts "#{daemon.classname} is running (#{daemon.pid})"
135
+ else
136
+ puts "#{daemon.classname} is NOT running"
137
+ end
138
+ when 'restart'
139
+ daemon = self.new(opts)
140
+ DaemonSpawn.stop(daemon)
141
+ DaemonSpawn.start(daemon, ARGV)
142
+ when '-h', '--help', 'help'
143
+ DaemonSpawn.usage
144
+ exit
145
+ else
146
+ DaemonSpawn.usage "Invalid command"
147
+ exit 1
148
+ end
149
+ end
150
+ end
151
+ end
File without changes
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: alexvollmer-daemon-spawn
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Alex Vollmer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-01-26 00:00:00 -08:00
13
+ default_executable: daemon-spawn
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hoe
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.8.2
23
+ version:
24
+ 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).
25
+ email:
26
+ - alex@evri.com
27
+ executables:
28
+ - daemon-spawn
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - History.txt
33
+ - Manifest.txt
34
+ - README.txt
35
+ files:
36
+ - History.txt
37
+ - Manifest.txt
38
+ - README.txt
39
+ - Rakefile
40
+ - bin/daemon-spawn
41
+ - lib/daemon-spawn.rb
42
+ - test/test_daemon-spawn.rb
43
+ - examples/echo_server.rb
44
+ has_rdoc: true
45
+ homepage: http://github.com/alexvollmer/daemon-spawn
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --main
49
+ - README.txt
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: "0"
57
+ version:
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ requirements: []
65
+
66
+ rubyforge_project: daemon-spawn
67
+ rubygems_version: 1.2.0
68
+ signing_key:
69
+ specification_version: 2
70
+ summary: Daemon launching and management made dead simple
71
+ test_files:
72
+ - test/test_daemon-spawn.rb