starry_api/imp/
signal.rs

1use core::{mem, time::Duration};
2
3use alloc::sync::Arc;
4use axerrno::{LinuxError, LinuxResult};
5use axhal::arch::TrapFrame;
6use axprocess::{Pid, Thread};
7use axsignal::{SignalInfo, SignalSet, SignalStack, Signo};
8use axtask::{TaskExtRef, current};
9use linux_raw_sys::general::{
10    MINSIGSTKSZ, SI_TKILL, SI_USER, SIG_BLOCK, SIG_SETMASK, SIG_UNBLOCK, kernel_sigaction, siginfo,
11    timespec,
12};
13use starry_core::task::{get_process, get_process_group, get_thread, processes};
14
15use crate::{
16    ptr::{UserConstPtr, UserPtr, nullable},
17    signal::{check_signals, send_signal_process, send_signal_process_group, send_signal_thread},
18    time::TimeValueLike,
19};
20
21fn check_sigset_size(size: usize) -> LinuxResult<()> {
22    if size != size_of::<SignalSet>() {
23        return Err(LinuxError::EINVAL);
24    }
25    Ok(())
26}
27
28fn parse_signo(signo: u32) -> LinuxResult<Signo> {
29    Signo::from_repr(signo as u8).ok_or(LinuxError::EINVAL)
30}
31
32pub fn sys_rt_sigprocmask(
33    how: i32,
34    set: UserConstPtr<SignalSet>,
35    oldset: UserPtr<SignalSet>,
36    sigsetsize: usize,
37) -> LinuxResult<isize> {
38    check_sigset_size(sigsetsize)?;
39
40    current()
41        .task_ext()
42        .thread_data()
43        .signal
44        .with_blocked_mut::<LinuxResult<_>>(|blocked| {
45            if let Some(oldset) = nullable!(oldset.get_as_mut())? {
46                *oldset = *blocked;
47            }
48
49            if let Some(set) = nullable!(set.get_as_ref())? {
50                match how as u32 {
51                    SIG_BLOCK => *blocked |= *set,
52                    SIG_UNBLOCK => *blocked &= !*set,
53                    SIG_SETMASK => *blocked = *set,
54                    _ => return Err(LinuxError::EINVAL),
55                }
56            }
57            Ok(())
58        })?;
59
60    Ok(0)
61}
62
63pub fn sys_rt_sigaction(
64    signo: u32,
65    act: UserConstPtr<kernel_sigaction>,
66    oldact: UserPtr<kernel_sigaction>,
67    sigsetsize: usize,
68) -> LinuxResult<isize> {
69    check_sigset_size(sigsetsize)?;
70
71    let signo = parse_signo(signo)?;
72    if matches!(signo, Signo::SIGKILL | Signo::SIGSTOP) {
73        return Err(LinuxError::EINVAL);
74    }
75
76    let curr = current();
77    let mut actions = curr.task_ext().process_data().signal.actions.lock();
78    if let Some(oldact) = nullable!(oldact.get_as_mut())? {
79        actions[signo].to_ctype(oldact);
80    }
81    if let Some(act) = nullable!(act.get_as_ref())? {
82        actions[signo] = (*act).try_into()?;
83    }
84    Ok(0)
85}
86
87pub fn sys_rt_sigpending(set: UserPtr<SignalSet>, sigsetsize: usize) -> LinuxResult<isize> {
88    check_sigset_size(sigsetsize)?;
89    *set.get_as_mut()? = current().task_ext().thread_data().signal.pending();
90    Ok(0)
91}
92
93fn make_siginfo(signo: u32, code: i32) -> LinuxResult<Option<SignalInfo>> {
94    if signo == 0 {
95        return Ok(None);
96    }
97    let signo = parse_signo(signo)?;
98    Ok(Some(SignalInfo::new(signo, code)))
99}
100
101pub fn sys_kill(pid: i32, signo: u32) -> LinuxResult<isize> {
102    let Some(sig) = make_siginfo(signo, SI_USER as _)? else {
103        // TODO: should also check permissions
104        return Ok(0);
105    };
106
107    let curr = current();
108    match pid {
109        1.. => {
110            let proc = get_process(pid as Pid)?;
111            send_signal_process(&proc, sig)?;
112            Ok(1)
113        }
114        0 => {
115            let pg = curr.task_ext().thread.process().group();
116            Ok(send_signal_process_group(&pg, sig) as _)
117        }
118        -1 => {
119            let mut count = 0;
120            for proc in processes() {
121                if proc.is_init() {
122                    // init process
123                    continue;
124                }
125                send_signal_process(&proc, sig.clone())?;
126                count += 1;
127            }
128            Ok(count)
129        }
130        ..-1 => {
131            let pg = get_process_group((-pid) as Pid)?;
132            Ok(send_signal_process_group(&pg, sig) as _)
133        }
134    }
135}
136
137pub fn sys_tkill(tid: Pid, signo: u32) -> LinuxResult<isize> {
138    let Some(sig) = make_siginfo(signo, SI_TKILL)? else {
139        // TODO: should also check permissions
140        return Ok(0);
141    };
142
143    let thr = get_thread(tid)?;
144    send_signal_thread(&thr, sig)?;
145    Ok(0)
146}
147
148pub fn sys_tgkill(tgid: Pid, tid: Pid, signo: u32) -> LinuxResult<isize> {
149    let Some(sig) = make_siginfo(signo, SI_TKILL)? else {
150        // TODO: should also check permissions
151        return Ok(0);
152    };
153
154    send_signal_thread(find_thread_in_group(tgid, tid)?.as_ref(), sig)?;
155    Ok(0)
156}
157
158fn find_thread_in_group(tgid: Pid, tid: Pid) -> LinuxResult<Arc<Thread>> {
159    let thr = get_thread(tid)?;
160    if thr.process().pid() != tgid {
161        return Err(LinuxError::ESRCH);
162    }
163    Ok(thr)
164}
165
166fn make_queue_signal_info(
167    tgid: Pid,
168    signo: u32,
169    sig: UserConstPtr<SignalInfo>,
170) -> LinuxResult<SignalInfo> {
171    let signo = parse_signo(signo)?;
172    let mut sig = sig.get_as_ref()?.clone();
173    sig.set_signo(signo);
174    if current().task_ext().thread.process().pid() != tgid
175        && (sig.code() >= 0 || sig.code() == SI_TKILL)
176    {
177        return Err(LinuxError::EPERM);
178    }
179    Ok(sig)
180}
181
182pub fn sys_rt_sigqueueinfo(
183    tgid: Pid,
184    signo: u32,
185    sig: UserConstPtr<SignalInfo>,
186    sigsetsize: usize,
187) -> LinuxResult<isize> {
188    check_sigset_size(sigsetsize)?;
189
190    let sig = make_queue_signal_info(tgid, signo, sig)?;
191    send_signal_process(get_process(tgid)?.as_ref(), sig)?;
192    Ok(0)
193}
194
195pub fn sys_rt_tgsigqueueinfo(
196    tgid: Pid,
197    tid: Pid,
198    signo: u32,
199    sig: UserConstPtr<SignalInfo>,
200    sigsetsize: usize,
201) -> LinuxResult<isize> {
202    check_sigset_size(sigsetsize)?;
203
204    let sig = make_queue_signal_info(tgid, signo, sig)?;
205    send_signal_thread(find_thread_in_group(tgid, tid)?.as_ref(), sig)?;
206    Ok(0)
207}
208
209pub fn sys_rt_sigreturn(tf: &mut TrapFrame) -> LinuxResult<isize> {
210    let curr = current();
211    curr.task_ext().thread_data().signal.restore(tf);
212    Ok(tf.retval() as isize)
213}
214
215pub fn sys_rt_sigtimedwait(
216    set: UserConstPtr<SignalSet>,
217    info: UserPtr<siginfo>,
218    timeout: UserConstPtr<timespec>,
219    sigsetsize: usize,
220) -> LinuxResult<isize> {
221    check_sigset_size(sigsetsize)?;
222
223    let set = *set.get_as_ref()?;
224    let timeout: Option<Duration> = nullable!(timeout.get_as_ref())?.map(|ts| ts.to_time_value());
225
226    let Some(sig) = current()
227        .task_ext()
228        .thread_data()
229        .signal
230        .wait_timeout(set, timeout)
231    else {
232        return Err(LinuxError::EAGAIN);
233    };
234
235    if let Some(info) = nullable!(info.get_as_mut())? {
236        *info = sig.0;
237    }
238
239    Ok(0)
240}
241
242pub fn sys_rt_sigsuspend(
243    tf: &mut TrapFrame,
244    set: UserConstPtr<SignalSet>,
245    sigsetsize: usize,
246) -> LinuxResult<isize> {
247    check_sigset_size(sigsetsize)?;
248
249    let curr = current();
250    let thr_data = curr.task_ext().thread_data();
251    let mut set = *set.get_as_ref()?;
252
253    set.remove(Signo::SIGKILL);
254    set.remove(Signo::SIGSTOP);
255
256    let old_blocked = thr_data
257        .signal
258        .with_blocked_mut(|blocked| mem::replace(blocked, set));
259
260    tf.set_retval(-LinuxError::EINTR.code() as usize);
261
262    loop {
263        if check_signals(tf, Some(old_blocked)) {
264            break;
265        }
266        curr.task_ext().process_data().signal.wait_signal();
267    }
268
269    Ok(0)
270}
271
272pub fn sys_sigaltstack(
273    ss: UserConstPtr<SignalStack>,
274    old_ss: UserPtr<SignalStack>,
275) -> LinuxResult<isize> {
276    current()
277        .task_ext()
278        .thread_data()
279        .signal
280        .with_stack_mut(|stack| {
281            if let Some(old_ss) = nullable!(old_ss.get_as_mut())? {
282                *old_ss = stack.clone();
283            }
284            if let Some(ss) = nullable!(ss.get_as_ref())? {
285                if ss.size <= MINSIGSTKSZ as usize {
286                    return Err(LinuxError::ENOMEM);
287                }
288                let stack_ptr: UserConstPtr<u8> = ss.sp.into();
289                let _ = stack_ptr.get_as_slice(ss.size)?;
290
291                *stack = ss.clone();
292            }
293            Ok(0)
294        })
295}