alexvollmer-daemon-spawn 0.1.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,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