Most applications are interactive in nature; that is, a user provides input, and the application provides output in response. Users start an application when they want to begin using the application's functionality, then exit the application when they're finished. However, many applications don't provide functionality directly to users. Instead, those applications manage resources that other applications or multiple users share, or provide functionality that must always be available regardless of who uses the computer. These types of applications are essentially extensions to an OS because they execute from system boot to shutdown, similarly to an OS's built-in resource management. Another characteristic of these applications is that they almost never have a user interface (UI). Because their execution usually doesn't depend on a user even being logged on to the system, these applications perform their work quietly.
In Windows 2000 (Win2K) and Windows NT, such applications are called services, or Win32 services, because they rely on the Win32 API to interact with Windows. Services resemble UNIX daemon processes and often implement the server side of client/server applications. For example, most Web servers for Win2K use a service process as the primary interface to Web clients. A Web server's requirements match the facilities that Win32 services provide: The server must be running regardless of whether a user is logged on to the computer (usually, administrators performing maintenance tasks are the only users who ever log on to servers); the server must start running when the system starts so that an administrator doesn't have to remember (or even be present at the machine) to start it; and finally, the core of a Web server requires no UI for processing Web requests. Web servers often require configuration interfaces, but separate, administrator-invoked applications that communicate settings to the service implement these interfaces.
Win32 services require three components: a service application, a service control program (SCP), and a Service Control Manager (SCM, pronounced scum). If you understand how services, SCPs, and the SCM work, you'll also have a clearer idea of what Win2K and NT are doing as the system boots and shuts down. This understanding will help you properly configure service parameters and troubleshoot problems that occur during these phases of the system life cycle. In this two-part series, you'll learn about the three Win32 services components' internals, including differences between Win2K and NT 4.0. This month, I describe service applications and service accounts, and I begin describing the SCM's operation.
Service applications, such as Web servers, consist of at least one executable file that runs as a Win32 service. A user who wants to start, stop, or configure a service uses an SCP. Win2K and NT both supply built-in SCPs that provide general stop, pause, and continue functionality, but applications often include their own SCP that lets an administrator specify settings particular to the service that the application manages. Microsoft supplies the SCM that starts services according to their start parameters and that serves as the communications interface for service control commands (e.g., stop, start, pause) that pass from SCPs to services.
Because services play a different role from the role that Win32 GUI applications play, you might assume that a service's implementation is different from that of an application. However, the only difference between most services and GUI applications is that a service includes code to receive commands from the SCM and to communicate the service's status back to the SCM. Because typical services don't require a UI, developers implement them as console programs. Graphical applications receive windowing event notifications and user input via their windows' message loop, and console programs that a user launches from the Run dialog box or a command prompt receive keyboard input via a command-prompt window. However, console programs that launch as services aren't visible and—most of the time—don't receive input. All Win32 executable files are formatted according to Microsoft's Portable Executable (PE) file format, and Win2K's image loader checks flags in the image's PE header to determine whether an executable image is graphical or console-based.
When you install an application that includes a service, the application's setup program must register the service with the system. To do so, the application calls the Win32 CreateService API, a services-related API that the system implements in the Advanced API DLL, ADVAPI32.DLL (winnt\system32\advapi32.dll). ADVAPI32 implements all of the client-side SCM APIs. When a setup program registers a service, the program indirectly creates a Registry key for the service under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services. A service's Registry key is the nonvolatile storage for a service's parameters, and so the Services key is the nonvolatile representation of the SCM's service database. The SCM database is also known as the ServicesActive database. After creating a service, an installation application can start the service with the StartService API, thus avoiding the necessity of a reboot. Many service-based applications must initialize during the boot to function, so setup programs often register a service as an automatic-start service, ask the user to reboot the system to complete the installation, and let the SCM start the service as the system boots.
When a program calls CreateService, the program must specify the parameters that describe the service's characteristics. The characteristics include the service's type, the location of the service image file, an optional description of the service, whether the service starts automatically when the system boots or starts manually under an SCP's direction, how the system reacts if the service indicates an error when starting, and the service's group membership (i.e., optional information that specifies when the service starts with respect to other services). The SCM stores each characteristic as a value in the service's Registry key. Table 1 lists service and driver parameters, and Screen 1 shows an example of a service's Registry key. I'll describe most of the Type value's characteristics when I describe the SCM; however, the Type characteristic in general affects a service's implementation and requires explanation here.
The Type values that Table 1 shows include three that apply to device drivers: SERVICE_KERNEL_DRIVER, SERVICE_FILE_SYSTEM_DRIVER, and SERVICE_RECOGNIZER_DRIVER. Win2K device drivers use these values and store their parameters as Registry data in the Services Registry branch. The SCM starts drivers with auto-start Start values, so the services database naturally includes drivers. Services use other Type values, SERVICE_WIN32_OWN_PROCESS and SERVICE_WIN32_SHARE_PROCESS, which are mutually exclusive. An executable file can host more than one service, and executable files that do host more than one service specify the SERVICE_WIN32_SHARE_PROCESS Type value. An advantage to having a process run more than one service is that doing so saves the system resources that would otherwise be required to run the services in discrete processes. A potential disadvantage of this capability is that if one service of multiple services that run in the same process causes an error that terminates the process, all the services in that process terminate.
When the SCM starts a service process, the process immediately invokes the StartServiceCtrlDispatcher API, which ADVAPI32 implements. StartServiceCtrlDispatcher takes a list of entry points that consists of one entry point for each service in the process; the name of the service that the entry point corresponds to identifies each entry point. After making a named-pipe communications connection to the SCM, StartServiceCtrlDispatcher waits in a loop for commands to come through the pipe from the SCM. The SCM sends a service-start command each time it starts a service that the process owns. For each start command it receives, the StartServiceCtrlDispatcher function creates 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' main function only when all of the process' service threads have terminated. This behavior lets the service process clean up resources before exiting.
A service entry point's first action is to call the RegisterServiceCtrlHandler API. This routine, also implemented in ADVAPI32, 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 stores the table in local process memory for the StartServiceCtrlDispatcher function. The service entry point continues initializing the service, which might involve allocating memory, creating communications end points, and reading private configuration data from the Registry. A convention that most services follow is to store their parameters under the Parameters subkey of their service Registry key. While the entry point is initializing, it might periodically send the SCM status messages indicating how startup is progressing. After the entry point finishes initialization, a service thread usually waits in a loop 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' 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 to locate and invoke the service function that responds to each command. SCM commands include stop, pause, resume, interrogate, and shut down. Some commands can be application-defined commands. Figure 1, page 68, shows the internal organization of a service process. The figure depicts the two threads—the main thread and the service thread—that make up a process that hosts one service.
Unless a service installation program or administrator specifies otherwise, services run in Local System, the security context of the system account. From a security perspective, this account is extremely powerful. All of Win2K's user-mode OS components run in Local System, including the Win2K session manager (\winnt\system32\smss.exe), the Win32 subsystem process (csrss.exe), the local security authority subsystem (LSASS—\winnt\system32\lsass.exe), and the WinLogon process (\winnt\system32\winlogon.exe). The system account owns virtually every defined privilege (e.g., take ownership, reboot, create security token), and most files and Registry keys allow access to the system account. Even if those files and keys don't have access to Local System, a system account process can exercise the take-ownership privilege to gain access. Thus, the system account is more powerful than any local or domain account in terms of security potential on a local system.
A service's security context is an important consideration for service developers and systems administrators because that context has limitations. First, because the system account isn't associated with a particular user, its HKEY_CURRENT_USER key is that of the Default profile. In addition, a service running in the system account can't access the profile information for any other account. Second, even though a service has access to most local resources without needing to perform any special actions, the service has only limited access to network resources. Services in the system account can access only file and printer shares and named pipes that allow null sessions: that is, connections that require no credentials. You can specify the shares and pipes on a particular computer that permit null sessions in the NullSessionPipes and NullSessionShares Registry values under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters. In Win2K, the system account's security context is that of the machine account, so a service can also access network resources that explicitly grant access to the machine account of the computer on which the service is running.
Another security-related services restriction isn't the direct result of being in the system account but rather is affected by the system account restriction. The Win32 subsystem associates every Win32 process with a window station. A window station contains the desktop interface, and 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 all services run as part of the console session. Win32 names the visible window station WinSta0, and all interactive processes access WinSta0. Unless the system directs otherwise, the SCM associates services with Service-0x0-3e7$, a nonvisible window station that all noninteractive services share. The number 3e7 in this station's name is the logon session identifier that LSASS assigns to the logon session that the SCM uses for noninteractive services running in the system account. Services running with the default service window station therefore can't receive input from a user or display windows on the console. In fact, if a service were to present a dialog box on the window station, the service would appear to hang because a user wouldn't be able to see the dialog box or enter keyboard or mouse input to dismiss the box and let the service continue executing.
Most services experience no problems with any of the default security-related restrictions, but some services might need to interact with users through dialog boxes or access profile information for a particular user account. Therefore, the SCM provides two options to change the way services start. One option lets an installation application or SCP add the SERVICE_INTERACTIVE_PROCESS modifier to a service's Type parameter. When the SCM starts an interactive service, the SCM launches the service's process in the system account's security context but connects the service with WinSta0 rather than with the noninteractive service window station. This option lets the service display on the console dialog boxes and windows that can respond to user input.
The second option lets a service run in a user account rather than the system account. A service running in a user account has access to the account's HKEY_CURRENT_USER profile information and to any network resources that the user can access. When an SCP or installation program wants the service to run in a user account, the program specifies a user account and password, and the SCM starts the service process by logging the service on to the system as the specified user. Rather than run the process with the visible window station or the default service window station, the service runs in a window station whose name is the LSASS logon identifier that LSASS assigned for the service's logon session. As a result, the service doesn't have access to the console or user input; therefore, making a service run in a user account is incompatible with making the service interactive.
Noninteractive services also have a way to gain limited interaction with users. If a noninteractive service uses either the MB_DEFAULT_DESKTOP_ONLY or MB_SERVICE_NOTIFICATION flag, the service can display a message box. MB_SERVICE_NOTIFICATION displays the dialog box on the current active desktop, even if no user is logged on. MB_DEFAULT_DESKTOP_ONLY displays the dialog box on the interactive window station's default desktop.
Screen 2 displays the Win2K Services Microsoft Management Console (MMC) snap-in's dialog box, which lets you configure a service's logon parameters. Screen 3 shows the WinObj tool viewing the Object Manager directory, in which Win32 places window station objects. (You can download a free version of WinObj from the Systems Internals Web site at http://sysinternals.com/winobj.htm.) Visible in Screen 3 are the interactive window station (WinSta0), the noninteractive system service window station (Service-0x0-3e7$), and a noninteractive window station that the system assigned to a service process that has logged on as a user (Service-0x0-2eba40$).
The Service Control Manager
The SCM's executable file is \winnt\system32\services.exe in both Win2K and NT 4.0. Similarly to most service processes, this file runs as a Win32 console program. The Winlogon process starts the SCM early during the system boot. The SCM's startup function, SvcCtrlMain, launches services that are configured for automatic startup. SvcCtrlMain executes shortly after the screen switches to a blank desktop but generally before Winlogon loads the Graphical Identification and Authentication (GINA) interface that displays the logon dialog box. SvcCtrlMain first creates SvcCtrlEvent_A3752DX, a synchronization event that SvcCtrlMain initializes as nonsignaled. Only after the SCM completes the steps necessary to prepare SvcCtrlEvent_A3752DX to receive commands from SCPs does the SCM set the event to a signaled state. An SCP uses the OpenSCManager API, which ADVAPI32 supplies, to establish a dialog with the SCM. To prevent an SCP from trying to contact the SCM before the SCM initializes, OpenSCManager waits for SvcCtrlEvent_A3752DX to become signaled before establishing a dialog with the SCM.
Next, SvcCtrlMain calls ScCreateServiceDB, the function that builds the SCM's internal service database. ScCreateServiceDB reads and stores the contents of the MULTI_SZ value HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\ Control\ServiceGroupOrder\List, which lists the names and order of the defined service groups. A service's Registry key specifies an optional Group value if another service or device driver needs to control that service's startup ordering in relation to services from other groups. For example, the Win2K networking stack is built from the bottom up, so networking services must specify groups that order the stack later in the startup sequence than do groups to which networking device drivers belong. The SCM creates a group list that preserves the ordering of the groups that the SCM reads from the Registry. These groups include NDIS, TDI, Primary Disk, Keyboard Port, and Keyboard Class. Add-on and third-party applications can define their own groups and add them to the SCM's list. Microsoft Transaction Server (MTS), for example, adds the MS Transactions group.
Next, ScCreateServiceDB scans the contents of HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services and creates an entry in the SCM's service database for each key it encounters. Each entry includes all the service-related parameters that the system defines for a service, as well as fields that track the service's status. The SCM adds entries for device drivers as well as services because the SCM starts services and drivers marked as Auto Start and detects startup failures for drivers marked Boot and System Start. The kernel's I/O Manager loads drivers marked Boot and System Start before any user-mode processes execute; therefore, any drivers with these start types load before the SCM starts.
ScCreateServiceDB reads a service's Group value to determine the service's membership in a group, then associates the service with the group's entry in the group list that the SCM created earlier. The function also queries its DependOnGroup and DependOnService Registry values to read and record in the SCM's database a service's group and service dependencies. Figure 2 shows how the SCM organizes the service entry and group order lists. The service list appears in alphabetical order because the SCM creates the list from the Services Registry key, and Win2K stores Registry keys alphabetically.
During service startup, the SCM might need to call LSASS (e.g., 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 shows that LSASS has finished initializing. Winlogon also starts the LSASS process, so LSASS's initialization is concurrent with the SCM's initialization. Then, SvcCtrlMain calls ScGetBootAndSystemDriverState to scan the service database and look for Boot and System Start device-driver entries. ScGetBootAndSystemDriverState looks up a driver's name in the Object Manager's Driver namespace directory to determine whether a driver started successfully. When a device driver loads successfully, the I/O Manager inserts the driver's object in the namespace under the Driver directory; if a driver's name is not in this directory, then the driver hasn't loaded. Screen 4 shows WinObj viewing the contents of the Driver directory. If a driver doesn't load, the SCM looks for the driver's name in the list of drivers that the PnP_DeviceList API returns. PnP_DeviceList supplies the names of the drivers that the system's current hardware profile includes. SvcCtrlMain notes the names of drivers that haven't started and that are part of the current hardware profile in the ScFailedDrivers list.
Before starting the auto-start services, the SCM performs a few more steps. It creates its remote procedure call (RPC) named pipe (pipe\ntsvcs), then launches a thread to listen on the pipe for incoming messages from SCPs. Then, the SCM signals its initialization-complete event, SvcCtrlEvent_A3752DX. Registering a console application shutdown event handler with the Win32 subsystem process via RegisterServicesProcess prepares the SCM for system shutdown.
Back to Startup
Next month, I'll conclude this series with a description of how the SCM starts auto-start services, reacts to services that report errors during their initialization, and shuts down services. I'll describe how the Win32 subsystem shuts down the SCM, and how SCPs such as Win2K's Services MMC snap-in work. Then, I'll explore the SrvAny tool's internals. This utility lets you convert any executable file into a service. I'll also mention some roles the SCM fills that are unrelated to services.