Bug 1838754 - Pre 1: Remove remote kill switch from WDBA. r=nalexander

Differential Revision: https://phabricator.services.mozilla.com/D183437
This commit is contained in:
Nicholas Rishel 2023-09-05 20:55:25 +00:00
Родитель facab4f3e3
Коммит eab78be4af
13 изменённых файлов: 0 добавлений и 758 удалений

25
Cargo.lock сгенерированный
Просмотреть файл

@ -1247,22 +1247,6 @@ dependencies = [
"uuid",
]
[[package]]
name = "defaultagent-static"
version = "0.1.0"
dependencies = [
"log",
"mozilla-central-workspace-hack",
"serde",
"serde_derive",
"serde_json",
"url",
"viaduct",
"winapi",
"wineventlog",
"wio",
]
[[package]]
name = "derive_arbitrary"
version = "1.3.1"
@ -6442,15 +6426,6 @@ dependencies = [
"syn",
]
[[package]]
name = "wineventlog"
version = "0.1.0"
dependencies = [
"log",
"winapi",
"wio",
]
[[package]]
name = "winreg"
version = "0.10.1"

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

@ -17,7 +17,6 @@ members = [
"toolkit/crashreporter/rust_minidump_writer_linux",
"toolkit/library/gtest/rust",
"toolkit/library/rust/",
"toolkit/mozapps/defaultagent/rust",
]
# Excluded crates may be built as dependencies, but won't be considered members

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

@ -1,105 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 http://mozilla.org/MPL/2.0/. */
#include "RemoteSettings.h"
#include <iostream>
#include <string>
#include <windows.h>
#include <shlwapi.h>
#include "common.h"
#include "EventLog.h"
#include "Registry.h"
#include "mozilla/Maybe.h"
#include "mozilla/Result.h"
#include "mozilla/Unused.h"
// All strings cross the C/C++ <-> Rust FFI boundary as
// null-terminated UTF-8.
extern "C" {
HRESULT IsAgentRemoteDisabledRust(const char* szUrl, DWORD* lpdwDisabled);
}
#define PROD_ENDPOINT "https://firefox.settings.services.mozilla.com/v1"
#define PROD_BID "main"
#define PROD_CID "windows-default-browser-agent"
#define PROD_ID "state"
#define PATH "buckets/" PROD_BID "/collections/" PROD_CID "/records/" PROD_ID
using BoolResult = mozilla::WindowsErrorResult<bool>;
// Use Rust library to query remote service endpoint to determine if
// WDBA is disabled.
//
// Pass through errors.
static BoolResult IsAgentRemoteDisabledInternal() {
// Fetch remote settings server root from registry.
auto serverResult =
RegistryGetValueString(IsPrefixed::Prefixed, L"ServicesSettingsServer");
// Unconfigured? Fallback to production.
std::string url =
serverResult.unwrapOr(mozilla::Some(std::string(PROD_ENDPOINT)))
.valueOr(std::string(PROD_ENDPOINT));
if (url.length() > 0 && url[url.length() - 1] != '/') {
url += '/';
}
url += PATH;
std::cerr << "default-browser-agent: Remote service disabled state URL: '"
<< url << "'" << std::endl;
// Invoke Rust to query remote settings.
DWORD isRemoteDisabled;
HRESULT hr = IsAgentRemoteDisabledRust(url.c_str(), &isRemoteDisabled);
std::cerr << "default-browser-agent: HRESULT: 0x" << std::hex << hr
<< std::endl;
if (SUCCEEDED(hr)) {
return (0 != isRemoteDisabled);
}
LOG_ERROR(hr);
return mozilla::Err(mozilla::WindowsError::FromHResult(hr));
}
// Query remote service endpoint to determine if WDBA is disabled.
//
// Handle errors, failing to the last state witnessed without error.
bool IsAgentRemoteDisabled() {
// Fetch last witnessed state from registry. If we can't get the last
// state, or there is no last state, assume we're not disabled.
bool lastRemoteDisabled =
RegistryGetValueBool(IsPrefixed::Prefixed,
L"DefaultAgentLastRemoteDisabled")
.unwrapOr(mozilla::Some(false))
.valueOr(false);
std::cerr << "default-browser-agent: Last remote disabled: "
<< lastRemoteDisabled << std::endl;
auto remoteDisabledResult = IsAgentRemoteDisabledInternal();
if (remoteDisabledResult.isErr()) {
// Fail to the last state witnessed without error.
return lastRemoteDisabled;
}
bool remoteDisabled = remoteDisabledResult.unwrap();
std::cerr << "default-browser-agent: Next remote disabled: " << remoteDisabled
<< std::endl;
// Update last witnessed state in registry.
mozilla::Unused << RegistrySetValueBool(
IsPrefixed::Prefixed, L"DefaultAgentLastRemoteDisabled", remoteDisabled);
return remoteDisabled;
}

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

@ -1,14 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* 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 http://mozilla.org/MPL/2.0/. */
#ifndef __DEFAULT_BROWSER_AGENT_REMOTE_SETTINGS_H__
#define __DEFAULT_BROWSER_AGENT_REMOTE_SETTINGS_H__
#include <windows.h>
bool IsAgentRemoteDisabled();
#endif // __DEFAULT_BROWSER_AGENT_REMOTE_SETTINGS_H__

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

@ -21,7 +21,6 @@
#include "EventLog.h"
#include "Notification.h"
#include "Registry.h"
#include "RemoteSettings.h"
#include "ScheduledTask.h"
#include "SetDefaultBrowser.h"
#include "Telemetry.h"
@ -308,11 +307,6 @@ int wmain(int argc, wchar_t** argv) {
}
return RemoveTasks(argv[2], WhichTasks::WdbaTaskOnly);
} else if (!wcscmp(argv[1], L"debug-remote-disabled")) {
int disabled = IsAgentRemoteDisabled();
std::cerr << "default-browser-agent: IsAgentRemoteDisabled: " << disabled
<< std::endl;
return S_OK;
}
// We check for disablement by policy because that's assumed to be static.
@ -384,12 +378,6 @@ int wmain(int argc, wchar_t** argv) {
return SCHED_E_TASK_ATTEMPTED;
}
// Check for remote disable and (re-)enable before (potentially)
// updating registry entries and showing notifications.
if (IsAgentRemoteDisabled()) {
return S_OK;
}
DefaultBrowserResult defaultBrowserResult = GetDefaultBrowserInfo();
if (defaultBrowserResult.isErr()) {
return defaultBrowserResult.unwrapErr().AsHResult();

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

@ -8,8 +8,6 @@ Program("default-browser-agent")
SPHINX_TREES["default-browser-agent"] = "docs"
DIRS += ["rust"]
UNIFIED_SOURCES += [
"/mfbt/Poison.cpp",
"/mfbt/Unused.cpp",
@ -22,7 +20,6 @@ UNIFIED_SOURCES += [
"Notification.cpp",
"Policy.cpp",
"Registry.cpp",
"RemoteSettings.cpp",
"ScheduledTask.cpp",
"SetDefaultBrowser.cpp",
"Telemetry.cpp",
@ -41,7 +38,6 @@ SOURCES += [
SOURCES["/third_party/WinToast/wintoastlib.cpp"].flags += ["-Wno-implicit-fallthrough"]
USE_LIBS += [
"defaultagent-static",
"jsoncpp",
]

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

@ -1,23 +0,0 @@
[package]
name = "defaultagent-static"
version = "0.1.0"
authors = ["The Mozilla Install/Update Team <install-update@mozilla.com>"]
edition = "2018"
description = "FFI to Rust for use in Firefox's default browser agent."
repository = "https://github.com/mozilla/defaultagent-static"
license = "MPL-2.0"
[dependencies]
log = { version = "0.4", features = ["std"] }
mozilla-central-workspace-hack = { version = "0.1", features = ["defaultagent-static"], optional = true }
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
url = "2.1"
viaduct = "0.1"
wineventlog = { path = "wineventlog"}
wio = "0.2"
winapi = { version = "0.3", features = ["errhandlingapi", "handleapi", "minwindef", "winerror", "wininet", "winuser"] }
[lib]
crate-type = ["staticlib"]

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

@ -1,7 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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 http://mozilla.org/MPL/2.0/.
RustLibrary("defaultagent-static")

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

@ -1,166 +0,0 @@
/* -*- Mode: rust; rust-indent-offset: 4 -*- */
/* 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 http://mozilla.org/MPL/2.0/. */
#![allow(non_snake_case)]
use std::ffi::{CStr, OsString};
use std::os::raw::c_char;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate log;
use winapi::shared::ntdef::HRESULT;
use winapi::shared::winerror::{HRESULT_FROM_WIN32, S_OK};
use wio::wide::FromWide;
mod viaduct_wininet;
use viaduct_wininet::WinInetBackend;
// HRESULT with 0x80000000 is an error, 0x20000000 set is a customer error code.
#[allow(overflowing_literals)]
const HR_NETWORK_ERROR: HRESULT = 0x80000000 | 0x20000000 | 0x1;
#[allow(overflowing_literals)]
const HR_SETTINGS_ERROR: HRESULT = 0x80000000 | 0x20000000 | 0x2;
#[derive(Debug, Deserialize)]
pub struct EnabledRecord {
// Unknown fields are ignored by serde: see the docs for `#[serde(deny_unknown_fields)]`.
pub(crate) enabled: bool,
}
pub enum Error {
/// A backend error with an attached Windows error code from `GetLastError()`.
WindowsError(u32),
/// A network or otherwise transient error.
NetworkError,
/// A configuration or settings data error that probably requires code, configuration, or
/// server-side changes to address.
SettingsError,
}
impl From<viaduct::UnexpectedStatus> for Error {
fn from(_err: viaduct::UnexpectedStatus) -> Self {
Error::NetworkError
}
}
impl From<viaduct::Error> for Error {
fn from(err: viaduct::Error) -> Self {
match err {
viaduct::Error::NetworkError(_) => Error::NetworkError,
viaduct::Error::BackendError(raw) => {
// If we have a string that's a hex error code like
// "0xabcde", that's a Windows error.
if raw.starts_with("0x") {
let without_prefix = raw.trim_start_matches("0x");
let parse_result = u32::from_str_radix(without_prefix, 16);
if let Ok(parsed) = parse_result {
return Error::WindowsError(parsed);
}
}
Error::SettingsError
}
_ => Error::SettingsError,
}
}
}
impl From<serde_json::Error> for Error {
fn from(_err: serde_json::Error) -> Self {
Error::SettingsError
}
}
impl From<url::ParseError> for Error {
fn from(_err: url::ParseError) -> Self {
Error::SettingsError
}
}
fn is_agent_remote_disabled<S: AsRef<str>>(url: S) -> Result<bool, Error> {
// Be careful setting the viaduct backend twice. If the backend
// has been set already, assume that it's our backend: we may as
// well at least try to continue.
match viaduct::set_backend(&WinInetBackend) {
Ok(_) => {}
Err(viaduct::Error::SetBackendError) => {}
e => e?,
}
let url = url::Url::parse(url.as_ref())?;
let req = viaduct::Request::new(viaduct::Method::Get, url);
let resp = req.send()?;
let resp = resp.require_success()?;
let body: serde_json::Value = resp.json()?;
let data = body.get("data").ok_or(Error::SettingsError)?;
let record: EnabledRecord = serde_json::from_value(data.clone())?;
let disabled = !record.enabled;
Ok(disabled)
}
// This is an easy way to consume `MOZ_APP_DISPLAYNAME` from Rust code.
extern "C" {
static gWinEventLogSourceName: *const u16;
}
#[allow(dead_code)]
#[no_mangle]
extern "C" fn IsAgentRemoteDisabledRust(szUrl: *const c_char, lpdwDisabled: *mut u32) -> HRESULT {
let wineventlog_name = unsafe { OsString::from_wide_ptr_null(gWinEventLogSourceName) };
let logger = wineventlog::EventLogger::new(&wineventlog_name);
// It's fine to initialize logging twice.
let _ = log::set_boxed_logger(Box::new(logger));
log::set_max_level(log::LevelFilter::Info);
// Use an IIFE for `?`.
let disabled_result = (|| {
if lpdwDisabled.is_null() {
return Err(Error::SettingsError);
}
let url = unsafe { CStr::from_ptr(szUrl).to_str().map(|x| x.to_string()) }
.map_err(|_| Error::SettingsError)?;
info!("Using remote settings URL: {}", url);
is_agent_remote_disabled(url)
})();
match disabled_result {
Err(e) => {
return match e {
Error::WindowsError(errno) => {
let hr = HRESULT_FROM_WIN32(errno);
error!("Error::WindowsError({}) (HRESULT: 0x{:x})", errno, hr);
hr
}
Error::NetworkError => {
let hr = HR_NETWORK_ERROR;
error!("Error::NetworkError (HRESULT: 0x{:x})", hr);
hr
}
Error::SettingsError => {
let hr = HR_SETTINGS_ERROR;
error!("Error::SettingsError (HRESULT: 0x{:x})", hr);
hr
}
};
}
Ok(remote_disabled) => {
// We null-checked `lpdwDisabled` earlier, but just to be safe.
if !lpdwDisabled.is_null() {
unsafe { *lpdwDisabled = if remote_disabled { 1 } else { 0 } };
}
return S_OK;
}
}
}

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

@ -1,53 +0,0 @@
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// All files in the project carrying such notice may not be copied, modified, or distributed
// except according to those terms.
//! Wrapping and automatically closing Internet handles. Copy-pasted from
//! [comedy-rs](https://github.com/agashlin/comedy-rs/blob/c244b91e9237c887f6a7bc6cd03db98b51966494/src/handle.rs).
use winapi::shared::minwindef::DWORD;
use winapi::shared::ntdef::NULL;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::wininet::{InternetCloseHandle, HINTERNET};
/// Check and automatically close a Windows `HINTERNET`.
#[repr(transparent)]
#[derive(Debug)]
pub struct InternetHandle(HINTERNET);
impl InternetHandle {
/// Take ownership of a `HINTERNET`, which will be closed with `InternetCloseHandle` upon drop.
/// Returns an error in case of `NULL`.
///
/// # Safety
///
/// `h` should be the only copy of the handle. `GetLastError()` is called to
/// return an error, so the last Windows API called on this thread should have been
/// what produced the invalid handle.
pub unsafe fn new(h: HINTERNET) -> Result<InternetHandle, DWORD> {
if h == NULL {
Err(GetLastError())
} else {
Ok(InternetHandle(h))
}
}
/// Obtains the raw `HINTERNET` without transferring ownership.
///
/// Do __not__ close this handle because it is still owned by the `InternetHandle`.
///
/// Do __not__ use this handle beyond the lifetime of the `InternetHandle`.
pub fn as_raw(&self) -> HINTERNET {
self.0
}
}
impl Drop for InternetHandle {
fn drop(&mut self) {
unsafe {
InternetCloseHandle(self.0);
}
}
}

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

@ -1,257 +0,0 @@
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// All files in the project carrying such notice may not be copied, modified, or distributed
// except according to those terms.
use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER;
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::wininet;
use wio::wide::ToWide;
use viaduct::Backend;
mod internet_handle;
use internet_handle::InternetHandle;
pub struct WinInetBackend;
/// Errors
fn to_viaduct_error(e: u32) -> viaduct::Error {
// Like "0xabcde".
viaduct::Error::BackendError(format!("{:#x}", e))
}
fn get_status(req: wininet::HINTERNET) -> Result<u16, viaduct::Error> {
let mut status: u32 = 0;
let mut size: u32 = std::mem::size_of::<u32>() as u32;
let result = unsafe {
wininet::HttpQueryInfoW(
req,
wininet::HTTP_QUERY_STATUS_CODE | wininet::HTTP_QUERY_FLAG_NUMBER,
&mut status as *mut _ as *mut _,
&mut size,
std::ptr::null_mut(),
)
};
if 0 == result {
return Err(to_viaduct_error(unsafe { GetLastError() }));
}
Ok(status as u16)
}
fn get_headers(req: wininet::HINTERNET) -> Result<viaduct::Headers, viaduct::Error> {
// We follow https://docs.microsoft.com/en-us/windows/win32/wininet/retrieving-http-headers.
//
// Per
// https://docs.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-httpqueryinfoa:
// The `HttpQueryInfoA` function represents headers as ISO-8859-1 characters
// not ANSI characters.
let mut size: u32 = 0;
let result = unsafe {
wininet::HttpQueryInfoA(
req,
wininet::HTTP_QUERY_RAW_HEADERS,
std::ptr::null_mut(),
&mut size,
std::ptr::null_mut(),
)
};
if 0 == result {
let error = unsafe { GetLastError() };
if error == wininet::ERROR_HTTP_HEADER_NOT_FOUND {
return Ok(viaduct::Headers::new());
} else if error != ERROR_INSUFFICIENT_BUFFER {
return Err(to_viaduct_error(error));
}
}
let mut buffer = vec![0 as u8; size as usize];
let result = unsafe {
wininet::HttpQueryInfoA(
req,
wininet::HTTP_QUERY_RAW_HEADERS,
buffer.as_mut_ptr() as *mut _,
&mut size,
std::ptr::null_mut(),
)
};
if 0 == result {
let error = unsafe { GetLastError() };
if error == wininet::ERROR_HTTP_HEADER_NOT_FOUND {
return Ok(viaduct::Headers::new());
} else {
return Err(to_viaduct_error(error));
}
}
// The API returns all of the headers as a single char buffer in
// ISO-8859-1 encoding. Each header is terminated by '\0' and
// there's a trailing '\0' terminator as well.
//
// We want UTF-8. It's not worth include a non-trivial encoding
// library like `encoding_rs` just for these headers, so let's use
// the fact that ISO-8859-1 and UTF-8 intersect on the lower 7 bits
// and decode lossily. It will at least be reasonably clear when
// there is an encoding issue.
let allheaders = String::from_utf8_lossy(&buffer);
let mut headers = viaduct::Headers::new();
for header in allheaders.split(0 as char) {
let mut it = header.splitn(2, ":");
if let (Some(name), Some(value)) = (it.next(), it.next()) {
headers.insert(name.trim().to_string(), value.trim().to_string())?;
}
}
return Ok(headers);
}
fn get_body(req: wininet::HINTERNET) -> Result<Vec<u8>, viaduct::Error> {
let mut body = Vec::new();
const BUFFER_SIZE: usize = 65535;
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
loop {
let mut bytes_downloaded: u32 = 0;
let result = unsafe {
wininet::InternetReadFile(
req,
buffer.as_mut_ptr() as *mut _,
BUFFER_SIZE as u32,
&mut bytes_downloaded,
)
};
if 0 == result {
return Err(to_viaduct_error(unsafe { GetLastError() }));
}
if bytes_downloaded == 0 {
break;
}
body.extend_from_slice(&buffer[0..bytes_downloaded as usize]);
}
Ok(body)
}
impl Backend for WinInetBackend {
fn send(&self, request: viaduct::Request) -> Result<viaduct::Response, viaduct::Error> {
viaduct::note_backend("wininet.dll");
let request_method = request.method;
let url = request.url;
let session = unsafe {
InternetHandle::new(wininet::InternetOpenW(
"DefaultAgent/1.0".to_wide_null().as_ptr(),
wininet::INTERNET_OPEN_TYPE_PRECONFIG,
std::ptr::null_mut(),
std::ptr::null_mut(),
0,
))
}
.map_err(to_viaduct_error)?;
// Consider asserting the scheme here too, for documentation purposes.
// Viaduct itself only allows HTTPS at this time, but that might change.
let host = url
.host_str()
.ok_or(viaduct::Error::BackendError("no host".to_string()))?;
let conn = unsafe {
InternetHandle::new(wininet::InternetConnectW(
session.as_raw(),
host.to_wide_null().as_ptr(),
wininet::INTERNET_DEFAULT_HTTPS_PORT as u16,
std::ptr::null_mut(),
std::ptr::null_mut(),
wininet::INTERNET_SERVICE_HTTP,
0,
0,
))
}
.map_err(to_viaduct_error)?;
let path = url[url::Position::BeforePath..].to_string();
let req = unsafe {
wininet::HttpOpenRequestW(
conn.as_raw(),
request_method.as_str().to_wide_null().as_ptr(),
path.to_wide_null().as_ptr(),
std::ptr::null_mut(), /* lpszVersion */
std::ptr::null_mut(), /* lpszReferrer */
std::ptr::null_mut(), /* lplpszAcceptTypes */
// Avoid the cache as best we can.
wininet::INTERNET_FLAG_NO_AUTH
| wininet::INTERNET_FLAG_NO_CACHE_WRITE
| wininet::INTERNET_FLAG_NO_COOKIES
| wininet::INTERNET_FLAG_NO_UI
| wininet::INTERNET_FLAG_PRAGMA_NOCACHE
| wininet::INTERNET_FLAG_RELOAD
| wininet::INTERNET_FLAG_SECURE,
0,
)
};
if req.is_null() {
return Err(to_viaduct_error(unsafe { GetLastError() }));
}
for header in request.headers {
// Per
// https://docs.microsoft.com/en-us/windows/win32/api/wininet/nf-wininet-httpaddrequestheadersw,
// "Each header must be terminated by a CR/LF (carriage return/line
// feed) pair."
let h = format!("{}: {}\r\n", header.name(), header.value());
let result = unsafe {
wininet::HttpAddRequestHeadersW(
req,
h.to_wide_null().as_ptr(), /* lpszHeaders */
-1i32 as u32, /* dwHeadersLength */
wininet::HTTP_ADDREQ_FLAG_ADD | wininet::HTTP_ADDREQ_FLAG_REPLACE, /* dwModifiers */
)
};
if 0 == result {
return Err(to_viaduct_error(unsafe { GetLastError() }));
}
}
// Future work: support sending a body.
if request.body.is_some() {
return Err(viaduct::Error::BackendError(
"non-empty body is not yet supported".to_string(),
));
}
let result = unsafe {
wininet::HttpSendRequestW(
req,
std::ptr::null_mut(), /* lpszHeaders */
0, /* dwHeadersLength */
std::ptr::null_mut(), /* lpOptional */
0, /* dwOptionalLength */
)
};
if 0 == result {
return Err(to_viaduct_error(unsafe { GetLastError() }));
}
let status = get_status(req)?;
let headers = get_headers(req)?;
// Not all responses have a body.
let has_body = headers.get_header("content-type").is_some()
|| headers.get_header("content-length").is_some();
let body = if has_body { get_body(req)? } else { Vec::new() };
Ok(viaduct::Response {
request_method,
body,
url,
status,
headers,
})
}
}

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

@ -1,15 +0,0 @@
[package]
name = "wineventlog"
version = "0.1.0"
authors = ["The Mozilla Project Developers"]
license = "MPL-2.0"
autobins = false
edition = "2018"
[target."cfg(windows)".dependencies]
log = "0.4"
wio = "0.2"
[target."cfg(windows)".dependencies.winapi]
version = "0.3.7"
features = ["errhandlingapi", "minwindef", "ntdef", "oaidl", "oleauto", "sysinfoapi", "taskschd", "winbase", "winerror", "winnt", "winreg", "wtypes"]

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

@ -1,76 +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::ffi::OsStr;
use std::ptr;
use log::{Level, Metadata, Record};
use winapi::shared::minwindef::WORD;
use winapi::um::{winbase, winnt};
use wio::wide::ToWide;
pub struct EventLogger {
pub name: Vec<u16>,
}
impl EventLogger {
pub fn new(name: impl AsRef<OsStr>) -> Self {
EventLogger {
name: name.to_wide_null(),
}
}
}
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 = self.name.as_ptr();
let msg = format!("{} - {}", record.level(), record.args()).to_wide_null();
// Open and close the event log handle on every message, for simplicity.
let event_log;
unsafe {
event_log = winbase::RegisterEventSourceW(ptr::null(), name);
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) {}
}