Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
wine-winehq
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wine
wine-winehq
Commits
c0c68ee8
Commit
c0c68ee8
authored
Mar 29, 2023
by
Paul Gofman
Committed by
Alexandre Julliard
Mar 30, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
services: Change running service state once its process dies.
parent
3580d466
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
178 additions
and
3 deletions
+178
-3
rpc.c
programs/services/rpc.c
+1
-1
services.c
programs/services/services.c
+59
-2
services.h
programs/services/services.h
+1
-0
service.c
programs/services/tests/service.c
+117
-0
No files found.
programs/services/rpc.c
View file @
c0c68ee8
...
...
@@ -868,7 +868,7 @@ static void fill_notify(struct sc_notify_handle *notify, struct service_entry *s
SetEvent
(
notify
->
event
);
}
static
void
notify_service_state
(
struct
service_entry
*
service
)
void
notify_service_state
(
struct
service_entry
*
service
)
{
struct
sc_service_handle
*
service_handle
;
DWORD
mask
;
...
...
programs/services/services.c
View file @
c0c68ee8
...
...
@@ -47,7 +47,7 @@ static DWORD default_preshutdown_timeout = 180000;
static
DWORD
autostart_delay
=
120000
;
static
void
*
environment
=
NULL
;
static
HKEY
service_current_key
=
NULL
;
static
HANDLE
job_object
;
static
HANDLE
job_object
,
job_completion_port
;
static
const
BOOL
is_win64
=
(
sizeof
(
void
*
)
>
sizeof
(
int
));
...
...
@@ -1280,6 +1280,50 @@ static void load_registry_parameters(void)
RegCloseKey
(
key
);
}
static
DWORD
WINAPI
process_monitor_thread_proc
(
void
*
arg
)
{
struct
scmdatabase
*
db
=
active_database
;
struct
service_entry
*
service
;
struct
process_entry
*
process
;
OVERLAPPED
*
overlapped
;
ULONG_PTR
value
;
DWORD
key
,
pid
;
while
(
GetQueuedCompletionStatus
(
job_completion_port
,
&
key
,
&
value
,
&
overlapped
,
INFINITE
))
{
if
(
!
key
)
break
;
if
(
key
!=
JOB_OBJECT_MSG_EXIT_PROCESS
)
continue
;
pid
=
(
ULONG_PTR
)
overlapped
;
WINE_TRACE
(
"pid %04lx exited.
\n
"
,
pid
);
scmdatabase_lock
(
db
);
LIST_FOR_EACH_ENTRY
(
service
,
&
db
->
services
,
struct
service_entry
,
entry
)
{
if
(
service
->
status
.
dwCurrentState
!=
SERVICE_RUNNING
||
!
service
->
process
||
service
->
process
->
process_id
!=
pid
)
continue
;
WINE_TRACE
(
"Stopping service %s.
\n
"
,
debugstr_w
(
service
->
config
.
lpBinaryPathName
));
service
->
status
.
dwCurrentState
=
SERVICE_STOPPED
;
service
->
status
.
dwControlsAccepted
=
0
;
service
->
status
.
dwWin32ExitCode
=
ERROR_PROCESS_ABORTED
;
service
->
status
.
dwServiceSpecificExitCode
=
0
;
service
->
status
.
dwCheckPoint
=
0
;
service
->
status
.
dwWaitHint
=
0
;
SetEvent
(
service
->
status_changed_event
);
process
=
service
->
process
;
service
->
process
=
NULL
;
process
->
use_count
--
;
release_process
(
process
);
notify_service_state
(
service
);
}
scmdatabase_unlock
(
db
);
}
WINE_TRACE
(
"Terminating.
\n
"
);
return
0
;
}
int
__cdecl
main
(
int
argc
,
char
*
argv
[])
{
static
const
WCHAR
service_current_key_str
[]
=
{
'S'
,
'Y'
,
'S'
,
'T'
,
'E'
,
'M'
,
'\\'
,
...
...
@@ -1288,7 +1332,8 @@ int __cdecl main(int argc, char *argv[])
'S'
,
'e'
,
'r'
,
'v'
,
'i'
,
'c'
,
'e'
,
'C'
,
'u'
,
'r'
,
'r'
,
'e'
,
'n'
,
't'
,
0
};
static
const
WCHAR
svcctl_started_event
[]
=
SVCCTL_STARTED_EVENT
;
JOBOBJECT_EXTENDED_LIMIT_INFORMATION
job_limit
;
HANDLE
started_event
;
JOBOBJECT_ASSOCIATE_COMPLETION_PORT
port_info
;
HANDLE
started_event
,
process_monitor_thread
;
DWORD
err
;
job_object
=
CreateJobObjectW
(
NULL
,
NULL
);
...
...
@@ -1298,6 +1343,15 @@ int __cdecl main(int argc, char *argv[])
WINE_ERR
(
"Failed to initialized job object, err %lu.
\n
"
,
GetLastError
());
return
GetLastError
();
}
job_completion_port
=
CreateIoCompletionPort
(
INVALID_HANDLE_VALUE
,
NULL
,
0
,
1
);
port_info
.
CompletionPort
=
job_completion_port
;
port_info
.
CompletionKey
=
job_object
;
if
(
!
SetInformationJobObject
(
job_object
,
JobObjectAssociateCompletionPortInformation
,
&
port_info
,
sizeof
(
port_info
)))
{
WINE_ERR
(
"Failed to set completion port for job, err %lu.
\n
"
,
GetLastError
());
return
GetLastError
();
}
started_event
=
CreateEventW
(
NULL
,
TRUE
,
FALSE
,
svcctl_started_event
);
...
...
@@ -1316,8 +1370,11 @@ int __cdecl main(int argc, char *argv[])
if
((
err
=
RPC_Init
())
==
ERROR_SUCCESS
)
{
scmdatabase_autostart_services
(
active_database
);
process_monitor_thread
=
CreateThread
(
NULL
,
0
,
process_monitor_thread_proc
,
NULL
,
0
,
NULL
);
SetEvent
(
started_event
);
WaitForSingleObject
(
exit_event
,
INFINITE
);
PostQueuedCompletionStatus
(
job_completion_port
,
0
,
0
,
NULL
);
WaitForSingleObject
(
process_monitor_thread
,
INFINITE
);
scmdatabase_wait_terminate
(
active_database
);
if
(
delayed_autostart_cleanup
)
{
...
...
programs/services/services.h
View file @
c0c68ee8
...
...
@@ -93,6 +93,7 @@ void release_service(struct service_entry *service);
void
service_lock
(
struct
service_entry
*
service
);
void
service_unlock
(
struct
service_entry
*
service
);
DWORD
service_start
(
struct
service_entry
*
service
,
DWORD
service_argc
,
LPCWSTR
*
service_argv
);
void
notify_service_state
(
struct
service_entry
*
service
);
/* Process functions */
...
...
programs/services/tests/service.c
View file @
c0c68ee8
...
...
@@ -616,6 +616,122 @@ static void test_runner(void (*p_run_test)(void))
CloseHandle
(
thread
);
}
static
void
CALLBACK
notify_cb
(
void
*
user
)
{
}
static
inline
void
test_kill_service_process
(
void
)
{
static
const
char
*
argv
[
2
]
=
{
"param1"
,
"param2"
};
SC_HANDLE
service_handle
=
register_service
(
"simple_service"
);
SERVICE_STATUS_PROCESS
status2
;
SERVICE_NOTIFYW
notify
;
SERVICE_STATUS
status
;
DWORD
bytes
,
error
;
HANDLE
process
;
BOOL
res
;
if
(
!
service_handle
)
return
;
trace
(
"starting...
\n
"
);
res
=
StartServiceA
(
service_handle
,
2
,
argv
);
ok
(
res
,
"StartService failed: %lu
\n
"
,
GetLastError
());
if
(
!
res
)
{
DeleteService
(
service_handle
);
CloseServiceHandle
(
service_handle
);
return
;
}
expect_event
(
"RUNNING"
);
memset
(
&
notify
,
0
,
sizeof
(
notify
));
notify
.
dwVersion
=
SERVICE_NOTIFY_STATUS_CHANGE
;
notify
.
dwNotificationStatus
=
0xdeadbeef
;
notify
.
pfnNotifyCallback
=
notify_cb
;
notify
.
pContext
=
&
notify
;
error
=
NotifyServiceStatusChangeW
(
service_handle
,
SERVICE_NOTIFY_STOPPED
|
SERVICE_NOTIFY_RUNNING
|
SERVICE_NOTIFY_PAUSED
|
SERVICE_NOTIFY_PAUSE_PENDING
|
SERVICE_NOTIFY_STOP_PENDING
,
&
notify
);
ok
(
error
==
ERROR_SUCCESS
,
"got error %lu.
\n
"
,
error
);
/* This shouldn't wait, we are supposed to get service start notification before. */
SleepEx
(
5000
,
TRUE
);
ok
(
!
notify
.
dwNotificationStatus
,
"got %#lx.
\n
"
,
notify
.
dwNotificationStatus
);
ok
(
notify
.
dwNotificationTriggered
==
SERVICE_NOTIFY_RUNNING
,
"got %#lx.
\n
"
,
notify
.
dwNotificationTriggered
);
ok
(
notify
.
ServiceStatus
.
dwCurrentState
==
SERVICE_RUNNING
,
"got %#lx.
\n
"
,
notify
.
ServiceStatus
.
dwCurrentState
);
ok
(
!
notify
.
ServiceStatus
.
dwWin32ExitCode
,
"got %#lx.
\n
"
,
notify
.
ServiceStatus
.
dwWin32ExitCode
);
res
=
QueryServiceStatus
(
service_handle
,
&
status
);
ok
(
res
,
"got error %lu.
\n
"
,
GetLastError
());
ok
(
status
.
dwServiceType
==
SERVICE_WIN32_OWN_PROCESS
,
"got %lx.
\n
"
,
status
.
dwServiceType
);
ok
(
status
.
dwCurrentState
==
SERVICE_RUNNING
,
"got %lx.
\n
"
,
status
.
dwCurrentState
);
res
=
QueryServiceStatusEx
(
service_handle
,
SC_STATUS_PROCESS_INFO
,
(
BYTE
*
)
&
status2
,
sizeof
(
status2
),
&
bytes
);
ok
(
res
,
"got error %lu.
\n
"
,
GetLastError
());
ok
(
status2
.
dwCurrentState
==
SERVICE_RUNNING
,
"got %lx.
\n
"
,
status2
.
dwCurrentState
);
ok
(
status2
.
dwProcessId
,
"got %ld
\n
"
,
status2
.
dwProcessId
);
process
=
OpenProcess
(
PROCESS_TERMINATE
,
FALSE
,
status2
.
dwProcessId
);
ok
(
!!
process
,
"got NULL.
\n
"
);
res
=
TerminateProcess
(
process
,
0xdeadbeef
);
ok
(
res
,
"got error %lu.
\n
"
,
GetLastError
());
CloseHandle
(
process
);
memset
(
&
notify
,
0
,
sizeof
(
notify
));
notify
.
dwVersion
=
SERVICE_NOTIFY_STATUS_CHANGE
;
notify
.
dwNotificationStatus
=
0xdeadbeef
;
notify
.
pfnNotifyCallback
=
notify_cb
;
notify
.
pContext
=
&
notify
;
error
=
NotifyServiceStatusChangeW
(
service_handle
,
SERVICE_NOTIFY_STOPPED
|
SERVICE_NOTIFY_RUNNING
|
SERVICE_NOTIFY_PAUSED
|
SERVICE_NOTIFY_PAUSE_PENDING
|
SERVICE_NOTIFY_STOP_PENDING
,
&
notify
);
ok
(
error
==
ERROR_SUCCESS
,
"got error %lu.
\n
"
,
error
);
SleepEx
(
3000
,
TRUE
);
ok
(
!
notify
.
dwNotificationStatus
,
"got %#lx.
\n
"
,
notify
.
dwNotificationStatus
);
ok
(
notify
.
dwNotificationTriggered
==
SERVICE_NOTIFY_STOPPED
,
"got %#lx.
\n
"
,
notify
.
dwNotificationTriggered
);
ok
(
notify
.
ServiceStatus
.
dwCurrentState
==
SERVICE_STOPPED
,
"got %#lx.
\n
"
,
notify
.
ServiceStatus
.
dwCurrentState
);
ok
(
notify
.
ServiceStatus
.
dwWin32ExitCode
==
ERROR_PROCESS_ABORTED
,
"got %#lx.
\n
"
,
notify
.
ServiceStatus
.
dwWin32ExitCode
);
ok
(
!
notify
.
ServiceStatus
.
dwServiceSpecificExitCode
,
"got %#lx.
\n
"
,
notify
.
ServiceStatus
.
dwWin32ExitCode
);
res
=
QueryServiceStatus
(
service_handle
,
&
status
);
ok
(
res
,
"got error %lu.
\n
"
,
error
);
ok
(
status
.
dwServiceType
==
SERVICE_WIN32_OWN_PROCESS
,
"got %lx.
\n
"
,
status
.
dwServiceType
);
ok
(
status
.
dwCurrentState
==
SERVICE_STOPPED
,
"got %lx.
\n
"
,
status
.
dwCurrentState
);
ok
(
!
status
.
dwControlsAccepted
,
"got %lx
\n
"
,
status
.
dwControlsAccepted
);
ok
(
status
.
dwWin32ExitCode
==
ERROR_PROCESS_ABORTED
,
"got %ld.
\n
"
,
status
.
dwWin32ExitCode
);
ok
(
!
status
.
dwServiceSpecificExitCode
,
"got %ld.
\n
"
,
status
.
dwServiceSpecificExitCode
);
ok
(
!
status
.
dwCheckPoint
,
"got %ld.
\n
"
,
status
.
dwCheckPoint
);
ok
(
!
status
.
dwWaitHint
,
"got %ld.
\n
"
,
status
.
dwWaitHint
);
res
=
QueryServiceStatusEx
(
service_handle
,
SC_STATUS_PROCESS_INFO
,
(
BYTE
*
)
&
status2
,
sizeof
(
status2
),
&
bytes
);
ok
(
res
,
"got error %lu.
\n
"
,
error
);
ok
(
!
status2
.
dwProcessId
,
"got %ld.
\n
"
,
status2
.
dwProcessId
);
res
=
DeleteService
(
service_handle
);
ok
(
res
,
"got error %lu.
\n
"
,
error
);
res
=
QueryServiceStatus
(
service_handle
,
&
status
);
ok
(
res
,
"got error %lu.
\n
"
,
error
);
ok
(
status
.
dwServiceType
==
SERVICE_WIN32_OWN_PROCESS
,
"got %lx.
\n
"
,
status
.
dwServiceType
);
ok
(
status
.
dwCurrentState
==
SERVICE_STOPPED
,
"got %lx.
\n
"
,
status
.
dwCurrentState
);
ok
(
status
.
dwControlsAccepted
==
0
,
"got %lx.
\n
"
,
status
.
dwControlsAccepted
);
ok
(
status
.
dwWin32ExitCode
==
ERROR_PROCESS_ABORTED
,
"got %ld.
\n
"
,
status
.
dwWin32ExitCode
);
ok
(
status
.
dwServiceSpecificExitCode
==
0
,
"got %ld.
\n
"
,
status
.
dwServiceSpecificExitCode
);
ok
(
!
status
.
dwCheckPoint
,
"got %ld.
\n
"
,
status
.
dwCheckPoint
);
ok
(
!
status
.
dwWaitHint
,
"got %ld.
\n
"
,
status
.
dwWaitHint
);
res
=
QueryServiceStatusEx
(
service_handle
,
SC_STATUS_PROCESS_INFO
,
(
BYTE
*
)
&
status2
,
sizeof
(
status2
),
&
bytes
);
ok
(
res
,
"got error %lu.
\n
"
,
GetLastError
());
ok
(
!
status2
.
dwProcessId
,
"got %ld.
\n
"
,
status2
.
dwProcessId
);
CloseServiceHandle
(
service_handle
);
res
=
QueryServiceStatus
(
service_handle
,
&
status
);
ok
(
!
res
,
"QueryServiceStatus should have failed.
\n
"
);
ok
(
GetLastError
()
==
ERROR_INVALID_HANDLE
,
"got %ld.
\n
"
,
GetLastError
());
}
START_TEST
(
service
)
{
char
**
argv
;
...
...
@@ -641,6 +757,7 @@ START_TEST(service)
if
(
argc
<
3
)
{
test_runner
(
test_service
);
test_runner
(
test_no_stop
);
test_runner
(
test_kill_service_process
);
}
else
{
strcpy
(
service_name
,
argv
[
3
]);
sprintf
(
named_pipe_name
,
"
\\\\
.
\\
pipe
\\
%s_pipe"
,
service_name
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment