mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-29 14:53:24 +00:00
175 lines
5.1 KiB
Rust
175 lines
5.1 KiB
Rust
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
use super::{
|
|
credentials,
|
|
posix_thread::PosixThreadExt,
|
|
process_table,
|
|
signal::signals::{user::UserSignal, Signal},
|
|
Pgid, Pid, Process, Sid, Uid,
|
|
};
|
|
use crate::{
|
|
prelude::*,
|
|
thread::{thread_table, Tid},
|
|
};
|
|
|
|
/// Sends a signal to a process, using the current process as the sender.
|
|
///
|
|
/// The credentials of the current process will be checked to determine
|
|
/// if it is authorized to send the signal to this particular target process.
|
|
///
|
|
/// If `signal` is `None`, this method will only check permission without sending
|
|
/// any signal.
|
|
pub fn kill(pid: Pid, signal: Option<UserSignal>) -> Result<()> {
|
|
let process = process_table::get_process(&pid)
|
|
.ok_or_else(|| Error::with_message(Errno::ESRCH, "the target process does not exist"))?;
|
|
|
|
kill_process(&process, signal)
|
|
}
|
|
|
|
/// Sends a signal to all processes in a group, using the current process
|
|
/// as the sender.
|
|
///
|
|
/// The credentials of the current process will be checked to determine
|
|
/// if it is authorized to send the signal to the target group.
|
|
///
|
|
/// If `signal` is `None`, this method will only check permission without sending
|
|
/// any signal.
|
|
pub fn kill_group(pgid: Pgid, signal: Option<UserSignal>) -> Result<()> {
|
|
let process_group = process_table::get_process_group(&pgid)
|
|
.ok_or_else(|| Error::with_message(Errno::ESRCH, "target group does not exist"))?;
|
|
|
|
let inner = process_group.inner.lock();
|
|
for process in inner.processes.values() {
|
|
kill_process(process, signal)?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Sends a signal to a target thread, using the current process
|
|
/// as the sender.
|
|
///
|
|
/// If `signal` is `None`, this method will only check permission without sending
|
|
/// any signal.
|
|
pub fn tgkill(tid: Tid, tgid: Pid, signal: Option<UserSignal>) -> Result<()> {
|
|
let thread = thread_table::get_thread(tid)
|
|
.ok_or_else(|| Error::with_message(Errno::ESRCH, "target thread does not exist"))?;
|
|
|
|
if thread.status().is_exited() {
|
|
return Ok(());
|
|
}
|
|
|
|
let posix_thread = thread.as_posix_thread().unwrap();
|
|
|
|
// Check tgid
|
|
let pid = posix_thread.process().pid();
|
|
if pid != tgid {
|
|
return_errno_with_message!(
|
|
Errno::EINVAL,
|
|
"the combination of tgid and pid is not valid"
|
|
);
|
|
}
|
|
|
|
// Check permission
|
|
let signum = signal.map(|signal| signal.num());
|
|
let sender = current_thread_sender_ids();
|
|
posix_thread.check_signal_perm(signum.as_ref(), &sender)?;
|
|
|
|
if let Some(signal) = signal {
|
|
posix_thread.enqueue_signal(Box::new(signal));
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Sends a signal to all processes except current process and init process, using
|
|
/// the current process as the sender.
|
|
///
|
|
/// The credentials of the current process will be checked to determine
|
|
/// if it is authorized to send the signal to the target group.
|
|
pub fn kill_all(signal: Option<UserSignal>) -> Result<()> {
|
|
let current = current!();
|
|
for process in process_table::process_table().iter() {
|
|
if Arc::ptr_eq(¤t, process) || process.is_init_process() {
|
|
continue;
|
|
}
|
|
|
|
kill_process(process, signal)?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn kill_process(process: &Process, signal: Option<UserSignal>) -> Result<()> {
|
|
let threads = process.threads().lock();
|
|
let posix_threads = threads
|
|
.iter()
|
|
.map(|thread| thread.as_posix_thread().unwrap());
|
|
|
|
// First check permission
|
|
let signum = signal.map(|signal| signal.num());
|
|
let sender_ids = current_thread_sender_ids();
|
|
let mut permitted_threads = {
|
|
posix_threads.clone().filter(|posix_thread| {
|
|
posix_thread
|
|
.check_signal_perm(signum.as_ref(), &sender_ids)
|
|
.is_ok()
|
|
})
|
|
};
|
|
|
|
if permitted_threads.clone().count() == 0 {
|
|
return_errno_with_message!(Errno::EPERM, "cannot send signal to the target process");
|
|
}
|
|
|
|
let Some(signal) = signal else { return Ok(()) };
|
|
|
|
// Send signal to any thread that does not blocks the signal.
|
|
for thread in permitted_threads.clone() {
|
|
if !thread.has_signal_blocked(&signal) {
|
|
thread.enqueue_signal(Box::new(signal));
|
|
return Ok(());
|
|
}
|
|
}
|
|
|
|
// If all threads block the signal, send signal to the first thread.
|
|
let first_thread = permitted_threads.next().unwrap();
|
|
first_thread.enqueue_signal(Box::new(signal));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn current_thread_sender_ids() -> SignalSenderIds {
|
|
let credentials = credentials();
|
|
let ruid = credentials.ruid();
|
|
let euid = credentials.euid();
|
|
let sid = current!().session().unwrap().sid();
|
|
SignalSenderIds::new(ruid, euid, sid)
|
|
}
|
|
|
|
/// The ids of the signal sender process.
|
|
///
|
|
/// This struct now includes effective user id, real user id and session id.
|
|
pub(super) struct SignalSenderIds {
|
|
ruid: Uid,
|
|
euid: Uid,
|
|
sid: Sid,
|
|
}
|
|
|
|
impl SignalSenderIds {
|
|
fn new(ruid: Uid, euid: Uid, sid: Sid) -> Self {
|
|
Self { ruid, euid, sid }
|
|
}
|
|
|
|
pub(super) fn ruid(&self) -> Uid {
|
|
self.ruid
|
|
}
|
|
|
|
pub(super) fn euid(&self) -> Uid {
|
|
self.euid
|
|
}
|
|
|
|
pub(super) fn sid(&self) -> Sid {
|
|
self.sid
|
|
}
|
|
}
|