textgoeshere-daemon-kit 0.1.8rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. data/.gitignore +5 -0
  2. data/Configuration.txt +110 -0
  3. data/Deployment.txt +113 -0
  4. data/History.txt +124 -0
  5. data/Logging.txt +96 -0
  6. data/PostInstall.txt +6 -0
  7. data/README.rdoc +128 -0
  8. data/Rakefile +29 -0
  9. data/RuoteParticipants.txt +113 -0
  10. data/TODO.txt +27 -0
  11. data/bin/daemon-kit +18 -0
  12. data/config/website.yml +2 -0
  13. data/daemon-kit.gemspec +265 -0
  14. data/lib/daemon_kit/abstract_logger.rb +249 -0
  15. data/lib/daemon_kit/amqp.rb +39 -0
  16. data/lib/daemon_kit/application.rb +230 -0
  17. data/lib/daemon_kit/arguments.rb +165 -0
  18. data/lib/daemon_kit/commands/console.rb +38 -0
  19. data/lib/daemon_kit/commands/destroy.rb +10 -0
  20. data/lib/daemon_kit/commands/generate.rb +10 -0
  21. data/lib/daemon_kit/config.rb +113 -0
  22. data/lib/daemon_kit/console_daemon.rb +2 -0
  23. data/lib/daemon_kit/core_ext/configurable.rb +96 -0
  24. data/lib/daemon_kit/core_ext/string.rb +22 -0
  25. data/lib/daemon_kit/core_ext.rb +1 -0
  26. data/lib/daemon_kit/cron.rb +48 -0
  27. data/lib/daemon_kit/cucumber/world.rb +38 -0
  28. data/lib/daemon_kit/deployment/capistrano.rb +516 -0
  29. data/lib/daemon_kit/em.rb +43 -0
  30. data/lib/daemon_kit/error_handlers/base.rb +32 -0
  31. data/lib/daemon_kit/error_handlers/hoptoad.rb +180 -0
  32. data/lib/daemon_kit/exceptions.rb +15 -0
  33. data/lib/daemon_kit/generators/base.rb +60 -0
  34. data/lib/daemon_kit/generators.rb +67 -0
  35. data/lib/daemon_kit/initializer.rb +453 -0
  36. data/lib/daemon_kit/jabber.rb +171 -0
  37. data/lib/daemon_kit/nanite/agent.rb +77 -0
  38. data/lib/daemon_kit/nanite.rb +7 -0
  39. data/lib/daemon_kit/pid_file.rb +61 -0
  40. data/lib/daemon_kit/ruote_participants.rb +125 -0
  41. data/lib/daemon_kit/ruote_pseudo_participant.rb +68 -0
  42. data/lib/daemon_kit/ruote_workitem.rb +187 -0
  43. data/lib/daemon_kit/safety.rb +84 -0
  44. data/lib/daemon_kit/tasks/environment.rake +10 -0
  45. data/lib/daemon_kit/tasks/framework.rake +123 -0
  46. data/lib/daemon_kit/tasks/god.rake +62 -0
  47. data/lib/daemon_kit/tasks/log.rake +8 -0
  48. data/lib/daemon_kit/tasks/monit.rake +29 -0
  49. data/lib/daemon_kit/tasks.rb +2 -0
  50. data/lib/daemon_kit/vendor/thor-0.13.6/CHANGELOG.rdoc +89 -0
  51. data/lib/daemon_kit/vendor/thor-0.13.6/LICENSE +20 -0
  52. data/lib/daemon_kit/vendor/thor-0.13.6/README.rdoc +297 -0
  53. data/lib/daemon_kit/vendor/thor-0.13.6/Thorfile +69 -0
  54. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions/create_file.rb +103 -0
  55. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions/directory.rb +91 -0
  56. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions/empty_directory.rb +134 -0
  57. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions/file_manipulation.rb +223 -0
  58. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions/inject_into_file.rb +104 -0
  59. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions.rb +296 -0
  60. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/base.rb +540 -0
  61. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/core_ext/file_binary_read.rb +9 -0
  62. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  63. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/core_ext/ordered_hash.rb +100 -0
  64. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/error.rb +30 -0
  65. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/group.rb +271 -0
  66. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/invocation.rb +180 -0
  67. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/parser/argument.rb +67 -0
  68. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/parser/arguments.rb +150 -0
  69. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/parser/option.rb +128 -0
  70. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/parser/options.rb +169 -0
  71. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/parser.rb +4 -0
  72. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/rake_compat.rb +66 -0
  73. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/runner.rb +314 -0
  74. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/shell/basic.rb +239 -0
  75. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/shell/color.rb +108 -0
  76. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/shell.rb +83 -0
  77. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/task.rb +102 -0
  78. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/util.rb +224 -0
  79. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/version.rb +3 -0
  80. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor.rb +244 -0
  81. data/lib/daemon_kit/xmpp.rb +100 -0
  82. data/lib/daemon_kit.rb +59 -0
  83. data/lib/generators/daemon_kit/amqp/USAGE +5 -0
  84. data/lib/generators/daemon_kit/amqp/amqp_generator.rb +24 -0
  85. data/lib/generators/daemon_kit/amqp/templates/config/amqp.yml +28 -0
  86. data/lib/generators/daemon_kit/amqp/templates/config/pre-daemonize/amqp.rb +7 -0
  87. data/lib/generators/daemon_kit/amqp/templates/libexec/%app_name%-daemon.rb +37 -0
  88. data/lib/generators/daemon_kit/app/USAGE +7 -0
  89. data/lib/generators/daemon_kit/app/app_generator.rb +140 -0
  90. data/lib/generators/daemon_kit/app/templates/Gemfile +8 -0
  91. data/lib/generators/daemon_kit/app/templates/README.tt +58 -0
  92. data/lib/generators/daemon_kit/app/templates/Rakefile +6 -0
  93. data/lib/generators/daemon_kit/app/templates/bin/daemon.tt +7 -0
  94. data/lib/generators/daemon_kit/app/templates/config/arguments.rb +12 -0
  95. data/lib/generators/daemon_kit/app/templates/config/boot.rb +70 -0
  96. data/lib/generators/daemon_kit/app/templates/config/environment.rb.tt +26 -0
  97. data/lib/generators/daemon_kit/app/templates/config/environments/development.rb +2 -0
  98. data/lib/generators/daemon_kit/app/templates/config/environments/production.rb +5 -0
  99. data/lib/generators/daemon_kit/app/templates/config/environments/test.rb +2 -0
  100. data/lib/generators/daemon_kit/app/templates/config/post-daemonize/readme +5 -0
  101. data/lib/generators/daemon_kit/app/templates/config/pre-daemonize/readme +12 -0
  102. data/lib/generators/daemon_kit/app/templates/lib/%app_name%.rb +2 -0
  103. data/lib/generators/daemon_kit/app/templates/libexec/%app_name%-daemon.rb +18 -0
  104. data/lib/generators/daemon_kit/app/templates/script/console +3 -0
  105. data/lib/generators/daemon_kit/app/templates/script/destroy +3 -0
  106. data/lib/generators/daemon_kit/app/templates/script/generate +3 -0
  107. data/lib/generators/daemon_kit/capistrano/capistrano_generator.rb +26 -0
  108. data/lib/generators/daemon_kit/capistrano/templates/Capfile +10 -0
  109. data/lib/generators/daemon_kit/capistrano/templates/USAGE +10 -0
  110. data/lib/generators/daemon_kit/capistrano/templates/config/deploy/logrotate.erb +13 -0
  111. data/lib/generators/daemon_kit/capistrano/templates/config/deploy/production.rb.tt +6 -0
  112. data/lib/generators/daemon_kit/capistrano/templates/config/deploy/staging.rb.tt +6 -0
  113. data/lib/generators/daemon_kit/capistrano/templates/config/deploy.rb.tt +67 -0
  114. data/lib/generators/daemon_kit/capistrano/templates/config/environments/staging.rb +0 -0
  115. data/lib/generators/daemon_kit/cron/USAGE +5 -0
  116. data/lib/generators/daemon_kit/cron/cron_generator.rb +24 -0
  117. data/lib/generators/daemon_kit/cron/templates/config/pre-daemonize/cron.rb +11 -0
  118. data/lib/generators/daemon_kit/cron/templates/libexec/%app_name%-daemon.rb +43 -0
  119. data/lib/generators/daemon_kit/cucumber/USAGE +11 -0
  120. data/lib/generators/daemon_kit/cucumber/cucumber_generator.rb +45 -0
  121. data/lib/generators/daemon_kit/cucumber/templates/config/environments/cucumber.rb +2 -0
  122. data/lib/generators/daemon_kit/cucumber/templates/features/step_definitions/.empty_directory +0 -0
  123. data/lib/generators/daemon_kit/cucumber/templates/features/support/env.rb +7 -0
  124. data/lib/generators/daemon_kit/cucumber/templates/script/cucumber +7 -0
  125. data/lib/generators/daemon_kit/cucumber/templates/tasks/cucumber.rake +13 -0
  126. data/lib/generators/daemon_kit/nanite_agent/USAGE +5 -0
  127. data/lib/generators/daemon_kit/nanite_agent/nanite_agent_generator.rb +29 -0
  128. data/lib/generators/daemon_kit/nanite_agent/templates/config/nanite.yml +35 -0
  129. data/lib/generators/daemon_kit/nanite_agent/templates/config/pre-daemonize/nanite_agent.rb +6 -0
  130. data/lib/generators/daemon_kit/nanite_agent/templates/lib/actors/sample.rb +11 -0
  131. data/lib/generators/daemon_kit/nanite_agent/templates/libexec/%app_name%-daemon.rb +31 -0
  132. data/lib/generators/daemon_kit/rspec/USAGE +5 -0
  133. data/lib/generators/daemon_kit/rspec/rspec_generator.rb +20 -0
  134. data/lib/generators/daemon_kit/rspec/templates/spec/%app_name%_spec.rb +11 -0
  135. data/lib/generators/daemon_kit/rspec/templates/spec/spec.opts +1 -0
  136. data/lib/generators/daemon_kit/rspec/templates/spec/spec_helper.rb +23 -0
  137. data/lib/generators/daemon_kit/rspec/templates/tasks/rspec.rake +19 -0
  138. data/lib/generators/daemon_kit/ruote/USAGE +5 -0
  139. data/lib/generators/daemon_kit/ruote/ruote_generator.rb +29 -0
  140. data/lib/generators/daemon_kit/ruote/templates/config/amqp.yml +30 -0
  141. data/lib/generators/daemon_kit/ruote/templates/config/pre-daemonize/ruote.rb +13 -0
  142. data/lib/generators/daemon_kit/ruote/templates/config/ruote.yml +23 -0
  143. data/lib/generators/daemon_kit/ruote/templates/lib/%app_name%.rb +4 -0
  144. data/lib/generators/daemon_kit/ruote/templates/lib/sample.rb +26 -0
  145. data/lib/generators/daemon_kit/ruote/templates/libexec/%app_name%-daemon.rb +33 -0
  146. data/lib/generators/daemon_kit/test_unit/USAGE +5 -0
  147. data/lib/generators/daemon_kit/test_unit/templates/tasks/test_unit.rake +7 -0
  148. data/lib/generators/daemon_kit/test_unit/templates/test/%app_name%_test.rb.tt +9 -0
  149. data/lib/generators/daemon_kit/test_unit/templates/test/test_helper.rb +6 -0
  150. data/lib/generators/daemon_kit/test_unit/test_unit_generator.rb +20 -0
  151. data/lib/generators/daemon_kit/xmpp/templates/config/pre-daemonize/xmpp.rb +6 -0
  152. data/lib/generators/daemon_kit/xmpp/templates/config/xmpp.yml +29 -0
  153. data/lib/generators/daemon_kit/xmpp/templates/libexec/%app_name%-daemon.rb +27 -0
  154. data/lib/generators/daemon_kit/xmpp/xmpp_generator.rb +24 -0
  155. data/script/console +10 -0
  156. data/script/destroy +14 -0
  157. data/script/generate +14 -0
  158. data/script/txt2html +71 -0
  159. data/spec/abstract_logger_spec.rb +126 -0
  160. data/spec/argument_spec.rb +70 -0
  161. data/spec/config_spec.rb +83 -0
  162. data/spec/configurable_spec.rb +56 -0
  163. data/spec/daemon_kit_spec.rb +7 -0
  164. data/spec/error_handlers_spec.rb +23 -0
  165. data/spec/fixtures/env.yml +15 -0
  166. data/spec/fixtures/noenv.yml +4 -0
  167. data/spec/initializer_spec.rb +26 -0
  168. data/spec/spec.opts +1 -0
  169. data/spec/spec_helper.rb +27 -0
  170. data/tasks/cucumber.rake +13 -0
  171. data/tasks/rspec.rake +21 -0
  172. data/tasks/tests.rake +6 -0
  173. data/templates/god/god.erb +69 -0
  174. data/templates/monit/monit.erb +14 -0
  175. data/test/test_amqp_generator.rb +48 -0
  176. data/test/test_cron_generator.rb +45 -0
  177. data/test/test_daemon-kit_generator.rb +84 -0
  178. data/test/test_daemon_kit_config.rb +28 -0
  179. data/test/test_deploy_capistrano_generator.rb +48 -0
  180. data/test/test_generator_helper.rb +29 -0
  181. data/test/test_helper.rb +7 -0
  182. data/test/test_nanite_agent_generator.rb +49 -0
  183. data/test/test_ruote_generator.rb +51 -0
  184. data/test/test_test_unit_generator.rb +46 -0
  185. metadata +325 -0
@@ -0,0 +1,249 @@
1
+ require 'logger'
2
+
3
+ module DaemonKit
4
+ # One of the key parts of succesful background processes is adequate
5
+ # logging. The AbstractLogger aims to simplify logging from inside
6
+ # daemon processes by providing additional useful information with
7
+ # each log line, including calling file name and line number and
8
+ # support for cleanly logging exceptions.
9
+ #
10
+ # The logger can be accessed through #DaemonKit.logger.
11
+ #
12
+ # AbstractLogger provides an interface that is fully compatible with
13
+ # the Logger class provided by Ruby's Standard Library, and is
14
+ # extended with some additional conveniences.
15
+ #
16
+ # The AbstractLogger supports different backends, by default it uses
17
+ # a Logger instance, but can by swapped out for a SysLogLogger
18
+ # logger as well.
19
+ class AbstractLogger
20
+
21
+ attr_accessor :copy_to_stdout
22
+
23
+ @severities = {
24
+ :debug => Logger::DEBUG,
25
+ :info => Logger::INFO,
26
+ :warn => Logger::WARN,
27
+ :error => Logger::ERROR,
28
+ :fatal => Logger::FATAL,
29
+ :unknown => Logger::UNKNOWN
30
+ }
31
+
32
+ @silencer = true
33
+
34
+ class << self
35
+ attr_reader :severities
36
+ attr_accessor :silencer
37
+ end
38
+
39
+ # Optional log path, defaults to
40
+ # <em>DAEMON_ROOT/log/DAEMON_ENV.log</em>
41
+ def initialize( log_path = nil )
42
+ if log_path.to_s == "syslog"
43
+ @backend = :syslog
44
+ else
45
+ @logger_file = log_path || "#{DAEMON_ROOT}/log/#{DAEMON_ENV}.log"
46
+ @backend = :logger
47
+ end
48
+
49
+ @copy_to_stdout = false
50
+ end
51
+
52
+ # Silence the logger for the duration of the block.
53
+ def silence( temporary_level = :error )
54
+ if self.class.silencer
55
+ begin
56
+ old_level, self.level = self.level, temporary_level
57
+ yield self
58
+ ensure
59
+ self.level = old_level
60
+ end
61
+ else
62
+ yield self
63
+ end
64
+ end
65
+
66
+ # Write unformatted message to logging device, mostly useful for Logger interface
67
+ # compatibility and debugging soap4r (possibly others)
68
+ def <<( msg ) #:nodoc:
69
+ self.logger.write( msg ) if self.logger && self.logger.respond_to?( :write )
70
+ end
71
+
72
+ def debug( msg = nil, &block )
73
+ add( :debug, msg, &block )
74
+ end
75
+
76
+ def debug?
77
+ self.level == :debug
78
+ end
79
+
80
+ def info( msg = nil, &block )
81
+ add( :info, msg, &block )
82
+ end
83
+
84
+ def info?
85
+ self.level == :info
86
+ end
87
+
88
+ def warn( msg = nil, &block )
89
+ add( :warn, msg, &block )
90
+ end
91
+
92
+ def warn?
93
+ self.level == :warn
94
+ end
95
+
96
+ def error( msg = nil, &block )
97
+ add( :error, msg, &block )
98
+ end
99
+
100
+ def error?
101
+ self.level == :error
102
+ end
103
+
104
+ def fatal( msg = nil, &block )
105
+ add( :fatal, msg, &block )
106
+ end
107
+
108
+ def fatal?
109
+ self.level == :fatal
110
+ end
111
+
112
+ def unknown( msg = nil, &block )
113
+ add( :unknown, msg, &block )
114
+ end
115
+
116
+ def unknown?
117
+ self.level == :unknown
118
+ end
119
+
120
+ # Conveniently log an exception and the backtrace
121
+ def exception( e )
122
+ message = "EXCEPTION: #{e.message}: #{clean_trace( e.backtrace )}"
123
+ self.add( :error, message, true )
124
+ end
125
+
126
+ def add( severity, message = nil, skip_caller = false, &block )
127
+ message = yield if block_given?
128
+
129
+ message = "#{called(caller)}: #{message}" unless skip_caller
130
+
131
+ self.logger.add( self.class.severities[ severity ] ) { message }
132
+
133
+ STDOUT.puts( message ) if self.copy_to_stdout
134
+ end
135
+
136
+ def level
137
+ self.class.severities.invert[ @logger.level ]
138
+ end
139
+
140
+ def level=( level )
141
+ level = ( Symbol === level ? self.class.severities[ level ] : level )
142
+ self.logger.level = level
143
+ end
144
+
145
+ def logger
146
+ @logger ||= create_logger
147
+ end
148
+
149
+ def logger=( logger )
150
+ if logger.is_a?( Symbol )
151
+ @backend = logger
152
+ @logger.close rescue nil
153
+ @logger = create_logger
154
+ else
155
+ @logger.close rescue nil
156
+ @logger = logger
157
+ end
158
+ end
159
+
160
+ def clean_trace( trace )
161
+ trace = trace.map { |l| l.gsub(DAEMON_ROOT, '') }
162
+ trace = trace.reject { |l| l =~ /gems\/daemon[\-_]kit/ }
163
+ trace = trace.reject { |l| l =~ /vendor\/daemon[\-_]kit/ }
164
+ trace
165
+ end
166
+
167
+ def close
168
+ case @backend
169
+ when :logger
170
+ self.logger.close
171
+ @logger = nil
172
+ end
173
+ end
174
+
175
+ private
176
+
177
+ def called( trace )
178
+ l = trace.detect('unknown:0') { |l| l.index('abstract_logger.rb').nil? }
179
+ file, num, _ = l.split(':')
180
+
181
+ if file =~ /daemon[\-_]kit/
182
+ "[daemon-kit]"
183
+
184
+ else
185
+ [ File.basename(file), num ].join(':')
186
+ end
187
+ end
188
+
189
+ def create_logger
190
+ case @backend
191
+ when :logger
192
+ create_standard_logger
193
+ when :syslog
194
+ create_syslog_logger
195
+ end
196
+ end
197
+
198
+ def create_standard_logger
199
+ log_path = File.dirname( @logger_file )
200
+ unless File.directory?( log_path )
201
+ begin
202
+ FileUtils.mkdir_p( log_path )
203
+ rescue
204
+ STDERR.puts "#{log_path} not writable, using STDERR for logging"
205
+ @logger_file = STDERR
206
+ end
207
+ end
208
+
209
+ l = Logger.new( @logger_file )
210
+ l.formatter = Formatter.new
211
+ l.progname = if DaemonKit.configuration
212
+ DaemonKit.configuration.daemon_name
213
+ else
214
+ File.basename($0)
215
+ end
216
+ l
217
+ end
218
+
219
+ def create_syslog_logger
220
+ begin
221
+ require 'syslog_logger'
222
+ SyslogLogger.new( DaemonKit.configuration ? DaemonKit.configuration.daemon_name : File.basename($0) )
223
+ rescue LoadError
224
+ self.logger = :logger
225
+ self.error( "Couldn't load syslog_logger gem, reverting to standard logger" )
226
+ end
227
+ end
228
+
229
+ class Formatter
230
+
231
+ # YYYY:MM:DD HH:MM:SS.MS daemon_name(pid) level: message
232
+ @format = "%s %s(%d) [%s] %s\n"
233
+
234
+ class << self
235
+ attr_accessor :format
236
+ end
237
+
238
+ def call(severity, time, progname, msg)
239
+ self.class.format % [ format_time( time ), progname, $$, severity, msg.to_s ]
240
+ end
241
+
242
+ private
243
+
244
+ def format_time( time )
245
+ time.strftime( "%Y-%m-%d %H:%M:%S." ) + time.usec.to_s
246
+ end
247
+ end
248
+ end
249
+ end
@@ -0,0 +1,39 @@
1
+ require 'yaml'
2
+ require 'mq'
3
+
4
+ module DaemonKit
5
+ # Thin wrapper around the amqp gem, specifically designed to ease
6
+ # configuration of a AMQP consumer daemon and provide some added
7
+ # simplicity
8
+ class AMQP
9
+
10
+ @@instance = nil
11
+
12
+ class << self
13
+
14
+ def instance
15
+ @instance ||= new
16
+ end
17
+
18
+ private :new
19
+
20
+ def run(&block)
21
+ instance.run(&block)
22
+ end
23
+ end
24
+
25
+ def initialize( config = {} )
26
+ @config = DaemonKit::Config.load('amqp').to_h( true )
27
+ end
28
+
29
+ def run(&block)
30
+ # Ensure graceful shutdown of the connection to the broker
31
+ DaemonKit.trap('INT') { ::AMQP.stop { ::EM.stop } }
32
+ DaemonKit.trap('TERM') { ::AMQP.stop { ::EM.stop } }
33
+
34
+ # Start our event loop and AMQP client
35
+ DaemonKit.logger.debug("AMQP.start(#{@config.inspect})")
36
+ ::AMQP.start(@config, &block)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,230 @@
1
+ require 'timeout'
2
+
3
+ module DaemonKit
4
+
5
+ # Class responsible for making the daemons run and keep them running.
6
+ class Application
7
+
8
+ class << self
9
+
10
+ # Run the specified file as a daemon process.
11
+ def exec( file )
12
+ raise DaemonNotFound.new( file ) unless File.exist?( file )
13
+
14
+ DaemonKit.configuration.daemon_name ||= File.basename( file )
15
+
16
+ command, configs, args = Arguments.parse( ARGV )
17
+
18
+ case command
19
+ when :run
20
+ parse_arguments( args )
21
+ run( file )
22
+ when :start
23
+ parse_arguments( args )
24
+ start( file )
25
+ when :stop
26
+ stop
27
+ end
28
+ end
29
+
30
+ # Run the daemon in the foreground without daemonizing
31
+ def run( file )
32
+ self.chroot
33
+ self.clean_fd
34
+ self.redirect_io( true )
35
+
36
+ DaemonKit.configuration.log_stdout = true
37
+
38
+ require file
39
+ end
40
+
41
+ # Run our file properly
42
+ def start( file )
43
+ self.drop_privileges
44
+ self.daemonize
45
+ self.chroot
46
+ self.clean_fd
47
+ self.redirect_io
48
+
49
+ require file
50
+ end
51
+
52
+ def stop
53
+ @pid_file = PidFile.new( DaemonKit.configuration.pid_file )
54
+
55
+ unless @pid_file.running?
56
+ @pid_file.cleanup
57
+ puts "Nothing to stop"
58
+ exit
59
+ end
60
+
61
+ target_pid = @pid_file.pid
62
+
63
+ puts "Sending TERM to #{target_pid}"
64
+ Process.kill( 'TERM', target_pid )
65
+
66
+ if seconds = DaemonKit.configuration.force_kill_wait
67
+ begin
68
+ Timeout::timeout( seconds ) do
69
+ loop do
70
+ puts "Waiting #{seconds} seconds for #{target_pid} before sending KILL"
71
+
72
+ break unless @pid_file.running?
73
+
74
+ seconds -= 1
75
+ sleep 1
76
+ end
77
+ end
78
+ rescue Timeout::Error
79
+ Process.kill( 'KILL', target_pid )
80
+ end
81
+ end
82
+
83
+ if @pid_file.running?
84
+ puts "Process still running, leaving pidfile behind! Consider using configuration.force_kill_wait."
85
+ else
86
+ @pid_file.cleanup
87
+ end
88
+ end
89
+
90
+ # Call this from inside a daemonized process to complete the
91
+ # initialization process
92
+ def running!
93
+ Initializer.continue!
94
+
95
+ yield DaemonKit.configuration if block_given?
96
+ end
97
+
98
+ # Exit the daemon
99
+ # TODO: Make configurable callback chain
100
+ # TODO: Hook into at_exit()
101
+ def exit!( code = 0 )
102
+ end
103
+
104
+ # http://gist.github.com/304739
105
+ #
106
+ # Stolen from Unicorn::Util
107
+ #
108
+ # This reopens ALL logfiles in the process that have been rotated
109
+ # using logrotate(8) (without copytruncate) or similar tools.
110
+ # A +File+ object is considered for reopening if it is:
111
+ # 1) opened with the O_APPEND and O_WRONLY flags
112
+ # 2) opened with an absolute path (starts with "/")
113
+ # 3) the current open file handle does not match its original open path
114
+ # 4) unbuffered (as far as userspace buffering goes, not O_SYNC)
115
+ # Returns the number of files reopened
116
+ def reopen_logs
117
+ nr = 0
118
+ append_flags = File::WRONLY | File::APPEND
119
+ DaemonKit.logger.info "Rotating logs" if DaemonKit.logger
120
+
121
+ #logs = [STDOUT, STDERR]
122
+ #logs.each do |fp|
123
+ ObjectSpace.each_object(File) do |fp|
124
+ next if fp.closed?
125
+ next unless (fp.sync && fp.path[0..0] == "/")
126
+ next unless (fp.fcntl(Fcntl::F_GETFL) & append_flags) == append_flags
127
+
128
+ begin
129
+ a, b = fp.stat, File.stat(fp.path)
130
+ next if a.ino == b.ino && a.dev == b.dev
131
+ rescue Errno::ENOENT
132
+ end
133
+
134
+ open_arg = 'a'
135
+ if fp.respond_to?(:external_encoding) && enc = fp.external_encoding
136
+ open_arg << ":#{enc.to_s}"
137
+ enc = fp.internal_encoding and open_arg << ":#{enc.to_s}"
138
+ end
139
+ DaemonKit.logger.info "Rotating path: #{fp.path}" if DaemonKit.logger
140
+ fp.reopen(fp.path, open_arg)
141
+ fp.sync = true
142
+ nr += 1
143
+ end # each_object
144
+ nr
145
+ end
146
+
147
+ protected
148
+
149
+ def parse_arguments( args )
150
+ DaemonKit.arguments = Arguments.new
151
+ DaemonKit.arguments.parse( args )
152
+ end
153
+
154
+ # Daemonize the process
155
+ def daemonize
156
+ @pid_file = PidFile.new( DaemonKit.configuration.pid_file )
157
+ @pid_file.ensure_stopped!
158
+
159
+ if RUBY_VERSION < "1.9"
160
+ exit if fork
161
+ Process.setsid
162
+ exit if fork
163
+ else
164
+ Process.daemon( true, true )
165
+ end
166
+
167
+ @pid_file.write!
168
+
169
+ # TODO: Convert into shutdown hook
170
+ at_exit { @pid_file.cleanup }
171
+ end
172
+
173
+ # Release the old working directory and insure a sensible umask
174
+ # TODO: Make chroot directory configurable
175
+ def chroot
176
+ Dir.chdir '/'
177
+ File.umask 0000
178
+ end
179
+
180
+ # Make sure all file descriptors are closed (with the exception
181
+ # of STDIN, STDOUT & STDERR)
182
+ def clean_fd
183
+ ObjectSpace.each_object(IO) do |io|
184
+ unless [STDIN, STDOUT, STDERR].include?(io)
185
+ begin
186
+ unless io.closed?
187
+ io.close
188
+ end
189
+ rescue ::Exception
190
+ end
191
+ end
192
+ end
193
+ end
194
+
195
+ # Redirect our IO
196
+ # TODO: make this configurable
197
+ def redirect_io( simulate = false )
198
+ begin
199
+ STDIN.reopen '/dev/null'
200
+ rescue ::Exception
201
+ end
202
+
203
+ unless simulate
204
+ STDOUT.reopen '/dev/null', 'a'
205
+ STDERR.reopen '/dev/null', 'a'
206
+ end
207
+ end
208
+
209
+ def drop_privileges
210
+ if DaemonKit.configuration.group
211
+ begin
212
+ group = Etc.getgrnam( DaemonKit.configuration.group )
213
+ Process::Sys.setgid( group.gid.to_i )
214
+ rescue => e
215
+ $stderr.puts "Caught exception while trying to drop group privileges: #{e.message}"
216
+ end
217
+ end
218
+ if DaemonKit.configuration.user
219
+ begin
220
+ user = Etc.getpwnam( DaemonKit.configuration.user )
221
+ Process::Sys.setuid( user.uid.to_i )
222
+ rescue => e
223
+ $stderr.puts "Caught exception while trying to drop user privileges: #{e.message}"
224
+ end
225
+ end
226
+ end
227
+ end
228
+
229
+ end
230
+ end