Starting the Service and Processing Service States
Now that the script has made the necessary preparations to run as a service, the script starts the service by calling the Win32::Daemon::StartService() function, as Listing 3 shows. If the call is successful, the SCM starts interacting with the Perl script as if the script were a built-in Win32 service. If the call fails, the script logs an error message and exits.
After the service starts, the script continuously checks the SCM for messages. The script uses a long loop that continuously calls the Win32::Daemon::State() method to check for any state change. In this loop, which Listing 4, page 12, shows, the script has a block of code for each possible state that can occur. The code executes an appropriate action for that state. For example, the block of code for the SERVICE_PAUSE_PENDING state doesn't do anything other than log an indicator that specifies that the script is entering the paused state. Each block of code calls the Win32::Daemon::State() method, which passes in a new state. The loop continues until the state changes to SERVICE_STOPPED, at which time the script cleans up any loose ends (e.g., closes open files, deletes temporary files), then ends.
Of the different states that the main loop monitors, the most important states are SERVICE_START_PENDING, SERVICE_STOP_PENDING, SERVICE_RUNNING, and SERVICE_STOPPED. The script enters the SERVICE_START_PENDING state only onceat the beginning of the script. As the code at callout A in Listing 4 shows, the code initiates a Windows Management Instrumentation (WMI) connection that the script uses throughout the life of the service. The code then instructs the SCM that the service has officially entered the SERVICE_RUNNING state.
The SCM returns a SERVICE_STOP_PENDING state when a user, application, or system component requests the service to stop. In response, the script cleans up any loose ends and prepares for the service's termination. Afterward, the script calls the Win32::Daemon::State(SERVICE_STOPPED) function to indicate that the script has stopped processing service requests. When the SCM returns the SERVICE_STOPPED state, the main loop ends and the script shuts down the service by calling the Win32::Daemon::StopService() functions, then exits.
The script uses the SERVICE_RUNNING state to do the bulk of its work. When the loop detects this state, it runs the code at callout B in Listing 4. This block of code first checks the clock to see whether enough time has passed since the last check of running processes. This amount of time is configurable through the -proctime command-line parameter. If the specified amount of time has passed, the code uses WMI to walk through each running process on the machine. The code compares each process name with the names in the %PROC_LIST hash. When a match occurs, the code compares how long the process has been running with the permitted time in the %PROC_LIST hash. If the process has been running longer than the permitted time, the code terminates the process.
The final line of the code at callout B is important. It tells the script to fall asleep for 25 milliseconds (ms). This sleep cycle occurs for each process to prevent WMI from spiking the CPU load.
The last state that the main loop checks for is an unknown state. Some services have special states that specific applications trigger. To the SCM, these states are unknown. Because the SCM can indicate an unknown state, the script must be able to handle this state. Every time the script tells the SCM that it successfully changed states (e.g., moved from SERVICE_PAUSE_PENDING to SERVICE_PAUSED), the script remembers the new state (e.g., SERVICE_PAUSED) by storing that information in the $PrevState variable. Then, when an unknown state occurs, the script ignores the unknown state and tells the SCM that the service is still in the state that the $PrevState variable specifies, as the code at callout C in Listing 4 shows.
Finally, the main loop falls asleep for a short amount of time, as the code at callout D in Listing 4 shows. This sleep cycle prevents any CPU spiking that might occur if the loop runs amok. Typically, the main loop doesn't need to run constantly anyway. By default, the script falls asleep for 100ms before starting over.
Customizing ProcMon.pl
Now that you understand how a Win32 Perl Service works, you can customize ProcMon.pl to monitor the processes that you want to limit. Simply modify the %PROC_LIST hash in Listing 1 to include those processes. Then, to install the script as a service, use the following command to launch ProcMon.pl:
Perl ProcMon.pl -install
To start the service, run the command
net start ProcMon
To test the service, run a program that's in your %PROC_LIST hash. You can then check the log file for details about what the service has done.
Prev. page
1
2
[3]
4
next page