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