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,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