mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1482 lines
47 KiB
1482 lines
47 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
start.c
|
|
|
|
Abstract:
|
|
|
|
Contains functions that are required for starting a service.
|
|
RStartServiceW
|
|
StartImage
|
|
InitStartImage
|
|
EndStartImage
|
|
|
|
Author:
|
|
|
|
Dan Lafferty (danl) 20-Jan-1992
|
|
|
|
Environment:
|
|
|
|
User Mode - Calls Win32 and NT native APIs.
|
|
|
|
|
|
Revision History:
|
|
|
|
01-Mar-1996 AnirudhS
|
|
ScStartImage: Notify the PNP manager when a service is started.
|
|
|
|
12-Apr-1995 AnirudhS
|
|
Allow services that run under accounts other than LocalSystem to
|
|
share processes too.
|
|
(_CAIRO_ only)
|
|
|
|
21-Feb-1995 AnirudhS
|
|
Since CreateProcess now handles quoted exe pathnames, removed the
|
|
(buggy) code that had been added to parse them, including
|
|
ScParseImagePath.
|
|
|
|
08-Sep-1994 Danl
|
|
ScLogonAndStartImage: Close the Duplicate token when we are done
|
|
with it. This allows logoff notification when the service process
|
|
exits.
|
|
|
|
30-Aug-1994 Danl
|
|
ScParseImagePath: Added this function to look for quotes around the
|
|
pathname. The Quotes may be necessary if the path contains a space
|
|
character.
|
|
|
|
18-Mar-1994 Danl
|
|
ScLogonAndStartImage: When starting a service in an account, we now
|
|
Impersonate using a duplicate of the token for the service, prior
|
|
to calling CreateProcess. This allows us to load executables
|
|
whose binaries reside on a remote server.
|
|
|
|
20-Oct-1993 Danl
|
|
ScStartService: If the NetLogon Service isn't in our database yet,
|
|
then we want to check to see if the service to be started is
|
|
NetLogon. If it is then we need to init our connection with the
|
|
Security Process.
|
|
|
|
17-Feb-1993 danl
|
|
Must release the database lock around the CreateProcess call so
|
|
that dll init routines can call OpenService.
|
|
|
|
12-Feb-1993 danl
|
|
Move the incrementing of the Service UseCount to
|
|
ScActivateServiceRecord. This is because if we fail beyond this
|
|
point, we call ScRemoveService to cleanup - but that assumes the
|
|
UseCount has already been incremented one for the service itself.
|
|
|
|
25-Apr-1992 ritaw
|
|
Changed ScStartImage to ScLogonAndStartImage
|
|
|
|
20-Jan-1992 danl
|
|
created
|
|
|
|
|
|
--*/
|
|
|
|
//
|
|
// Includes
|
|
//
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h> // DbgPrint prototype
|
|
#include <windef.h> // Can't use this until MIDL allows VOIDs
|
|
#include <nturtl.h> // needed for winbase.h when ntrtl is present
|
|
#include <winbase.h> // Critical Section function calls
|
|
#include <wingdi.h> // for winuserp.h
|
|
#include <winuser.h> // SW_HIDE
|
|
#include <winuserp.h> // STARTF_DESKTOPINHERIT
|
|
#include <winreg.h> // Needed by userenv.h
|
|
#include <cfgmgr32.h> // PNP manager functions
|
|
#include <pnp.h> // PNP manager functions, server side
|
|
#include <cfgmgrp.h> // PNP manager functions, server side, internal
|
|
#include <userenv.h> // UnloadUserProfile
|
|
#include <rpc.h> // DataTypes and runtime APIs
|
|
#include <stdlib.h> // wide character c runtimes.
|
|
|
|
#include <svcctl.h> // MIDL generated header file. (SC_RPC_HANDLE)
|
|
#include <tstr.h> // Unicode string macros
|
|
|
|
#include <scdebug.h> // SC_LOG
|
|
#include "dataman.h" // LPSERVICE_RECORD
|
|
#include <scconfig.h> // ScGetImageFileName
|
|
#include <control.h>
|
|
#include <scseclib.h> // ScCreateAndSetSD
|
|
#include "scopen.h" // Handle structures and signature definitions
|
|
#include <svcslib.h> // SvcAddWorkItem
|
|
#include "depend.h" // ScStartMarkedServices
|
|
#include "driver.h" // ScLoadDeviceDriver
|
|
#include "account.h" // ScLogonService
|
|
#include "svcctrl.h" // ScShutdownInProgress
|
|
|
|
#include "start.h" // ScStartService
|
|
#include <svcslib.h> // SvcStartLocalDispatcher()
|
|
|
|
//
|
|
// STATIC DATA
|
|
//
|
|
|
|
CRITICAL_SECTION ScStartImageCriticalSection;
|
|
|
|
STARTUPINFOW ScStartupInfo;
|
|
|
|
LPWSTR pszInteractiveDesktop=L"WinSta0\\Default";
|
|
|
|
//
|
|
// LOCAL FUNCTIONS
|
|
//
|
|
DWORD
|
|
ScLogonAndStartImage(
|
|
IN LPSERVICE_RECORD ServiceRecord,
|
|
IN LPWSTR ImageName,
|
|
OUT LPIMAGE_RECORD *ImageRecordPtr
|
|
);
|
|
|
|
DWORD
|
|
ScProcessHandleIsSignaled(
|
|
PVOID pContext,
|
|
DWORD dwWaitStatus
|
|
);
|
|
|
|
#ifdef _CAIRO_
|
|
BOOL
|
|
ScEqualAccountName(
|
|
IN LPWSTR Account1,
|
|
IN LPWSTR Account2
|
|
);
|
|
#endif
|
|
|
|
DWORD
|
|
RStartServiceW(
|
|
IN SC_RPC_HANDLE hService,
|
|
IN DWORD NumArgs,
|
|
IN LPSTRING_PTRSW CmdArgs
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function begins the execution of a service.
|
|
|
|
Arguments:
|
|
|
|
hService - A handle which is a pointer to a service handle structure.
|
|
|
|
dwNumServiceArgs - This indicates the number of argument vectors.
|
|
|
|
lpServiceArgVectors - This is a pointer to an array of string pointers.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was completely successful.
|
|
|
|
ERROR_ACCESS_DENIED - The specified handle was not opened with
|
|
SERVICE_START access.
|
|
|
|
ERROR_INVALID_HANDLE - The specified handle was invalid.
|
|
|
|
ERROR_SERVICE_WAS_STARTED - An instance of the service is already running.
|
|
|
|
ERROR_SERVICE_REQUEST_TIMEOUT - The service did not respond to the start
|
|
request in a timely fashion.
|
|
|
|
ERROR_SERVICE_NO_THREAD - A thread could not be created for the Win32
|
|
service.
|
|
|
|
ERROR_PATH_NOT_FOUND - The image file name could not be found in
|
|
the configuration database (registry), or the image file name
|
|
failed in a unicode/ansi conversion.
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
LPSERVICE_RECORD serviceRecord;
|
|
|
|
if (ScShutdownInProgress) {
|
|
return(ERROR_SHUTDOWN_IN_PROGRESS);
|
|
}
|
|
|
|
//
|
|
// Check the signature on the handle.
|
|
//
|
|
if (((LPSC_HANDLE_STRUCT)hService)->Signature != SERVICE_SIGNATURE) {
|
|
return(ERROR_INVALID_HANDLE);
|
|
}
|
|
|
|
//
|
|
// Was the handle opened with SERVICE_START access?
|
|
//
|
|
if (! RtlAreAllAccessesGranted(
|
|
((LPSC_HANDLE_STRUCT)hService)->AccessGranted,
|
|
SERVICE_START
|
|
)) {
|
|
return(ERROR_ACCESS_DENIED);
|
|
}
|
|
|
|
//
|
|
// A word about Locks....
|
|
// We don't bother to get locks here because (1) We know the service
|
|
// record cannot go away because we have an open handle to it,
|
|
// (2) For these checks, we don't care if state changes after we
|
|
// check them.
|
|
//
|
|
// (ScStartServiceAndDependencies also performs these checks; they are
|
|
// repeated here so we can fail quickly in these 2 cases.)
|
|
//
|
|
serviceRecord =
|
|
((LPSC_HANDLE_STRUCT)hService)->Type.ScServiceObject.ServiceRecord;
|
|
|
|
//
|
|
// We can never start a disabled service
|
|
//
|
|
if (serviceRecord->StartType == SERVICE_DISABLED) {
|
|
return ERROR_SERVICE_DISABLED;
|
|
}
|
|
|
|
//
|
|
// Cannot start a deleted service.
|
|
//
|
|
if (DELETE_FLAG_IS_SET(serviceRecord)) {
|
|
return ERROR_SERVICE_MARKED_FOR_DELETE;
|
|
}
|
|
|
|
status = ScStartServiceAndDependencies(serviceRecord, NumArgs, CmdArgs);
|
|
|
|
if (status == NO_ERROR) {
|
|
return serviceRecord->StartError;
|
|
}
|
|
else {
|
|
return status;
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
ScStartService(
|
|
IN LPSERVICE_RECORD ServiceRecord,
|
|
IN DWORD NumArgs,
|
|
IN LPSTRING_PTRSW CmdArgs
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function starts a service. This code is split from the RStartServiceW
|
|
so that the service controller internal code can bypass RPC and security
|
|
checking when auto-starting services and their dependencies.
|
|
|
|
Arguments:
|
|
|
|
ServiceRecord - This is a pointer to the service record.
|
|
|
|
NumArgs - This indicates the number of argument vectors.
|
|
|
|
CmdArgs - This is a pointer to an array of string pointers.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was completely successful.
|
|
|
|
ERROR_ACCESS_DENIED - The specified handle was not opened with
|
|
SERVICE_START access.
|
|
|
|
ERROR_INVALID_HANDLE - The specified handle was invalid.
|
|
|
|
ERROR_SERVICE_WAS_STARTED - An instance of the service is already running.
|
|
|
|
ERROR_SERVICE_REQUEST_TIMEOUT - The service did not respond to the start
|
|
request in a timely fashion.
|
|
|
|
ERROR_SERVICE_NO_THREAD - A thread could not be created for the Win32
|
|
service.
|
|
|
|
ERROR_PATH_NOT_FOUND - The image file name could not be found in
|
|
the configuration database (registry), or the image file name
|
|
failed in a unicode/ansi conversion.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
LPWSTR ImageName=NULL;
|
|
LPIMAGE_RECORD ImageRecord=NULL;
|
|
LPWSTR serviceName;
|
|
HANDLE pipeHandle;
|
|
DWORD startControl;
|
|
|
|
// NOTE: Only one thread at a time should be in this part of the code.
|
|
// This prevents two images from getting started as could happen if
|
|
// two threads get the Image Record at virtually the same time. In
|
|
// this case, they might both decide to start the same image.
|
|
//
|
|
// We need to do this before the check for the CurrentState. Otherwise,
|
|
// two threads could race down to start the same service, and they
|
|
// would both attempt to start it. We would end up with either
|
|
// two service images running, or two threads of the same service
|
|
// running in a single image.
|
|
//
|
|
|
|
EnterCriticalSection(&ScStartImageCriticalSection);
|
|
|
|
SC_LOG(LOCKS,"RStartServiceW: Entering StartImage Critical Section.....\n",0);
|
|
|
|
#ifndef _CAIRO_
|
|
//
|
|
// NETLOGON SPECIAL!
|
|
//
|
|
// If the NetLogon Service hasn't been created yet, then see if this
|
|
// is the NetLogon Service. If it is, then connect to the
|
|
// SecurityProcess. Even if ScInitSecurityProcess fails, we will
|
|
// set the global flag since we will not attempt to connect again.
|
|
//
|
|
if (!ScConnectedToSecProc) {
|
|
if (_wcsicmp(ScGlobalNetLogonName, ServiceRecord->ServiceName)==0) {
|
|
if (!ScInitSecurityProcess()) {
|
|
SC_LOG0(ERROR, "ScInitSecurityProcess Failed\n");
|
|
}
|
|
ScConnectedToSecProc = TRUE;
|
|
}
|
|
}
|
|
#endif // _CAIRO_
|
|
|
|
//
|
|
// We need to gain exclusive access to the database so that we may
|
|
// Read the database and make decisions based on its content.
|
|
//
|
|
ScDatabaseLock(SC_GET_SHARED,"Start1");
|
|
|
|
#ifdef TIMING_TEST
|
|
DbgPrint("[SC_TIMING] Start Next Service TickCount for\t%ws\t%d\n",
|
|
ServiceRecord->ServiceName, GetTickCount());
|
|
#endif // TIMING_TEST
|
|
|
|
//
|
|
// Check to see if the service is already running.
|
|
//
|
|
if (ServiceRecord->ServiceStatus.dwCurrentState != SERVICE_STOPPED){
|
|
ScDatabaseLock(SC_RELEASE,"Start2");
|
|
SC_LOG(LOCKS,"RStartServiceW: Leaving StartImage Critical Section....\n",0);
|
|
LeaveCriticalSection(&ScStartImageCriticalSection);
|
|
return(ERROR_SERVICE_ALREADY_RUNNING);
|
|
}
|
|
|
|
//
|
|
// If we are loading a driver, load it and return.
|
|
//
|
|
if (ServiceRecord->ServiceStatus.dwServiceType & SERVICE_DRIVER) {
|
|
status = ScLoadDeviceDriver(ServiceRecord);
|
|
ScDatabaseLock(SC_RELEASE,"Start3");
|
|
LeaveCriticalSection(&ScStartImageCriticalSection);
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Get the image record information out of the configuration database.
|
|
//
|
|
status = ScGetImageFileName(ServiceRecord->ServiceName, &ImageName);
|
|
|
|
if (status != NO_ERROR) {
|
|
SC_LOG(TRACE,"GetImageFileName failed rc = %d\n",status);
|
|
ScDatabaseLock(SC_RELEASE,"Start4");
|
|
SC_LOG(LOCKS,"RStartServiceW: Leaving StartImage Critical Section....\n",0);
|
|
LeaveCriticalSection(&ScStartImageCriticalSection);
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Make the service record active.
|
|
// Because the service effectively has a handle to itself, the
|
|
// UseCount gets incremented inside ScActivateServiceRecord() when
|
|
// called with a NULL ImageRecord pointer.
|
|
//
|
|
// We need to do this here because when we get to ScLogonAndStartImage,
|
|
// we have to release the database lock around the CreateProcess call.
|
|
// Since we open ourselves up to DeleteService and Control Service calls,
|
|
// We need to increment the use count, and set the START_PENDING status
|
|
// here.
|
|
//
|
|
ScDatabaseLock(SC_MAKE_EXCLUSIVE,"Start5");
|
|
ScActivateServiceRecord(ServiceRecord,NULL);
|
|
ScDatabaseLock(SC_MAKE_SHARED,"Start6");
|
|
|
|
//
|
|
// Is the image file for that service already running?
|
|
// If not, call StartImage.
|
|
//
|
|
// If the Image Record was NOT found in the database, then start the
|
|
// image file.
|
|
//
|
|
if ( (ServiceRecord->ServiceStatus.dwServiceType & SERVICE_WIN32_SHARE_PROCESS)
|
|
&&
|
|
(ScGetNamedImageRecord (ImageName,&ImageRecord)) ) {
|
|
|
|
//
|
|
// The service is configured to share its process with other services,
|
|
// and the image for the service is already running. So we don't need
|
|
// to start a new instance of the image.
|
|
//
|
|
// BUGBUG: Even if the first service in that process was configured to
|
|
// not share the process, we will still try to share the process.
|
|
// (This was the case in NT 3.51 too.)
|
|
// It could be argued that it is strange to setup an exe to service
|
|
// both shared and nonshared services, and that unpredictable
|
|
// behavior is acceptable... but we should probably check for this
|
|
// condition and start a separate process, or report an error.
|
|
//
|
|
|
|
#ifdef _CAIRO_
|
|
//
|
|
// We do need to check that the account for the service is the same
|
|
// as the one that the image was started under, and that the password
|
|
// is valid.
|
|
//
|
|
|
|
LPWSTR AccountName = NULL;
|
|
|
|
//
|
|
// Release the Database lock until we have validated the service
|
|
// configuration
|
|
//
|
|
ScDatabaseLock(SC_RELEASE,"Start21");
|
|
|
|
status = ScLookupServiceAccount(
|
|
ServiceRecord->ServiceName,
|
|
&AccountName
|
|
);
|
|
|
|
if (status == NO_ERROR) {
|
|
if (!ScEqualAccountName(AccountName, ImageRecord->AccountName)) {
|
|
status = ERROR_DIFFERENT_SERVICE_ACCOUNT;
|
|
SC_LOG3(ERROR,
|
|
"Can't start %ws service in account %ws because "
|
|
"image is already running under account %ws\n",
|
|
ServiceRecord->ServiceName,
|
|
AccountName,
|
|
ImageRecord->AccountName
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the account is not LocalSystem, validate the password by
|
|
// logging on the service
|
|
//
|
|
if (status == NO_ERROR && AccountName != NULL) {
|
|
|
|
HANDLE ServiceToken = NULL;
|
|
QUOTA_LIMITS ServiceQuotas;
|
|
PSID ServiceSid = NULL;
|
|
|
|
ServiceQuotas.PagedPoolLimit = 0;
|
|
status = ScLogonService(
|
|
ServiceRecord->ServiceName,
|
|
AccountName,
|
|
&ServiceToken,
|
|
NULL, // Don't need to load the user profile again
|
|
&ServiceQuotas,
|
|
&ServiceSid
|
|
);
|
|
|
|
if (status == NO_ERROR) {
|
|
CloseHandle(ServiceToken);
|
|
LocalFree(ServiceSid);
|
|
}
|
|
}
|
|
|
|
LocalFree(AccountName);
|
|
|
|
ScDatabaseLock(SC_GET_SHARED,"Start22");
|
|
|
|
#else // ndef _CAIRO_
|
|
;
|
|
#endif
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Start a new instance of the image
|
|
//
|
|
|
|
SC_LOG(TRACE,"Start: calling StartImage\n",0);
|
|
|
|
status = ScLogonAndStartImage(
|
|
ServiceRecord,
|
|
ImageName,
|
|
&ImageRecord
|
|
);
|
|
|
|
if (status != NO_ERROR) {
|
|
|
|
SC_LOG(TRACE,"Start: StartImage failed!\n",0);
|
|
}
|
|
}
|
|
|
|
LocalFree( ImageName );
|
|
|
|
if (status != NO_ERROR) {
|
|
|
|
ScDatabaseLock(SC_MAKE_EXCLUSIVE,"Start7");
|
|
(void)ScDeactivateServiceRecord(ServiceRecord);
|
|
|
|
ScDatabaseLock(SC_RELEASE,"Start8");
|
|
SC_LOG(LOCKS,"RStartServiceW: Leaving StartImage Critical Section........\n",0);
|
|
LeaveCriticalSection(&ScStartImageCriticalSection);
|
|
|
|
//
|
|
// This does a normal return because the database lock is
|
|
// no longer being held.
|
|
//
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// Before leaving the StartImage critical section, we need to gain
|
|
// exclusive access to the database so that we may add the image record
|
|
// pointer to the service record. (ActivateServiceRecord).
|
|
//
|
|
ScDatabaseLock(SC_MAKE_EXCLUSIVE,"Start9");
|
|
|
|
//
|
|
// By the time we get here, the Service Process will already be
|
|
// running and ready to accept its first control request.
|
|
//
|
|
|
|
//
|
|
// Add the ImageRecord Information to the active service record.
|
|
//
|
|
// Note that, as soon as we activate the service record and release
|
|
// the lock, we open ourselves up to receiving control requests.
|
|
// However, ScActivateServiceRecord sets the ControlsAccepted field
|
|
// to 0, so that the service cannot accept any controls. Thus, until
|
|
// the service actually sends its own status, the service controller
|
|
// will reject any controls other than INTERROGATE.
|
|
//
|
|
// Because the service effectively has a handle to itself, the
|
|
// UseCount gets incremented inside ScActivateServiceRecord().
|
|
//
|
|
ScActivateServiceRecord(ServiceRecord,ImageRecord);
|
|
|
|
pipeHandle = ServiceRecord->ImageRecord->PipeHandle;
|
|
serviceName = ServiceRecord->ServiceName;
|
|
|
|
ScDatabaseLock(SC_RELEASE,"Start10");
|
|
|
|
SC_LOG(LOCKS,"RStartServiceW: Leaving StartImage Critical Section........\n",0);
|
|
LeaveCriticalSection(&ScStartImageCriticalSection);
|
|
|
|
//
|
|
// Start the Service
|
|
//
|
|
|
|
if (ServiceRecord->ServiceStatus.dwServiceType & SERVICE_WIN32_OWN_PROCESS) {
|
|
startControl = SERVICE_CONTROL_START_OWN;
|
|
}
|
|
else {
|
|
startControl = SERVICE_CONTROL_START_SHARE;
|
|
}
|
|
|
|
status = ScSendControl (
|
|
serviceName, // ServiceName
|
|
pipeHandle, // pipeHandle
|
|
startControl, // Opcode
|
|
(LPWSTR *)CmdArgs, // CmdArgs (vector ptr)
|
|
NumArgs, // NumArgs
|
|
(DWORD)ServiceRecord); // StatusHandle
|
|
|
|
if (status != NO_ERROR) {
|
|
//
|
|
// If an error occured, remove the service by de-activating the
|
|
// service record and terminating the service process if it is
|
|
// the only one running in the process.
|
|
//
|
|
SC_LOG2(ERROR,"Start: SendControl to %ws service failed! status=%ld\n",
|
|
serviceName, status);
|
|
|
|
//
|
|
// BUGBUG: If possible, don't do anything with locks here.
|
|
// It would be better if ScRemoveService would get
|
|
// the exclusive lock itself - rather than expect the
|
|
// shared lock and converting it to exclusive.
|
|
// Check into places that call ScRemoveService.
|
|
//
|
|
// NOTE: Because ScRemoveService will expect the use count
|
|
// to already be incremented (for the service's own handle),
|
|
// it is necessary to increment that use count prior to
|
|
// removing it.
|
|
//
|
|
|
|
ScDatabaseLock(SC_GET_SHARED,"Start11");
|
|
ScRemoveService(ServiceRecord);
|
|
ScDatabaseLock(SC_RELEASE,"Start12");
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Notify the PNP manager that the service was started.
|
|
// The PNP manager uses this information to resolve ambiguities in
|
|
// reconfiguration scenarios where there could temporarily be more
|
|
// than one controlling service for a device. It remembers the last
|
|
// service started for each device, and marks it as the "active"
|
|
// service for the device in the registry.
|
|
// We don't need to do this for drivers, because NtLoadDriver itself
|
|
// notifies the PNP manager.
|
|
//
|
|
CONFIGRET PnpStatus = PNP_SetActiveService(
|
|
NULL, // hBinding
|
|
serviceName, // pszService
|
|
PNP_SERVICE_STARTED // ulFlags
|
|
);
|
|
if (PnpStatus != CR_SUCCESS)
|
|
{
|
|
SC_LOG2(ERROR, "PNP_SetActiveService failed %#lx for service %ws\n",
|
|
PnpStatus, serviceName);
|
|
}
|
|
}
|
|
return(status);
|
|
}
|
|
|
|
/****************************************************************************/
|
|
DWORD
|
|
ScLogonAndStartImage(
|
|
IN LPSERVICE_RECORD ServiceRecord,
|
|
IN LPWSTR ImageName,
|
|
OUT LPIMAGE_RECORD *ImageRecordPtr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called when the first service in an instance of an
|
|
image needs to be started.
|
|
|
|
This function creates a pipe instance for control messages and invokes
|
|
the executable image. It then waits for the new process to connect
|
|
to the control data pipe. An image Record is created with the
|
|
above information by calling CreateImageRecord.
|
|
|
|
Arguments:
|
|
|
|
ImageName - This is the name of the image file that is to be started.
|
|
This is expected to be a fully qualified path name.
|
|
|
|
ImageRecordPtr - This is a location where the pointer to the new
|
|
Image Record is returned.
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - The operation was successful. It any other return value
|
|
is returned, a pipe instance will not be created, a process will
|
|
not be started, and an image record will not be created.
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY - Unable to allocate buffer for the image record.
|
|
|
|
other - Any error returned by the following could be returned:
|
|
CreateNamedPipe
|
|
ConnectNamedPipe
|
|
CreateProcess
|
|
ScCreateControlInstance
|
|
ScLogonService
|
|
|
|
Note:
|
|
LOCKS:
|
|
The Shared Database Lock is held when this function is called.
|
|
|
|
CODEWORK: This function badly needs to use C++ destructors for safe
|
|
cleanup in error conditions.
|
|
|
|
--*/
|
|
|
|
{
|
|
PROCESS_INFORMATION processInfo;
|
|
DWORD servicePID;
|
|
HANDLE pipeHandle;
|
|
DWORD status;
|
|
BOOL runningInThisProcess=FALSE;
|
|
HANDLE ServiceToken = NULL;
|
|
HANDLE ProfileHandle = NULL;
|
|
QUOTA_LIMITS ServiceQuotas;
|
|
PSID ServiceSid = NULL;
|
|
LPWSTR AccountName = NULL; // unused ifndef _CAIRO_
|
|
|
|
//
|
|
// IMPORTANT:
|
|
// Only one thread at a time should be allowed to execute this
|
|
// code.
|
|
|
|
//
|
|
// Create an instance of the control pipe.
|
|
//
|
|
|
|
status = ScCreateControlInstance(&pipeHandle);
|
|
|
|
if (status != NO_ERROR) {
|
|
SC_LOG(ERROR,"LogonAndStartImage: CreateControlInstance Failure\n",0);
|
|
return (status);
|
|
}
|
|
//
|
|
// Release the Database lock until we have created the process and
|
|
// have connected to it.
|
|
//
|
|
ScDatabaseLock(SC_RELEASE,"Start13");
|
|
|
|
#ifdef _CAIRO_
|
|
//
|
|
// Lookup the account that the service is to be started under.
|
|
// An AccountName of NULL means the LocalSystem account.
|
|
//
|
|
status = ScLookupServiceAccount(
|
|
ServiceRecord->ServiceName,
|
|
&AccountName
|
|
);
|
|
#else
|
|
if (ServiceRecord->ServiceStatus.dwServiceType & SERVICE_WIN32_OWN_PROCESS) {
|
|
|
|
ServiceQuotas.PagedPoolLimit = 0;
|
|
|
|
//
|
|
// Get service token by logging on the service. A service token
|
|
// is returned only if the service is specified to run in an
|
|
// account other than LocalSystem.
|
|
//
|
|
status = ScLogonService(
|
|
ServiceRecord->ServiceName,
|
|
&ServiceToken,
|
|
&ProfileHandle,
|
|
&ServiceQuotas,
|
|
&ServiceSid
|
|
);
|
|
#endif
|
|
|
|
if (status != NO_ERROR) {
|
|
(void) CloseHandle(pipeHandle);
|
|
ScDatabaseLock(SC_GET_SHARED,"Start14");
|
|
return status;
|
|
}
|
|
|
|
#ifdef _CAIRO_
|
|
if (AccountName != NULL) {
|
|
#else
|
|
if (ServiceToken != (HANDLE) NULL) {
|
|
#endif
|
|
//*******************************************************************
|
|
// Start Service in an Account
|
|
//*******************************************************************
|
|
|
|
//
|
|
// A token can be created via service logon, if the service
|
|
// account name is not LocalSystem. Assign this token into
|
|
// the service process.
|
|
//
|
|
|
|
BOOLEAN WasEnabled;
|
|
NTSTATUS ntstatus, AdjustStatus;
|
|
PROCESS_ACCESS_TOKEN ProcessTokenInfo;
|
|
SECURITY_ATTRIBUTES SaProcess;
|
|
PSECURITY_DESCRIPTOR SecurityDescriptor;
|
|
HANDLE ImpersonationToken;
|
|
|
|
#define SC_PROCESSSD_ACECOUNT 2
|
|
SC_ACE_DATA AceData[SC_PROCESSSD_ACECOUNT] = {
|
|
{ACCESS_ALLOWED_ACE_TYPE, 0, 0,
|
|
PROCESS_ALL_ACCESS, 0},
|
|
|
|
{ACCESS_ALLOWED_ACE_TYPE, 0, 0,
|
|
PROCESS_SET_INFORMATION |
|
|
PROCESS_TERMINATE |
|
|
SYNCHRONIZE, &LocalSystemSid}
|
|
};
|
|
|
|
|
|
#ifdef _CAIRO_
|
|
//
|
|
// Get service token, to be assigned into the service process,
|
|
// by logging on the service
|
|
//
|
|
ServiceQuotas.PagedPoolLimit = 0;
|
|
status = ScLogonService(
|
|
ServiceRecord->ServiceName,
|
|
AccountName,
|
|
&ServiceToken,
|
|
&ProfileHandle,
|
|
&ServiceQuotas,
|
|
&ServiceSid
|
|
);
|
|
if (status != NO_ERROR) {
|
|
(void) CloseHandle(pipeHandle);
|
|
ScDatabaseLock(SC_GET_SHARED,"Start23");
|
|
LocalFree(AccountName);
|
|
return status;
|
|
}
|
|
#endif
|
|
|
|
////////////////////////////////////////////////////
|
|
//
|
|
// In here we want to start running in the context of
|
|
// the service account.
|
|
//
|
|
//
|
|
if (!DuplicateToken(
|
|
ServiceToken,
|
|
SecurityImpersonation,
|
|
&ImpersonationToken)) {
|
|
|
|
SC_LOG(ERROR,"Failed to Duplicate Token %d\n",GetLastError());
|
|
}
|
|
|
|
//
|
|
// Apply this impersonation token to our thread.
|
|
//
|
|
ntstatus = NtSetInformationThread(
|
|
GetCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
(PVOID)&ImpersonationToken,
|
|
sizeof(ImpersonationToken));
|
|
|
|
if (!NT_SUCCESS(ntstatus)) {
|
|
SC_LOG(ERROR,"LogonAndStartImage: Failed to impersonate 0x%lx\n",
|
|
ntstatus);
|
|
}
|
|
|
|
CloseHandle(ImpersonationToken);
|
|
//
|
|
//
|
|
// Now we're running in the service account.
|
|
//
|
|
//
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
|
//
|
|
// Fill pointer in AceData structure with ServiceSid we just
|
|
// got back.
|
|
//
|
|
AceData[0].Sid = &ServiceSid;
|
|
|
|
//
|
|
// Create a security descriptor for the process we are about
|
|
// to create
|
|
//
|
|
ntstatus = ScCreateAndSetSD(
|
|
AceData, // AceData
|
|
SC_PROCESSSD_ACECOUNT, // AceCount
|
|
NULL, // OwnerSid (optional)
|
|
NULL, // GroupSid (optional)
|
|
&SecurityDescriptor // pNewDescriptor
|
|
);
|
|
#undef SC_PROCESSSD_ACECOUNT
|
|
|
|
if (! NT_SUCCESS(ntstatus)) {
|
|
SC_LOG1(ERROR, "ScCreateAndSetSD failed " FORMAT_NTSTATUS
|
|
"\n", ntstatus);
|
|
// BUGBUG AnirudhS 11/30/95 -- the following are done while
|
|
// still impersonating -- and we don't remove our impersonation
|
|
// before returning -- is this really correct??
|
|
(void) UnloadUserProfile(ServiceToken, ProfileHandle);
|
|
(void) CloseHandle(pipeHandle);
|
|
(void) CloseHandle(ServiceToken);
|
|
(void) LocalFree(ServiceSid);
|
|
LocalFree(AccountName);
|
|
ScDatabaseLock(SC_GET_SHARED,"Start15");
|
|
return(RtlNtStatusToDosError(ntstatus));
|
|
}
|
|
|
|
//
|
|
// Initialize process security info
|
|
//
|
|
SaProcess.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
SaProcess.lpSecurityDescriptor = SecurityDescriptor;
|
|
SaProcess.bInheritHandle = FALSE;
|
|
|
|
//
|
|
// Set the flags that prevent the service from interacting
|
|
// with the desktop
|
|
//
|
|
ScStartupInfo.dwFlags &= (~STARTF_DESKTOPINHERIT);
|
|
ScStartupInfo.lpDesktop = NULL;
|
|
|
|
//
|
|
// Create the process in suspended mode to set the token
|
|
// into the process.
|
|
//
|
|
// Note: If someone tries to start a service in a user account
|
|
// with an image name of services.exe, a second instance of
|
|
// services.exe will start, but will exit because it won't be
|
|
// able to open the start event with write access (see
|
|
// ScGetStartEvent).
|
|
//
|
|
status = NO_ERROR;
|
|
if (!CreateProcessW (
|
|
NULL, // Fully qualified image name
|
|
ImageName, // Command Line
|
|
&SaProcess, // Process Attributes
|
|
NULL, // Thread Attributes
|
|
FALSE, // Inherit Handles
|
|
DETACHED_PROCESS |
|
|
CREATE_SUSPENDED, // Creation Flags
|
|
NULL, // Pointer to Environment block
|
|
NULL, // Pointer to Current Directory
|
|
&ScStartupInfo, // Startup Info
|
|
&processInfo)) // ProcessInformation
|
|
{
|
|
status = GetLastError();
|
|
}
|
|
|
|
(void) RtlDeleteSecurityObject(&SecurityDescriptor);
|
|
(void) LocalFree(ServiceSid);
|
|
|
|
///////////////////////////////////////////////////
|
|
//
|
|
// Remove our impersonation of the service account.
|
|
//
|
|
ImpersonationToken = NULL;
|
|
|
|
ntstatus = NtSetInformationThread(
|
|
GetCurrentThread(),
|
|
ThreadImpersonationToken,
|
|
(PVOID)&ImpersonationToken,
|
|
sizeof(ImpersonationToken));
|
|
|
|
if (!NT_SUCCESS(ntstatus)) {
|
|
SC_LOG(ERROR,"LogonAndStartImage: Failed to revert to self 0x%lx\n",
|
|
ntstatus);
|
|
}
|
|
///////////////////////////////////////////////////
|
|
|
|
if (status != NO_ERROR) {
|
|
SC_LOG2(ERROR,
|
|
"LogonAndStartImage: CreateProcess %ws failed " FORMAT_DWORD "\n",
|
|
ImageName, status);
|
|
(void) UnloadUserProfile(ServiceToken, ProfileHandle);
|
|
(void) CloseHandle(pipeHandle);
|
|
(void) CloseHandle(ServiceToken);
|
|
LocalFree(AccountName);
|
|
ScDatabaseLock(SC_GET_SHARED,"Start16");
|
|
return (status);
|
|
}
|
|
|
|
|
|
//
|
|
// Enable assign primary token privilege
|
|
//
|
|
ntstatus = RtlAdjustPrivilege(
|
|
SE_ASSIGNPRIMARYTOKEN_PRIVILEGE,
|
|
TRUE,
|
|
FALSE,
|
|
&WasEnabled
|
|
);
|
|
|
|
if (! NT_SUCCESS(ntstatus)) {
|
|
SC_LOG1(ERROR,
|
|
"LogonAndStartImage: RtlAdjustPrivilege enable "
|
|
"ASSIGNPRIMARYTOKEN failed "
|
|
FORMAT_NTSTATUS "\n", ntstatus);
|
|
status = ERROR_SERVICE_LOGON_FAILED;
|
|
goto ExitAccountError;
|
|
}
|
|
|
|
ProcessTokenInfo.Token = ServiceToken;
|
|
ProcessTokenInfo.Thread = processInfo.hThread;
|
|
|
|
ntstatus = NtSetInformationProcess(
|
|
processInfo.hProcess,
|
|
ProcessAccessToken,
|
|
(PVOID) &ProcessTokenInfo,
|
|
(ULONG) sizeof(PROCESS_ACCESS_TOKEN)
|
|
);
|
|
|
|
//
|
|
// Disable assign primary token privilege
|
|
//
|
|
AdjustStatus = RtlAdjustPrivilege(
|
|
SE_ASSIGNPRIMARYTOKEN_PRIVILEGE,
|
|
WasEnabled,
|
|
FALSE,
|
|
&WasEnabled
|
|
);
|
|
|
|
if (! NT_SUCCESS(ntstatus)) {
|
|
SC_LOG1(ERROR,
|
|
"LogonAndStartImage: NtSetInformationProcess token returned "
|
|
FORMAT_NTSTATUS "\n", ntstatus);
|
|
status = ERROR_SERVICE_LOGON_FAILED;
|
|
goto ExitAccountError;
|
|
}
|
|
|
|
if (! NT_SUCCESS(AdjustStatus)) {
|
|
//
|
|
// Failed to disable the assign primary token privilege.
|
|
// Ignore the error.
|
|
//
|
|
SC_LOG1(ACCOUNT,
|
|
"LogonAndStartImage: RtlAdjustPrivilege disable "
|
|
"ASSIGNPRIMARYTOKEN failed "
|
|
FORMAT_NTSTATUS "\n", AdjustStatus);
|
|
}
|
|
|
|
if (ServiceQuotas.PagedPoolLimit != 0) {
|
|
|
|
#ifndef _CAIRO_
|
|
//
|
|
// Be sure these are zero, since the thread hasn't yet
|
|
// run, and the system would be quite unhappy.
|
|
//
|
|
ServiceQuotas.MinimumWorkingSetSize = 0;
|
|
ServiceQuotas.MaximumWorkingSetSize = 0;
|
|
#endif
|
|
|
|
//
|
|
// Enable increase quota privilege
|
|
//
|
|
ntstatus = RtlAdjustPrivilege(
|
|
SE_INCREASE_QUOTA_PRIVILEGE,
|
|
TRUE,
|
|
FALSE,
|
|
&WasEnabled
|
|
);
|
|
|
|
if (! NT_SUCCESS(ntstatus)) {
|
|
SC_LOG1(ERROR,
|
|
"LogonAndStartImage: RtlAdjustPrivilege enable "
|
|
"INCREASE_QUOTA failed "
|
|
FORMAT_NTSTATUS "\n", ntstatus);
|
|
status = ERROR_SERVICE_LOGON_FAILED;
|
|
goto ExitAccountError;
|
|
}
|
|
|
|
ntstatus = NtSetInformationProcess(
|
|
processInfo.hProcess,
|
|
ProcessQuotaLimits,
|
|
(PVOID) &ServiceQuotas,
|
|
(ULONG) sizeof(QUOTA_LIMITS)
|
|
);
|
|
|
|
//
|
|
// Disable increase quota privilege
|
|
//
|
|
AdjustStatus = RtlAdjustPrivilege(
|
|
SE_INCREASE_QUOTA_PRIVILEGE,
|
|
WasEnabled,
|
|
FALSE,
|
|
&WasEnabled
|
|
);
|
|
|
|
if (! NT_SUCCESS(ntstatus)) {
|
|
SC_LOG1(ERROR,
|
|
"LogonAndStartImage: NtSetInformationProcess quotas returned "
|
|
FORMAT_NTSTATUS "\n", ntstatus);
|
|
status = ERROR_SERVICE_LOGON_FAILED;
|
|
goto ExitAccountError;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Let the suspended process run.
|
|
//
|
|
ResumeThread(processInfo.hThread);
|
|
|
|
SC_LOG1(ACCOUNT, "LogonAndStartImage: Service " FORMAT_LPWSTR
|
|
" was spawned to run in an account\n", ServiceRecord->ServiceName);
|
|
#ifndef _CAIRO_
|
|
}
|
|
#endif
|
|
//*******************************************************************
|
|
// End of Service In Account Stuff.
|
|
//*******************************************************************
|
|
|
|
}
|
|
|
|
#ifdef _CAIRO_
|
|
else {
|
|
#else
|
|
if (ServiceToken == (HANDLE) NULL) {
|
|
#endif
|
|
//-----------------------------------------------
|
|
// Service to run with the LocalSystem account.
|
|
//-----------------------------------------------
|
|
|
|
BOOL bServicesInteractive = FALSE;
|
|
|
|
if (_wcsicmp(ImageName,ScGlobalThisExePath)==0) {
|
|
|
|
//
|
|
// The service is to run in this image (services.exe).
|
|
// Since this is the first service to be started in this image,
|
|
// we need to start the local dispatcher.
|
|
//
|
|
|
|
HANDLE hThread;
|
|
|
|
status = SvcStartLocalDispatcher(&hThread);
|
|
if (status != NO_ERROR) {
|
|
SC_LOG1(ERROR,"LogonAndStartImage: SvcStartLocalDispatcher "
|
|
" failed %d",status);
|
|
CloseHandle(pipeHandle);
|
|
ScDatabaseLock(SC_GET_SHARED,"Start17");
|
|
return (status);
|
|
}
|
|
CloseHandle(hThread);
|
|
processInfo.hProcess = NULL;
|
|
processInfo.dwProcessId = 0;
|
|
runningInThisProcess = TRUE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The service is to run in some other image.
|
|
//
|
|
// If the service is to run interactively, check the flag in the
|
|
// registry to see if this system is to allow interactive services.
|
|
// REG KEY = HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\-
|
|
// Windows\NoInteractiveServices.
|
|
//
|
|
if (ServiceRecord->ServiceStatus.dwServiceType & SERVICE_INTERACTIVE_PROCESS) {
|
|
|
|
HKEY WindowsKey=NULL;
|
|
DWORD NoInteractiveFlag=0;
|
|
|
|
bServicesInteractive = TRUE;
|
|
|
|
status = ScRegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
CONTROL_WINDOWS_KEY_W,
|
|
REG_OPTION_NON_VOLATILE, // options
|
|
KEY_READ, // desired access
|
|
&WindowsKey
|
|
);
|
|
|
|
if (status != ERROR_SUCCESS) {
|
|
SC_LOG1(TRACE,
|
|
"LogonAndStartImage: ScRegOpenKeyExW of Control\\Windows failed "
|
|
FORMAT_LONG "\n", status);
|
|
}
|
|
else {
|
|
status = ScReadNoInteractiveFlag(WindowsKey,&NoInteractiveFlag);
|
|
if ((status == ERROR_SUCCESS) && (NoInteractiveFlag != 0)) {
|
|
|
|
LPWSTR SubStrings[1];
|
|
|
|
bServicesInteractive = FALSE;
|
|
//
|
|
// Write an event to indicate that an interactive service
|
|
// was started, but the system is configured to not allow
|
|
// services to be interactive.
|
|
//
|
|
SubStrings[0] = ServiceRecord->DisplayName;
|
|
ScLogEvent(
|
|
EVENT_SERVICE_NOT_INTERACTIVE,
|
|
1,
|
|
SubStrings);
|
|
}
|
|
|
|
ScRegCloseKey(WindowsKey);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the process is to be interactive, set the appropriate flags.
|
|
//
|
|
if (bServicesInteractive) {
|
|
ScStartupInfo.dwFlags |= STARTF_DESKTOPINHERIT;
|
|
ScStartupInfo.lpDesktop = pszInteractiveDesktop;
|
|
}
|
|
else {
|
|
ScStartupInfo.dwFlags &= (~STARTF_DESKTOPINHERIT);
|
|
ScStartupInfo.lpDesktop = NULL;
|
|
}
|
|
|
|
//
|
|
// Spawn the Image Process
|
|
//
|
|
|
|
SC_LOG0(TRACE,"LogonAndStartImage: about to spawn a Service Process\n");
|
|
|
|
if (!CreateProcessW (
|
|
NULL, // Fully qualified image name
|
|
ImageName, // Command Line
|
|
NULL, // Process Attributes
|
|
NULL, // Thread Attributes
|
|
FALSE, // Inherit Handles
|
|
DETACHED_PROCESS, // Creation Flags
|
|
NULL, // Pointer to Environment block
|
|
NULL, // Pointer to Current Directory
|
|
&ScStartupInfo, // Startup Info
|
|
&processInfo) // ProcessInformation
|
|
) {
|
|
|
|
status = GetLastError();
|
|
SC_LOG2(ERROR,
|
|
"LogonAndStartImage: CreateProcess %ws failed %d \n",
|
|
ImageName,
|
|
status);
|
|
|
|
CloseHandle(pipeHandle);
|
|
ScDatabaseLock(SC_GET_SHARED,"Start18");
|
|
return (status);
|
|
}
|
|
|
|
SC_LOG1(ACCOUNT, "LogonAndStartImage: Service " FORMAT_LPWSTR
|
|
" was spawned to run as LocalSystem\n", ServiceRecord->ServiceName);
|
|
}
|
|
//-----------------------------------------------
|
|
// End of LocalSystem account stuff
|
|
//-----------------------------------------------
|
|
}
|
|
|
|
status = ScWaitForConnect(pipeHandle,&servicePID);
|
|
ScDatabaseLock(SC_GET_SHARED,"Start19");
|
|
|
|
//
|
|
// BUGBUG: Write some code to check the servicePID against what we
|
|
// think it should be. If not the same, then what???
|
|
//
|
|
|
|
if (status != NO_ERROR) {
|
|
|
|
SC_LOG0(TRACE,
|
|
"LogonAndStartImage: Failed to connect to pipe - Terminating the Process...\n");
|
|
CloseHandle(pipeHandle);
|
|
TerminateProcess(processInfo.hProcess,0);
|
|
CloseHandle(processInfo.hProcess);
|
|
CloseHandle(processInfo.hThread);
|
|
UnloadUserProfile(ServiceToken, ProfileHandle);
|
|
if (ServiceToken != NULL) {
|
|
CloseHandle(ServiceToken);
|
|
}
|
|
LocalFree(AccountName);
|
|
return(status);
|
|
}
|
|
|
|
status = ScCreateImageRecord (
|
|
ImageRecordPtr,
|
|
ImageName,
|
|
#ifdef _CAIRO_
|
|
AccountName,
|
|
#endif
|
|
processInfo.dwProcessId,
|
|
pipeHandle,
|
|
processInfo.hProcess,
|
|
ServiceToken,
|
|
ProfileHandle);
|
|
|
|
if (status != NO_ERROR) {
|
|
SC_LOG0(TRACE,
|
|
"LogonAndStartImage: Failed to create imageRecord - Terminating the Process...\n",);
|
|
CloseHandle(pipeHandle);
|
|
TerminateProcess(processInfo.hProcess,0);
|
|
CloseHandle(processInfo.hProcess);
|
|
CloseHandle(processInfo.hThread);
|
|
if (ProfileHandle != NULL) {
|
|
(void) UnloadUserProfile(ServiceToken, ProfileHandle);
|
|
}
|
|
if (ServiceToken != NULL) {
|
|
CloseHandle(ServiceToken);
|
|
}
|
|
LocalFree(AccountName);
|
|
return(status);
|
|
}
|
|
|
|
//
|
|
// If the dispatcher is running in this process, then we want to
|
|
// increment the service count an extra time so that the dispatcher
|
|
// never goes away. Also, we don't really have a process handle for
|
|
// the watcher to wait on.
|
|
// It could wait on the ThreadHandle, but handling that when it becomes
|
|
// signaled becomes a special case. So we won't try that.
|
|
//
|
|
if (runningInThisProcess) {
|
|
(*ImageRecordPtr)->ServiceCount = 1;
|
|
}
|
|
else {
|
|
HANDLE hWaitObject=NULL;
|
|
|
|
CloseHandle(processInfo.hThread);
|
|
//
|
|
// Add the process handle to the ObjectWatcher list of waitable
|
|
// objects.
|
|
//
|
|
|
|
//
|
|
// Add the process handle as a handle to wait on.
|
|
// Retain the WaitObject handle for when we need shutdown the
|
|
// process because all the services stopped.
|
|
//
|
|
hWaitObject = SvcAddWorkItem(
|
|
processInfo.hProcess,
|
|
ScProcessHandleIsSignaled,
|
|
processInfo.hProcess,
|
|
SVC_QUEUE_WORK_ITEM,
|
|
INFINITE,
|
|
NULL);
|
|
|
|
(*ImageRecordPtr)->ObjectWaitHandle = hWaitObject;
|
|
}
|
|
LocalFree(AccountName);
|
|
return(NO_ERROR);
|
|
|
|
ExitAccountError:
|
|
ScDatabaseLock(SC_GET_SHARED,"Start20");
|
|
(void) CloseHandle(pipeHandle);
|
|
(void) UnloadUserProfile(ServiceToken, ProfileHandle);
|
|
(void) CloseHandle(ServiceToken);
|
|
(void) CloseHandle(processInfo.hProcess);
|
|
(void) CloseHandle(processInfo.hThread);
|
|
LocalFree(AccountName);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
#ifdef _CAIRO_
|
|
|
|
BOOL
|
|
ScEqualAccountName(
|
|
IN LPWSTR Account1,
|
|
IN LPWSTR Account2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function compares two account names, either of which may be NULL,
|
|
for equality.
|
|
|
|
Arguments:
|
|
|
|
Account1, Account2 - account names to be compared
|
|
|
|
Return Value:
|
|
|
|
TRUE - names are equal
|
|
|
|
FALSE - names are not equal
|
|
|
|
--*/
|
|
{
|
|
if (Account1 == NULL && Account2 == NULL)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (Account1 == NULL || Account2 == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
return (_wcsicmp(Account1, Account2) == 0);
|
|
}
|
|
|
|
#endif // _CAIRO_
|
|
|
|
|
|
VOID
|
|
ScInitStartImage(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes the Critical Section that protects
|
|
entry into the ScStartImage Routine.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
InitializeCriticalSection(&ScStartImageCriticalSection);
|
|
|
|
ScStartupInfo.cb = sizeof(STARTUPINFOW); // size
|
|
ScStartupInfo.lpReserved = NULL; // lpReserved
|
|
ScStartupInfo.lpDesktop = NULL; // DeskTop
|
|
ScStartupInfo.lpTitle = NULL; // Title
|
|
ScStartupInfo.dwX = 0; // X (position)
|
|
ScStartupInfo.dwY = 0; // Y (position)
|
|
ScStartupInfo.dwXSize = 0; // XSize (dimension)
|
|
ScStartupInfo.dwYSize = 0; // YSize (dimension)
|
|
ScStartupInfo.dwXCountChars = 0; // XCountChars
|
|
ScStartupInfo.dwYCountChars = 0; // YCountChars
|
|
ScStartupInfo.dwFillAttribute = 0; // FillAttributes
|
|
ScStartupInfo.dwFlags = STARTF_FORCEOFFFEEDBACK;
|
|
// Flags - should be STARTF_TASKNOTCLOSABLE
|
|
ScStartupInfo.wShowWindow = SW_HIDE; // ShowWindow
|
|
ScStartupInfo.cbReserved2 = 0L; // cbReserved
|
|
ScStartupInfo.lpReserved2 = NULL; // lpReserved
|
|
}
|
|
|
|
|
|
VOID
|
|
ScEndStartImage(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function deletes the Critical Section that protects
|
|
entry into the ScStartImage Routine.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
DeleteCriticalSection(&ScStartImageCriticalSection);
|
|
}
|
|
|
|
DWORD
|
|
ScProcessHandleIsSignaled(
|
|
PVOID pContext,
|
|
DWORD dwWaitStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
pContext - This is the process handle.
|
|
dwWaitStatus - This is the status from the wait (WaitForSingleObject)
|
|
on the process handle.
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
if (dwWaitStatus != WAIT_OBJECT_0) {
|
|
SC_LOG1(ERROR,"ScProcessCleanup received bad WaitStatus %d\n",
|
|
dwWaitStatus);
|
|
return(0);
|
|
}
|
|
|
|
SC_LOG1(THREADS,"Process Handle is signaled 0x%lx\n",pContext);
|
|
|
|
ScNotifyChangeState();
|
|
ScProcessCleanup((HANDLE)pContext);
|
|
return(0);
|
|
}
|
|
|