Backed out 3 changesets (bug 1515451) for causing sm failures in components/updateagent/Cargo.toml

CLOSED TREE

Backed out changeset 85ea1d36da66 (bug 1515451)
Backed out changeset 779bc1fa07ae (bug 1515451)
Backed out changeset 0c6771b60b76 (bug 1515451)
This commit is contained in:
Mihai Alexandru Michis 2020-03-11 22:03:19 +02:00
Родитель 5ad35aac9b
Коммит e1e250413b
21 изменённых файлов: 0 добавлений и 1351 удалений

11
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"

Просмотреть файл

@ -14,7 +14,6 @@ members = [
"security/manager/ssl/osclientcerts",
"testing/geckodriver",
"toolkit/crashreporter/rust",
"toolkit/components/updateagent",
"toolkit/library/gtest/rust",
"toolkit/library/rust/",
]

Просмотреть файл

@ -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"

Просмотреть файл

@ -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"

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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}"

Просмотреть файл

@ -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

Просмотреть файл

@ -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"

Просмотреть файл

@ -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"

Просмотреть файл

@ -1,2 +0,0 @@
RUST_PROGRAMS += ['updateagent']
RCINCLUDE = 'updateagent.rc'

Просмотреть файл

@ -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) {}
}

Просмотреть файл

@ -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<Command> {
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(())
}

Просмотреть файл

@ -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<u16>);
impl BString {
pub fn from_slice(v: impl AsRef<[u16]>) -> Result<BString, HResult> {
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<OsStr>) -> Result<BString, HResult> {
BString::from_slice(s.as_ref().encode_wide().collect::<Vec<_>>().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<Self> {
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<OsStr>) -> Vec<u16> {
s.as_ref().encode_wide().chain(Some(0)).collect()
}

Просмотреть файл

@ -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<BString, HResult> {
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(())
}

Просмотреть файл

@ -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<chrono::Utc>) -> 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<OsStr>`
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<ITaskService>);
impl TaskService {
pub fn connect_local() -> Result<TaskService, ConnectTaskServiceError> {
use self::ConnectTaskServiceError::*;
let task_service = create_instance_inproc_server::<taskschd::TaskScheduler, ITaskService>()
.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<TaskFolder, HResult> {
self.get_folder(&try_to_bstring!("\\")?)
}
pub fn get_folder(&mut self, path: &BString) -> Result<TaskFolder, HResult> {
unsafe {
com_call_getter!(
|folder| self.0,
ITaskService::GetFolder(path.as_raw_ptr(), folder)
)
}
.map(TaskFolder)
}
pub fn new_task_definition(&mut self) -> Result<TaskDefinition, HResult> {
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<ITaskFolder>);
impl TaskFolder {
pub fn get_task(&mut self, task_name: &BString) -> Result<RegisteredTask, HResult> {
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<LONG, HResult> {
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<TaskFolder, HResult> {
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<ITaskDefinition>);
impl TaskDefinition {
pub fn get_settings(&mut self) -> Result<TaskSettings, HResult> {
unsafe { com_call_getter!(|s| self.0, ITaskDefinition::get_Settings(s)) }.map(TaskSettings)
}
pub fn get_registration_info(&mut self) -> Result<RegistrationInfo, HResult> {
unsafe { com_call_getter!(|ri| self.0, ITaskDefinition::get_RegistrationInfo(ri)) }
.map(RegistrationInfo)
}
unsafe fn add_action<T: winapi::Interface>(
&mut self,
action_type: taskschd::TASK_ACTION_TYPE,
) -> Result<ComRef<T>, 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<ExecAction, HResult> {
unsafe { self.add_action(taskschd::TASK_ACTION_EXEC) }.map(ExecAction)
}
unsafe fn add_trigger<T: winapi::Interface>(
&mut self,
trigger_type: taskschd::TASK_TRIGGER_TYPE2,
) -> Result<ComRef<T>, 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<DailyTrigger, HResult> {
unsafe { self.add_trigger(taskschd::TASK_TRIGGER_DAILY) }.map(DailyTrigger)
}
pub fn get_daily_triggers(&mut self) -> Result<Vec<DailyTrigger>, 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<RegisteredTask, HResult> {
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<RegisteredTask, HResult> {
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<ITaskDefinition>) -> Result<OsString, String> {
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<ITaskSettings>);
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<IRegistrationInfo>);
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<IDailyTrigger>);
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<BString, HResult> {
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<IExecAction>);
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<u16>, 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<IRegisteredTask>);
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<TaskDefinition, HResult> {
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<impl AsRef<OsStr>>) -> Result<ComRef<IRunningTask>, 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)
)
}
}
}

Просмотреть файл

@ -1,25 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="*"
name="Mozilla Update Agent"
type="win32"
/>
<description>Mozilla Update Agent</description>
<ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3">
<ms_asmv3:security>
<ms_asmv3:requestedPrivileges>
<ms_asmv3:requestedExecutionLevel level="asInvoker" uiAccess="false" />
</ms_asmv3:requestedPrivileges>
</ms_asmv3:security>
</ms_asmv3:trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
</application>
</compatibility>
</assembly>

Просмотреть файл

@ -1,3 +0,0 @@
#include <winresrc.h>
1 RT_MANIFEST "updateagent.exe.manifest"

Просмотреть файл

@ -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,

Просмотреть файл

@ -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':

Просмотреть файл

@ -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)
# ==============================================================