Windows Service Silent Update
Before this feature, when a user installs into the default Program Files directory, updates cannot be applied without us first prompting the user for elevated permissions with a User Account Control (UAC) dialog.
Now that this feature is implemented, Firefox uses a service to execute updates so that UAC prompts are not displayed. The service is run "on demand" and will remain stopped until it is needed. When the service is needed, it will be started again for the period of the update. If there are any problems updating through the service, it will fall back to updating the old way via the UAC prompt.
The bugzilla task related to creating a service and not prompting the user for UAC is in bug 481815.
The feature page is located at: this feature page.
Windows Service component information
Display name: Mozilla Maintenance Service
Service name: MozillaMaintenance
File name: maintenanceservice.exe
Installation directory: %PROGRAMFILES%\Mozilla Maintenance Service
Other details about the service:
- The service deals with user tokens and therefore runs as the SYSTEM account.
- The service implementation is located under /toolkit/components/maintenanceservice
To test and see if the service is installed, from a command prompt you can run:
sc query MozillaMaintenance
If it is installed you will see output of:
SERVICE_NAME: MozillaMaintenance TYPE : 10 WIN32_OWN_PROCESS STATE : 1 STOPPED WIN32_EXIT_CODE : 1077 (0x435) SERVICE_EXIT_CODE : 0 (0x0) CHECKPOINT : 0x0 WAIT_HINT : 0x0
If it is not installed, you will see output of:
[SC] EnumQueryServicesStatus:OpenService FAILED 1060: The specified service does not exist as an installed service.
How the service works
Since the service is only started when needed, we pass the information to the service via command line on service start. The service security is modified with a special ACE which allows non elevated processes to start and stop it. Setting these permissions on the service is needed because by default services can only be started and stopped by elevated processes. This is possible via the Win32 API SetServiceObjectSecurity.
The service logs all operations of the last update, as well as taking a backup of the last 10 update operation logs before it. The install process and update processes are logged separately. The logs live in %programdata%\Mozilla\logs.
Since the service executes an update via updater.exe in session 0, no UI is displayed. An alternate implementation could have been to have the UI display by running updater.exe with a user token from the service instead, but to avoid permission problems on files, and inconsistencies in different OS versions and user types, the update is always run as session 0 with the SYSTEM account.
A single service and service versioning
The service was first built for Firefox, but has since been ported to Thunderbird. Only one service exists per computer across all channels. Other Mozilla products use the same service as well.
If a service is already installed, the service will be replaced on updates and installs only if it is newer than what is installed. This means that if a user has Nightly installed, the Nightly service will be used to update all channels.
As of Firefox 35, the service is also installed and used with x64 native builds. If both x86 and x64 builds are used, then the service will be installed and updated into the first location it was installed to. The x86 service can update both x86 and x64 builds. The x64 service can also update both x86 and x64 builds. Work was done for native x64 builds in Bug 715876.
The service decides whether or not it is newer by looking at the updater file's version number in comparison to a new one being updated by application update or the installer. Only if the newer version number is greater will it be replaced. This means that if the user runs builds like Nightly, then the Nightly service would update all other channels. It will therefore always be backwards compatible.
Service command line parameters
A service operation is executed by starting the service with special command line parameters.
The command line parameters are as follows:
- The name of the service: MozillaMaintenance
- The name of the service command to run. Currently only the "software-update" command is allowed.
- All service-command specific command line arguments.
The "software-update" command specific command line arguments are as follows:
- [callback-dir] (unused by the service but should be passed)
- [callback-path] (unused by the service but should be passed, updater.exe uses this to determine which application to lock to avoid re-launching errors)
- [callback args] (unused by the service but should be passed)
Note: The normal updater.exe command line parameters are as follows: updater.exe update-dir apply [wait-pid [callback-dir callback-path args]]
Applying an application update from Firefox's perspective
- On Firefox (or any other Mozilla application that uses the service) startup, Firefox detects an update is ready to be applied
- Whether or not the service should be used, Firefox executes updater.exe unelevated.
- Firefox shuts itself down.
- updater.exe unelevated checks to see if the service should be used, and if so tries to start the service with the appropriate command line parameters.
- If the service can't be started, is disabled, or does not exist, Firefox will execute updater.exe as it used to without the service.
- If the service was used, updater.exe unelevated waits for the service to be stopped, which indicates the update is done.
- Once done updater.exe unelevated will run the callback application and the post update process.
Applying an application update from the service's perspective
- The service gets started
- The service determines which action should be performed from its command line parameters.
- If the command is an action other than a software update, the action is performed.
- If the command is a software update then continue on...
- The service verifies the updater.exe file (See section Signing builds below)
- The service will execute the update with updater.exe under the context of its own session (session 0) using CreateProcess.
- The path of updater.exe that is run is from a copy of the updater.exe from the installation directory being updated.
- The post update process (helper.exe) currently does i) system level stuff, and ii) user level stuff. This component will be executed twice. The system level stuff will also be executed under session 0 using CreateProcess. The user level stuff will be executed by the unelevated updater.exe with its own session ID.
- The service stops itself when it is done the operation.
When the service fails
- If there is an error creating the process, or during update, or a binary windows authenticode certificate check error, an error is written to update.status and info is reported via telemetry on the next restart of the callback application. The next restart of the callback application will also set the status back to pending so that the update will be applied next time without the service.
- If there are more than 10 consecutive service specific errors (tracked with the app.update.service.errors pref), the service will automatically disable itself and reset this count to 0. A user can re-enable the service via preferences (app.update.service.enabled).
Service as an optional component
- If the service is not installed, or disabled, we will fall back to the old way of updating: using updater.exe only.
- A user can choose to not install the service at install time of Firefox.
Limited user accounts
Whether a user is an administrator or a limited user account, they can initiate an update. The ability for limited user accounts to do a manual software update through the service was landed in Bug 711475.
The service will be installed for users automatically via application update. There are reasons why the service will not be installed via application update though:
- An OS lower than XP is being used (this also applies to the installer).
- The update is being performed as a limited user account (this also applies to the installer).
- The service was previously installed and manually uninstalled. (registry key: HKEY_LOCAL_MACHINE\SOFTWARE\Mozilla\MaintenanceService and value Attempted=1 means it will never be attempted on application update again).
For the installer, if the service is not installed, there will be a checkbox in the installer for whether or not to install the service component.
The service can be uninstalled separately; it shows up as a new item under add/remove programs.
When the last product that uses the service gets uninstalled, so does the service.
You can also control whether the installer installs the service using a configuration ini method provided by the installer.
Verifying the updater is our own binary
To ensure we execute our own binary, we verify the signature of updater.exe to make sure it is signed by us.
Applying updates faster
Another bug which is not part of this feature moves updates away from on startup, and performs them to an alternate directory in the background. This work was implemented in Bug 307181.
- There is a new about:config option for whether or not to use the service. It is exposed in update preferences at Options -> Advanced -> Use a background service to install updates.
- The new setting is a boolean setting called app.update.service.enabled.
- This setting is defaulted to False if it does not exist, but we set this to True for Firefox profiles.
- There is a new about:config option for keeping track of the number of service errors called app.update.service.errors.
- There is a new about:config option for keeping track of the max number of service errors to occur before disabling the service called app.update.service.enabled.
- When app.update.service.errors reaches app.update.service.maxerrors, or 10 if maxerrors does not exist, the service will be disabled and app.update.service.errors will be reset to 0.
A configuration INI setting can be used to optionally default the service installation off. This is useful for silent installs. https://wiki.mozilla.org/Installer:Command_Line_Arguments
Below I describe some important things that we tested. All of the usual update tests and more were also tested.
- Test that using a limited user account does not install the service, nor prompt to install the service.
- Test that if the service is already installed, installing an update of a higher service number will replace the old service.
- Test that if the service is already installed, installing an update of a lower service number will NOT replace the old service.
- Test that if the service is already installed, and another product with a higher version number gets installed, it will replace the old service.
- Test that if the service is already installed, and another product with a lower version number gets installed, it will NOT replace the old service.
- Test the same above rules cross architecture, from both x86 and x64.
- Test applying updates from a limited user account.
- Test applying updates from a Windows 2000 machine.
- Test applying updates from a Windows XP machine.
- Test applying updates from a Windows Vista machine at each of the UAC levels.
- Test applying updates from a Windows 7 machine at each of the UAC levels.
- Test doing 2 updates at once.
- Test having a service with the same name that is not ours, we should apply update the old way on the next browser startup after the one that should have updated.
- Test that limited user accounts have access to all of their files after an upgrade through the service.
QA's test plan can be found here: https://wiki.mozilla.org/Silent_Update_OS_Dialogs/TestPlan
Individual tests (Simona): http://bit.ly/v74fFt
Test slave requirements for automated testing (Ehsan): https://wiki.mozilla.org/Silent_Update_OS_Dialogs/Automated_testing/Test_slaves_requirements