textgoeshere-daemon-kit 0.1.8rc3

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