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