diff --git a/Cargo.lock b/Cargo.lock index 4a13b1ad0023..4b5cc6750086 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4661,17 +4661,6 @@ dependencies = [ "void", ] -[[package]] -name = "updateagent" -version = "0.1.0" -dependencies = [ - "chrono", - "comedy", - "failure", - "log", - "winapi 0.3.7", -] - [[package]] name = "url" version = "2.1.0" diff --git a/Cargo.toml b/Cargo.toml index 05a193f73709..04c2d6d551b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,6 @@ members = [ "security/manager/ssl/osclientcerts", "testing/geckodriver", "toolkit/crashreporter/rust", - "toolkit/components/updateagent", "toolkit/library/gtest/rust", "toolkit/library/rust/", ] diff --git a/browser/config/mozconfigs/win32/mingwclang b/browser/config/mozconfigs/win32/mingwclang index d0624c475582..d0da1b2f074b 100644 --- a/browser/config/mozconfigs/win32/mingwclang +++ b/browser/config/mozconfigs/win32/mingwclang @@ -43,7 +43,6 @@ ac_add_options --enable-proxy-bypass-protection # These aren't supported on mingw at this time ac_add_options --disable-webrtc # Bug 1393901 ac_add_options --disable-geckodriver # Bug 1489320 -ac_add_options --disable-update-agent # Bug 1561797 # Find our toolchain HOST_CC="$MOZ_FETCHES_DIR/clang/bin/clang" diff --git a/browser/config/mozconfigs/win64/mingwclang b/browser/config/mozconfigs/win64/mingwclang index 6e2112558dbf..c5e603598f59 100755 --- a/browser/config/mozconfigs/win64/mingwclang +++ b/browser/config/mozconfigs/win64/mingwclang @@ -43,7 +43,6 @@ ac_add_options --enable-proxy-bypass-protection # These aren't supported on mingw at this time ac_add_options --disable-webrtc # Bug 1393901 ac_add_options --disable-geckodriver # Bug 1489320 -ac_add_options --disable-update-agent # Bug 1561797 # Find our toolchain HOST_CC="$MOZ_FETCHES_DIR/clang/bin/clang" diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index ae70bf461f98..95b0f2ee92ce 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -403,11 +403,6 @@ bin/libfreebl_64int_3.so @BINPATH@/maintenanceservice_installer.exe #endif -; [Background Update Agent] -#ifdef MOZ_UPDATE_AGENT -@BINPATH@/updateagent@BIN_SUFFIX@ -#endif - ; [Crash Reporter] ; #ifdef MOZ_CRASHREPORTER diff --git a/browser/installer/windows/nsis/defines.nsi.in b/browser/installer/windows/nsis/defines.nsi.in index 81d7eb4cac7f..3629cb765f2a 100644 --- a/browser/installer/windows/nsis/defines.nsi.in +++ b/browser/installer/windows/nsis/defines.nsi.in @@ -118,11 +118,6 @@ !define MOZ_MAINTENANCE_SERVICE #endif -#ifdef MOZ_UPDATE_AGENT -!define MOZ_UPDATE_AGENT -!define UpdateAgentFullName "Mozilla Update Agent" -#endif - #ifdef MOZ_BITS_DOWNLOAD !define MOZ_BITS_DOWNLOAD #endif diff --git a/browser/installer/windows/nsis/installer.nsi b/browser/installer/windows/nsis/installer.nsi index d0409a6c004c..ecff93e41c85 100755 --- a/browser/installer/windows/nsis/installer.nsi +++ b/browser/installer/windows/nsis/installer.nsi @@ -487,17 +487,6 @@ Section "-Application" APP_IDX ${EndIf} !endif -!ifdef MOZ_UPDATE_AGENT - ${PushRegisterUpdateAgentTaskCommand} "register" - Pop $0 - ${If} "$0" != "" - ${LogMsg} "Registering update agent task: $0" - nsExec::Exec $0 - Pop $0 - ${LogMsg} "nsExec::Exec returned $0" - ${EndIf} -!endif - ; These need special handling on uninstall since they may be overwritten by ; an install into a different location. StrCpy $0 "Software\Microsoft\Windows\CurrentVersion\App Paths\${FileMainEXE}" diff --git a/browser/installer/windows/nsis/shared.nsh b/browser/installer/windows/nsis/shared.nsh index 0c0cb37376e3..dac266d9286c 100755 --- a/browser/installer/windows/nsis/shared.nsh +++ b/browser/installer/windows/nsis/shared.nsh @@ -159,25 +159,6 @@ ${EndIf} !endif -!ifdef MOZ_UPDATE_AGENT - ; This macro runs the update agent with the update-task-local-service - ; command, if it detects the needed admin privileges. Otherwise it - ; runs with update-task. - ; Both commands attempt to remove the scheduled task, then register - ; a new one. If the task was registered by an elevated user, it won't - ; be removable when not elevated, so the unelevated attempt will fail - ; harmlessly. - ; Therefore it is safe to run this in both elevated and nonelevated - ; PostUpdate: The highest privileged run will win out, so the task can - ; run as Local Service if it was ever possible to register it that way. - ${PushRegisterUpdateAgentTaskCommand} "update" - Pop $0 - ${If} "$0" != "" - nsExec::Exec $0 - Pop $0 - ${EndIf} -!endif - !ifdef MOZ_LAUNCHER_PROCESS ${ResetLauncherProcessDefaults} !endif @@ -1403,7 +1384,6 @@ Push "minidump-analyzer.exe" Push "pingsender.exe" Push "updater.exe" - Push "updateagent.exe" Push "${FileMainEXE}" !macroend !define PushFilesToCheck "!insertmacro PushFilesToCheck" @@ -1668,44 +1648,3 @@ FunctionEnd !macroend !define ResetLauncherProcessDefaults "!insertmacro ResetLauncherProcessDefaults" !endif - -!ifdef MOZ_UPDATE_AGENT -; Push, onto the stack, the command line used to register (or update) the -; update agent scheduled task. -; -; InitHashAppModelId must have already been called to set $AppUserModelID, -; if that is empty then an empty string will be pushed instead. -; -; COMMAND_BASE must be "register" or "update". Both will remove any -; pre-existing task and register a new one, but "update" will first attempt -; to copy some settings. -!macro PushRegisterUpdateAgentTaskCommand COMMAND_BASE - Push $0 - Push $1 - - Call IsUserAdmin - Pop $0 - ; Register the update agent to run as Local Service if the user is an admin... - ${If} $0 == "true" - ; ...and if we have HKLM write access - ${AndIf} $TmpVal == "HKLM" - StrCpy $1 "${COMMAND_BASE}-task-local-service" - ${Else} - ; Otherwise attempt to register the task for the current user. - ; If we had previously registered the task while elevated, then we shouldn't - ; be able to replace it now with another task of the same name, so this - ; will fail harmlessly. - StrCpy $1 "${COMMAND_BASE}-task" - ${EndIf} - - ${If} "$AppUserModelID" != "" - StrCpy $0 '"$INSTDIR\updateagent.exe" $1 "${UpdateAgentFullName} $AppUserModelID" "$AppUserModelID" "$INSTDIR"' - ${Else} - StrCpy $0 '' - ${EndIf} - - Pop $1 - Exch $0 -!macroend -!define PushRegisterUpdateAgentTaskCommand "!insertmacro PushRegisterUpdateAgentTaskCommand" -!endif diff --git a/browser/installer/windows/nsis/uninstaller.nsi b/browser/installer/windows/nsis/uninstaller.nsi index f1c52934e8f4..abc25a830ca9 100755 --- a/browser/installer/windows/nsis/uninstaller.nsi +++ b/browser/installer/windows/nsis/uninstaller.nsi @@ -453,11 +453,6 @@ Section "Uninstall" DeleteRegValue HKCU ${MOZ_LAUNCHER_SUBKEY} "$INSTDIR\${FileMainEXE}|Telemetry" !endif -!ifdef MOZ_UPDATE_AGENT - ; Unregister the update agent - nsExec::Exec '"$INSTDIR\updateagent.exe" unregister-task "${UpdateAgentFullName} $AppUserModelID"' -!endif - ${un.RemovePrecompleteEntries} "false" ${If} ${FileExists} "$INSTDIR\defaults\pref\channel-prefs.js" diff --git a/toolkit/components/updateagent/Cargo.toml b/toolkit/components/updateagent/Cargo.toml deleted file mode 100644 index 3534e4566f3b..000000000000 --- a/toolkit/components/updateagent/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "updateagent" -version = "0.1.0" -authors = ["The Mozilla Project Developers"] -license = "MPL-2.0" -autobins = false -edition = "2018" - -[target."cfg(windows)".dependencies] -chrono = "0.4" -comedy = "0.1" -failure = "0.1" -log = "0.4" - -[target."cfg(windows)".dependencies.winapi] -version = "0.3.7" -features = ["minwindef", "ntdef", "oaidl", "oleauto", "taskschd", "winbase", "winerror", "winnt", "wtypes"] - -[[bin]] -name = "updateagent" -path = "src/main.rs" diff --git a/toolkit/components/updateagent/moz.build b/toolkit/components/updateagent/moz.build deleted file mode 100644 index 437aaa8c4f02..000000000000 --- a/toolkit/components/updateagent/moz.build +++ /dev/null @@ -1,2 +0,0 @@ -RUST_PROGRAMS += ['updateagent'] -RCINCLUDE = 'updateagent.rc' diff --git a/toolkit/components/updateagent/src/event_log.rs b/toolkit/components/updateagent/src/event_log.rs deleted file mode 100644 index 57983f1558c3..000000000000 --- a/toolkit/components/updateagent/src/event_log.rs +++ /dev/null @@ -1,65 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Very simple implementation of logging via the Windows Event Log - -use std::ptr; - -use crate::ole_utils::to_u16_nul; -use log::{Level, Metadata, Record}; -use winapi::shared::minwindef::WORD; -use winapi::um::{winbase, winnt}; - -pub struct EventLogger; - -impl log::Log for EventLogger { - fn enabled(&self, metadata: &Metadata) -> bool { - metadata.level() <= log::max_level() - } - - fn log(&self, record: &Record) { - if !self.enabled(record.metadata()) { - return; - } - - let name = to_u16_nul(crate::DESCRIPTION); - let msg = to_u16_nul(format!("{} - {}", record.level(), record.args())); - - // Open and close the event log handle on every message, for simplicity. - let event_log; - unsafe { - event_log = winbase::RegisterEventSourceW(ptr::null(), name.as_ptr()); - if event_log.is_null() { - return; - } - } - - let level = match record.level() { - Level::Error => winnt::EVENTLOG_ERROR_TYPE, - Level::Warn => winnt::EVENTLOG_WARNING_TYPE, - Level::Info | Level::Debug | Level::Trace => winnt::EVENTLOG_INFORMATION_TYPE, - }; - - unsafe { - // mut only to match the LPCWSTR* signature - let mut msg_array: [*const u16; 1] = [msg.as_ptr()]; - - let _ = winbase::ReportEventW( - event_log, - level, - 0, // no category - 0, // event id 0 - ptr::null_mut(), // no user sid - msg_array.len() as WORD, // string count - 0, // 0 bytes raw data - msg_array.as_mut_ptr(), // strings - ptr::null_mut(), // no raw data - ); - - let _ = winbase::DeregisterEventSource(event_log); - } - } - - fn flush(&self) {} -} diff --git a/toolkit/components/updateagent/src/main.rs b/toolkit/components/updateagent/src/main.rs deleted file mode 100644 index 74e223a85a06..000000000000 --- a/toolkit/components/updateagent/src/main.rs +++ /dev/null @@ -1,161 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -// This code is Windows-specific, don't build this module for another platform. -#![cfg(windows)] -// We want to use the "windows" subsystem to avoid popping up a console window. This also -// prevents Windows consoles from picking up output, however, so it is disabled when debugging. -#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] - -mod event_log; -mod ole_utils; -mod task_setup; -pub mod taskschd; - -use std::env; -use std::ffi::OsString; -use std::process; - -use comedy::com::ComApartmentScope; -use log::{debug, error, info}; - -// Used as the name of the task folder and author of the task -pub static VENDOR: &str = "Mozilla"; -// Used as the description of the task and the event log application -pub static DESCRIPTION: &str = "Mozilla Update Agent"; - -fn main() { - log::set_logger(&event_log::EventLogger).unwrap(); - log::set_max_level(log::LevelFilter::Info); - - // TODO: Appropriate threading and security settings will depend on the main work the task - // will be doing. - let _com = ComApartmentScope::init_mta().unwrap(); - - process::exit(match fallible_main() { - Ok(_) => { - debug!("success"); - 0 - } - Err(e) => { - error!("{}", e); - 1 - } - }); -} - -/// Command types -pub mod cmd { - // Create a new task, removing any old one. - // `updateagent.exe register-task TaskName [TaskArgs ...]` - pub static REGISTER_TASK: &str = "register-task"; - pub static REGISTER_TASK_LOCAL_SERVICE: &str = "register-task-local-service"; - - // Create a new task, removing any old one, but copying its schedule as appropriate. - pub static UPDATE_TASK: &str = "update-task"; - pub static UPDATE_TASK_LOCAL_SERVICE: &str = "update-task-local-service"; - - // Remove the task. - pub static UNREGISTER_TASK: &str = "unregister-task"; - - // Request to be run immediately by Task Scheduler. - pub static RUN_ON_DEMAND: &str = "run-on-demand"; - - // The task is set up to execute this command, using the TaskArgs from registration. - // `updateagent.exe do-task [TaskArgs ...]` - pub static DO_TASK: &str = "do-task"; - - #[derive(Clone)] - pub enum Command { - RegisterTask, - RegisterTaskLocalService, - UpdateTask, - UpdateTaskLocalService, - UnregisterTask, - RunOnDemand, - DoTask, - } - - impl Command { - pub fn parse(s: &str) -> Option { - use Command::*; - - // Build a map to lookup the string. This only runs once so performance isn't critical. - let lookup_map: std::collections::HashMap<_, _> = [ - (REGISTER_TASK, RegisterTask), - (REGISTER_TASK_LOCAL_SERVICE, RegisterTaskLocalService), - (UPDATE_TASK, UpdateTask), - (UPDATE_TASK_LOCAL_SERVICE, UpdateTaskLocalService), - (UNREGISTER_TASK, UnregisterTask), - (RUN_ON_DEMAND, RunOnDemand), - (DO_TASK, DoTask), - ] - .iter() - .cloned() - .collect(); - - lookup_map.get(s).cloned() - } - } -} - -use cmd::Command; - -pub fn fallible_main() -> Result<(), String> { - let args_os: Vec<_> = env::args_os().collect(); - - let command = { - let command_str = args_os - .get(1) - .ok_or_else(|| String::from("missing command"))? - .to_string_lossy() - .to_owned(); - - Command::parse(&command_str) - .ok_or_else(|| format!("unknown command \"{}\"", command_str))? - }; - - // all commands except DoTask take TaskName as args_os[2] - let maybe_task_name = args_os - .get(2) - .ok_or_else(|| String::from("missing TaskName")); - - match command { - Command::RegisterTask - | Command::RegisterTaskLocalService - | Command::UpdateTask - | Command::UpdateTaskLocalService => { - let exe = env::current_exe().map_err(|e| format!("get current exe failed: {}", e))?; - let task_name = maybe_task_name?; - - task_setup::register(&*exe, task_name, &args_os[3..], command) - .map_err(|e| format!("register failed: {}", e)) - } - Command::UnregisterTask => { - if args_os.len() != 3 { - return Err("unregister-task takes only one argument: TaskName".into()); - } - - task_setup::unregister(maybe_task_name?) - .map_err(|e| format!("unregister failed: {}", e)) - } - Command::RunOnDemand => { - if args_os.len() != 3 { - return Err("run-on-demand takes only one argument: TaskName".into()); - } - - task_setup::run_on_demand(maybe_task_name?) - .map_err(|e| format!("run on demand failed: {}", e)) - } - Command::DoTask => task_action(&args_os[2..]), - } -} - -fn task_action(args: &[OsString]) -> Result<(), String> { - // TODO actual task content - - info!("task_action({:?})", args); - - Ok(()) -} diff --git a/toolkit/components/updateagent/src/ole_utils.rs b/toolkit/components/updateagent/src/ole_utils.rs deleted file mode 100644 index ac6c9b7fd094..000000000000 --- a/toolkit/components/updateagent/src/ole_utils.rs +++ /dev/null @@ -1,151 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use std::convert::TryInto; -use std::ffi::OsStr; -use std::mem; -use std::os::windows::ffi::OsStrExt; -use std::ptr::NonNull; -use std::slice; - -use winapi::shared::{winerror, wtypes}; -use winapi::um::{oaidl, oleauto}; - -use comedy::HResult; - -/// Conveniently create and destroy a `BSTR`. -/// -/// This is called `BString` by analogy to `String` since it owns the data. -/// -/// The internal `BSTR` is always non-null, even for an empty string, for simple safety reasons. - -#[derive(Debug)] -pub struct BString(NonNull); - -impl BString { - pub fn from_slice(v: impl AsRef<[u16]>) -> Result { - let v = v.as_ref(); - let real_len = v.len(); - let len = real_len - .try_into() - .map_err(|_| HResult::new(winerror::E_OUTOFMEMORY))?; - let bs = unsafe { oleauto::SysAllocStringLen(v.as_ptr(), len) }; - - Ok(Self(NonNull::new(bs).ok_or_else(|| { - HResult::new(winerror::E_OUTOFMEMORY).function("SysAllocStringLen") - })?)) - } - - pub fn from_os_str(s: impl AsRef) -> Result { - BString::from_slice(s.as_ref().encode_wide().collect::>().as_slice()) - } - - /// Take ownership of a `BSTR`. - /// - /// This will be freed when the `BString` is dropped, so the pointer shouldn't be used - /// after calling this function. - /// - /// Returns `None` if the pointer is null; though this means an empty string in most - /// contexts where `BSTR` is used, `BString` is always non-null. - pub unsafe fn from_raw(p: *mut u16) -> Option { - Some(Self(NonNull::new(p)?)) - } - - /// Get a pointer to the `BSTR`. - /// - /// The caller must ensure that the `BString` outlives the pointer this function returns, - /// or else it will end up pointing to garbage. - /// - /// This pointer shouldn't be written to, but most APIs require a mutable pointer. - pub fn as_raw_ptr(&self) -> *mut u16 { - self.0.as_ptr() - } - - /// Build a raw `VARIANT`, essentially a typed pointer. - /// - /// The caller must ensure that the `BString` outlives the `VARIANT` this function returns, - /// or else it will end up pointing to garbage. - /// - /// This is meant for passing by value to Windows APIs. - pub fn as_raw_variant(&self) -> oaidl::VARIANT { - unsafe { - let mut v: oaidl::VARIANT = mem::zeroed(); - { - let tv = v.n1.n2_mut(); - *tv.n3.bstrVal_mut() = self.as_raw_ptr(); - tv.vt = wtypes::VT_BSTR as wtypes::VARTYPE; - } - - v - } - } -} - -impl Drop for BString { - fn drop(&mut self) { - unsafe { oleauto::SysFreeString(self.0.as_ptr()) } - } -} - -impl AsRef<[u16]> for BString { - fn as_ref(&self) -> &[u16] { - unsafe { - let len = oleauto::SysStringLen(self.0.as_ptr()); - - slice::from_raw_parts(self.0.as_ptr(), len as usize) - } - } -} - -/// Try to convert, decorate `Err` with call site info -#[macro_export] -macro_rules! try_to_bstring { - ($ex:expr) => { - $crate::ole_utils::BString::from_os_str($ex).map_err(|e| e.file_line(file!(), line!())) - }; -} - -pub fn empty_variant() -> oaidl::VARIANT { - unsafe { - let mut v: oaidl::VARIANT = mem::zeroed(); - { - let tv = v.n1.n2_mut(); - tv.vt = wtypes::VT_EMPTY as wtypes::VARTYPE; - } - - v - } -} - -pub trait OptionBstringExt { - fn as_raw_variant(&self) -> oaidl::VARIANT; -} - -/// Shorthand for unwrapping, returns `BString::as_raw_variant()` or `empty_variant()` -impl OptionBstringExt for Option<&BString> { - fn as_raw_variant(&self) -> oaidl::VARIANT { - self.map(|bs| bs.as_raw_variant()) - .unwrap_or_else(empty_variant) - } -} - -// Note: A `VARIANT_BOOL` is not a `VARIANT`, rather it would go into a `VARIANT` of type -// `VT_BOOL`. Some APIs use it directly. -pub trait IntoVariantBool { - fn into_variant_bool(self) -> wtypes::VARIANT_BOOL; -} - -impl IntoVariantBool for bool { - fn into_variant_bool(self) -> wtypes::VARIANT_BOOL { - if self { - wtypes::VARIANT_TRUE - } else { - wtypes::VARIANT_FALSE - } - } -} - -pub fn to_u16_nul(s: impl AsRef) -> Vec { - s.as_ref().encode_wide().chain(Some(0)).collect() -} diff --git a/toolkit/components/updateagent/src/task_setup.rs b/toolkit/components/updateagent/src/task_setup.rs deleted file mode 100644 index fdd0abfb8828..000000000000 --- a/toolkit/components/updateagent/src/task_setup.rs +++ /dev/null @@ -1,209 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use crate::cmd::Command; -use std::ffi::{OsStr, OsString}; -use std::path::Path; - -use comedy::HResult; -use log::warn; - -use crate::cmd; -use crate::ole_utils::BString; -use crate::taskschd::{hr_is_not_found, TaskService}; -use crate::try_to_bstring; - -fn folder_name() -> Result { - try_to_bstring!(crate::VENDOR) -} - -pub fn register( - exe: &Path, - name: &OsStr, - args: &[OsString], - command: Command, -) -> Result<(), failure::Error> { - let name = try_to_bstring!(name)?; - let folder_name = folder_name()?; - - let local_service = match command { - Command::RegisterTask | Command::UpdateTask => false, - Command::RegisterTaskLocalService | Command::UpdateTaskLocalService => true, - _ => unreachable!(), - }; - let update_task = match command { - Command::RegisterTask | Command::RegisterTaskLocalService => false, - Command::UpdateTask | Command::UpdateTaskLocalService => true, - _ => unreachable!(), - }; - - let mut service = TaskService::connect_local()?; - - // Get or create the folder - let mut folder = service.get_folder(&folder_name).or_else(|e| { - if hr_is_not_found(&e) { - service - .get_root_folder() - .and_then(|mut root| root.create_folder(&folder_name)) - } else { - Err(e) - } - })?; - - // When updating, we still delete and recreate the task. - // The only part that is currently copied over is the start boundary of the - // daily trigger, since that it otherwise set to 5 minutes before the registration; in this way - // updates won't delay the task's next scheduled run time. - - // TODO: We may want to also track if the task was disabled. - - let start_time = if update_task { - // Ignoring any failures, if we can't get the time for any reason we choose a new one. - folder - .get_task(&name) - .ok() - .and_then(|mut task| task.get_definition().ok()) - .and_then(|mut def| def.get_daily_triggers().ok()) - .and_then(|mut triggers| { - // Currently we are only using 1 daily trigger. - triggers - .get_mut(0) - .and_then(|trigger| trigger.get_StartBoundary().ok()) - }) - } else { - None - }; - - folder.delete_task(&name).unwrap_or_else(|e| { - // Don't even warn if the task didn't exist. - if !hr_is_not_found(&e) { - warn!("delete task failed: {}", e); - } - }); - - let mut task_def = service.new_task_definition()?; - - { - let mut task_args = vec![OsString::from(cmd::DO_TASK)]; - task_args.extend_from_slice(args); - - let mut action = task_def.add_exec_action()?; - action.put_Path(exe)?; - action.put_Arguments(task_args.as_slice())?; - // TODO working directory? - } - - { - let mut settings = task_def.get_settings()?; - settings.put_DisallowStartIfOnBatteries(false)?; - settings.put_StopIfGoingOnBatteries(false)?; - settings.put_StartWhenAvailable(true)?; - settings.put_ExecutionTimeLimit(chrono::Duration::minutes(5))?; - } - - { - let mut info = task_def.get_registration_info()?; - info.put_Author(&try_to_bstring!(crate::VENDOR)?)?; - info.put_Description(&try_to_bstring!(crate::DESCRIPTION)?)?; - } - - // A daily trigger starting 5 minutes ago. - { - let mut daily_trigger = task_def.add_daily_trigger()?; - if let Some(ref start_time) = start_time { - daily_trigger.put_StartBoundary_BString(start_time)?; - } else { - daily_trigger.put_StartBoundary(chrono::Utc::now() - chrono::Duration::minutes(5))?; - } - daily_trigger.put_DaysInterval(1)?; - // TODO: 12-hourly trigger? logon trigger? - } - - let service_account = if local_service { - Some(try_to_bstring!("NT AUTHORITY\\LocalService")?) - } else { - None - }; - - let mut registered_task = task_def.create(&mut folder, &name, service_account.as_ref())?; - - if local_service { - // SDDL seem to be the only way to set the security descriptor. - // https://docs.microsoft.com/en-us/windows/win32/secauthz/security-descriptor-string-format - // Setting just the DACL here allows us to avoid specifying the ownership information, - // which I think is required if SDDL is provided on initial task registration. - let sddl = try_to_bstring!(concat!( - "D:(", // DACL - "A;", // ace_type = Allow - ";", // ace_flags = none - "GRGX;", // rights = Generic Read, Generic Execute - ";;", // object_guid, inherit_object_guid = none - "BU)" // account_sid = Built-in users - ))?; - - registered_task.set_sd(&sddl)?; - } - - Ok(()) -} - -pub fn unregister(name: &OsStr) -> Result<(), failure::Error> { - let name = try_to_bstring!(name)?; - let folder_name = folder_name()?; - - let mut service = TaskService::connect_local()?; - let maybe_folder = service.get_folder(&folder_name); - let mut folder = match maybe_folder { - Err(e) => { - if hr_is_not_found(&e) { - // Just warn and exit if the folder didn't exist. - warn!("failed to unregister: task folder didn't exist"); - return Ok(()); - } else { - // Other errors are fatal. - return Err(e.into()); - } - } - Ok(folder) => folder, - }; - - folder.delete_task(&name).or_else(|e| { - if hr_is_not_found(&e) { - // Only warn if the task didn't exist, still try to remove the folder below. - warn!("failed to unregister task that didn't exist"); - Ok(()) - } else { - // Other errors are fatal. - Err(e) - } - })?; - - let count = folder.get_task_count(true).unwrap_or_else(|e| { - warn!("failed getting task count: {}", e); - 1 - }); - - if count == 0 { - let result = service - .get_root_folder() - .and_then(|mut root| root.delete_folder(&folder_name)); - if let Err(e) = result { - warn!("failed deleting folder: {}", e); - } - } - - Ok(()) -} - -pub fn run_on_demand(name: &OsStr) -> Result<(), failure::Error> { - let name = try_to_bstring!(name)?; - let folder_name = folder_name()?; - - let mut service = TaskService::connect_local()?; - let task = service.get_folder(&folder_name)?.get_task(&name)?; - - task.run()?; - - Ok(()) -} diff --git a/toolkit/components/updateagent/src/taskschd.rs b/toolkit/components/updateagent/src/taskschd.rs deleted file mode 100644 index 5e9d861dd371..000000000000 --- a/toolkit/components/updateagent/src/taskschd.rs +++ /dev/null @@ -1,584 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! A partial type-safe interface for Windows Task Scheduler 2.0 -//! -//! This provides structs thinly wrapping the taskschd interfaces, with methods implemented as -//! they've been needed for the update agent. -//! -//! If it turns out that much more flexibility is needed in task definitions, it may be worth -//! generating an XML string and using `ITaskFolder::RegisterTask` or -//! `ITaskDefinition::put_XmlText`, rather than adding more and more boilerplate here. -//! -//! See https://docs.microsoft.com/windows/win32/taskschd/task-scheduler-start-page for -//! Microsoft's documentation. - -use std::ffi::{OsStr, OsString}; -use std::os::windows::ffi::{OsStrExt, OsStringExt}; -use std::path::Path; -use std::ptr; - -use comedy::com::{create_instance_inproc_server, ComRef}; -use comedy::error::{HResult, Win32Error}; -use comedy::{com_call, com_call_getter}; -use failure::Fail; - -use crate::ole_utils::{empty_variant, BString, IntoVariantBool, OptionBstringExt}; -use crate::try_to_bstring; - -use winapi::shared::{ - ntdef::{LONG, SHORT}, - winerror::{ - ERROR_ALREADY_EXISTS, ERROR_BAD_ARGUMENTS, ERROR_FILE_NOT_FOUND, E_ACCESSDENIED, - SCHED_E_SERVICE_NOT_RUNNING, - }, -}; - -use winapi::um::taskschd::{ - self, IDailyTrigger, IExecAction, IRegisteredTask, IRegistrationInfo, IRunningTask, - ITaskDefinition, ITaskFolder, ITaskService, ITaskSettings, ITrigger, ITriggerCollection, -}; - -/// Check if the `HResult` represents the win32 `ERROR_FILE_NOT_FOUND`, returned by -/// several task scheduler methods. -pub fn hr_is_not_found(hr: &HResult) -> bool { - hr.code() == HResult::from(Win32Error::new(ERROR_FILE_NOT_FOUND)).code() -} - -/// Check if the `HResult` represents the win32 `ERROR_ALREADY_EXISTS`, returned by -/// several task scheduler methods. -pub fn hr_is_already_exists(hr: &HResult) -> bool { - hr.code() == HResult::from(Win32Error::new(ERROR_ALREADY_EXISTS)).code() -} - -// These macros simplify wrapping `put_*` property methods. They are each slightly different; -// I found it significantly more confusing to try to combine them. - -/// put a bool, converting to `VARIANT_BOOL` -macro_rules! bool_putter { - ($interface:ident :: $method:ident) => { - #[allow(non_snake_case)] - pub fn $method(&mut self, v: bool) -> Result<(), HResult> { - let v = v.into_variant_bool(); - unsafe { - com_call!( - self.0, - $interface :: $method(v))?; - } - Ok(()) - } - } -} - -/// put a value that is already available as a `BString` -macro_rules! bstring_putter { - ($interface:ident :: $method:ident) => { - #[allow(non_snake_case)] - pub fn $method(&mut self, v: &BString) -> Result<(), HResult> { - unsafe { - com_call!( - self.0, - $interface :: $method(v.as_raw_ptr()))?; - } - Ok(()) - } - } -} - -/// put a `chrono::DateTime` value -macro_rules! datetime_putter { - ($interface:ident :: $method:ident) => { - #[allow(non_snake_case)] - pub fn $method(&mut self, v: chrono::DateTime) -> Result<(), HResult> { - let v = try_to_bstring!(v.to_rfc3339_opts(chrono::SecondsFormat::Secs, true))?; - unsafe { - com_call!( - self.0, - $interface :: $method(v.as_raw_ptr()))?; - } - Ok(()) - } - } -} - -/// put a value of type `$ty`, which implements `AsRef` -macro_rules! to_os_str_putter { - ($interface:ident :: $method:ident, $ty:ty) => { - #[allow(non_snake_case)] - pub fn $method(&mut self, v: $ty) -> Result<(), HResult> { - let v = try_to_bstring!(v)?; - unsafe { - com_call!( - self.0, - $interface :: $method(v.as_raw_ptr()))?; - } - Ok(()) - } - } -} - -/// put a value of type `$ty`, which implements `ToString` -macro_rules! to_string_putter { - ($interface:ident :: $method:ident, $ty:ty) => { - #[allow(non_snake_case)] - pub fn $method(&mut self, v: $ty) -> Result<(), HResult> { - let v = try_to_bstring!(v.to_string())?; - unsafe { - com_call!( - self.0, - $interface :: $method(v.as_raw_ptr()))?; - } - Ok(()) - } - } -} - -pub struct TaskService(ComRef); - -impl TaskService { - pub fn connect_local() -> Result { - use self::ConnectTaskServiceError::*; - - let task_service = create_instance_inproc_server::() - .map_err(CreateInstanceFailed)?; - - // Connect to local service with no credentials. - unsafe { - com_call!( - task_service, - ITaskService::Connect( - empty_variant(), - empty_variant(), - empty_variant(), - empty_variant() - ) - ) - } - .map_err(|hr| match hr.code() { - E_ACCESSDENIED => AccessDenied(hr), - SCHED_E_SERVICE_NOT_RUNNING => ServiceNotRunning(hr), - _ => ConnectFailed(hr), - })?; - - Ok(TaskService(task_service)) - } - - pub fn get_root_folder(&mut self) -> Result { - self.get_folder(&try_to_bstring!("\\")?) - } - - pub fn get_folder(&mut self, path: &BString) -> Result { - unsafe { - com_call_getter!( - |folder| self.0, - ITaskService::GetFolder(path.as_raw_ptr(), folder) - ) - } - .map(TaskFolder) - } - - pub fn new_task_definition(&mut self) -> Result { - unsafe { - com_call_getter!( - |task_def| self.0, - ITaskService::NewTask( - 0, // flags (reserved) - task_def, - ) - ) - } - .map(TaskDefinition) - } -} - -#[derive(Clone, Debug, Fail)] -pub enum ConnectTaskServiceError { - #[fail(display = "{}", _0)] - CreateInstanceFailed(#[fail(cause)] HResult), - #[fail(display = "Access is denied to connect to the Task Scheduler service")] - AccessDenied(#[fail(cause)] HResult), - #[fail(display = "The Task Scheduler service is not running")] - ServiceNotRunning(#[fail(cause)] HResult), - #[fail(display = "{}", _0)] - ConnectFailed(#[fail(cause)] HResult), -} - -pub struct TaskFolder(ComRef); - -impl TaskFolder { - pub fn get_task(&mut self, task_name: &BString) -> Result { - unsafe { - com_call_getter!( - |task| self.0, - ITaskFolder::GetTask(task_name.as_raw_ptr(), task) - ) - } - .map(RegisteredTask) - } - - pub fn get_task_count(&mut self, include_hidden: bool) -> Result { - use self::taskschd::IRegisteredTaskCollection; - - let flags = if include_hidden { - taskschd::TASK_ENUM_HIDDEN - } else { - 0 - }; - - unsafe { - let tasks = com_call_getter!(|t| self.0, ITaskFolder::GetTasks(flags as LONG, t))?; - - let mut count = 0; - com_call!(tasks, IRegisteredTaskCollection::get_Count(&mut count))?; - - Ok(count) - } - } - - pub fn create_folder(&mut self, path: &BString) -> Result { - let sddl = empty_variant(); - unsafe { - com_call_getter!( - |folder| self.0, - ITaskFolder::CreateFolder(path.as_raw_ptr(), sddl, folder) - ) - } - .map(TaskFolder) - } - - pub fn delete_folder(&mut self, path: &BString) -> Result<(), HResult> { - unsafe { - com_call!( - self.0, - ITaskFolder::DeleteFolder( - path.as_raw_ptr(), - 0, // flags (reserved) - ) - )?; - } - - Ok(()) - } - - pub fn delete_task(&mut self, task_name: &BString) -> Result<(), HResult> { - unsafe { - com_call!( - self.0, - ITaskFolder::DeleteTask( - task_name.as_raw_ptr(), - 0, // flags (reserved) - ) - )?; - } - - Ok(()) - } -} - -pub struct TaskDefinition(ComRef); - -impl TaskDefinition { - pub fn get_settings(&mut self) -> Result { - unsafe { com_call_getter!(|s| self.0, ITaskDefinition::get_Settings(s)) }.map(TaskSettings) - } - - pub fn get_registration_info(&mut self) -> Result { - unsafe { com_call_getter!(|ri| self.0, ITaskDefinition::get_RegistrationInfo(ri)) } - .map(RegistrationInfo) - } - - unsafe fn add_action( - &mut self, - action_type: taskschd::TASK_ACTION_TYPE, - ) -> Result, HResult> { - use self::taskschd::IActionCollection; - - let actions = com_call_getter!(|ac| self.0, ITaskDefinition::get_Actions(ac))?; - let action = com_call_getter!(|a| actions, IActionCollection::Create(action_type, a))?; - action.cast() - } - - pub fn add_exec_action(&mut self) -> Result { - unsafe { self.add_action(taskschd::TASK_ACTION_EXEC) }.map(ExecAction) - } - - unsafe fn add_trigger( - &mut self, - trigger_type: taskschd::TASK_TRIGGER_TYPE2, - ) -> Result, HResult> { - let triggers = com_call_getter!(|tc| self.0, ITaskDefinition::get_Triggers(tc))?; - let trigger = com_call_getter!(|t| triggers, ITriggerCollection::Create(trigger_type, t))?; - trigger.cast() - } - - pub fn add_daily_trigger(&mut self) -> Result { - unsafe { self.add_trigger(taskschd::TASK_TRIGGER_DAILY) }.map(DailyTrigger) - } - - pub fn get_daily_triggers(&mut self) -> Result, HResult> { - let mut found_triggers = Vec::new(); - - unsafe { - let triggers = com_call_getter!(|tc| self.0, ITaskDefinition::get_Triggers(tc))?; - let mut count = 0; - com_call!(triggers, ITriggerCollection::get_Count(&mut count))?; - - // Item indexes start at 1 - for i in 1..=count { - let trigger = com_call_getter!(|t| triggers, ITriggerCollection::get_Item(i, t))?; - - let mut trigger_type = 0; - com_call!(trigger, ITrigger::get_Type(&mut trigger_type))?; - - if trigger_type == taskschd::TASK_TRIGGER_DAILY { - found_triggers.push(DailyTrigger(trigger.cast()?)) - } - } - } - - Ok(found_triggers) - } - - pub fn create( - &mut self, - folder: &mut TaskFolder, - task_name: &BString, - service_account: Option<&BString>, - ) -> Result { - self.register_impl(folder, task_name, service_account, taskschd::TASK_CREATE) - } - - fn register_impl( - &mut self, - folder: &mut TaskFolder, - task_name: &BString, - service_account: Option<&BString>, - creation_flags: taskschd::TASK_CREATION, - ) -> Result { - let task_definition = self.0.as_raw_ptr(); - - let password = empty_variant(); - - let logon_type = if service_account.is_some() { - taskschd::TASK_LOGON_SERVICE_ACCOUNT - } else { - taskschd::TASK_LOGON_INTERACTIVE_TOKEN - }; - - let sddl = empty_variant(); - - let registered_task = unsafe { - com_call_getter!( - |rt| folder.0, - ITaskFolder::RegisterTaskDefinition( - task_name.as_raw_ptr(), - task_definition, - creation_flags as LONG, - service_account.as_raw_variant(), - password, - logon_type, - sddl, - rt, - ) - )? - }; - - Ok(RegisteredTask(registered_task)) - } - - pub fn get_xml(task_definition: &ComRef) -> Result { - unsafe { - let mut xml = ptr::null_mut(); - com_call!(task_definition, ITaskDefinition::get_XmlText(&mut xml)) - .map_err(|e| format!("{}", e))?; - - Ok(OsString::from_wide( - BString::from_raw(xml) - .ok_or_else(|| "null xml".to_string())? - .as_ref(), - )) - } - } -} - -pub struct TaskSettings(ComRef); - -impl TaskSettings { - bool_putter!(ITaskSettings::put_AllowDemandStart); - bool_putter!(ITaskSettings::put_DisallowStartIfOnBatteries); - to_string_putter!(ITaskSettings::put_ExecutionTimeLimit, chrono::Duration); - bool_putter!(ITaskSettings::put_Hidden); - - #[allow(non_snake_case)] - pub fn put_MultipleInstances(&mut self, v: InstancesPolicy) -> Result<(), HResult> { - unsafe { - com_call!(self.0, ITaskSettings::put_MultipleInstances(v as u32))?; - } - Ok(()) - } - - bool_putter!(ITaskSettings::put_RunOnlyIfIdle); - bool_putter!(ITaskSettings::put_RunOnlyIfNetworkAvailable); - bool_putter!(ITaskSettings::put_StartWhenAvailable); - bool_putter!(ITaskSettings::put_StopIfGoingOnBatteries); - bool_putter!(ITaskSettings::put_Enabled); - bool_putter!(ITaskSettings::put_WakeToRun); -} - -pub struct RegistrationInfo(ComRef); - -impl RegistrationInfo { - bstring_putter!(IRegistrationInfo::put_Author); - bstring_putter!(IRegistrationInfo::put_Description); -} - -#[derive(Clone, Copy, Debug)] -#[repr(u32)] -pub enum InstancesPolicy { - Parallel = taskschd::TASK_INSTANCES_PARALLEL, - Queue = taskschd::TASK_INSTANCES_QUEUE, - IgnoreNew = taskschd::TASK_INSTANCES_IGNORE_NEW, - StopExisting = taskschd::TASK_INSTANCES_STOP_EXISTING, -} - -pub struct DailyTrigger(ComRef); - -impl DailyTrigger { - datetime_putter!(IDailyTrigger::put_StartBoundary); - - // I'd like to have this only use the type-safe DateTime, but when copying it seems less - // error-prone to use the string directly rather than try to parse it and then convert it back - // to string. - #[allow(non_snake_case)] - pub fn put_StartBoundary_BString(&mut self, v: &BString) -> Result<(), HResult> { - unsafe { - com_call!(self.0, IDailyTrigger::put_StartBoundary(v.as_raw_ptr()))?; - } - Ok(()) - } - - #[allow(non_snake_case)] - pub fn get_StartBoundary(&mut self) -> Result { - unsafe { - let mut start_boundary = ptr::null_mut(); - let hr = com_call!( - self.0, - IDailyTrigger::get_StartBoundary(&mut start_boundary) - )?; - BString::from_raw(start_boundary).ok_or_else(|| HResult::new(hr)) - } - } - - #[allow(non_snake_case)] - pub fn put_DaysInterval(&mut self, v: SHORT) -> Result<(), HResult> { - unsafe { - com_call!(self.0, IDailyTrigger::put_DaysInterval(v))?; - } - Ok(()) - } -} - -pub struct ExecAction(ComRef); - -impl ExecAction { - to_os_str_putter!(IExecAction::put_Path, &Path); - to_os_str_putter!(IExecAction::put_WorkingDirectory, &Path); - - #[allow(non_snake_case)] - pub fn put_Arguments(&mut self, args: &[OsString]) -> Result<(), HResult> { - // based on `make_command_line()` from libstd - // https://github.com/rust-lang/rust/blob/37ff5d388f8c004ca248adb635f1cc84d347eda0/src/libstd/sys/windows/process.rs#L457 - - let mut s = Vec::new(); - - fn append_arg(cmd: &mut Vec, arg: &OsStr) -> Result<(), HResult> { - cmd.push('"' as u16); - - let mut backslashes: usize = 0; - for x in arg.encode_wide() { - if x == 0 { - return Err(HResult::from(Win32Error::new(ERROR_BAD_ARGUMENTS)) - .file_line(file!(), line!())); - } - - if x == '\\' as u16 { - backslashes += 1; - } else { - if x == '"' as u16 { - // Add n+1 backslashes for a total of 2n+1 before internal '"'. - cmd.extend((0..=backslashes).map(|_| '\\' as u16)); - } - backslashes = 0; - } - cmd.push(x); - } - - // Add n backslashes for a total of 2n before ending '"'. - cmd.extend((0..backslashes).map(|_| '\\' as u16)); - cmd.push('"' as u16); - - Ok(()) - } - - for arg in args { - if !s.is_empty() { - s.push(' ' as u16); - } - - // always quote args - append_arg(&mut s, arg.as_ref())?; - } - - let args = BString::from_slice(s).map_err(|e| e.file_line(file!(), line!()))?; - - unsafe { - com_call!(self.0, IExecAction::put_Arguments(args.as_raw_ptr()))?; - } - Ok(()) - } -} - -pub struct RegisteredTask(ComRef); - -impl RegisteredTask { - pub fn set_sd(&mut self, sddl: &BString) -> Result<(), HResult> { - unsafe { - com_call!( - self.0, - IRegisteredTask::SetSecurityDescriptor( - sddl.as_raw_ptr(), - 0, // flags (none) - ) - )?; - } - Ok(()) - } - - pub fn get_definition(&mut self) -> Result { - unsafe { com_call_getter!(|tc| self.0, IRegisteredTask::get_Definition(tc)) } - .map(TaskDefinition) - } - - pub fn run(&self) -> Result<(), HResult> { - self.run_impl(Option::<&OsStr>::None)?; - Ok(()) - } - - fn run_impl(&self, param: Option>) -> Result, HResult> { - // Running with parameters isn't currently exposed. - // param can also be an array of strings, but that is not supported here - let param = if let Some(p) = param { - Some(try_to_bstring!(p)?) - } else { - None - }; - - unsafe { - com_call_getter!( - |rt| self.0, - IRegisteredTask::Run(param.as_ref().as_raw_variant(), rt) - ) - } - } -} diff --git a/toolkit/components/updateagent/updateagent.exe.manifest b/toolkit/components/updateagent/updateagent.exe.manifest deleted file mode 100644 index 0709f15c67fe..000000000000 --- a/toolkit/components/updateagent/updateagent.exe.manifest +++ /dev/null @@ -1,25 +0,0 @@ - - - -Mozilla Update Agent - - - - - - - - - - - - - - - - diff --git a/toolkit/components/updateagent/updateagent.rc b/toolkit/components/updateagent/updateagent.rc deleted file mode 100644 index 225c58301542..000000000000 --- a/toolkit/components/updateagent/updateagent.rc +++ /dev/null @@ -1,3 +0,0 @@ -#include - -1 RT_MANIFEST "updateagent.exe.manifest" diff --git a/toolkit/modules/AppConstants.jsm b/toolkit/modules/AppConstants.jsm index 7c4748c457f6..a4b56a2c0d8d 100644 --- a/toolkit/modules/AppConstants.jsm +++ b/toolkit/modules/AppConstants.jsm @@ -219,13 +219,6 @@ this.AppConstants = Object.freeze({ false, #endif - MOZ_UPDATE_AGENT: -#ifdef MOZ_UPDATE_AGENT - true, -#else - false, -#endif - MOZ_BITS_DOWNLOAD: #ifdef MOZ_BITS_DOWNLOAD true, diff --git a/toolkit/moz.build b/toolkit/moz.build index 201f2f01ace9..8fd0084e647c 100644 --- a/toolkit/moz.build +++ b/toolkit/moz.build @@ -39,11 +39,6 @@ if CONFIG['MOZ_MAINTENANCE_SERVICE']: 'components/maintenanceservice' ] -if CONFIG['MOZ_UPDATE_AGENT']: - DIRS += [ - 'components/updateagent' - ] - DIRS += ['xre'] if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android': diff --git a/toolkit/moz.configure b/toolkit/moz.configure index bb2be9f1c7a1..95b9d0640adb 100644 --- a/toolkit/moz.configure +++ b/toolkit/moz.configure @@ -1316,23 +1316,6 @@ set_config('MOZ_MAINTENANCE_SERVICE', depends_if('--enable-maintenance-service', when=target_is_windows)(lambda _: True)) -# Update agent (currently Windows only) -# This is an independent task that runs on a schedule to -# check for, download, and install updates. -# ============================================================== - -option('--enable-update-agent', - when=target_is_windows, default=False, - help='{Enable|Disable} building update agent') - -set_define('MOZ_UPDATE_AGENT', - depends_if('--enable-update-agent', - when=target_is_windows)(lambda _: True)) - -set_config('MOZ_UPDATE_AGENT', - depends_if('--enable-update-agent', - when=target_is_windows)(lambda _: True)) - # BITS download (Windows only) # ==============================================================