Almost every operating system has a mechanism to start processes at system startup time that provide services not tied to an interactive user. In Windows 2000, such processes are called services, or Win32 services, because they rely on the Win32 API to interact with the system. Services are similar to UNIX daemon processes and often implement the server side of client/server applications. An example of a Win32 service might be a Web server since it must be running regardless of whether anyone is logged on to the computer and it must start running when the system starts so that an administrator doesn't have to remember, or even be present, to start it.
Win32 services consist of three components: a service application, a service control program (SCP), and the service control manager (SCM). First, we'll describe service applications, service accounts, and the operations of the SCM. Then we'll explain how auto-start services are started during the system boot. We'll also cover the steps the SCM takes when a service fails during its startup and the way the SCM shuts down services.
Service applications, such as Web servers, consist of at least one executable that runs as a Win32 service. A user wanting to start, stop, or configure a service uses an SCP. Although Windows 2000 supplies built-in SCPs that provide general start, stop, pause, and continue functionality, some service applications include their own SCP that allows administrators to specify configuration settings particular to the service they manage.
Service applications are simply Win32 executables (GUI or console) with additional code to receive commands from the SCM as well as to communicate the application's status back to the SCM. Because most services don't have a user interface, they are built as console programs.
When you install an application that includes a service, the application's setup program must register the service with the system. To register the service, the setup program calls the Win32 CreateService function, a services-related function implemented in Advapi32.dll (\Winnt\System32\Advapi32.dll). Advapi32, the "Advanced API" DLL, implements all the client-side SCM APIs.
When a setup program registers a service by calling CreateService, a message is sent to the SCM on the machine where the service will reside. The SCM then creates a registry key for the service under HKLM\SYSTEM\CurrentControlSet\Services. The Services key is the nonvolatile representation of the SCM's database. The individual keys for each service define the path of the executable image that contains the service as well as parameters and configuration options.
After creating a service, an installation or management application can start the service via the StartService function. Because some service-based applications also must initialize during the boot process to function, it's not unusual for a setup program to register a service as an auto-start service, ask the user to reboot the system to complete an installation, and let the SCM start the service as the system boots.
When a program calls CreateService, it must specify a number of parameters describing the service's characteristics. The characteristics include the service's type (whether it's a service that runs in its own process rather than a service that shares a process with other services), the location of the service's executable image file, an optional display name, an optional account name and password used to start the service in a particular account's security context, a start type that indicates whether the service starts automatically when the system boots or manually under the direction of an SCP, an error code that indicates how the system should react if the service detects an error when starting, and, if the service starts automatically, optional information that specifies when the service starts relative to other services.
The SCM stores each characteristic as a value in the service's registry key. Figure 5-5 shows an example of a service registry key.
Figure 5-5 Example of a service registry key
Table 5-6 lists all the service characteristics. (Not every characteristic applies to every type of service.) If a service needs to store configuration information that is private to the service, the convention is to create a subkey named Parameters under its service key and then store the configuration information in values under that subkey. The service then can retrieve the values by using standard registry functions.
Table 5-6 Service and Driver Registry Parameters
|Value Name||Value Setting||Value Setting Description|
|Start||SERVICE_BOOT_START (0)||Ntldr or Osloader preloads the driver so that it is in memory during the boot. These drivers are initialized just prior to SERVICE_SYSTEM_START drivers.|
|SERVICE_SYSTEM_START (1)||The driver loads and initializes after SERVICE_BOOT_START drivers have initialized.|
|SERVICE_AUTO_START (2)||The SCM starts the driver or service.|
|SERVICE_DEMAND_START (3)||The SCM must start the driver or service on demand.|
|SERVICE_DISABLED (4)||The driver or service doesn't load or initialize.|
|ErrorControl||SERVICE_ERROR_IGNORE (0)||The I/O manager ignores errors the driver returns. No warning is logged or displayed.|
|SERVICE_ERROR_NORMAL (1)||If the driver reports an error, a warning displays.|
|SERVICE_ERROR_SEVERE (2)||If the driver returns an error and last known good isn't being used, reboot into last known good; otherwise, continue the boot.|
|SERVICE_ERROR_CRITICAL (3)||If the driver returns an error and last known good isn't being used, reboot into last known good; otherwise, stop the boot with a blue screen crash.|
|Type||SERVICE_KERNEL_DRIVER (1)||Device driver.|
|SERVICE_FILE_SYSTEM_DRIVER (2)||Kernel-mode file system driver.|
|SERVICE_RECOGNIZER_DRIVER (8)||File system recognizer driver.|
|SERVICE_WIN32_OWN_PROCESS (16)||The service runs in a process that hosts only one service.|
|SERVICE_WIN32_SHARE_PROCESS (32)||The service runs in a process that hosts multiple services.|
|SERVICE_INTERACTIVE_PROCESS (256)||The service is allowed to display windows on the console and receive user input.|
|Group||Group name||The driver or service initializes when its group is initialized.|
|Tag||Tag number||The specified location in a group initialization order. This parameter doesn't apply to services.|
|ImagePath||Path to service or driver executable file||If ImagePath isn't specified, the I/O manager looks for drivers in \Winnt\System32\Drivers and the SCM looks for services in \Winnt\ System32.|
|DependOnGroup||Group name||The driver or service won't load unless a driver or service from the specified group loads.|
|DependOnService||Service name||The service won't load until after the specified service loads. This parameter doesn't apply to device drivers.|
|ObjectName||Usually LocalSystem but can be an account name, such as .\Administrator||Specifies the account in which the service will run. If ObjectName isn'ts pecified, LocalSystem is the account used. This parameter doesn't apply to device drivers.|
|DisplayName||Name of service||The service application shows services by this name. If no name is specified, the name of the service's registry key becomes its name.|
|Description||Description of service||Up to 1024-byte description of the service.|
|FailureActions||Description of actions the SCM should take when service process exits unexpectedly||Failure actions include restarting the service process, rebooting the system, and running a specified program. This value doesn't apply to drivers.|
|FailureCommand||Program command line||The SCM reads this value only if FailureActions specifies that a program should execute upon service failure. This value doesn't apply to drivers.|
|Security||Security descriptor||This value contains the security descriptor that defines who has what access to the service.|
Notice that Type values include three that apply to device drivers: device driver, file system driver, and file system recognizer. These are used by Windows 2000 device drivers, which also store their parameters as registry data in the Services registry key. The SCM is responsible for starting drivers with a Start value of SERVICE_AUTO_START, so it's natural for the SCM database to include drivers. Services use the other types, SERVICE_WIN32_OWN_PROCESS and SERVICE_WIN32_SHARE_PROCESS, which are mutually exclusive. An executable that hosts more than one service specifies the SERVICE_WIN32_SHARE_PROCESS type. An advantage to having a process run more than one service is that the system resources that would otherwise be required to run them in distinct processes are saved. A potential disadvantage is that if one of the services of a collection running in the same process causes an error that terminates the process, all the services of that process terminate.
When the SCM starts a service process, the process immediately invokes the StartServiceCtrlDispatcher function. StartServiceCtrlDispatcher accepts a list of entry points into services, one entry point for each service in the process. Each entry point is identified by the name of the service the entry point corresponds to. After making a named pipe communications connection to the SCM, StartServiceCtrlDispatcher sits in a loop waiting for commands to come through the pipe from the SCM. The SCM sends a service-start command each time it starts a service the process owns. For each start command it receives, the StartServiceCtrlDispatcher function creates a thread, called a service thread, to invoke the starting service's entry point and implement the command loop for the service. StartServiceCtrlDispatcher waits indefinitely for commands from the SCM and returns control to the process's main function only when all the process's service threads have terminated, allowing the service process to clean up resources before exiting.
A service entry point's first action is to call the RegisterServiceCtrlHandler function. This function receives and stores a table of functions that the service implements to handle various commands it receives from the SCM. RegisterServiceCtrlHandler doesn't communicate with the SCM, but it stores the table in local process memory for the StartServiceCtrlDispatcher function. The service entry point continues initializing the service, which can include allocating memory, creating communications end points, and reading private configuration data from the registry. A convention most services follow is to store their parameters under a subkey of their service registry key, named Parameters. While the entry point is initializing the service, it might periodically send status messages to the SCM indicating how the service's startup is progressing. After the entry point finishes initialization, a service thread usually sits in a loop waiting for requests from client applications. For example, a Web server would initialize a TCP listen socket and wait for inbound HTTP connection requests.
A service process's main thread, which executes in the StartServiceCtrlDispatcher function, receives SCM commands directed at services in the process and uses the service's table of handler functions (stored by RegisterServiceCtrlHandler) to locate and invoke the service function responsible for responding to a command. SCM commands include stop, pause, resume, interrogate, and shutdown, or application-defined commands. Figure 5-6 shows the internal organization of a service process. Pictured are the two threads that make up a process hosting one service: the main thread and the service thread.
Figure 5-6 Inside a service process
If you have a program that you want to run as a service, you need to modify the startup code to conform to the requirements for services outlined in this section. If you don't have the source code, you can use the SrvAny tool in the Windows 2000 resource kits. SrvAny enables you to run any application as a service. It reads the path of the service file that it must load from the Parameters subkey of the service's registry key. When SrvAny starts, it notifies the SCM that it is hosting a particular service, and when it receives a start command, it launches the service executable as a child process. The child process receives a copy of the SrvAny process's access token and a reference to the same window station, so the executable runs within the same security account and with the same interactivity setting as you specified when configuring the SrvAny process. SrvAny services don't have the share-process Type value, so each application you install as a service with SrvAny runs in a separate process with a different instance of the SrvAny host program.
The security context of a service is an important consideration for service developers as well as for system administrators because it dictates what resources the process can access. Unless a service installation program or administrator specifies otherwise, services run in the security context of the local system account (displayed sometimes as SYSTEM and other times as LocalSystem). The following two subsections describe the special characteristics of this account.
The local system account is the same account in which all the Windows 2000 user-mode operating system components run, including the Session Manager (\Winnt\System32\Smss.exe), the Win32 subsystem process (Csrss.exe), the local security authority subsystem (\Winnt\System32\Lsass.exe), and the Winlogon process (\Winnt\System32\Winlogon.exe).
From a security perspective, the local system account is extremely powerful—more powerful than any local or domain account when it comes to security ability on a local system. This account has the following characteristics:
Because of the restrictions just outlined, some services need to run with the security credentials of a user account. You can configure a service to run in an alternate account when the service is created or by specifying an account and password that the service should run under with the Windows 2000 Services MMC snap-in. In the Services snap-in, right-click on a service and select Properties, click the Log On tab, and select the This Account option, as shown in Figure 5-7.
Figure 5-7 Service account settings
Another restriction for services running under the local system account is that they can't (without using a special flag on the MessageBox function, discussed in a moment) display dialog boxes or windows on the interactive user's desktop. This limitation isn't the direct result of running under the local system account but rather a consequence of the way the service controller assigns service processes to window stations.
The Win32 subsystem associates every Win32 process with a window station. A window station contains desktops and desktops contain windows. Only one window station can be visible on a console and receive user mouse and keyboard input. In a Terminal Services environment, one window station per session is visible, but services all run as part of the console session. Win32 names the visible window station WinSta0, and all interactive processes access WinSta0.
Unless otherwise directed, the SCM associates services with a nonvisible window station named Service-0x0-3e7$ that all noninteractive services share. The number in the name, 3e7, represents the logon session identifier Lsass assigns to the logon session the SCM uses for noninteractive services running in the local system account.
Services configured to run under a user account (that is, not the local system account) are run in a different nonvisible window station named with the Lsass logon identifier assigned for the service's logon session. Figure 5-8 shows an example display from \Sysint\Winobj (which you'll find on this book's companion CD) viewing the object manager directory in which Win32 places window station objects. Visible are the interactive window station (WinSta0), the noninteractive system service window station (Service-0x0-3e7$), and a noninteractive window station assigned to a service process logged on as a user (Service-0x0-6368f$).
Regardless of whether services are running in a user account or in the local system account, services that aren't running on the visible window station can't receive input from a user or display windows on the console. In fact, if a service were to pop up a normal dialog box on the window station, the service would appear hung because no user would be able to see the dialog box, which of course would prevent the user from providing keyboard or mouse input to dismiss it and allow the service to continue executing. (The one exception is if the special MB_SERVICE_NOTIFICATION or MB_DEFAULT_DESKTOP_ONLY flag is set on the MessageBox call—if MB_SERVICE_NOTIFICATION is specified, the message box will always be displayed on the interactive window station, even if the service wasn't configured with permission to interact with the user; if MB_DEFAULT_DESKTOP_ONLY is specified, the message box is displayed on the default desktop of the interactive window station.)
Figure 5-8 List of window stations
Although rare, some services can have a valid reason to interact with the user via dialog boxes or windows. An example of a built-in Windows 2000 service that has this requirement is the Windows Installer—the interactive user needs to see messages relating to on-demand software installation. To configure a service with the right to interact with the user, the SERVICE_INTERACTIVE_PROCESS modifier must be present in the service's registry key's Type parameter. (Note that services configured to run under a user account can't be marked as interactive.) When the SCM starts a service marked as interactive, it launches the service's process in the local system account's security context but connects the service with WinSta0 instead of the noninteractive service window station. This connection to WinSta0 allows the service to display dialog boxes and windows on the console and allows those windows to respond to user input.
The SCM's executable file is \Winnt\System32\Services.exe, and like most service processes, it runs as a Win32 console program. The Winlogon process starts the SCM early during the system boot. (Refer to Chapter 4 for details on the boot process.) The SCM's startup function, SvcCtrlMain, orchestrates the launching of services that are configured for automatic startup. SvcCtrlMain executes shortly after the screen switches to a blank desktop but generally before Winlogon has loaded the graphical identification and authentication interface (GINA) that presents a logon dialog box.
SvcCtrlMain first creates a synchronization event named SvcCtrlEvent_A3752DX that it initializes as nonsignaled. Only after the SCM completes steps necessary to prepare it to receive commands from SCPs does the SCM set the event to a signaled state. The function that an SCP uses to establish a dialog with the SCM is OpenSCManager. OpenSCManager prevents an SCP from trying to contact the SCM before the SCM has initialized by waiting for SvcCtrlEvent_A3752DX to become signaled.
Next, SvcCtrlMain gets down to business and calls ScCreateServiceDB, the function that builds the SCM's internal service database. ScCreateServiceDB reads and stores the contents of HKLM\SYSTEM\CurrentControlSet\Control\ServiceGroupOrder\List, a REG_MULTI_SZ value that lists the names and order of the defined service groups. A service's registry key contains an optional Group value if that service or device driver needs to control its startup ordering with respect to services from other groups. For example, the Windows 2000 networking stack is built from the bottom up, so networking services must specify Group values that place them later in the startup sequence than networking device drivers. SCM internally creates a group list that preserves the ordering of the groups it reads from the registry. Groups include (but are not limited to) NDIS, TDI, Primary Disk, Keyboard Port, and Keyboard Class. Add-on and third-party applications can even define their own groups and add them to the list. Microsoft Transaction Server, for example, adds a group named MS Transactions.
ScCreateServiceDB then scans the contents of HKLM\SYSTEM\CurrentControlSet\Services, creating an entry in the service database for each key it encounters. A database entry includes all the service-related parameters defined for a service as well as fields that track the service's status. The SCM adds entries for device drivers as well as for services because the SCM starts services and drivers marked as auto-start and detects startup failures for drivers marked boot-start and system-start. The I/O manager loads drivers marked boot-start and system-start before any user-mode processes execute, and therefore any drivers having these start types load before the SCM starts.
ScCreateServiceDB reads a service's Group value to determine its membership in a group and associates this value with the group's entry in the group list created earlier. The function also reads and records in the database the service's group and service dependencies by querying its DependOnGroup and DependOnService registry values. Figure 5-9 shows how the SCM organizes the service entry and group order lists. Notice that the service list is alphabetically sorted. The reason this list is sorted alphabetically is that the SCM creates the list from the Services registry key, and Windows 2000 stores registry keys alphabetically.
Figure 5-9 Organization of a service database
During service startup, the SCM might need to call on Lsass (for example, to log on a service in a user account), so the SCM waits for Lsass to signal the LSA_RPC_SERVER_ACTIVE synchronization event, which it does when it finishes initializing. Winlogon also starts the Lsass process, so the initialization of Lsass is concurrent with that of the SCM, and the order in which Lsass and the SCM complete initialization can vary. Then SvcCtrlMain calls ScGetBootAndSystemDriverState to scan the service database looking for boot-start and system-start device driver entries. ScGetBootAndSystemDriverState determines whether or not a driver successfully started by looking up its name in the object manager namespace directory named \Driver. When a device driver successfully loads, the I/O manager inserts the driver's object in the namespace under this directory, so if its name isn't present, it hasn't loaded. Figure 5-10 shows Winobj displaying the contents of the Driver directory. If a driver isn't loaded, the SCM looks for its name in the list of drivers returned by the PnP_DeviceList function. PnP_DeviceList supplies the drivers included in the system's current hardware profile. SvcCtrlMain notes the names of drivers that haven't started and that are part of the current profile in a list named ScFailedDrivers.
Before starting the auto-start services, the SCM performs a few more steps. It creates its remote procedure call (RPC) named pipe, which is named \Pipe\Ntsvcs, and then launches a thread to listen on the pipe for incoming messages from SCPs. It then signals its initialization-complete event, SvcCtrlEvent_A3752DX. Registering a console application shutdown event handler and registering with the Win32 subsystem process via RegisterServiceProcess prepares the SCM for system shutdown.
Figure 5-10 List of driver objects
Network Drive Letters
In addition to its role as an interface to services, the SCM has another totally unrelated responsibility: it notifies GUI applications in a system whenever the system creates or deletes a network drive-letter connection. The SCM waits for the LAN Manager workstation service to signal a named event, ScNetDrvMsg, which the workstation service signals whenever an application assigns a drive letter to a remote network share or deletes a remote-share drive-letter assignment. When the workstation service signals the event, the SCM calls the GetDriveType Win32 function to query the list of connected network drive letters. If the list changes across the event signal, the SCM sends a Windows broadcast message of type WM_DEVICECHANGE. The SCM uses either DBT_DEVICEREMOVECOMPELTE or DBT_DEVICEARRIVAL as the message's subtype. This message is primarily intended for Windows Explorer so that it can update any open My Computer windows to show the presence or absence of a network drive letter.
SvcCtrlMain invokes the SCM function ScAutoStartServices to start all services that have a Start value designating auto-start. ScAutoStartServices also starts auto-start device drivers. To avoid confusion, you should assume that the term services means services and drivers unless indicated otherwise. The algorithm in ScAutoStartServices for starting services in the correct order proceeds in phases, whereby a phase corresponds to a group and phases proceed in the sequence defined by the group ordering stored in the HKLM\SYSTEM\CurrentControlSet\Control\ServiceGroupOrder\List registry value. The List value, shown in Figure 5-11, includes the names of groups in the order that the SCM should start them. Thus, assigning a service to a group has no effect other than to fine-tune its startup with respect to other services belonging to different groups.
Figure 5-11 ServiceGroupOrder registry key
When a phase starts, ScAutoStartServices marks all the service entries belonging to the phase's group for startup. Then ScAutoStartServices loops through the marked services seeing whether it can start each one. Part of the check it makes consists of determining whether the service has a dependency on another group, as specified by the existence of the DependOnGroup value in the service's registry key. If a dependency exists, the group on which the service is dependent must have already initialized, and at least one service of that group must have successfully started. If the service depends on a group that starts later than the service's group in the group startup sequence, the SCM notes a "circular dependency" error for the service. If ScAutoStartServices is considering a Win32 service and not a device driver, it next checks to see whether the service depends on one or more other services, and if so, if those services have already started. Service dependencies are indicated with the DependOnService registry value in a service's registry key. If a service depends on other services that belong to groups that come later in the ServiceGroupOrder\List, the SCM also generates a "circular dependency" error and doesn't start the service. If the service depends on any services from the same group that haven't yet started, the service is skipped.
When the dependencies of a service have been satisfied, ScAutoStartServices makes a final check to see whether the service is part of the current boot configuration before starting the service. When the system is booted in safe mode, the SCM ensures that the service is either identified by name or by group in the appropriate safe boot registry key. There are two safe boot keys, Minimal and Network, under HKLM\SYSTEM\CurrentControlSet\Control\SafeBoot, and the one that the SCM checks depends on what safe mode the user booted. If the user chose Safe Mode or Safe Mode With Command Prompt at the special boot menu (which you can access by pressing F8 when prompted in the boot process), the SCM references the Minimal key; if the user chose Safe Mode With Networking, the SCM refers to Network. The existence of a string value named Option under the SafeBoot key indicates not only that the system booted in safe mode but also the type of safe mode the user selected. For more information about safe boots, see the section "Safe Mode" in Chapter 4.
Once the SCM decides to start a service, it calls ScStartService, which takes different steps for services than for device drivers. When ScStartService starts a Win32 service, it first determines the name of the file that runs the service's process by reading the ImagePath value from the service's registry key. It then examines the service's Type value, and if that value is SERVICE_WIN32_SHARE_PROCESS (0x20), the SCM ensures that the process the service runs in, if already started, is logged on using the same account as specified for the service being started. A service's ObjectName registry value stores the user account in which the service should run. A service with no ObjectName or an ObjectName of LocalSystem runs in the local system account.
The SCM verifies that the service's process hasn't already been started in a different account by checking to see whether the service's ImagePath value has an entry in an internal SCM database called the image database. If the image database doesn't have an entry for the ImagePath value, the SCM creates one. When the SCM creates a new entry, it stores the logon account name used for the service and the data from the service's ImagePath value. The SCM requires services to have an ImagePath value. If a service doesn't have an ImagePath value, the SCM reports an error stating that it couldn't find the service's path and isn't able to start the service. If the SCM locates an existing image database entry with matching ImagePath data, the SCM ensures that the user account information for the service it's starting is the same as the information stored in the database entry—a process can be logged on as only one account, so the SCM reports an error when a service specifies a different account name than another service that has already started in the same process.
The SCM calls ScLogonAndStartImage to log on a service if the service's configuration specifies and to start the service's process. The SCM logs on services that don't run in the system account by calling the Lsass function LsaLogonUser. LsaLogonUser normally requires a password, but the SCM indicates to Lsass that the password is stored as a service's Lsass "secret" under the key HKLM\SECURITY\Policy\Secrets in the registry. (Keep in mind that the contents of the SECURITY aren't typically visible because its default security settings permit access only from the system account.) When the SCM calls LsaLogonUser, it specifies a service logon as the logon type, so Lsass looks up the password in the Secrets subkey that has a name in the form _SC_<service name>. The SCM directs Lsass to store a logon password as a secret when an SCP configures a service's logon information. When a logon is successful, LsaLogonUser returns a handle to an access token to the caller. Windows 2000 uses access tokens to represent a user's security context, and the SCM later associates the access token with the process that implements the service.
After a successful logon, the SCM loads the account's profile information, if it's not already loaded, by calling the UserEnv DLL's (\Winnt\System32\Userenv.dll) LoadUserProfile function. The value HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\<user profile key>\ProfileImagePath contains the location on disk of a registry hive that LoadUserProfile loads into the registry, making the information in the hive the HKEY_CURRENT_USER key for the service.
An interactive service must open the WinSta0 window station, but before ScLogonAndStartImage allows an interactive service to access WinSta0 it checks to see whether the value HKLM\SYSTEM\CurrentControlSet\Control\Windows\NoInteractiveServices is set. Administrators set this value to prevent services marked as interactive from displaying windows on the console. This option is desirable in unattended server environments in which no user is present to respond to popups from interactive services.
As its next step, ScLogonAndStartImage proceeds to launch the service's process, if the process hasn't already been started (for another service, for example). The SCM starts the process in a suspended state with the CreateProcessAsUser Win32 function. The SCM next creates a named pipe through which it communicates with the service process, and it assigns the pipe the name \Pipe\Net\NetControlPipeX, where X is a number that increments each time the SCM creates a pipe. The SCM resumes the service process via the ResumeThread function and waits for the service to connect to its SCM pipe. If it exists, the registry value HKLM\SYSTEM\CurrentControlSet\Control\ServicesPipeTimeout determines the length of time that the SCM waits for a service to call StartServiceCtrlDispatcher and connect before it gives up, terminates the process, and concludes that the service failed to start. If ServicesPipeTimeout doesn't exist, the SCM uses a default timeout of 30 seconds. The SCM uses the same timeout value for all its service communications.
When a service connects to the SCM through the pipe, the SCM sends the service a start command. If the service fails to respond positively to the start command within the timeout period, the SCM gives up and moves on to start the next service. When a service doesn't respond to a start request, the SCM doesn't terminate the process, as it does when a service doesn't call StartServiceCtrlDispatcher within the timeout; instead, it notes an error in the system Event Log that indicates the service failed to start in a timely manner.
If the service the SCM starts with a call to ScStartService has a Type registry value of SERVICE_KERNEL_DRIVER or SERVICE_FILE_SYSTEM_DRIVER, the service is really a device driver, and so ScStartService calls ScLoadDeviceDriver to load the driver. ScLoadDeviceDriver enables the load driver security privilege for the SCM process and then invokes the kernel service NtLoadDriver, passing in the data in the ImagePath value of the driver's registry key. Unlike services, drivers don't need to specify an ImagePath value, and if the value is absent, the SCM builds an image path by appending the driver's name to the string \Winnt\System32\Drivers\.
ScAutoStartServices continues looping through the services belonging to a group until all the services have either started or generated dependency errors. This looping is the SCM's way of automatically ordering services within a group according to their DependOnService dependencies. The SCM will start the services that other services depend on in earlier loops, skipping the dependent services until subsequent loops. Note that the SCM ignores Tag values for Win32 services, which you might come across in subkeys under the HKLM\SYSTEM\CurrentControlSet\Services key; the I/O manager honors Tag values to order device driver startup for boot and system-start drivers.
Once the SCM completes phases for all the groups listed in the ServiceGroupOrder\List value, it performs a phase for services belonging to groups not listed in the value and a final phase for services without a group.
If a driver or a service reports an error in response to the SCM's startup command, the ErrorControl value of the service's registry key determines how the SCM reacts. If the ErrorControl value is SERVICE_ERROR_IGNORE (0) or the ErrorControl value isn't specified, the SCM simply ignores the error and continues processing service startups. If the ErrorControl value is SERVICE_ERROR_NORMAL (1), the SCM writes an event to the system Event Log that says, "The <service name> service failed to start due to the following error:" The SCM includes the textual representation of the Win32 error code that the service returned to the SCM as the reason for the startup failure in the Event Log record. Figure 5-12 shows the Event Log entry that reports a service startup error.
Figure 5-12 Service startup failure Event Log entry
If a service with an ErrorControl value of SERVICE_ERROR_SEVERE (2) or SERVICE_ERROR_CRITICAL (3) reports a startup error, the SCM logs a record to the Event Log and then calls the internal function ScRevertToLastKnownGood. This function switches the system's registry configuration to a version, named last known good, with which the system last booted successfully. Then it restarts the system using the NtShutdownSystem system service, which is implemented in the executive. If the system is already booting with the last known good configuration, the system just reboots.
Besides starting services, the system charges the SCM with determining when the system's registry configuration, HKLM\SYSTEM\CurrentControlSet, should be saved as the last known good control set. The CurrentControlSet key contains the Services key as a subkey, so CurrentControlSet includes the registry representation of the SCM database. It also contains the Control key, which stores many kernel-mode and user-mode subsystem configuration settings. By default, a successful boot consists of a successful startup of auto-start services and a successful user logon. A boot fails if the system halts because a device driver crashes the system during the boot or if an auto-start service with an ErrorControl value of SERVICE_ERROR_SEVERE or SERVICE_ERROR_CRITICAL reports a startup error.
The SCM obviously knows when it has completed a successful startup of the auto-start services, but Winlogon (\Winnt\System32\Winlogon.exe) must notify it when there is a successful logon. Winlogon invokes the NotifyBootConfigStatus function when a user logs on, and NotifyBootConfigStatus sends a message to the SCM. Following the successful start of the auto-start services or the receipt of the message from NotifyBootConfigStatus (whichever comes last), the SCM calls the system function NtInitializeRegistry to save the current registry startup configuration.
Third-party software developers can supersede Winlogon's definition of a successful logon with their own definition. For example, a system running Microsoft SQL Server might not consider a boot successful until after SQL Server is able to accept and process transactions. Developers impose their successful-boot definition by writing a boot-verification program and installing the program by pointing to its location on disk with the value stored in the registry key HKLM\SYSTEM\CurrentControlSet\Control\BootVerificationProgram. In addition, a boot-verification program's installation must disable Winlogon's call to NotifyBootConfigStatus by setting HKLM\SOFTWARE\Microsoft\ Windows NT\CurrentVersion\Winlogon\ReportBootOk to 0. When a boot-verification program is installed, the SCM launches it after finishing auto-start services and waits for the program's call to NotifyBootConfigStatus before saving the last known good control set.
Windows 2000 maintains several copies of CurrentControlSet, and CurrentControlSet is really a symbolic registry link that points to one of the copies. The control sets have names in the form HKLM\SYSTEM\ControlSetnnn, where nnn is a number such as 001 or 002. The HKLM\SYSTEM\Select key contains values that identify the role of each control set. For example, if CurrentControlSet points to ControlSet001, the Current value under Select has a value of 1. The LastKnownGood value under Select contains the number of the last known good control set, which is the control set last used to boot successfully. Another value that might be on your system under the Select key is Failed, which points to the last control set for which the boot was deemed unsuccessful and aborted in favor of an attempt at booting with the last known good control set. Figure 5-13 displays a system's control sets and Select values.
Figure 5-13 Control set selection key
NtInitializeRegistry takes the contents of the last known good control set and synchronizes it with that of the CurrentControlSet key's tree. If this was the system's first successful boot, the last known good won't exist and the system will create a new control set for it. If the last known good tree exists, the system simply updates it with differences between it and CurrentControlSet.
Last known good is helpful in situations in which a change to CurrentControlSet, such as the modification of a system performance-tuning value under HKLM\SYSTEM\Control or the addition of a service or device driver, causes the subsequent boot to fail. Users can press F8 early in the boot process to bring up a menu that lets them direct the boot to use the last known good control set, rolling the system's registry configuration back to the way it was the last time the system booted successfully.
A service can have optional FailureActions and FailureCommand values in its registry key that the SCM records during the service's startup. The SCM registers with the system so that the system signals the SCM when a service process exits. When a service process terminates unexpectedly, the SCM determines which services ran in the process and takes the recovery steps specified by their failure-related registry values.
Actions that a service can configure for the SCM include restarting the service, running a program, and rebooting the computer. Furthermore, a service can specify the failure actions that take place the first time the service process fails, the second time, and subsequent times, and can indicate a delay period that the SCM waits before restarting the service if the service asks to be restarted. The service failure action of the IIS Admin Service results in the SCM running the IISReset application, which performs cleanup work and then restarts the service. You can easily manage the recovery actions for a service with the Recovery tab of the service's Properties dialog box in the Services MMC snap-in, as shown in Figure 5-14.
Figure 5-14 Service recovery options
When Winlogon calls the Win32 ExitWindowsEx function, ExitWindowsEx sends a message to Csrss, the Win32 subsystem process, to invoke Csrss's shutdown routine. Csrss loops through the active processes and notifies them that the system is shutting down. For every system process except the SCM, Csrss waits up to the number of seconds specified by HKU\.DEFAULT\Control Panel\ Desktop\WaitToKillAppTimeout (which defaults to 20 seconds) for the process to exit before moving on to the next process. When Csrss encounters the SCM process, it also notifies it that the system is shutting down but employs a timeout specific to the SCM. Csrss recognizes the SCM using the process ID Csrss saved when the SCM registered with Csrss using the RegisterServicesProcess function during system initialization. The SCM's timeout differs from that of other processes because Csrss knows that the SCM communicates with services that need to perform cleanup when they shutdown, and so an administrator might need to tune only the SCM's timeout. The SCM's timeout value resides in the HKLM\SYSTEM\CurrentControlSet\Control \WaitToKillServiceTimeout registry value, and it defaults to 20 seconds.
The SCM's shutdown handler is responsible for sending shutdown notifications to all the services that requested shutdown notification when they initialized with the SCM. The SCM function ScShutdownAllServices loops through the SCM services database searching for services desiring shutdown notification and sends each one a shutdown command. For each service to which it sends a shutdown command, the SCM records the value of the service's wait hint, a value that a service also specifies when it registers with the SCM. The SCM keeps track of the largest wait hint it receives. After sending the shutdown messages, the SCM waits either until one of the services it notified of shutdown exits or until the time specified by the largest wait hint passes.
If the wait hint expires without a service exiting, the SCM determines whether one or more of the services it was waiting on to exit have sent a message to the SCM telling the SCM that the service is progressing in its shutdown process. If at least one service made progress, the SCM waits again for the duration of the wait hint. The SCM continues executing this wait loop until either all the services have exited or none of the services upon which it's waiting has notified it of progress within the wait hint timeout period.
While the SCM is busy telling services to shut down and waiting for them to exit, Csrss waits for the SCM to exit. If Csrss's wait ends without the SCM having exited (the WaitToKillServiceTimeout time expires), Csrss simply moves on, continuing the shutdown process. Thus, services that fail to shut down in a timely manner are simply left running, along with the SCM, as the system shuts down. Unfortunately, there's no way for administrators to know whether they should raise the WaitToKillServiceTimeout value on systems where services aren't getting a chance to shut down completely before the system shuts down.
Running every service in its own process instead of having services share a process whenever possible wastes system resources. However, sharing processes means that if any of the services in the process has a bug that causes the process to exit, all the services in that process terminate.
Of the Windows 2000 built-in services, some run in their own process and some share a process with other services. For example, the SCM process hosts the Event Log service, the file server service (LanmanServer), and the LAN Manager name resolution service. The services that SCM hosts in Windows 2000 are listed in Table 5-7. (Not all these services are active on every system.)
Table 5-7 Windows 2000 Services That Run in the SCM
|Alerter||Notifies selected users and computers of administrative alerts.|
|AppMgmt||Provides software installation services such as Assign, Publish, and Remove.|
|Browser||Maintains an up-to-date list of computers on your network and supplies the list to programs that request it.|
|Dhcp||Manages network configuration by registering and updating IP addresses and Domain Name System (DNS) names.|
|Dmserver||Logical Disk Manager Watchdog Service|
|Dnscache||Resolves and caches DNS names.|
|Eventlog||Logs event messages that applications and Windows issue. Event Log reports contain information that can be useful in diagnosing problems. Reports are viewed in Event Viewer.|
|LanmanServer||Provides remote procedure call (RPC) support and file, print, and named pipe sharing.|
|LanmanWorkstation||Provides network connections and communications.|
|LmHosts||Enables support for NetBIOS over TCP/IP (NetBT) service and for NetBIOS name resolution.|
|Messenger||Sends and receives messages that administrators or the Alerter service transmit.|
|PlugPlay||Manages device installation and configuration and notifies programs of device changes.|
|ProtectedStorage||Provides protected storage for sensitive data, such as private keys, to prevent access by unauthorized services, processes, or users.|
|Seclogon||Enables starting processes under alternate credentials.|
|TrkSvr||Stores information so that files moved between volumes can be tracked for each volume in the domain.|
|TrkWks||Sends notifications of files moving between NTFS volumes in a network domain.|
|W32Time||Sets the computer clock.|
|Wmi||Provides systems management information to and from drivers.|
The security-related services, such as the Security Accounts Manager (SamSs) service, the Net Logon (Netlogon) service, and the IPSec Policy Agent (PolicyAgent) service, share the Lsass process.
There is also a "generic" process named Service Host (SvcHost - \Winnt\System32\Svchost.exe) to contain multiple services. Multiple instances of SvcHost can be running in different processes. Services that run in SvcHost processes include Telephony (TapiSrv), Remote Procedure Call (RpcSs), and Remote Access Connection Manager (RasMan). Windows 2000 implements services that run in SvcHost as DLLs and includes an ImagePath definition of the form "%SystemRoot%\System32\svchost.exe -k netsvcs" in the service's registry key. The service's registry key must also have a registry value named ServiceDll under a Parameters subkey that points to the service's DLL file.
All services that share a common SvcHost process specify the same parameter ("-k netsvcs" in the example in the preceding paragraph) so that they have a single entry in the SCM's image database. When the SCM encounters the first service that has a SvcHost ImagePath with a particular parameter during service startup, it creates a new image database entry and launches a SvcHost process with the parameter. The new SvcHost process takes the parameter and looks for a value having the same name as the parameter under HKLM\ SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost. SvcHost reads the contents of the value, interpreting it as a list of service names, and notifies the SCM that it's hosting those services when SvcHost registers with the SCM. Figure 5-15 presents an example Svchost registry key that shows that a SvcHost process started with the "-k netsvcs" parameter is prepared to host a number of different network-related services.
Figure 5-15 Svchost registry key
When the SCM encounters a SvcHost service during service startup with an ImagePath matching an entry it already has in the image database, it doesn't launch a second process but instead just sends a start command for the service to the SvcHost it already started for that ImagePath value. The existing SvcHost process reads the ServiceDll parameter in the service's registry key and loads the DLL into its process to start the service.
Viewing Services Running Inside Processes
The Tlist utility, in the Windows 2000 Support Tools, accepts the /s command-line option. With this option specified, Tlist displays a list of the services, if any, that are executing within processes, as shown in the following example output. A process that has "Svcs:" instead of "Title:" following its name is a service process, and the names listed are those of services hosted by that process.
C:\>tlist /s 0 System Process 8 System 144 smss.exe 172 csrss.exe Title: 192 winlogon.exe Title: NetDDE Agent 220 services.exe Svcs: Browser,Dhcp,dmserver,Dnscache, Eventlog,lanmanserver,lanmanworkstation, LmHosts,Messenger,PlugPlay,ProtectedStorage, seclogon,TrkWks,Wmi 232 lsass.exe Svcs: PolicyAgent,SamSs 392 svchost.exe Svcs: RpcSs 428 spoolsv.exe Svcs: Spooler 456 ati2plab.exe Svcs: Ati HotKey Poller 472 svchost.exe Svcs: EventSystem,Netman,NtmsSvc,RasMan,SENS,TapiSrv 508 regsvc.exe Svcs: RemoteRegistry 544 WinMgmt.exe Svcs: WinMgmt 836 Explorer.Exe Title: Program Manager 712 RealPlay.exe Title: 748 Atiptaxx.exe Title: ATI Tray Icon Application 724 DESKTOPS.EXE Title: MultiDesk 824 Explorer.Exe Title: Program Manager 900 Explorer.Exe Title: Program Manager 564 cmd.exe Title: Command Prompt - tlist /s 752 ntvdm.exe 668 calc.exe Title: Calculator 848 calc.exe Title: Calculator 308 calc.exe Title: Calculator 660 tlist.exe
Service control programs are standard Win32 applications that use the SCM functions CreateService, OpenService, StartService, ControlService, QueryServiceStatus, and DeleteService. To use the SCM functions, an SCP must first open a communications channel to the SCM by calling the OpenSCManager function. At the time of the open call, the SCP must specify what types of actions it wants to perform. For example, if an SCP simply wants to enumerate and display the services present in the SCM's database, it requests enumerate-service access in its call to OpenSCManager. During its initialization, the SCM creates an internal object that represents the SCM database and uses the Windows 2000 security functions to protect the object with a security descriptor that specifies what accounts can open the object with what access permissions. For example, the security descriptor indicates that the Everyone group (of which every account is a member) can open the SCM object with enumerate-service access. However, only administrators can open the object with the access required to create or delete a service.
As it does for the SCM database, the SCM implements security for services themselves. When an SCP creates a service by using the CreateService function, it specifies a security descriptor that the SCM associates internally with the service's entry in the service database. The SCM stores the security descriptor in the service's registry key as the Security value, and it reads that value when it scans the registry's Services key during initialization so that the security settings persist across reboots. In the same way that an SCP must specify what types of access it wants to the SCM database in its call to OpenSCManager, an SCP must tell the SCM what access it wants to a service in a call to OpenService. Accesses that an SCP can request include the ability to query a service's status and to configure, stop, and start a service.
The SCP you're probably most familiar with is the Services MMC snap-in that's included in Windows 2000, which resides in \Winnt\System32\Filemgr.dll. The Windows 2000 resource kits include a command-line SCP named Sc.exe (Service Controller tool) and a GUI SCP named Srvinstw.exe (Service Creation Wizard).
SCPs sometimes layer service policy on top of what the SCM implements. A good example is the timeout that the Services MMC snap-in implements when a service is started manually. The snap-in presents a progress bar that represents the progress of a service's startup. Whereas the SCM waits indefinitely for a service to respond to a start command, the Services snap-in waits only 2 minutes before the progress bar reaches 100 percent and the snap-in announces that the service didn't start in a timely manner. Services indirectly interact with SCPs by setting their configuration status to reflect their progress as they respond to SCM commands such as the start command. SCPs query the status with the QueryServiceStatus function. They can tell when a service actively updates the status versus when a service appears to be hung, and the SCM can take appropriate actions in notifying a user about what the service is doing.