зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1635494 - nsIProcessToolsService;r=lina
A small xpcom module implemented in Rust, designed to provide low-level tools to deal with processes from JS. For the moment, the only notable feature is `kill()`, designed to be used from about:processes Differential Revision: https://phabricator.services.mozilla.com/D82552
This commit is contained in:
Родитель
d3bbed0006
Коммит
d76fedbfbb
|
@ -1994,6 +1994,7 @@ dependencies = [
|
||||||
"nserror",
|
"nserror",
|
||||||
"nsstring",
|
"nsstring",
|
||||||
"prefs_parser",
|
"prefs_parser",
|
||||||
|
"processtools",
|
||||||
"profiler_helper",
|
"profiler_helper",
|
||||||
"remote",
|
"remote",
|
||||||
"rlbox_lucet_sandbox",
|
"rlbox_lucet_sandbox",
|
||||||
|
@ -3824,6 +3825,16 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f566249236c6ca4340f7ca78968271f0ed2b0f234007a61b66f9ecd0af09260"
|
checksum = "9f566249236c6ca4340f7ca78968271f0ed2b0f234007a61b66f9ecd0af09260"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "processtools"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"nserror",
|
||||||
|
"winapi 0.3.7",
|
||||||
|
"xpcom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "profiler_helper"
|
name = "profiler_helper"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
@ -56,6 +56,7 @@ DIRS += [
|
||||||
'perfmonitoring',
|
'perfmonitoring',
|
||||||
'pictureinpicture',
|
'pictureinpicture',
|
||||||
'places',
|
'places',
|
||||||
|
'processtools',
|
||||||
'processsingleton',
|
'processsingleton',
|
||||||
'promiseworker',
|
'promiseworker',
|
||||||
'prompts',
|
'prompts',
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
[package]
|
||||||
|
name = "processtools"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["David Teller <dteller@mozilla.com>"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nserror = { path = "../../../xpcom/rust/nserror" }
|
||||||
|
xpcom = { path = "../../../xpcom/rust/xpcom" }
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
winapi = "0.3.7"
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
libc = "0.2"
|
|
@ -0,0 +1,33 @@
|
||||||
|
/* 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 "mozilla/ClearOnShutdown.h"
|
||||||
|
#include "mozilla/StaticPtr.h"
|
||||||
|
#include "nsCOMPtr.h"
|
||||||
|
#include "ProcessToolsService.h"
|
||||||
|
|
||||||
|
// This anonymous namespace prevents outside C++ code from improperly accessing
|
||||||
|
// these implementation details.
|
||||||
|
namespace {
|
||||||
|
extern "C" {
|
||||||
|
// Implemented in Rust.
|
||||||
|
void new_process_tools_service(nsIProcessToolsService** result);
|
||||||
|
}
|
||||||
|
|
||||||
|
static mozilla::StaticRefPtr<nsIProcessToolsService> sProcessToolsService;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
already_AddRefed<nsIProcessToolsService> GetProcessToolsService() {
|
||||||
|
nsCOMPtr<nsIProcessToolsService> processToolsService;
|
||||||
|
|
||||||
|
if (sProcessToolsService) {
|
||||||
|
processToolsService = sProcessToolsService;
|
||||||
|
} else {
|
||||||
|
new_process_tools_service(getter_AddRefs(processToolsService));
|
||||||
|
sProcessToolsService = processToolsService;
|
||||||
|
mozilla::ClearOnShutdown(&sProcessToolsService);
|
||||||
|
}
|
||||||
|
|
||||||
|
return processToolsService.forget();
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* 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 "nsIProcessToolsService.h"
|
||||||
|
|
||||||
|
already_AddRefed<nsIProcessToolsService> GetProcessToolsService();
|
|
@ -0,0 +1,10 @@
|
||||||
|
Classes = [
|
||||||
|
{
|
||||||
|
'cid': '{79A13656-A472-4713-B0E1-AB39A15CF790}',
|
||||||
|
'contract_ids': ["@mozilla.org/processtools-service;1"],
|
||||||
|
'type': 'nsIProcessToolsService',
|
||||||
|
'constructor': 'GetProcessToolsService',
|
||||||
|
'singleton': True,
|
||||||
|
'headers': ['ProcessToolsService.h'],
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,30 @@
|
||||||
|
# -*- 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/.
|
||||||
|
|
||||||
|
with Files('**'):
|
||||||
|
BUG_COMPONENT = ('Core', 'DOM: Content Processes')
|
||||||
|
|
||||||
|
XPIDL_MODULE = 'toolkit_processtools'
|
||||||
|
|
||||||
|
XPCOM_MANIFESTS += [
|
||||||
|
'components.conf',
|
||||||
|
]
|
||||||
|
|
||||||
|
XPIDL_SOURCES += [
|
||||||
|
'nsIProcessToolsService.idl',
|
||||||
|
]
|
||||||
|
|
||||||
|
EXPORTS += [
|
||||||
|
'ProcessToolsService.h',
|
||||||
|
]
|
||||||
|
|
||||||
|
UNIFIED_SOURCES += [
|
||||||
|
'ProcessToolsService.cpp',
|
||||||
|
]
|
||||||
|
|
||||||
|
FINAL_LIBRARY = 'xul'
|
||||||
|
|
||||||
|
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
|
|
@ -0,0 +1,40 @@
|
||||||
|
/* 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 "nsISupports.idl"
|
||||||
|
|
||||||
|
[scriptable, uuid(1341f571-ebed-4305-b264-4d8fc3b6b11c)]
|
||||||
|
interface nsIProcessToolsService: nsISupports {
|
||||||
|
/**
|
||||||
|
* Kill a process running on this system.
|
||||||
|
*
|
||||||
|
* Does not cause a crash report to be generated and sent.
|
||||||
|
*
|
||||||
|
* # Note
|
||||||
|
*
|
||||||
|
* `pid` is the unique-to-the-system process identifier, as
|
||||||
|
* obtained with attribute `pid` of this service.
|
||||||
|
*
|
||||||
|
* Under Un*ix, that's what you obtain with `getpid()`, etc.
|
||||||
|
* Under Windows, that's what you obtain with `GetCurrentProcessId()`,
|
||||||
|
* NOT the same thing as the process `HANDLE`.
|
||||||
|
*
|
||||||
|
* # Failure
|
||||||
|
*
|
||||||
|
* Under Windows, if two processes race to `kill()` a third process,
|
||||||
|
* or two threads race to `kill()` a process there is a (small) window
|
||||||
|
* during which this can cause a crash in the losing process.
|
||||||
|
*
|
||||||
|
* # Caveats
|
||||||
|
*
|
||||||
|
* Under Windows, process killing is asynchronous. Therefore, this
|
||||||
|
* function can return before process `pid` is actually dead.
|
||||||
|
*/
|
||||||
|
void kill(in unsigned long long pid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pid for the current process.
|
||||||
|
*/
|
||||||
|
readonly attribute unsigned long long pid;
|
||||||
|
};
|
|
@ -0,0 +1,103 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
extern crate libc;
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
extern crate winapi;
|
||||||
|
|
||||||
|
extern crate nserror;
|
||||||
|
extern crate xpcom;
|
||||||
|
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use nserror::{nsresult, NS_ERROR_FAILURE, NS_OK};
|
||||||
|
use xpcom::{interfaces::nsIProcessToolsService, xpcom, xpcom_method, RefPtr};
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn new_process_tools_service(result: *mut *const nsIProcessToolsService) {
|
||||||
|
let service: RefPtr<ProcessToolsService> = ProcessToolsService::new();
|
||||||
|
RefPtr::new(service.coerce::<nsIProcessToolsService>()).forget(&mut *result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation note:
|
||||||
|
//
|
||||||
|
// We're following the strategy employed by the `kvstore`.
|
||||||
|
// See https://searchfox.org/mozilla-central/rev/a87a1c3b543475276e6d57a7a80cb02f3e42b6ed/toolkit/components/kvstore/src/lib.rs#78
|
||||||
|
|
||||||
|
#[derive(xpcom)]
|
||||||
|
#[refcnt = "atomic"]
|
||||||
|
#[xpimplements(nsIProcessToolsService)]
|
||||||
|
pub struct InitProcessToolsService {}
|
||||||
|
|
||||||
|
impl ProcessToolsService {
|
||||||
|
pub fn new() -> RefPtr<ProcessToolsService> {
|
||||||
|
ProcessToolsService::allocate(InitProcessToolsService {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Method `kill`.
|
||||||
|
|
||||||
|
xpcom_method!(
|
||||||
|
kill => Kill(id: u64)
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub fn kill(&self, pid: u64) -> Result<(), nsresult> {
|
||||||
|
let handle = unsafe {
|
||||||
|
winapi::um::processthreadsapi::OpenProcess(
|
||||||
|
/* dwDesiredAccess */
|
||||||
|
winapi::um::winnt::PROCESS_TERMINATE | winapi::um::winnt::SYNCHRONIZE,
|
||||||
|
/* bInheritHandle */ 0,
|
||||||
|
/* dwProcessId */ pid.try_into().unwrap(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if handle.is_null() {
|
||||||
|
// Could not open process.
|
||||||
|
return Err(NS_ERROR_NOT_AVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = unsafe {
|
||||||
|
winapi::um::processthreadsapi::TerminateProcess(
|
||||||
|
/* hProcess */ handle, /* uExitCode */ 0,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Close handle regardless of success.
|
||||||
|
let _ = unsafe { winapi::um::handleapi::CloseHandle(handle) };
|
||||||
|
|
||||||
|
if result == 0 {
|
||||||
|
return Err(NS_ERROR_FAILURE);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
pub fn kill(&self, pid: u64) -> Result<(), nsresult> {
|
||||||
|
let pid = pid.try_into().or(Err(NS_ERROR_FAILURE))?;
|
||||||
|
let result = unsafe { libc::kill(pid, libc::SIGKILL) };
|
||||||
|
if result == 0 {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(NS_ERROR_FAILURE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attribute `pid`
|
||||||
|
|
||||||
|
xpcom_method!(
|
||||||
|
get_pid => GetPid() -> u64
|
||||||
|
);
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
pub fn get_pid(&self) -> Result<u64, nsresult> {
|
||||||
|
let pid = unsafe { libc::getpid() } as u64;
|
||||||
|
Ok(pid)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub fn get_pid(&self) -> Result<u64, nsresult> {
|
||||||
|
let pid = unsafe { winapi::um::processthreadsapi::GetCurrentProcessId() } as u64;
|
||||||
|
Ok(pid)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* eslint-disable mozilla/no-arbitrary-setTimeout */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { AppConstants } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/AppConstants.jsm"
|
||||||
|
);
|
||||||
|
const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");
|
||||||
|
|
||||||
|
const { Subprocess } = ChromeUtils.import(
|
||||||
|
"resource://gre/modules/Subprocess.jsm"
|
||||||
|
);
|
||||||
|
|
||||||
|
const ProcessTools = Cc["@mozilla.org/processtools-service;1"].getService(
|
||||||
|
Ci.nsIProcessToolsService
|
||||||
|
);
|
||||||
|
|
||||||
|
const env = Cc["@mozilla.org/process/environment;1"].getService(
|
||||||
|
Ci.nsIEnvironment
|
||||||
|
);
|
||||||
|
|
||||||
|
let PYTHON;
|
||||||
|
|
||||||
|
// Find Python.
|
||||||
|
add_task(async function setup() {
|
||||||
|
PYTHON = await Subprocess.pathSearch(env.get("PYTHON"));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ensure that killing a process... kills the process.
|
||||||
|
add_task(async function test_subprocess_kill() {
|
||||||
|
// We launch Python, as it's a long-running process and it exists
|
||||||
|
// on all desktop platforms on which we run tests.
|
||||||
|
let proc = await Subprocess.call({
|
||||||
|
command: PYTHON,
|
||||||
|
arguments: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
let isTerminated = false;
|
||||||
|
|
||||||
|
proc.wait().then(() => {
|
||||||
|
isTerminated = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
Assert.ok(
|
||||||
|
!isTerminated,
|
||||||
|
"We haven't killed the process yet, it should still be running."
|
||||||
|
);
|
||||||
|
|
||||||
|
// Time to kill the process.
|
||||||
|
ProcessTools.kill(proc.pid);
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 100));
|
||||||
|
Assert.ok(
|
||||||
|
isTerminated,
|
||||||
|
"We have killed the process already, it shouldn't be running anymore."
|
||||||
|
);
|
||||||
|
});
|
|
@ -0,0 +1,6 @@
|
||||||
|
[DEFAULT]
|
||||||
|
firefox-appdir = browser
|
||||||
|
skip-if = os == 'android'
|
||||||
|
subprocess = true
|
||||||
|
|
||||||
|
[test_process_kill.js]
|
|
@ -66,6 +66,8 @@ fluent = { version = "0.12" , features = ["fluent-pseudo"] }
|
||||||
fluent-ffi = { path = "../../../../intl/l10n/rust/fluent-ffi" }
|
fluent-ffi = { path = "../../../../intl/l10n/rust/fluent-ffi" }
|
||||||
firefox-accounts-bridge = { path = "../../../../services/fxaccounts/rust-bridge/firefox-accounts-bridge", optional=true }
|
firefox-accounts-bridge = { path = "../../../../services/fxaccounts/rust-bridge/firefox-accounts-bridge", optional=true }
|
||||||
|
|
||||||
|
processtools = { path = "../../../components/processtools" }
|
||||||
|
|
||||||
[target.'cfg(not(target_os = "android"))'.dependencies]
|
[target.'cfg(not(target_os = "android"))'.dependencies]
|
||||||
viaduct = { git = "https://github.com/mozilla/application-services", rev = "9ba519a5739b1976f1d333923d34b7f4916b9e26"}
|
viaduct = { git = "https://github.com/mozilla/application-services", rev = "9ba519a5739b1976f1d333923d34b7f4916b9e26"}
|
||||||
webext_storage_bridge = { path = "../../../components/extensions/storage/webext_storage_bridge" }
|
webext_storage_bridge = { path = "../../../components/extensions/storage/webext_storage_bridge" }
|
||||||
|
|
|
@ -41,6 +41,7 @@ extern crate netwerk_helper;
|
||||||
extern crate nserror;
|
extern crate nserror;
|
||||||
extern crate nsstring;
|
extern crate nsstring;
|
||||||
extern crate prefs_parser;
|
extern crate prefs_parser;
|
||||||
|
extern crate processtools;
|
||||||
#[cfg(feature = "gecko_profiler")]
|
#[cfg(feature = "gecko_profiler")]
|
||||||
extern crate profiler_helper;
|
extern crate profiler_helper;
|
||||||
extern crate rsdparsa_capi;
|
extern crate rsdparsa_capi;
|
||||||
|
|
Загрузка…
Ссылка в новой задаче