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