thin_service 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE +53 -0
- data/README.md +26 -0
- data/Rakefile +17 -0
- data/bin/thin_service +17 -0
- data/lib/thin_service.rb +15 -0
- data/lib/thin_service/command.rb +343 -0
- data/lib/thin_service/logger.rb +74 -0
- data/lib/thin_service/service_manager.rb +78 -0
- data/lib/thin_service/version.rb +3 -0
- data/resource/thin_service_wrapper.exe +0 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/thin_service_spec.rb +43 -0
- data/src/ServiceFB/ServiceFB.bas +651 -0
- data/src/ServiceFB/ServiceFB.bi +109 -0
- data/src/ServiceFB/ServiceFB_Utils.bas +495 -0
- data/src/ServiceFB/ServiceFB_Utils.bi +70 -0
- data/src/ServiceFB/_internals.bi +50 -0
- data/src/ServiceFB/_utils_internals.bi +51 -0
- data/src/thin_service/_debug.bi +61 -0
- data/src/thin_service/boolean.bi +18 -0
- data/src/thin_service/console_process.bas +397 -0
- data/src/thin_service/console_process.bi +75 -0
- data/src/thin_service/thin_service.bas +199 -0
- data/src/thin_service/thin_service.bi +61 -0
- data/tasks/native_lib.rake +40 -0
- data/tasks/native_service.rake +43 -0
- data/tasks/tests.rake +55 -0
- data/tests/all_tests.bas +18 -0
- data/tests/fixtures/mock_process.bas +92 -0
- data/tests/test_console_process.bas +402 -0
- data/tests/test_helpers.bas +35 -0
- data/tests/test_helpers.bi +8 -0
- data/thin_service.gemspec +21 -0
- data/tools/freebasic.rb +359 -0
- metadata +106 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw
|
2
|
+
<zedshaw at zedshaw dot com> and contributors. You can redistribute it
|
3
|
+
and/or modify it under either the terms of the GPL2 or the conditions below:
|
4
|
+
|
5
|
+
1. You may make and give away verbatim copies of the source form of the
|
6
|
+
software without restriction, provided that you duplicate all of the
|
7
|
+
original copyright notices and associated disclaimers.
|
8
|
+
|
9
|
+
2. You may modify your copy of the software in any way, provided that
|
10
|
+
you do at least ONE of the following:
|
11
|
+
|
12
|
+
a) place your modifications in the Public Domain or otherwise make them
|
13
|
+
Freely Available, such as by posting said modifications to Usenet or an
|
14
|
+
equivalent medium, or by allowing the author to include your
|
15
|
+
modifications in the software.
|
16
|
+
|
17
|
+
b) use the modified software only within your corporation or
|
18
|
+
organization.
|
19
|
+
|
20
|
+
c) rename any non-standard executables so the names do not conflict with
|
21
|
+
standard executables, which must also be provided.
|
22
|
+
|
23
|
+
d) make other distribution arrangements with the author.
|
24
|
+
|
25
|
+
3. You may distribute the software in object code or executable
|
26
|
+
form, provided that you do at least ONE of the following:
|
27
|
+
|
28
|
+
a) distribute the executables and library files of the software,
|
29
|
+
together with instructions (in the manual page or equivalent) on where
|
30
|
+
to get the original distribution.
|
31
|
+
|
32
|
+
b) accompany the distribution with the machine-readable source of the
|
33
|
+
software.
|
34
|
+
|
35
|
+
c) give non-standard executables non-standard names, with
|
36
|
+
instructions on where to get the original software distribution.
|
37
|
+
|
38
|
+
d) make other distribution arrangements with the author.
|
39
|
+
|
40
|
+
4. You may modify and include the part of the software into any other
|
41
|
+
software (possibly commercial). But some files in the distribution
|
42
|
+
are not written by the author, so that they are not under this terms.
|
43
|
+
|
44
|
+
5. The scripts and library files supplied as input to or produced as
|
45
|
+
output from the software do not automatically fall under the
|
46
|
+
copyright of the software, but belong to whomever generated them,
|
47
|
+
and may be sold commercially, and may be aggregated with this
|
48
|
+
software.
|
49
|
+
|
50
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
51
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
52
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
53
|
+
PURPOSE.
|
data/README.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# ThinService
|
2
|
+
|
3
|
+
Run Thin as a Windows Service - based on mongrel_service by Luis Lavena
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'thin_service'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install thin_service
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Use the following commands to get more help:
|
22
|
+
|
23
|
+
thin_service install --help
|
24
|
+
thin_service remove --help
|
25
|
+
|
26
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require "rake/clean"
|
4
|
+
require "rspec/core/rake_task"
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new
|
7
|
+
|
8
|
+
task :default => :spec
|
9
|
+
task :test => :spec
|
10
|
+
task :package => [:compile]
|
11
|
+
task :build => [:compile]
|
12
|
+
|
13
|
+
CLOBBER.include('pkg')
|
14
|
+
|
15
|
+
load 'tasks/native_lib.rake'
|
16
|
+
load 'tasks/native_service.rake'
|
17
|
+
|
data/bin/thin_service
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!C:/Ruby193/bin/ruby.exe
|
2
|
+
|
3
|
+
|
4
|
+
if ARGV.first == "start"
|
5
|
+
ARGV.shift
|
6
|
+
ARGV = ["exec", "thin", "start"] + ARGV
|
7
|
+
p ARGV
|
8
|
+
Dir.chdir( "C:/Users/Owner/Documents/NetBeansProjects/hgcs" )
|
9
|
+
gem 'bundler', version
|
10
|
+
p 'aa'
|
11
|
+
|
12
|
+
load Gem.bin_path('bundler', 'bundle', version)
|
13
|
+
else
|
14
|
+
require 'thin_service'
|
15
|
+
ThinService::Service.new(ARGV).run!
|
16
|
+
end
|
17
|
+
|
data/lib/thin_service.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative "thin_service/version.rb"
|
2
|
+
require_relative "thin_service/command.rb"
|
3
|
+
|
4
|
+
module ThinService
|
5
|
+
class Service
|
6
|
+
def initialize( args )
|
7
|
+
@args = args
|
8
|
+
end
|
9
|
+
|
10
|
+
def run!
|
11
|
+
ThinService::Command::Registry.new.run( @args )
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,343 @@
|
|
1
|
+
# Mongrel Copyright (c) 2005 Zed A. Shaw
|
2
|
+
# You can redistribute it and/or modify it under the same terms as Ruby.
|
3
|
+
#
|
4
|
+
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
|
5
|
+
# for more information.
|
6
|
+
#
|
7
|
+
# Adapted for thin_service by Garth Smedley
|
8
|
+
|
9
|
+
require 'optparse'
|
10
|
+
require 'fileutils'
|
11
|
+
require 'thin_service/service_manager'
|
12
|
+
|
13
|
+
|
14
|
+
module ThinService
|
15
|
+
|
16
|
+
module Command
|
17
|
+
|
18
|
+
BANNER = "Usage: thin_service <command> [options]"
|
19
|
+
COMMANDS = ['start', 'install', 'remove']
|
20
|
+
|
21
|
+
class Base
|
22
|
+
|
23
|
+
attr_reader :valid, :done_validating
|
24
|
+
|
25
|
+
# Called by the implemented command to set the options for that command.
|
26
|
+
# Every option has a short and long version, a description, a variable to
|
27
|
+
# set, and a default value. No exceptions.
|
28
|
+
def options(opts)
|
29
|
+
opts.each do |short, long, help, variable, default|
|
30
|
+
self.instance_variable_set(variable, default)
|
31
|
+
@opt.on(short, long, help) do |arg|
|
32
|
+
self.instance_variable_set(variable, arg)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Called by the subclass to setup the command and parse the argv arguments.
|
38
|
+
# The call is destructive on argv since it uses the OptionParser#parse! function.
|
39
|
+
def initialize(options={})
|
40
|
+
argv = options[:argv] || []
|
41
|
+
@opt = OptionParser.new
|
42
|
+
@opt.banner = ThinService::Command::BANNER
|
43
|
+
@valid = true
|
44
|
+
@done_validating = false
|
45
|
+
|
46
|
+
configure
|
47
|
+
|
48
|
+
@opt.on_tail("-h", "--help", "Show this message") do
|
49
|
+
@done_validating = true
|
50
|
+
puts(@opt)
|
51
|
+
end
|
52
|
+
|
53
|
+
# I need to add my own -v definition to prevent the -v from exiting by default as well.
|
54
|
+
@opt.on_tail("--version", "Show version") do
|
55
|
+
@done_validating = true
|
56
|
+
puts("Version #{ThinService::VERSION}")
|
57
|
+
end
|
58
|
+
|
59
|
+
@opt.parse! argv
|
60
|
+
end
|
61
|
+
|
62
|
+
def configure
|
63
|
+
options []
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns true/false depending on whether the command is configured properly.
|
67
|
+
def validate
|
68
|
+
return @valid
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns a help message. Defaults to OptionParser#help which should be good.
|
72
|
+
def help
|
73
|
+
@opt.help
|
74
|
+
end
|
75
|
+
|
76
|
+
# Runs the command doing it's job. You should implement this otherwise it will
|
77
|
+
# throw a NotImplementedError as a reminder.
|
78
|
+
def run
|
79
|
+
raise NotImplementedError
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
# Validates the given expression is true and prints the message if not, exiting.
|
84
|
+
def valid?(exp, message)
|
85
|
+
if !@done_validating && !exp
|
86
|
+
failure message
|
87
|
+
@valid = false
|
88
|
+
@done_validating = true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Validates that a file exists and if not displays the message
|
93
|
+
def valid_exists?(file, message)
|
94
|
+
valid?(file != nil && File.exist?(file), message)
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
# Validates that the file is a file and not a directory or something else.
|
99
|
+
def valid_file?(file, message)
|
100
|
+
valid?(file != nil && File.file?(file), message)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Validates that the given directory exists
|
104
|
+
def valid_dir?(file, message)
|
105
|
+
valid?(file != nil && File.directory?(file), message)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Just a simple method to display failure until something better is developed.
|
109
|
+
def failure(message)
|
110
|
+
STDERR.puts "!!! #{message}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
module Commands
|
115
|
+
class Install < ThinService::Command::Base
|
116
|
+
|
117
|
+
def configure
|
118
|
+
options [
|
119
|
+
['-N', '--name SVC_NAME', "Required name for the service to be registered/installed.", :@svc_name, nil],
|
120
|
+
['-D', '--display SVC_DISPLAY', "Adjust the display name of the service.", :@svc_display, nil],
|
121
|
+
["-e", "--environment ENV", "Rails environment to run as", :@environment, ENV['RAILS_ENV'] || "development"],
|
122
|
+
['-p', '--port PORT', "Which port to bind to", :@port, 3000],
|
123
|
+
['-a', '--address ADDR', "Address to bind to", :@address, "0.0.0.0"],
|
124
|
+
['-t', '--timeout TIME', "Timeout for requests in seconds", :@timeout, 30],
|
125
|
+
['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, Dir.pwd],
|
126
|
+
['-D', '--debug', "Enable debugging mode", :@debug, false],
|
127
|
+
['' , '--max-persistent-conns INT', "Maximum number of persistent connections", :@max_persistent_conns, 512],
|
128
|
+
['' , '--ssl', "Enables SSL", :@ssl, nil],
|
129
|
+
['' , '--ssl-key-file PATH', "Path to private key", :@ssl_key_file, nil],
|
130
|
+
['' , '--ssl-cert-file PATH', "Path to certificate", :@ssl_cert_file, nil],
|
131
|
+
['' , '--ssl-verify', "Enables SSL certificate verification", :@ssl_verify, nil],
|
132
|
+
['' , '--prefix PATH', "URL prefix for Rails app", :@prefix, nil],
|
133
|
+
]
|
134
|
+
end
|
135
|
+
|
136
|
+
# When we validate the options, we need to make sure the --root is actually RAILS_ROOT
|
137
|
+
# of the rails application we wanted to serve, because later "as service" no error
|
138
|
+
# show to trace this.
|
139
|
+
def validate
|
140
|
+
@cwd = File.expand_path(@cwd)
|
141
|
+
valid_dir? @cwd, "Invalid path to change to: #@cwd"
|
142
|
+
|
143
|
+
# change there to start, then we'll have to come back after daemonize
|
144
|
+
Dir.chdir(@cwd)
|
145
|
+
|
146
|
+
valid? @svc_name != nil, "A service name is mandatory."
|
147
|
+
valid? !ServiceManager.exist?(@svc_name), "The service already exist, please remove it first."
|
148
|
+
|
149
|
+
# default service display to service name
|
150
|
+
@svc_display = @svc_name if !@svc_display
|
151
|
+
|
152
|
+
# start with the premise of app really exist.
|
153
|
+
app_exist = true
|
154
|
+
%w{app config log}.each do |path|
|
155
|
+
if !File.directory?(File.join(@cwd, path))
|
156
|
+
app_exist = false
|
157
|
+
break
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
valid?(@prefix[0].chr == "/" && @prefix[-1].chr != "/", "Prefix must begin with / and not end in /") if @prefix
|
162
|
+
|
163
|
+
valid? app_exist == true, "The path you specified isn't a valid Rails application."
|
164
|
+
|
165
|
+
return @valid
|
166
|
+
end
|
167
|
+
|
168
|
+
def run
|
169
|
+
# check if thin_service.exe is in ruby bindir.
|
170
|
+
gem_root = File.join(File.dirname(__FILE__), "..", "..")
|
171
|
+
gem_executable = File.join(gem_root, "resource/thin_service_wrapper.exe")
|
172
|
+
bindir_executable = File.join(RbConfig::CONFIG['bindir'], '/thin_service_wrapper.exe')
|
173
|
+
|
174
|
+
unless File.exist?(bindir_executable)
|
175
|
+
STDERR.puts "** Copying native thin_service executable..."
|
176
|
+
FileUtils.cp gem_executable, bindir_executable rescue nil
|
177
|
+
end
|
178
|
+
|
179
|
+
unless FileUtils.compare_file(bindir_executable, gem_executable)
|
180
|
+
STDERR.puts "** Updating native thin_service executable..."
|
181
|
+
FileUtils.rm_f bindir_executable rescue nil
|
182
|
+
FileUtils.cp gem_executable, bindir_executable rescue nil
|
183
|
+
end
|
184
|
+
|
185
|
+
# build the command line
|
186
|
+
argv = []
|
187
|
+
|
188
|
+
# start using the native executable
|
189
|
+
argv << '"' + bindir_executable + '"'
|
190
|
+
|
191
|
+
# force indication of service mode
|
192
|
+
argv << "start"
|
193
|
+
|
194
|
+
# now the options
|
195
|
+
argv << "-e #{@environment}" if @environment
|
196
|
+
argv << "-p #{@port}"
|
197
|
+
argv << "-a #{@host}" if @host
|
198
|
+
argv << "-c \"#{@cwd}\"" if @cwd
|
199
|
+
argv << "-t #{@timeout}" if @timeout
|
200
|
+
argv << "-D" if @debug
|
201
|
+
argv << "--max-persistent-conns #{@max_persistent_conns}" if @max_persistent_conns
|
202
|
+
argv << "--ssl" if @ssl
|
203
|
+
argv << "--ssl-key-file \"#{@ssl_key_file}\"" if @ssl_key_file
|
204
|
+
argv << "--ssl-cert-file \"#{@ssl_cert_file}\"" if @ssl_cert_file
|
205
|
+
argv << "--ssl-verify" if @ssl_verify
|
206
|
+
argv << "--prefix \"#{@prefix}\"" if @prefix
|
207
|
+
|
208
|
+
# concat remaining non-parsed ARGV
|
209
|
+
argv.concat(ARGV)
|
210
|
+
|
211
|
+
begin
|
212
|
+
ServiceManager.create(
|
213
|
+
@svc_name,
|
214
|
+
@svc_display,
|
215
|
+
argv.join(' ')
|
216
|
+
)
|
217
|
+
puts "#{@svc_display} service created."
|
218
|
+
rescue ServiceManager::CreateError => e
|
219
|
+
puts "There was a problem installing the service:"
|
220
|
+
puts e
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
module ServiceValidation
|
226
|
+
def configure
|
227
|
+
options [
|
228
|
+
['-N', '--name SVC_NAME', "Required name for the service to be registered/installed.", :@svc_name, nil],
|
229
|
+
]
|
230
|
+
end
|
231
|
+
|
232
|
+
def validate
|
233
|
+
valid? @svc_name != nil, "A service name is mandatory."
|
234
|
+
|
235
|
+
# Validate that the service exists
|
236
|
+
valid? ServiceManager.exist?(@svc_name), "There is no service with that name, cannot proceed."
|
237
|
+
if @valid then
|
238
|
+
ServiceManager.open(@svc_name) do |svc|
|
239
|
+
valid? svc.binary_path_name.include?("thin_service"), "The service specified isn't a Thin service."
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
return @valid
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
class Remove < ThinService::Command::Base
|
248
|
+
include ServiceValidation
|
249
|
+
|
250
|
+
def run
|
251
|
+
display_name = ServiceManager.getdisplayname(@svc_name)
|
252
|
+
|
253
|
+
begin
|
254
|
+
puts "Stopping #{display_name} if running..."
|
255
|
+
ServiceManager.stop(@svc_name)
|
256
|
+
rescue ServiceManager::ServiceError => e
|
257
|
+
end
|
258
|
+
|
259
|
+
begin
|
260
|
+
ServiceManager.delete(@svc_name)
|
261
|
+
rescue ServiceManager::ServiceError => e
|
262
|
+
puts e
|
263
|
+
end
|
264
|
+
|
265
|
+
unless ServiceManager.exist?(@svc_name) then
|
266
|
+
puts "#{display_name} service removed."
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
# Manages all of the available commands
|
273
|
+
# and handles running them.
|
274
|
+
class Registry
|
275
|
+
|
276
|
+
# Builds a list of possible commands from the Command derivates list
|
277
|
+
def commands
|
278
|
+
ThinService::Command::COMMANDS
|
279
|
+
end
|
280
|
+
|
281
|
+
# Prints a list of available commands.
|
282
|
+
def print_command_list
|
283
|
+
puts("#{ThinService::Command::BANNER}\nAvailable commands are:\n\n")
|
284
|
+
|
285
|
+
self.commands.each do |name|
|
286
|
+
puts(" - #{name}\n")
|
287
|
+
end
|
288
|
+
|
289
|
+
puts("\nEach command takes -h as an option to get help.")
|
290
|
+
|
291
|
+
end
|
292
|
+
|
293
|
+
def constantize(class_name)
|
294
|
+
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ class_name
|
295
|
+
raise NameError, "#{class_name.inspect} is not a valid constant name!"
|
296
|
+
end
|
297
|
+
|
298
|
+
Object.module_eval("::#{$1}", __FILE__, __LINE__)
|
299
|
+
end
|
300
|
+
|
301
|
+
|
302
|
+
# Runs the args against the first argument as the command name.
|
303
|
+
# If it has any errors it returns a false, otherwise it return true.
|
304
|
+
def run(args)
|
305
|
+
# find the command
|
306
|
+
cmd_name = args.shift
|
307
|
+
|
308
|
+
if !cmd_name || cmd_name == "-?" || cmd_name == "--help"
|
309
|
+
print_command_list
|
310
|
+
return true
|
311
|
+
elsif cmd_name == "--version"
|
312
|
+
puts("ThinService #{ThinService::VERSION}")
|
313
|
+
return true
|
314
|
+
end
|
315
|
+
|
316
|
+
begin
|
317
|
+
cmd_class_name = "ThinService::Command::Commands::" + cmd_name.capitalize
|
318
|
+
command = constantize(cmd_class_name).new( :argv => args )
|
319
|
+
rescue OptionParser::InvalidOption
|
320
|
+
STDERR.puts "#$! for command '#{cmd_name}'"
|
321
|
+
STDERR.puts "Try #{cmd_name} -h to get help."
|
322
|
+
return false
|
323
|
+
rescue
|
324
|
+
STDERR.puts "ERROR RUNNING '#{cmd_name}': #$!"
|
325
|
+
STDERR.puts "Use help command to get help"
|
326
|
+
return false
|
327
|
+
end
|
328
|
+
|
329
|
+
if !command.done_validating
|
330
|
+
if !command.validate
|
331
|
+
STDERR.puts "#{cmd_name} reported an error. Use thin_service #{cmd_name} -h to get help."
|
332
|
+
return false
|
333
|
+
else
|
334
|
+
command.run
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
return true
|
339
|
+
end
|
340
|
+
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|