mirror of
https://github.com/puma/puma.git
synced 2022-11-09 13:48:40 -05:00
Replaced Spawn/Terminate functions with experimental ConsoleProcess Class.
Small tweaks to Rakefile for new gem release. git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@621 19e92222-5c0b-0410-8929-a290d50e31e9
This commit is contained in:
parent
5a948694ba
commit
4cf7422cd9
7 changed files with 130 additions and 504 deletions
|
@ -1,26 +1,17 @@
|
||||||
* SVN *
|
|
||||||
|
v0.3.3.
|
||||||
* Properly display package/gem version for mongrel_service. Closes #13823.
|
Properly display package/gem version for mongrel_service; Closes #13823.
|
||||||
|
Updated ServiceFB to r80 to solve issue when compiling with FB > 0.17.
|
||||||
* Updated ServiceFB to r80 to solve issue when compiling with FB > 0.17.
|
Replaced Spawn/Terminate functions with experimental ConsoleProcess Class.
|
||||||
|
|
||||||
* 0.3.2 *
|
v0.3.2.
|
||||||
|
Solved detection of parent process at ServiceFB level (solves the x64 Windows issues).
|
||||||
* Solved detection of parent process at ServiceFB level
|
Upgraded to ServiceFB 'trunk' (but pistoned it, just in case).
|
||||||
(solves the x64 Windows issues).
|
Fixed problems with ruby installations outside PATH or inside folders with spaces.
|
||||||
|
Activate FB pedantic warnings by default (is really useful).
|
||||||
* Upgraded to ServiceFB 'trunk' (but pistoned it, just in case).
|
|
||||||
|
v0.3.1.
|
||||||
* Fixed problems with ruby installations outside PATH or inside folders with spaces.
|
Single Service (SingleMongrel) object type implemented.
|
||||||
|
Updated Rakefile to reflect the new building steps.
|
||||||
* Activate FB pedantic warnings by default (is really useful).
|
Removed SendSignal, too hackish for my taste, replaced with complete FB solution.
|
||||||
|
Added basic Process monitoring and re-spawning.
|
||||||
* 0.3.1 *
|
|
||||||
|
|
||||||
* Single Service (SingleMongrel) object type implemented.
|
|
||||||
|
|
||||||
* Updated Rakefile to reflect the new building steps.
|
|
||||||
|
|
||||||
* Removed SendSignal, too hackish for my taste, replaced with complete FB solution.
|
|
||||||
|
|
||||||
* Added basic Process monitoring and re-spawning.
|
|
||||||
|
|
|
@ -3,10 +3,10 @@ tools/freebasic.rb
|
||||||
TODO
|
TODO
|
||||||
resources/defaults.yaml
|
resources/defaults.yaml
|
||||||
README
|
README
|
||||||
native/process.bi
|
|
||||||
native/process.bas
|
|
||||||
native/mongrel_service.bi
|
native/mongrel_service.bi
|
||||||
native/mongrel_service.bas
|
native/mongrel_service.bas
|
||||||
|
native/console_process.bi
|
||||||
|
native/console_process.bas
|
||||||
native/_debug.bi
|
native/_debug.bi
|
||||||
LICENSE
|
LICENSE
|
||||||
lib/ServiceFB/ServiceFB_Utils.bi
|
lib/ServiceFB/ServiceFB_Utils.bi
|
||||||
|
|
|
@ -1,92 +1,87 @@
|
||||||
|
require 'echoe'
|
||||||
require 'echoe'
|
require 'tools/freebasic'
|
||||||
require 'tools/freebasic'
|
|
||||||
|
echoe_spec = Echoe.new("mongrel_service", "0.3.3") do |p|
|
||||||
echoe_spec = Echoe.new("mongrel_service") do |p|
|
p.summary = "Mongrel Native Win32 Service Plugin for Rails"
|
||||||
p.summary = "Mongrel Native Win32 Service Plugin for Rails"
|
p.summary += " (debug build)" unless ENV['RELEASE']
|
||||||
p.summary += " (debug build)" unless ENV['RELEASE']
|
p.description = "This plugin offer native win32 services for rails, powered by Mongrel."
|
||||||
p.description = "This plugin offer native win32 services for rails, powered by Mongrel."
|
p.author = "Luis Lavena"
|
||||||
p.author = "Luis Lavena"
|
p.platform = Gem::Platform::WIN32
|
||||||
p.platform = Gem::Platform::WIN32
|
p.dependencies = ['gem_plugin >=0.2.2', 'mongrel >=1.0.1', 'win32-service >=0.5.0']
|
||||||
p.dependencies = ['gem_plugin >=0.2.1', 'mongrel >=0.3.12.4', 'win32-service >=0.5.0']
|
|
||||||
|
p.need_tar_gz = false
|
||||||
p.need_tar_gz = false
|
p.need_zip = true
|
||||||
p.need_zip = true
|
p.certificate_chain = ['~/gem_certificates/mongrel-public_cert.pem',
|
||||||
p.certificate_chain = ['/Users/eweaver/p/configuration/gem_certificates/mongrel/mongrel-public_cert.pem',
|
'~/gem_certificates/luislavena-mongrel-public_cert.pem']
|
||||||
'/Users/eweaver/p/configuration/gem_certificates/evan_weaver-mongrel-public_cert.pem']
|
p.require_signed = true
|
||||||
p.require_signed = true
|
end
|
||||||
end
|
|
||||||
|
desc "Compile native code"
|
||||||
desc "Compile native code"
|
task :compile => [:native_lib, :native_service]
|
||||||
task :compile => [:native_lib, :native_service]
|
|
||||||
|
#task :"bin/mongrel_service.exe" => [:clean, :compile]
|
||||||
task :"bin/mongrel_service.exe" => [:clean, :compile]
|
|
||||||
|
# global options shared by all the project in this Rakefile
|
||||||
# global options shared by all the project in this Rakefile
|
OPTIONS = {
|
||||||
OPTIONS = {
|
:debug => false,
|
||||||
:debug => false,
|
:profile => false,
|
||||||
:profile => false,
|
:errorchecking => :ex,
|
||||||
:errorchecking => :ex,
|
:mt => true,
|
||||||
:mt => true,
|
:pedantic => true }
|
||||||
:pedantic => true }
|
|
||||||
|
OPTIONS[:debug] = true if ENV['DEBUG']
|
||||||
OPTIONS[:debug] = true if ENV['DEBUG']
|
OPTIONS[:profile] = true if ENV['PROFILE']
|
||||||
OPTIONS[:profile] = true if ENV['PROFILE']
|
OPTIONS[:errorchecking] = :exx if ENV['EXX']
|
||||||
OPTIONS[:errorchecking] = :exx if ENV['EXX']
|
OPTIONS[:pedantic] = false if ENV['NOPEDANTIC']
|
||||||
OPTIONS[:pedantic] = false if ENV['NOPEDANTIC']
|
|
||||||
|
# ServiceFB namespace (lib)
|
||||||
# ServiceFB namespace (lib)
|
namespace :lib do
|
||||||
namespace :lib do
|
project_task 'servicefb' do
|
||||||
project_task 'servicefb' do
|
lib 'ServiceFB'
|
||||||
lib 'ServiceFB'
|
build_to 'lib'
|
||||||
build_to 'lib'
|
|
||||||
|
define 'SERVICEFB_DEBUG_LOG' unless ENV['RELEASE']
|
||||||
define 'SERVICEFB_DEBUG_LOG' unless ENV['RELEASE']
|
source 'lib/ServiceFB/ServiceFB.bas'
|
||||||
source 'lib/ServiceFB/ServiceFB.bas'
|
|
||||||
|
option OPTIONS
|
||||||
option OPTIONS
|
end
|
||||||
end
|
|
||||||
|
project_task 'servicefb_utils' do
|
||||||
project_task 'servicefb_utils' do
|
lib 'ServiceFB_Utils'
|
||||||
lib 'ServiceFB_Utils'
|
build_to 'lib'
|
||||||
build_to 'lib'
|
|
||||||
|
define 'SERVICEFB_DEBUG_LOG' unless ENV['RELEASE']
|
||||||
define 'SERVICEFB_DEBUG_LOG' unless ENV['RELEASE']
|
source 'lib/ServiceFB/ServiceFB_Utils.bas'
|
||||||
source 'lib/ServiceFB/ServiceFB_Utils.bas'
|
|
||||||
|
option OPTIONS
|
||||||
option OPTIONS
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
# add lib namespace to global tasks
|
# add lib namespace to global tasks
|
||||||
#include_projects_of :lib
|
#include_projects_of :lib
|
||||||
task :native_lib => "lib:build"
|
task :native_lib => "lib:build"
|
||||||
task :clean => "lib:clobber"
|
task :clean => "lib:clobber"
|
||||||
|
|
||||||
# mongrel_service (native)
|
# mongrel_service (native)
|
||||||
namespace :native do
|
namespace :native do
|
||||||
project_task 'mongrel_service' do
|
project_task 'mongrel_service' do
|
||||||
executable 'mongrel_service'
|
executable 'mongrel_service'
|
||||||
build_to 'bin'
|
build_to 'bin'
|
||||||
|
|
||||||
define 'DEBUG_LOG' unless ENV['RELEASE']
|
define 'DEBUG_LOG' unless ENV['RELEASE']
|
||||||
define "GEM_VERSION=#{echoe_spec.version}"
|
define "GEM_VERSION=#{echoe_spec.version}"
|
||||||
|
|
||||||
main 'native/mongrel_service.bas'
|
main 'native/mongrel_service.bas'
|
||||||
source 'native/process.bas'
|
source 'native/console_process.bas'
|
||||||
|
|
||||||
# including the precompiled file show warnings when linking with
|
lib_path 'lib'
|
||||||
# ld, due pial m$ directives in the obj file
|
library 'ServiceFB', 'ServiceFB_Utils'
|
||||||
# will solve that later, when migrate the asm part of code to gcc
|
library 'user32', 'advapi32', 'psapi'
|
||||||
# source 'native/send_signal.o'
|
|
||||||
|
option OPTIONS
|
||||||
lib_path 'lib'
|
end
|
||||||
library 'ServiceFB', 'ServiceFB_Utils'
|
end
|
||||||
library 'user32', 'advapi32', 'psapi'
|
|
||||||
|
#include_projects_of :native
|
||||||
option OPTIONS
|
task :native_service => "native:build"
|
||||||
end
|
task :clean => "native:clobber"
|
||||||
end
|
|
||||||
#include_projects_of :native
|
|
||||||
task :native_service => "native:build"
|
|
||||||
task :clean => "native:clobber"
|
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
'##################################################################
|
'##################################################################
|
||||||
'# Requirements:
|
'# Requirements:
|
||||||
'# - FreeBASIC 0.17, Win32 CVS Build (as for November 09, 2006).
|
'# - FreeBASIC 0.18
|
||||||
'#
|
'#
|
||||||
'##################################################################
|
'##################################################################
|
||||||
|
|
||||||
|
@ -23,9 +23,9 @@
|
||||||
#include once "_debug.bi"
|
#include once "_debug.bi"
|
||||||
|
|
||||||
namespace mongrel_service
|
namespace mongrel_service
|
||||||
using fb.process
|
|
||||||
|
|
||||||
constructor SingleMongrel()
|
constructor SingleMongrel()
|
||||||
|
dim redirect_file as string
|
||||||
|
|
||||||
with this.__service
|
with this.__service
|
||||||
.name = "single"
|
.name = "single"
|
||||||
.description = "Mongrel Single Process service"
|
.description = "Mongrel Single Process service"
|
||||||
|
@ -39,6 +39,12 @@ namespace mongrel_service
|
||||||
.onStop = @single_onStop
|
.onStop = @single_onStop
|
||||||
end with
|
end with
|
||||||
|
|
||||||
|
with this.__console
|
||||||
|
redirect_file = EXEPATH + "\mongrel.log"
|
||||||
|
debug("redirecting to: " + redirect_file)
|
||||||
|
.redirect(ProcessStdBoth, redirect_file)
|
||||||
|
end with
|
||||||
|
|
||||||
'# TODO: fix inheritance here
|
'# TODO: fix inheritance here
|
||||||
single_mongrel_ref = @this
|
single_mongrel_ref = @this
|
||||||
end constructor
|
end constructor
|
||||||
|
@ -64,6 +70,10 @@ namespace mongrel_service
|
||||||
'# SingleMongrel instance. now we should call StillAlive
|
'# SingleMongrel instance. now we should call StillAlive
|
||||||
self.StillAlive()
|
self.StillAlive()
|
||||||
if (len(self.commandline) > 0) then
|
if (len(self.commandline) > 0) then
|
||||||
|
'# assign the program
|
||||||
|
single_mongrel_ref->__console.filename = mongrel_cmd
|
||||||
|
single_mongrel_ref->__console.arguments = self.commandline
|
||||||
|
|
||||||
'# fix commandline, it currently contains params to be passed to
|
'# fix commandline, it currently contains params to be passed to
|
||||||
'# mongrel_rails, and not ruby.exe nor the script to be run.
|
'# mongrel_rails, and not ruby.exe nor the script to be run.
|
||||||
self.commandline = mongrel_cmd + " " + self.commandline
|
self.commandline = mongrel_cmd + " " + self.commandline
|
||||||
|
@ -71,7 +81,9 @@ namespace mongrel_service
|
||||||
'# now launch the child process
|
'# now launch the child process
|
||||||
debug("starting child process with cmdline: " + self.commandline)
|
debug("starting child process with cmdline: " + self.commandline)
|
||||||
single_mongrel_ref->__child_pid = 0
|
single_mongrel_ref->__child_pid = 0
|
||||||
single_mongrel_ref->__child_pid = Spawn(self.commandline)
|
if (single_mongrel_ref->__console.start() = true) then
|
||||||
|
single_mongrel_ref->__child_pid = single_mongrel_ref->__console.pid
|
||||||
|
end if
|
||||||
self.StillAlive()
|
self.StillAlive()
|
||||||
|
|
||||||
'# check if pid is valid
|
'# check if pid is valid
|
||||||
|
@ -96,13 +108,15 @@ namespace mongrel_service
|
||||||
do while (self.state = Running) or (self.state = Paused)
|
do while (self.state = Running) or (self.state = Paused)
|
||||||
'# instead of sitting idle here, we must monitor the pid
|
'# instead of sitting idle here, we must monitor the pid
|
||||||
'# and re-spawn a new process if needed
|
'# and re-spawn a new process if needed
|
||||||
if not (Status(single_mongrel_ref->__child_pid) = ProcessStillActive) then
|
if not (single_mongrel_ref->__console.running = true) then
|
||||||
'# check if we aren't terminating
|
'# check if we aren't terminating
|
||||||
if (self.state = Running) or (self.state = Paused) then
|
if (self.state = Running) or (self.state = Paused) then
|
||||||
debug("child process terminated!, re-spawning a new one")
|
debug("child process terminated!, re-spawning a new one")
|
||||||
|
|
||||||
single_mongrel_ref->__child_pid = 0
|
single_mongrel_ref->__child_pid = 0
|
||||||
single_mongrel_ref->__child_pid = Spawn(self.commandline)
|
if (single_mongrel_ref->__console.start() = true) then
|
||||||
|
single_mongrel_ref->__child_pid = single_mongrel_ref->__console.pid
|
||||||
|
end if
|
||||||
|
|
||||||
if (single_mongrel_ref->__child_pid > 0) then
|
if (single_mongrel_ref->__child_pid > 0) then
|
||||||
debug("new child process pid: " + str(single_mongrel_ref->__child_pid))
|
debug("new child process pid: " + str(single_mongrel_ref->__child_pid))
|
||||||
|
@ -123,8 +137,7 @@ namespace mongrel_service
|
||||||
'# now terminates the child process
|
'# now terminates the child process
|
||||||
if not (single_mongrel_ref->__child_pid = 0) then
|
if not (single_mongrel_ref->__child_pid = 0) then
|
||||||
debug("trying to kill pid: " + str(single_mongrel_ref->__child_pid))
|
debug("trying to kill pid: " + str(single_mongrel_ref->__child_pid))
|
||||||
'if not (send_break(single_mongrel_ref->__child_pid) = 0) then
|
if not (single_mongrel_ref->__console.terminate() = true) then
|
||||||
if not (Terminate(single_mongrel_ref->__child_pid) = TRUE) then
|
|
||||||
debug("Terminate() reported a problem when terminating process " + str(single_mongrel_ref->__child_pid))
|
debug("Terminate() reported a problem when terminating process " + str(single_mongrel_ref->__child_pid))
|
||||||
else
|
else
|
||||||
debug("child process terminated with success.")
|
debug("child process terminated with success.")
|
||||||
|
|
|
@ -14,13 +14,13 @@
|
||||||
|
|
||||||
'##################################################################
|
'##################################################################
|
||||||
'# Requirements:
|
'# Requirements:
|
||||||
'# - FreeBASIC 0.17, Win32 CVS Build (as for November 09, 2006).
|
'# - FreeBASIC 0.18.
|
||||||
'#
|
'#
|
||||||
'##################################################################
|
'##################################################################
|
||||||
|
|
||||||
#define SERVICEFB_INCLUDE_UTILS
|
#define SERVICEFB_INCLUDE_UTILS
|
||||||
#include once "lib/ServiceFB/ServiceFB.bi"
|
#include once "lib/ServiceFB/ServiceFB.bi"
|
||||||
#include once "process.bi"
|
#include once "console_process.bi"
|
||||||
|
|
||||||
'# use for debug versions
|
'# use for debug versions
|
||||||
#if not defined(GEM_VERSION)
|
#if not defined(GEM_VERSION)
|
||||||
|
@ -52,6 +52,7 @@ namespace mongrel_service
|
||||||
'declare sub onStop()
|
'declare sub onStop()
|
||||||
|
|
||||||
__service as ServiceProcess
|
__service as ServiceProcess
|
||||||
|
__console as ConsoleProcess
|
||||||
__child_pid as uinteger
|
__child_pid as uinteger
|
||||||
end type
|
end type
|
||||||
|
|
||||||
|
|
|
@ -1,316 +0,0 @@
|
||||||
'##################################################################
|
|
||||||
'#
|
|
||||||
'# mongrel_service: Win32 native implementation for mongrel
|
|
||||||
'# (using ServiceFB and FreeBASIC)
|
|
||||||
'#
|
|
||||||
'# Copyright (c) 2006 Multimedia systems
|
|
||||||
'# (c) and code by Luis Lavena
|
|
||||||
'#
|
|
||||||
'# mongrel_service (native) and mongrel_service gem_pluing are licensed
|
|
||||||
'# in the same terms as mongrel, please review the mongrel license at
|
|
||||||
'# http://mongrel.rubyforge.org/license.html
|
|
||||||
'#
|
|
||||||
'##################################################################
|
|
||||||
|
|
||||||
'##################################################################
|
|
||||||
'# Requirements:
|
|
||||||
'# - FreeBASIC 0.17, Win32 CVS Build (as for November 09, 2006).
|
|
||||||
'#
|
|
||||||
'##################################################################
|
|
||||||
|
|
||||||
#include once "process.bi"
|
|
||||||
#define DEBUG_LOG_FILE EXEPATH + "\mongrel_service.log"
|
|
||||||
#include once "_debug.bi"
|
|
||||||
|
|
||||||
namespace fb
|
|
||||||
namespace process
|
|
||||||
'# Spawn(cmdline) will try to create a new process, monitor
|
|
||||||
'# if it launched successfuly (5 seconds) and then return the
|
|
||||||
'# new children PID (Process IDentification) or 0 in case of problems
|
|
||||||
function Spawn(byref cmdline as string) as uinteger
|
|
||||||
dim result as uinteger
|
|
||||||
dim success as BOOL
|
|
||||||
|
|
||||||
'# New Process resources
|
|
||||||
dim child as PROCESS_INFORMATION
|
|
||||||
dim context as STARTUPINFO
|
|
||||||
dim child_sa as SECURITY_ATTRIBUTES = type(sizeof(SECURITY_ATTRIBUTES), NULL, TRUE)
|
|
||||||
dim wait_code as DWORD
|
|
||||||
|
|
||||||
'# StdIn, StdOut, StdErr Read and Write Pipes.
|
|
||||||
dim as HANDLE StdInRd, StdOutRd, StdErrRd
|
|
||||||
dim as HANDLE StdInWr, StdOutWr, StdErrWr
|
|
||||||
|
|
||||||
debug("Spawn() init")
|
|
||||||
|
|
||||||
'# to ensure everything will work, we must allocate a console
|
|
||||||
'# using AllocConsole, even if it fails.
|
|
||||||
'# This solve the problems when running as service.
|
|
||||||
if (AllocConsole() = 0) then
|
|
||||||
debug("Success in AllocConsole()")
|
|
||||||
else
|
|
||||||
debug("AllocConsole failed, maybe already allocated, safely to discart.")
|
|
||||||
end if
|
|
||||||
|
|
||||||
'# presume all worked
|
|
||||||
success = TRUE
|
|
||||||
|
|
||||||
'# Create the pipes
|
|
||||||
'# StdIn
|
|
||||||
if (CreatePipe( @StdInRd, @StdInWr, @child_sa, 0 ) = 0) then
|
|
||||||
debug("Error creating StdIn pipes.")
|
|
||||||
success = FALSE
|
|
||||||
end if
|
|
||||||
|
|
||||||
'# StdOut
|
|
||||||
if (CreatePipe( @StdOutRd, @StdOutWr, @child_sa, 0 ) = 0) then
|
|
||||||
debug("Error creating StdOut pipes.")
|
|
||||||
success = FALSE
|
|
||||||
end if
|
|
||||||
|
|
||||||
'# StdErr
|
|
||||||
if (CreatePipe( @StdErrRd, @StdErrWr, @child_sa, 0 ) = 0) then
|
|
||||||
debug("Error creating StdErr pipes.")
|
|
||||||
success = FALSE
|
|
||||||
end if
|
|
||||||
|
|
||||||
'# Ensure the handles to the pipe are not inherited.
|
|
||||||
if (SetHandleInformation( StdInWr, HANDLE_FLAG_INHERIT, 0) = 0) then
|
|
||||||
debug("Error disabling StdInWr handle.")
|
|
||||||
success = FALSE
|
|
||||||
end if
|
|
||||||
|
|
||||||
if (SetHandleInformation( StdOutRd, HANDLE_FLAG_INHERIT, 0) = 0) then
|
|
||||||
debug("Error disabling StdOutRd handle.")
|
|
||||||
success = FALSE
|
|
||||||
end if
|
|
||||||
|
|
||||||
if (SetHandleInformation( StdErrRd, HANDLE_FLAG_INHERIT, 0) = 0) then
|
|
||||||
debug("Error disabling StdErrRd handle.")
|
|
||||||
success = FALSE
|
|
||||||
end if
|
|
||||||
|
|
||||||
'# without the pipes, we shouldn't continue!
|
|
||||||
if (success = TRUE) then
|
|
||||||
'# Set the Std* handles ;-)
|
|
||||||
with context
|
|
||||||
.cb = sizeof( context )
|
|
||||||
.hStdError = StdErrWr
|
|
||||||
.hStdOutput = StdOutWr
|
|
||||||
.hStdInput = StdInRd
|
|
||||||
.dwFlags = STARTF_USESTDHANDLES
|
|
||||||
end with
|
|
||||||
|
|
||||||
'# now creates the process
|
|
||||||
debug("Creating child process with cmdline: " + cmdline)
|
|
||||||
if (CreateProcess(NULL, _
|
|
||||||
strptr(cmdline), _
|
|
||||||
NULL, _
|
|
||||||
NULL, _
|
|
||||||
TRUE, _
|
|
||||||
0, _
|
|
||||||
NULL, _
|
|
||||||
NULL, _
|
|
||||||
@context, _
|
|
||||||
@child) = 0) then
|
|
||||||
|
|
||||||
debug("Error creating child process, error #" + str(GetLastError()))
|
|
||||||
result = 0
|
|
||||||
else
|
|
||||||
'# close the Std* handles
|
|
||||||
debug("Closing handles.")
|
|
||||||
CloseHandle(StdInRd)
|
|
||||||
CloseHandle(StdInWr)
|
|
||||||
CloseHandle(StdOutRd)
|
|
||||||
CloseHandle(StdOutWr)
|
|
||||||
CloseHandle(StdErrRd)
|
|
||||||
CloseHandle(StdErrWr)
|
|
||||||
|
|
||||||
'# close children main Thread handle
|
|
||||||
if (CloseHandle(child.hThread) = 0) then
|
|
||||||
debug("Problem closing children main thread.")
|
|
||||||
end if
|
|
||||||
|
|
||||||
'# now wait 2 seconds if the children process unexpectly quits
|
|
||||||
wait_code = WaitForSingleObject(child.hProcess, 2000)
|
|
||||||
debug("wait_code: " + str(wait_code))
|
|
||||||
if (wait_code = WAIT_TIMEOUT) then
|
|
||||||
'# the process is still running, we are good
|
|
||||||
'# save a reference to the pid
|
|
||||||
result = child.dwProcessId
|
|
||||||
debug("New children PID: " + str(result))
|
|
||||||
|
|
||||||
'# now close the handle
|
|
||||||
CloseHandle(child.hProcess)
|
|
||||||
else
|
|
||||||
'# the process failed or terminated earlier
|
|
||||||
debug("failed, the process terminate earlier.")
|
|
||||||
result = 0
|
|
||||||
end if '# (wait_code = WAIT_OBJECT_0)
|
|
||||||
end if '# (CreateProcess() = 0)
|
|
||||||
else
|
|
||||||
debug("problem preparing environment for child process, no success")
|
|
||||||
result = 0
|
|
||||||
end if '# (success = TRUE)
|
|
||||||
|
|
||||||
debug("Spawn() done")
|
|
||||||
return result
|
|
||||||
end function
|
|
||||||
|
|
||||||
|
|
||||||
'# Terminate(PID) will hook the special console handler (_child_console_handler)
|
|
||||||
'# and try sending CTRL_C_EVENT, CTRL_BREAK_EVENT and TerminateProcess
|
|
||||||
'# in case of the first two fails.
|
|
||||||
function Terminate(byval pid as uinteger) as BOOL
|
|
||||||
dim result as BOOL
|
|
||||||
dim success as BOOL
|
|
||||||
dim exit_code as DWORD
|
|
||||||
dim wait_code as DWORD
|
|
||||||
|
|
||||||
'# process resources
|
|
||||||
dim child as HANDLE
|
|
||||||
|
|
||||||
debug("Terminate() init")
|
|
||||||
|
|
||||||
'# is pid valid?
|
|
||||||
if (pid > 0) then
|
|
||||||
'# hook our custom console handler
|
|
||||||
debug("hooking console handler")
|
|
||||||
if (SetConsoleCtrlHandler(@_child_console_handler, TRUE) = 0) then
|
|
||||||
debug("error hooking our custom error handler")
|
|
||||||
end if
|
|
||||||
|
|
||||||
'# get a handle to Process
|
|
||||||
debug("OpenProcess() with Terminate and Query flags")
|
|
||||||
child = OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_TERMINATE or SYNCHRONIZE, FALSE, pid)
|
|
||||||
if not (child = NULL) then
|
|
||||||
'# process is valid, perform actions
|
|
||||||
success = FALSE
|
|
||||||
|
|
||||||
'# send CTRL_C_EVENT and wait for result
|
|
||||||
debug("sending Ctrl-C to child pid " + str(pid))
|
|
||||||
if not (GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0) = 0) then
|
|
||||||
'# it worked, wait 5 seconds terminates.
|
|
||||||
debug("send worked, waiting 5 seconds to terminate...")
|
|
||||||
wait_code = WaitForSingleObject(child, 5000)
|
|
||||||
debug("wait_code: " + str(wait_code))
|
|
||||||
if not (wait_code = WAIT_TIMEOUT) then
|
|
||||||
debug("child process terminated properly.")
|
|
||||||
success = TRUE
|
|
||||||
end if
|
|
||||||
else
|
|
||||||
debug("cannot generate event, error " + str(GetLastError()))
|
|
||||||
success = FALSE
|
|
||||||
end if
|
|
||||||
|
|
||||||
'# Ctrl-C didn't work, try Ctrl-Break
|
|
||||||
if (success = FALSE) then
|
|
||||||
'# send CTRL_BREAK_EVENT and wait for result
|
|
||||||
debug("sending Ctrl-Break to child pid " + str(pid))
|
|
||||||
if not (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, 0) = 0) then
|
|
||||||
'# it worked, wait 5 seconds terminates.
|
|
||||||
debug("send worked, waiting 5 seconds to terminate...")
|
|
||||||
wait_code = WaitForSingleObject(child, 5000)
|
|
||||||
debug("wait_code: " + str(wait_code))
|
|
||||||
if not (wait_code = WAIT_TIMEOUT) then
|
|
||||||
debug("child process terminated properly.")
|
|
||||||
success = TRUE
|
|
||||||
end if
|
|
||||||
else
|
|
||||||
debug("cannot generate event, error " + str(GetLastError()))
|
|
||||||
success = FALSE
|
|
||||||
end if
|
|
||||||
end if
|
|
||||||
|
|
||||||
'# still no luck? we should do a hard kill then
|
|
||||||
if (success = FALSE) then
|
|
||||||
debug("doing kill using TerminateProcess")
|
|
||||||
if (TerminateProcess(child, 3) = 0) then
|
|
||||||
debug("TerminateProcess failed, error " + str(GetLastError()))
|
|
||||||
else
|
|
||||||
success = TRUE
|
|
||||||
end if
|
|
||||||
end if
|
|
||||||
|
|
||||||
'# now get process exit code
|
|
||||||
if not (GetExitCodeProcess(child, @exit_code) = 0) then
|
|
||||||
debug("getting child process exit code: " + str(exit_code))
|
|
||||||
if (exit_code = 0) then
|
|
||||||
debug("process terminated ok.")
|
|
||||||
result = TRUE
|
|
||||||
elseif (exit_code = STILL_ACTIVE) then
|
|
||||||
debug("process still active")
|
|
||||||
result = FALSE
|
|
||||||
else
|
|
||||||
debug("process terminated with exit_code: " + str(exit_code))
|
|
||||||
result = TRUE
|
|
||||||
end if
|
|
||||||
else
|
|
||||||
debug("error getting child process exit code, value: " + str(exit_code) + ", error " + str(GetLastError()))
|
|
||||||
result = FALSE
|
|
||||||
end if
|
|
||||||
else
|
|
||||||
'# invalid process handler
|
|
||||||
result = FALSE
|
|
||||||
end if
|
|
||||||
|
|
||||||
'# remove hooks
|
|
||||||
SetConsoleCtrlhandler(@_child_console_handler, FALSE)
|
|
||||||
|
|
||||||
'# clean up all open handles
|
|
||||||
CloseHandle(child)
|
|
||||||
end if '# (pid > 0)
|
|
||||||
|
|
||||||
return result
|
|
||||||
end function
|
|
||||||
|
|
||||||
|
|
||||||
'# StillActive(PID) will return FALSE (0) in case the process no longer
|
|
||||||
'# exist of get terminated with error.
|
|
||||||
function Status(byval pid as uinteger) as ProcessStateEnum
|
|
||||||
dim result as ProcessStateEnum
|
|
||||||
dim exit_code as DWORD
|
|
||||||
|
|
||||||
'# process reference
|
|
||||||
dim child as HANDLE
|
|
||||||
|
|
||||||
'# presume error?
|
|
||||||
result = ProcessQueryError
|
|
||||||
|
|
||||||
'# is pid valid?
|
|
||||||
if (pid > 0) then
|
|
||||||
'# try getting a handler to the process
|
|
||||||
child = OpenProcess(PROCESS_QUERY_INFORMATION or SYNCHRONIZE, FALSE, pid)
|
|
||||||
if not (child = NULL) then
|
|
||||||
'# the process reference is valid, get the exit_code
|
|
||||||
if not (GetExitCodeProcess(child, @exit_code) = 0) then
|
|
||||||
'# no error in the query, get result
|
|
||||||
if (exit_code = STILL_ACTIVE) then
|
|
||||||
result = ProcessStillActive
|
|
||||||
end if '# (exit_code = STILL_ACTIVE)
|
|
||||||
end if '# not (GetExitCodeProcess())
|
|
||||||
|
|
||||||
'# closes the process handle
|
|
||||||
CloseHandle(child)
|
|
||||||
end if '# not (child = NULL)
|
|
||||||
end if '# (pid > 0)
|
|
||||||
|
|
||||||
return result
|
|
||||||
end function
|
|
||||||
|
|
||||||
'# Special hook used to avoid the process calling Terminate()
|
|
||||||
'# respond to CTRL_*_EVENTS when terminating child process
|
|
||||||
private function _child_console_handler(byval dwCtrlType as DWORD) as BOOL
|
|
||||||
dim result as BOOL
|
|
||||||
|
|
||||||
debug("_child_console_handler, dwCtrlType: " + str(dwCtrlType))
|
|
||||||
if (dwCtrlType = CTRL_C_EVENT) then
|
|
||||||
result = TRUE
|
|
||||||
elseif (dwCtrlType = CTRL_BREAK_EVENT) then
|
|
||||||
result = TRUE
|
|
||||||
end if
|
|
||||||
|
|
||||||
return result
|
|
||||||
end function
|
|
||||||
end namespace '# fb.process
|
|
||||||
end namespace '# fb
|
|
|
@ -1,58 +0,0 @@
|
||||||
'##################################################################
|
|
||||||
'#
|
|
||||||
'# mongrel_service: Win32 native implementation for mongrel
|
|
||||||
'# (using ServiceFB and FreeBASIC)
|
|
||||||
'#
|
|
||||||
'# Copyright (c) 2006 Multimedia systems
|
|
||||||
'# (c) and code by Luis Lavena
|
|
||||||
'#
|
|
||||||
'# mongrel_service (native) and mongrel_service gem_pluing are licensed
|
|
||||||
'# in the same terms as mongrel, please review the mongrel license at
|
|
||||||
'# http://mongrel.rubyforge.org/license.html
|
|
||||||
'#
|
|
||||||
'##################################################################
|
|
||||||
|
|
||||||
'##################################################################
|
|
||||||
'# Requirements:
|
|
||||||
'# - FreeBASIC 0.17, Win32 CVS Build (as for November 09, 2006).
|
|
||||||
'#
|
|
||||||
'##################################################################
|
|
||||||
|
|
||||||
#ifndef __Process_bi__
|
|
||||||
#define __Process_bi__
|
|
||||||
|
|
||||||
#include once "windows.bi"
|
|
||||||
|
|
||||||
namespace fb
|
|
||||||
namespace process
|
|
||||||
'# fb.process functions that allow creation/graceful termination
|
|
||||||
'# of child process.
|
|
||||||
|
|
||||||
'# Process Status Enum
|
|
||||||
enum ProcessStateEnum
|
|
||||||
ProcessQueryError = 0
|
|
||||||
ProcessStillActive = STILL_ACTIVE
|
|
||||||
end enum
|
|
||||||
|
|
||||||
|
|
||||||
'# Spawn(cmdline) will try to create a new process, monitor
|
|
||||||
'# if it launched successfuly (5 seconds) and then return the
|
|
||||||
'# new children PID (Process IDentification) or 0 in case of problems
|
|
||||||
declare function Spawn(byref as string) as uinteger
|
|
||||||
|
|
||||||
'# Terminate(PID) will hook the special console handler (_child_console_handler)
|
|
||||||
'# and try sending CTRL_C_EVENT, CTRL_BREAK_EVENT and TerminateProcess
|
|
||||||
'# in case of the first two fails.
|
|
||||||
declare function Terminate(byval as uinteger) as BOOL
|
|
||||||
|
|
||||||
'# StillActive(PID) will return FALSE (0) in case the process no longer
|
|
||||||
'# exist of get terminated with error.
|
|
||||||
declare function Status(byval as uinteger) as ProcessStateEnum
|
|
||||||
|
|
||||||
'# Special hook used to avoid the process calling Terminate()
|
|
||||||
'# respond to CTRL_*_EVENTS when terminating child process
|
|
||||||
declare function _child_console_handler(byval as DWORD) as BOOL
|
|
||||||
end namespace '# fb.process
|
|
||||||
end namespace '# fb
|
|
||||||
|
|
||||||
#endif '# __Process_bi__
|
|
Loading…
Add table
Add a link
Reference in a new issue