mongrel_service 0.3.4-i386-mswin32
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +33 -0
- data/COPYING +20 -0
- data/LICENSE +55 -0
- data/Manifest +21 -0
- data/README +11 -0
- data/TODO +19 -0
- data/bin/mongrel_service.exe +0 -0
- data/lib/ServiceFB/ServiceFB.bas +650 -0
- data/lib/ServiceFB/ServiceFB.bi +109 -0
- data/lib/ServiceFB/ServiceFB_Utils.bas +480 -0
- data/lib/ServiceFB/ServiceFB_Utils.bi +70 -0
- data/lib/ServiceFB/_internals.bi +50 -0
- data/lib/ServiceFB/_utils_internals.bi +49 -0
- data/lib/mongrel_service/init.rb +211 -0
- data/mongrel_service.gemspec +117 -0
- data/native/_debug.bi +59 -0
- data/native/console_process.bas +389 -0
- data/native/console_process.bi +75 -0
- data/native/mongrel_service.bas +179 -0
- data/native/mongrel_service.bi +61 -0
- data/resources/defaults.yaml +2 -0
- data/tools/freebasic.rb +355 -0
- metadata +114 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
|
2
|
+
* 0.3.4 *
|
3
|
+
|
4
|
+
* Strict Gem dependencies for mongrel_service. This version is compatible
|
5
|
+
only with mongrel 1.0.x, 1.1.x and with win32-service 0.5.x.
|
6
|
+
|
7
|
+
* 0.3.3 *
|
8
|
+
|
9
|
+
* Properly display package/gem version for mongrel_service. Closes #13823.
|
10
|
+
|
11
|
+
* Updated ServiceFB to r80 to solve issue when compiling with FB > 0.17.
|
12
|
+
|
13
|
+
* 0.3.2 *
|
14
|
+
|
15
|
+
* Solved detection of parent process at ServiceFB level
|
16
|
+
(solves the x64 Windows issues).
|
17
|
+
|
18
|
+
* Upgraded to ServiceFB 'trunk' (but pistoned it, just in case).
|
19
|
+
|
20
|
+
* Fixed problems with ruby installations outside PATH or inside folders with spaces.
|
21
|
+
|
22
|
+
* Activate FB pedantic warnings by default (is really useful).
|
23
|
+
|
24
|
+
* 0.3.1 *
|
25
|
+
|
26
|
+
* Single Service (SingleMongrel) object type implemented.
|
27
|
+
|
28
|
+
* Updated Rakefile to reflect the new building steps.
|
29
|
+
|
30
|
+
* Removed SendSignal, too hackish for my taste, replaced with complete FB solution.
|
31
|
+
|
32
|
+
* Added basic Process monitoring and re-spawning.
|
33
|
+
|
data/COPYING
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2006 Luis Lavena, luislavena@gmail.com
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/LICENSE
ADDED
@@ -0,0 +1,55 @@
|
|
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.
|
54
|
+
|
55
|
+
|
data/Manifest
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
bin/mongrel_service.exe
|
2
|
+
tools/freebasic.rb
|
3
|
+
TODO
|
4
|
+
resources/defaults.yaml
|
5
|
+
README
|
6
|
+
native/mongrel_service.bi
|
7
|
+
native/mongrel_service.bas
|
8
|
+
native/console_process.bi
|
9
|
+
native/console_process.bas
|
10
|
+
native/_debug.bi
|
11
|
+
LICENSE
|
12
|
+
lib/ServiceFB/ServiceFB_Utils.bi
|
13
|
+
lib/ServiceFB/ServiceFB_Utils.bas
|
14
|
+
lib/ServiceFB/ServiceFB.bi
|
15
|
+
lib/ServiceFB/ServiceFB.bas
|
16
|
+
lib/ServiceFB/_utils_internals.bi
|
17
|
+
lib/ServiceFB/_internals.bi
|
18
|
+
lib/mongrel_service/init.rb
|
19
|
+
COPYING
|
20
|
+
CHANGELOG
|
21
|
+
Manifest
|
data/README
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
== Mongrel Native Win32 Service Plugin
|
2
|
+
|
3
|
+
This plugin offer native win32 services for rails. This replace mongrel_rails_service.
|
4
|
+
It will work like before, with this this syntax when calling mongrel_rails:
|
5
|
+
|
6
|
+
service::install
|
7
|
+
service::remove
|
8
|
+
service::update
|
9
|
+
|
10
|
+
= Author:
|
11
|
+
Luis Lavena
|
data/TODO
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Legend:
|
2
|
+
[ ] not done
|
3
|
+
[X] done
|
4
|
+
[+] in progess
|
5
|
+
|
6
|
+
### General
|
7
|
+
[ ] Add more documentation about services and requirements
|
8
|
+
[ ] Add process monitoring.
|
9
|
+
|
10
|
+
### Dependencies
|
11
|
+
[+] Remove win32/service extension dependency (instead of relying in the non-official 0.5.0 one).
|
12
|
+
[ ] Add service management (from ServiceFB) to install and remove services.
|
13
|
+
|
14
|
+
### SingleMongrel
|
15
|
+
[+] Sanitize SingleMongrel and document the functions.
|
16
|
+
|
17
|
+
### ClusterMongrel
|
18
|
+
[ ] Parse mongrel_cluster configuration file (yaml) looking for port information
|
19
|
+
[ ] Reimplent SingleMongrel for ClusterMongrel, splitting source files for better organization.
|
Binary file
|
@@ -0,0 +1,650 @@
|
|
1
|
+
'#--
|
2
|
+
'# Copyright (c) 2006-2007 Luis Lavena, Multimedia systems
|
3
|
+
'#
|
4
|
+
'# This source code is released under the MIT License.
|
5
|
+
'# See MIT-LICENSE file for details
|
6
|
+
'#++
|
7
|
+
|
8
|
+
#include once "ServiceFB.bi"
|
9
|
+
#include once "_internals.bi"
|
10
|
+
|
11
|
+
namespace fb
|
12
|
+
namespace svc
|
13
|
+
'# I started this as simple, unique service served from one process
|
14
|
+
'# but the idea of share the same process space (and reduce resources use) was good.
|
15
|
+
'# to do that, I needed a references table (similar to service_table, but we will
|
16
|
+
'# hold the ServiceProcess registered by ServiceHost (the multi services host).
|
17
|
+
'# also, I needed a locking mechanism to avoid problems of two calls changing the table
|
18
|
+
'# at the same time.
|
19
|
+
dim shared _svc_references as ServiceProcess ptr ptr
|
20
|
+
dim shared _svc_references_count as integer
|
21
|
+
dim shared _svc_references_lock as any ptr
|
22
|
+
|
23
|
+
|
24
|
+
'#####################
|
25
|
+
'# ServiceProcess
|
26
|
+
'# ctor()
|
27
|
+
constructor ServiceProcess()
|
28
|
+
constructor("NewServiceProcess")
|
29
|
+
end constructor
|
30
|
+
|
31
|
+
|
32
|
+
'# ctor(name)
|
33
|
+
constructor ServiceProcess(byref new_name as string)
|
34
|
+
_dprint("ServiceProcess(new_name)")
|
35
|
+
'# assign the service name
|
36
|
+
this.name = new_name
|
37
|
+
|
38
|
+
'# initialize the status structure
|
39
|
+
with this._svcStatus
|
40
|
+
.dwServiceType = SERVICE_WIN32_OWN_PROCESS
|
41
|
+
.dwCurrentState = SERVICE_STOPPED
|
42
|
+
.dwControlsAccepted = (SERVICE_ACCEPT_STOP or SERVICE_ACCEPT_SHUTDOWN)
|
43
|
+
.dwWin32ExitCode = NO_ERROR
|
44
|
+
.dwServiceSpecificExitCode = NO_ERROR
|
45
|
+
.dwCheckPoint = 0
|
46
|
+
.dwWaitHint = 0
|
47
|
+
end with
|
48
|
+
|
49
|
+
'# use a state placeholder
|
50
|
+
this.state = this._svcStatus.dwCurrentState
|
51
|
+
|
52
|
+
'# disable shared process by default
|
53
|
+
this.shared_process = FALSE
|
54
|
+
|
55
|
+
'# create the stop event
|
56
|
+
this._svcStopEvent = CreateEvent( 0, FALSE, FALSE, 0 )
|
57
|
+
_dprint("ServiceProcess(new_name) done")
|
58
|
+
end constructor
|
59
|
+
|
60
|
+
|
61
|
+
'# dtor()
|
62
|
+
destructor ServiceProcess()
|
63
|
+
_dprint("ServiceProcess() destructor")
|
64
|
+
'# safe to destroy it. anyway, just checking
|
65
|
+
with this
|
66
|
+
.onInit = 0
|
67
|
+
.onStart = 0
|
68
|
+
.onStop = 0
|
69
|
+
.onPause = 0
|
70
|
+
.onContinue = 0
|
71
|
+
._threadHandle = 0
|
72
|
+
CloseHandle(._svcStopEvent)
|
73
|
+
end with
|
74
|
+
_dprint("ServiceProcess() destructor done")
|
75
|
+
end destructor
|
76
|
+
|
77
|
+
|
78
|
+
'# for single process, here I create the references table and then
|
79
|
+
'# delegate control to _run() which will call the service control dispatcher
|
80
|
+
sub ServiceProcess.Run()
|
81
|
+
_dprint("ServiceProcess.Run()")
|
82
|
+
|
83
|
+
'# add the unique reference
|
84
|
+
_add_to_references(this)
|
85
|
+
|
86
|
+
'# delegate control to _run()
|
87
|
+
_run()
|
88
|
+
|
89
|
+
_dprint("ServiceProcess.Run() done")
|
90
|
+
end sub
|
91
|
+
|
92
|
+
|
93
|
+
'# I use this method to simplify changing the service state
|
94
|
+
'# notification to the service manager.
|
95
|
+
'# is needed to set dwControlsAccepted = 0 if state is SERVICE_*_PENDING
|
96
|
+
'# also, StillAlive() call it to set the checkpoint and waithint
|
97
|
+
'# to avoid SCM shut us down.
|
98
|
+
'# is not for the the end-user (*you*) to access it, but implemented in this
|
99
|
+
'# way to reduce needed to pass the right service reference each time
|
100
|
+
sub ServiceProcess.UpdateState(byval state as DWORD, byval checkpoint as integer = 0, byval waithint as integer = 0)
|
101
|
+
_dprint("ServiceProcess.UpdateState()")
|
102
|
+
'# set the state
|
103
|
+
select case state
|
104
|
+
'# if the service is starting or stopping, I must disable the option to accept
|
105
|
+
'# other controls form SCM.
|
106
|
+
case SERVICE_START_PENDING, SERVICE_STOP_PENDING:
|
107
|
+
this._svcStatus.dwControlsAccepted = 0
|
108
|
+
|
109
|
+
'# in this case, running or paused, stop and shutdown must be available
|
110
|
+
'# also, we must check here if our service is capable of pause/continue �
|
111
|
+
'# functionality and allow them (or not).
|
112
|
+
case SERVICE_RUNNING, SERVICE_PAUSED:
|
113
|
+
this._svcStatus.dwControlsAccepted = (SERVICE_ACCEPT_STOP or SERVICE_ACCEPT_SHUTDOWN)
|
114
|
+
|
115
|
+
'# from start, the service accept stop and shutdown (see ctor(name)).
|
116
|
+
'# configure the accepted controls.
|
117
|
+
'# Pause and Continue only will be enabled if you setup onPause and onContinue
|
118
|
+
if not (this.onPause = 0) and _
|
119
|
+
not (this.onContinue = 0) then
|
120
|
+
this._svcStatus.dwControlsAccepted or= SERVICE_ACCEPT_PAUSE_CONTINUE
|
121
|
+
end if
|
122
|
+
|
123
|
+
end select
|
124
|
+
|
125
|
+
'# set the structure status
|
126
|
+
'# also the property
|
127
|
+
this._svcStatus.dwCurrentState = state
|
128
|
+
this.state = state
|
129
|
+
|
130
|
+
'# set checkpoint and waithint
|
131
|
+
this._svcStatus.dwCheckPoint = checkpoint
|
132
|
+
this._svcStatus.dwWaitHint = waithint
|
133
|
+
|
134
|
+
'# call the API
|
135
|
+
'# only we will call is _svcHandle is valid
|
136
|
+
'# this will allow use of UpdateState (and StillAlive) from console
|
137
|
+
if not (this._svcHandle = 0) then
|
138
|
+
_dprint("SetServiceStatus() API")
|
139
|
+
SetServiceStatus(this._svcHandle, @this._svcStatus)
|
140
|
+
end if
|
141
|
+
_dprint("ServiceProcess.UpdateState() done")
|
142
|
+
end sub
|
143
|
+
|
144
|
+
|
145
|
+
'# use StillAlive() method when performing lengthly tasks during onInit or onStop
|
146
|
+
'# (if they take too much time).
|
147
|
+
'# by default we set a wait hint gap of 10 seconds, but you could specify how many
|
148
|
+
'# you could specify how many seconds more will require your *work*
|
149
|
+
sub ServiceProcess.StillAlive(byval waithint as integer = 10)
|
150
|
+
dim as integer checkpoint
|
151
|
+
|
152
|
+
_dprint("ServiceProcess.StillAlive()")
|
153
|
+
'# start or stop pending?
|
154
|
+
if (this._svcStatus.dwCurrentState = SERVICE_START_PENDING) or _
|
155
|
+
(this._svcStatus.dwCurrentState = SERVICE_STOP_PENDING) then
|
156
|
+
with this
|
157
|
+
checkpoint = this._svcStatus.dwCheckPoint
|
158
|
+
checkpoint += 1
|
159
|
+
.UpdateState(._svcStatus.dwCurrentState, checkpoint, (waithint * 1000))
|
160
|
+
end with
|
161
|
+
end if
|
162
|
+
_dprint("ServiceProcess.StillAlive() done")
|
163
|
+
end sub
|
164
|
+
|
165
|
+
|
166
|
+
'# call_onStart() is a wrapper around the new limitation of threadcreate
|
167
|
+
'# sub used as pointers in threadcreate must conform the signature
|
168
|
+
sub ServiceProcess.call_onStart(byval any_service as any ptr)
|
169
|
+
var service = cast(ServiceProcess ptr, any_service)
|
170
|
+
service->onStart(*service)
|
171
|
+
end sub
|
172
|
+
|
173
|
+
'#####################
|
174
|
+
'# ServiceHost
|
175
|
+
'# ctor()
|
176
|
+
'# currently isn't needed, why I defined it?
|
177
|
+
constructor ServiceHost()
|
178
|
+
_dprint("ServiceHost()")
|
179
|
+
_dprint("ServiceHost() done")
|
180
|
+
end constructor
|
181
|
+
|
182
|
+
|
183
|
+
'# dtor()
|
184
|
+
'# currently isn't needed, why I defined it?
|
185
|
+
destructor ServiceHost()
|
186
|
+
_dprint("ServiceHost() destructor")
|
187
|
+
_dprint("ServiceHost() destructor done")
|
188
|
+
end destructor
|
189
|
+
|
190
|
+
|
191
|
+
'# using Add() will register an already initialized service into the references
|
192
|
+
'# table, which will be used later to launch and control the different services
|
193
|
+
'# we should be careful when handling references, so for that reference_lock is
|
194
|
+
'# provided ;-)
|
195
|
+
sub ServiceHost.Add(byref service as ServiceProcess)
|
196
|
+
_dprint("ServiceHost.Add()")
|
197
|
+
|
198
|
+
'# add the service reference to the references table
|
199
|
+
'# get the new count as result, so
|
200
|
+
'# increment the local counter
|
201
|
+
this.count = _add_to_references(service)
|
202
|
+
|
203
|
+
_dprint("ServiceHost.Add() done")
|
204
|
+
end sub
|
205
|
+
|
206
|
+
|
207
|
+
'# ServiceHost.Run() is just a placeholder, it delegates control to _run()
|
208
|
+
'# pretty simple, but still must be present to simplify user interaction.
|
209
|
+
sub ServiceHost.Run()
|
210
|
+
_dprint("ServiceHost.Run()")
|
211
|
+
|
212
|
+
'# the longest, hard coded function in the world!
|
213
|
+
'# just kidding
|
214
|
+
_run()
|
215
|
+
|
216
|
+
_dprint("ServiceHost.Run() done")
|
217
|
+
end sub
|
218
|
+
|
219
|
+
|
220
|
+
'# the purpose of this sub is provide a generic service creation and running
|
221
|
+
'# this is be called from exisitng ServiceProcess and ServiceHost.
|
222
|
+
'# this construct the SERVICE_TABLE_ENTRY based on the the references table,
|
223
|
+
'# which will be sent to StartServiceCtrlDispatcher()
|
224
|
+
private sub _run()
|
225
|
+
dim ServiceTable(_svc_references_count) as SERVICE_TABLE_ENTRY
|
226
|
+
dim idx as integer
|
227
|
+
|
228
|
+
_dprint("_run()")
|
229
|
+
|
230
|
+
_dprint("creating service table for " + str(_svc_references_count) + " services")
|
231
|
+
for idx = 0 to (_svc_references_count - 1)
|
232
|
+
'# we take the service name from the references and set as ServiceMain the same
|
233
|
+
'# _main() routine for all the services
|
234
|
+
ServiceTable(idx) = type<SERVICE_TABLE_ENTRY>(strptr(_svc_references[idx]->name), @_main)
|
235
|
+
_dprint(str(idx) + ": " + _svc_references[idx]->name)
|
236
|
+
next idx
|
237
|
+
'# last member of the table must be null
|
238
|
+
ServiceTable(_svc_references_count) = type<SERVICE_TABLE_ENTRY>(0, 0)
|
239
|
+
_dprint("service table created")
|
240
|
+
|
241
|
+
'# start the dispatcher
|
242
|
+
_dprint("start service dispatcher")
|
243
|
+
StartServiceCtrlDispatcher( @ServiceTable(0) )
|
244
|
+
|
245
|
+
_dprint("_run() done")
|
246
|
+
end sub
|
247
|
+
|
248
|
+
|
249
|
+
'# this sub is fired by StartServiceCtrlDispatcher in another thread.
|
250
|
+
'# because it is a global _main for all the services in the table, looking up
|
251
|
+
'# in the references for the right service is needed prior registering its
|
252
|
+
'# control handler.
|
253
|
+
private sub _main(byval argc as DWORD, byval argv as LPSTR ptr)
|
254
|
+
dim success as integer
|
255
|
+
dim service as ServiceProcess ptr
|
256
|
+
dim run_mode as string
|
257
|
+
dim service_name as string
|
258
|
+
dim commandline as string
|
259
|
+
dim param_line as string
|
260
|
+
dim temp as string
|
261
|
+
|
262
|
+
_dprint("_main()")
|
263
|
+
|
264
|
+
'# debug dump of argc and argv
|
265
|
+
dim idx as integer = 0
|
266
|
+
for idx = 0 to (argc - 1)
|
267
|
+
_dprint(str(idx) + ": " + *argv[idx])
|
268
|
+
next idx
|
269
|
+
|
270
|
+
'# retrieve all the information (mode, service name and command line
|
271
|
+
_build_commandline(run_mode, service_name, commandline)
|
272
|
+
service = _find_in_references(service_name)
|
273
|
+
|
274
|
+
'# build parameter line (passed from SCM)
|
275
|
+
if (argc > 1) then
|
276
|
+
param_line = ""
|
277
|
+
for idx = 1 to (argc - 1)
|
278
|
+
temp = *argv[idx]
|
279
|
+
if (instr(temp, chr(32)) > 0) then
|
280
|
+
param_line += """" + temp + """"
|
281
|
+
else
|
282
|
+
param_line += temp
|
283
|
+
end if
|
284
|
+
param_line += " "
|
285
|
+
next idx
|
286
|
+
end if
|
287
|
+
|
288
|
+
'# parameters passed using SCM have priority over ImagePath ones
|
289
|
+
if not (len(param_line) = 0) then
|
290
|
+
commandline = param_line
|
291
|
+
end if
|
292
|
+
|
293
|
+
'# a philosofical question: to run or not to run?
|
294
|
+
if not (service = 0) then
|
295
|
+
_dprint("got a valid service reference")
|
296
|
+
_dprint("real service name: " + service->name)
|
297
|
+
|
298
|
+
'# pass to the service the commandline
|
299
|
+
_dprint("passing service commandline: " + commandline)
|
300
|
+
service->commandline = commandline
|
301
|
+
|
302
|
+
'# ok, its a service!, its alive!
|
303
|
+
'# register his ControlHandlerEx
|
304
|
+
_dprint("register control handler ex")
|
305
|
+
service->_svcHandle = RegisterServiceCtrlHandlerEx(strptr(service_name), @_control_ex, cast(LPVOID, service))
|
306
|
+
|
307
|
+
'# check if evething is done right
|
308
|
+
if not (service->_svcHandle = 0) then
|
309
|
+
'# now, we are a single service or a bunch, like the bradys?
|
310
|
+
if (_svc_references_count > 1) then
|
311
|
+
'# determine if we share or not the process
|
312
|
+
if (service->shared_process = FALSE) then
|
313
|
+
service->_svcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS
|
314
|
+
else
|
315
|
+
'# this mean we will be sharing... hope neighbors don't crash the house!
|
316
|
+
service->_svcStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS
|
317
|
+
end if
|
318
|
+
else
|
319
|
+
'# ok, we have a full house (ehem, process) for us only!
|
320
|
+
service->_svcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS
|
321
|
+
end if
|
322
|
+
|
323
|
+
'# START_PENDING
|
324
|
+
_dprint("service start pending")
|
325
|
+
service->UpdateState(SERVICE_START_PENDING)
|
326
|
+
|
327
|
+
'# now delegate to the long running initialization if it exist.
|
328
|
+
if not (service->onInit = 0) then
|
329
|
+
_dprint("pass control to lengthly initialization")
|
330
|
+
success = service->onInit(*service)
|
331
|
+
else
|
332
|
+
'# if no onInit was defined (maybe you don't need it?)
|
333
|
+
'# we should simulate it was successful to proceed
|
334
|
+
success = (-1)
|
335
|
+
end if
|
336
|
+
_dprint("onInit result: " + str(success))
|
337
|
+
|
338
|
+
'# check if everything is ok
|
339
|
+
'# if onInit showed problems, 0 was returned and service must not continue
|
340
|
+
if not (success = 0) then
|
341
|
+
'# SERVICE_RUNNING
|
342
|
+
'# we must launch the onStart as thread, but first setting state as running
|
343
|
+
service->UpdateState(SERVICE_RUNNING)
|
344
|
+
if not (service->onStart = 0) then
|
345
|
+
_dprint("dispatch onStart() as new thread")
|
346
|
+
service->_threadHandle = threadcreate(@ServiceProcess.call_onStart, service)
|
347
|
+
'# my guess? was a hit!
|
348
|
+
end if
|
349
|
+
|
350
|
+
'# now that we are out of onStart thread, check if actually hit the stop sign
|
351
|
+
_dprint("waiting for stop signal")
|
352
|
+
do
|
353
|
+
'# do nothing ...
|
354
|
+
'# but not too often!
|
355
|
+
loop while (WaitForSingleObject(service->_svcStopEvent, 100) = WAIT_TIMEOUT)
|
356
|
+
|
357
|
+
'# now, wait for the thread (anyway, I hope it will be checking this.state, right?)
|
358
|
+
'# we should do this, or actualy jump and wait for StopEvent?
|
359
|
+
_dprint("waiting for onStart() thread to finish")
|
360
|
+
threadwait(service->_threadHandle)
|
361
|
+
end if
|
362
|
+
|
363
|
+
'# if we reach here, that means the service is not running, and the onStop was performed
|
364
|
+
'# so no more chat, stop it one and for all!
|
365
|
+
'# set SERVICE_STOPPED (just checking)
|
366
|
+
_dprint("service stopped")
|
367
|
+
service->UpdateState(SERVICE_STOPPED)
|
368
|
+
end if
|
369
|
+
|
370
|
+
'# ok, we are done!
|
371
|
+
end if
|
372
|
+
|
373
|
+
_dprint("_main() done")
|
374
|
+
end sub
|
375
|
+
|
376
|
+
|
377
|
+
'# this sub is used by _main when registering the ControlHandler for this service
|
378
|
+
'# (as callback from service manager).
|
379
|
+
'# we process each control codes and perform the actions using the pseudo-events (callbacks)
|
380
|
+
'# also we use lpContext to get the right reference when _main registered the control handler.
|
381
|
+
private function _control_ex(byval dwControl as DWORD, byval dwEventType as DWORD, byval lpEventData as LPVOID, byval lpContext as LPVOID) as DWORD
|
382
|
+
dim result as DWORD
|
383
|
+
dim service as ServiceProcess ptr
|
384
|
+
|
385
|
+
_dprint("_control_ex()")
|
386
|
+
|
387
|
+
'# we get a reference form the context
|
388
|
+
service = cast(ServiceProcess ptr, lpContext)
|
389
|
+
|
390
|
+
'# show if the service reference is valid?
|
391
|
+
_dprint("service name: " + service->name)
|
392
|
+
|
393
|
+
select case dwControl
|
394
|
+
case SERVICE_CONTROL_INTERROGATE:
|
395
|
+
'# we are running, so what we should do here?
|
396
|
+
_dprint("interrogation signal received")
|
397
|
+
'# in case we get a interrogation, we always should answer this way.
|
398
|
+
result = NO_ERROR
|
399
|
+
|
400
|
+
case SERVICE_CONTROL_SHUTDOWN, SERVICE_CONTROL_STOP:
|
401
|
+
_dprint("stop signal received")
|
402
|
+
'# ok, service manager requested us to stop.
|
403
|
+
'# we must call onStop if was defined.
|
404
|
+
service->UpdateState(SERVICE_STOP_PENDING)
|
405
|
+
if not (service->onStop = 0) then
|
406
|
+
_dprint("pass control to onStop()")
|
407
|
+
service->onStop(*service)
|
408
|
+
end if
|
409
|
+
'# now signal the stop event so _main could take care of the rest.
|
410
|
+
_dprint("signal stop event")
|
411
|
+
SetEvent(service->_svcStopEvent)
|
412
|
+
|
413
|
+
case SERVICE_CONTROL_PAUSE:
|
414
|
+
_dprint("pause signal received")
|
415
|
+
'# we must check if we could answer to the request.
|
416
|
+
if not (service->onPause = 0) and _
|
417
|
+
not (service->onContinue = 0) then
|
418
|
+
|
419
|
+
'# just to be sure
|
420
|
+
if not (service->onPause = 0) then
|
421
|
+
service->UpdateState(SERVICE_PAUSE_PENDING)
|
422
|
+
|
423
|
+
_dprint("pass control to onPause()")
|
424
|
+
service->onPause(*service)
|
425
|
+
|
426
|
+
service->UpdateState(SERVICE_PAUSED)
|
427
|
+
_dprint("service paused")
|
428
|
+
end if
|
429
|
+
result = NO_ERROR
|
430
|
+
|
431
|
+
else
|
432
|
+
'# ok, our service didn't support pause or continue
|
433
|
+
'# tell the service manager about that!
|
434
|
+
result = ERROR_CALL_NOT_IMPLEMENTED
|
435
|
+
end if
|
436
|
+
|
437
|
+
case SERVICE_CONTROL_CONTINUE:
|
438
|
+
_dprint("continue signal received")
|
439
|
+
'# we should resume from a paused state
|
440
|
+
'# we must check if we could answer to the request.
|
441
|
+
if not (service->onPause = 0) and _
|
442
|
+
not (service->onContinue = 0) then
|
443
|
+
|
444
|
+
'# just to be sure
|
445
|
+
if not (service->onPause = 0) then
|
446
|
+
service->UpdateState(SERVICE_CONTINUE_PENDING)
|
447
|
+
|
448
|
+
_dprint("pass control to onContinue()")
|
449
|
+
service->onContinue(*service)
|
450
|
+
|
451
|
+
service->UpdateState(SERVICE_RUNNING)
|
452
|
+
_dprint("service running")
|
453
|
+
end if
|
454
|
+
result = NO_ERROR
|
455
|
+
|
456
|
+
else
|
457
|
+
'# ok, our service didn't support pause or continue
|
458
|
+
'# tell the service manager about that!
|
459
|
+
result = ERROR_CALL_NOT_IMPLEMENTED
|
460
|
+
end if
|
461
|
+
|
462
|
+
case else:
|
463
|
+
result = NO_ERROR
|
464
|
+
end select
|
465
|
+
|
466
|
+
_dprint("_control_ex() done")
|
467
|
+
return result
|
468
|
+
end function
|
469
|
+
|
470
|
+
|
471
|
+
'# add_to_references is a helper used to reduce code duplication (DRY).
|
472
|
+
'# here is used a lock around _svc_references to avoid two threads try change the
|
473
|
+
'# reference count (just in case).
|
474
|
+
function _add_to_references(byref service as ServiceProcess) as integer
|
475
|
+
_dprint("_add_to_references()")
|
476
|
+
|
477
|
+
'# get a lock before even think touch references!
|
478
|
+
mutexlock(_svc_references_lock)
|
479
|
+
|
480
|
+
'# now, reallocate space
|
481
|
+
_svc_references_count += 1
|
482
|
+
_svc_references = reallocate(_svc_references, sizeof(ServiceProcess ptr) * _svc_references_count)
|
483
|
+
|
484
|
+
'# put the reference of this service into the table
|
485
|
+
_svc_references[(_svc_references_count - 1)] = @service
|
486
|
+
|
487
|
+
'# ok, done, unlock our weapons! ;-)
|
488
|
+
mutexunlock(_svc_references_lock)
|
489
|
+
|
490
|
+
_dprint("_add_to_references() done")
|
491
|
+
'# return the new references count
|
492
|
+
return _svc_references_count
|
493
|
+
end function
|
494
|
+
|
495
|
+
|
496
|
+
'# find_in_references is used by _main to lookup for the specified service in
|
497
|
+
'# references table.
|
498
|
+
function _find_in_references(byref service_name as string) as ServiceProcess ptr
|
499
|
+
dim result as ServiceProcess ptr
|
500
|
+
dim item as ServiceProcess ptr
|
501
|
+
dim idx as integer
|
502
|
+
|
503
|
+
_dprint("_find_in_references()")
|
504
|
+
|
505
|
+
'# we start with a pesimistic idea ;-)
|
506
|
+
result = 0
|
507
|
+
|
508
|
+
for idx = 0 to (_svc_references_count - 1)
|
509
|
+
'# hold a reference to the item
|
510
|
+
item = _svc_references[idx]
|
511
|
+
|
512
|
+
'# compare if we have a match
|
513
|
+
if (service_name = item->name) then
|
514
|
+
result = item
|
515
|
+
exit for
|
516
|
+
end if
|
517
|
+
next idx
|
518
|
+
|
519
|
+
_dprint("_find_in_references() done")
|
520
|
+
'# return the found (or not) reference
|
521
|
+
return result
|
522
|
+
end function
|
523
|
+
|
524
|
+
|
525
|
+
'# namespace constructor
|
526
|
+
'# first we must create the mutex to be used with references
|
527
|
+
private sub _initialize() constructor
|
528
|
+
_dprint("_initialize() constructor")
|
529
|
+
'# we do this in case was already defined... don't know the situation,
|
530
|
+
'# just to be sure
|
531
|
+
if (_svc_references_lock = 0) then
|
532
|
+
_svc_references_lock = mutexcreate()
|
533
|
+
|
534
|
+
'# also initialize our count :-)
|
535
|
+
_svc_references_count = 0
|
536
|
+
end if
|
537
|
+
|
538
|
+
_dprint("_initialize() constructor done")
|
539
|
+
end sub
|
540
|
+
|
541
|
+
|
542
|
+
'# namespace destructor
|
543
|
+
private sub _terminate() destructor
|
544
|
+
_dprint("_terminate() destructor")
|
545
|
+
'# to avoid removing everything, we must lock to the references
|
546
|
+
mutexlock(_svc_references_lock)
|
547
|
+
|
548
|
+
'# destroy our refernces allocated memory!
|
549
|
+
deallocate(_svc_references)
|
550
|
+
|
551
|
+
'# unlock the mutex and destroy it too.
|
552
|
+
mutexunlock(_svc_references_lock)
|
553
|
+
mutexdestroy(_svc_references_lock)
|
554
|
+
|
555
|
+
_dprint("_terminate() destructor done")
|
556
|
+
end sub
|
557
|
+
|
558
|
+
|
559
|
+
'# command line builder (helper)
|
560
|
+
'# this is used to gather information about:
|
561
|
+
'# mode (if present)
|
562
|
+
'# valid service name (after lookup in the table)
|
563
|
+
'# command line to be passed to service
|
564
|
+
sub _build_commandline(byref mode as string, byref service_name as string, byref commandline as string)
|
565
|
+
dim result_mode as string
|
566
|
+
dim result_name as string
|
567
|
+
dim result_cmdline as string
|
568
|
+
dim service as ServiceProcess ptr
|
569
|
+
dim idx as integer
|
570
|
+
dim temp as string
|
571
|
+
|
572
|
+
idx = 1
|
573
|
+
'# first, determine if mode is pressent in commandline, must me command(1)
|
574
|
+
temp = lcase(command(idx))
|
575
|
+
|
576
|
+
if (temp = "console") or _
|
577
|
+
(temp = "manage") then
|
578
|
+
result_mode = temp
|
579
|
+
idx += 1
|
580
|
+
end if
|
581
|
+
|
582
|
+
'# now, check if service name is present
|
583
|
+
temp = command(idx)
|
584
|
+
|
585
|
+
'# its present?
|
586
|
+
if (len(temp) > 0) then
|
587
|
+
'# lookup in references table
|
588
|
+
service = _find_in_references(temp)
|
589
|
+
if not (service = 0) then
|
590
|
+
'# was found, so must be valid
|
591
|
+
result_name = temp
|
592
|
+
'# adjust start index for cmdline
|
593
|
+
idx += 1
|
594
|
+
end if
|
595
|
+
end if
|
596
|
+
|
597
|
+
'# is service valid?
|
598
|
+
'# its really needed?
|
599
|
+
if (service = 0) then
|
600
|
+
if (_svc_references_count = 1) then
|
601
|
+
'# no, get the first one
|
602
|
+
service = _svc_references[0]
|
603
|
+
result_name = service->name
|
604
|
+
'# adjust start index for cmdline
|
605
|
+
else
|
606
|
+
'# this is needed!
|
607
|
+
result_name = ""
|
608
|
+
end if
|
609
|
+
end if
|
610
|
+
|
611
|
+
result_cmdline = ""
|
612
|
+
|
613
|
+
temp = command(idx)
|
614
|
+
do while (len(temp) > 0)
|
615
|
+
if (instr(temp, chr(32)) > 0) then
|
616
|
+
'# properly quote parameters with spaces
|
617
|
+
result_cmdline += """" + temp + """"
|
618
|
+
else
|
619
|
+
result_cmdline += temp
|
620
|
+
end if
|
621
|
+
result_cmdline += " "
|
622
|
+
idx += 1
|
623
|
+
|
624
|
+
temp = command(idx)
|
625
|
+
loop
|
626
|
+
|
627
|
+
'# now, return the results
|
628
|
+
mode = result_mode
|
629
|
+
service_name = result_name
|
630
|
+
commandline = result_cmdline
|
631
|
+
end sub
|
632
|
+
|
633
|
+
|
634
|
+
'# ### DEBUG ###
|
635
|
+
'# just for debuging purposes
|
636
|
+
'# (will be removed in the future when Loggers get implemented)
|
637
|
+
#ifdef SERVICEFB_DEBUG_LOG
|
638
|
+
sub _dprint(byref message as string)
|
639
|
+
dim handle as integer
|
640
|
+
|
641
|
+
handle = freefile
|
642
|
+
open EXEPATH + "\servicefb.log" for append as #handle
|
643
|
+
|
644
|
+
print #handle, message
|
645
|
+
|
646
|
+
close #handle
|
647
|
+
end sub
|
648
|
+
#endif
|
649
|
+
end namespace '# fb.svc
|
650
|
+
end namespace '# fb
|