mongrel_service 0.3.4-x86-mingw32
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/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
|