1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 use std::io;
15 use std::mem::MaybeUninit;
16 use std::os::raw::c_int;
17 use std::sync::atomic::{AtomicUsize, Ordering};
18 use std::sync::{Arc, Once};
19
20 /// SDV test cases
21 ///
22 /// Because the following tests cannot be executed in parallel for
23 /// there are only a few signals in windows that we have to use the same signal
24 /// in different test case, so there is a test all case which execute all tests
25 /// serially.
26 #[test]
sdv_test_all()27 fn sdv_test_all() {
28 sdv_signal_register_succeed();
29 sdv_signal_register_failed();
30 sdv_signal_register_with_old();
31 #[cfg(not(windows))]
32 sdv_signal_register_multi();
33 }
34
35 /// SDV cases for signal register
36 ///
37 /// # Brief
38 /// 1. Registers two different signals with actions that increment two different
39 /// atomic usize.
40 /// 2. Manually raises the two signals, checks if the registered action behave
41 /// correctly.
42 /// 3. Deregisters the action of the two signals
43 /// 4. Registers the same action for one of the signals again
44 /// 5. Manually raises the signal, checks if the registered action behave
45 /// correctly
46 /// 6. Deregisters both signal's handler hook, checks if the return is ok.
sdv_signal_register_succeed()47 fn sdv_signal_register_succeed() {
48 let value = Arc::new(AtomicUsize::new(0));
49 let value_cpy = value.clone();
50
51 let value2 = Arc::new(AtomicUsize::new(10));
52 let value2_cpy = value2.clone();
53 let value2_cpy2 = value2.clone();
54
55 let res = unsafe {
56 ylong_signal::register_signal_action(libc::SIGINT, move || {
57 value_cpy.fetch_add(1, Ordering::Relaxed);
58 })
59 };
60 assert!(res.is_ok());
61
62 let res = unsafe {
63 ylong_signal::register_signal_action(libc::SIGTERM, move || {
64 value2_cpy.fetch_add(10, Ordering::Relaxed);
65 })
66 };
67 assert!(res.is_ok());
68 assert_eq!(value.load(Ordering::Relaxed), 0);
69
70 unsafe { libc::raise(libc::SIGINT) };
71 assert_eq!(value.load(Ordering::Relaxed), 1);
72 assert_eq!(value2.load(Ordering::Relaxed), 10);
73
74 unsafe { libc::raise(libc::SIGTERM) };
75 assert_eq!(value.load(Ordering::Relaxed), 1);
76 assert_eq!(value2.load(Ordering::Relaxed), 20);
77
78 let res = ylong_signal::deregister_signal_action(libc::SIGTERM);
79 assert!(res.is_ok());
80
81 ylong_signal::deregister_signal_action(libc::SIGINT).unwrap();
82
83 let res = unsafe {
84 ylong_signal::register_signal_action(libc::SIGTERM, move || {
85 value2_cpy2.fetch_add(20, Ordering::Relaxed);
86 })
87 };
88 assert!(res.is_ok());
89
90 unsafe { libc::raise(libc::SIGTERM) };
91 assert_eq!(value2.load(Ordering::Relaxed), 40);
92
93 let res = ylong_signal::deregister_signal_hook(libc::SIGTERM);
94 assert!(res.is_ok());
95
96 let res = ylong_signal::deregister_signal_hook(libc::SIGINT);
97 assert!(res.is_ok());
98 }
99
100 /// SDV cases for signal register error handling
101 ///
102 /// # Brief
103 /// 1. Registers an action for a forbidden signal
104 /// 2. Checks if the return value is InvalidInput error
105 /// 3. Registers an action for an allowed signal
106 /// 4. Checks if the return value is Ok
107 /// 5. Registers an action for the same signal again
108 /// 6. Checks if the return value is AlreadyExists error
109 /// 7. Deregisters the signal hook of the previous registered signal
110 /// 8. Checks if the return value is OK
111 /// 9. Deregisters the signal action of an unregistered signal
112 /// 10. Deregisters the signal handler of an unregistered signal
113 /// 11. Checks if the return value is Ok
sdv_signal_register_failed()114 fn sdv_signal_register_failed() {
115 let res = unsafe { ylong_signal::register_signal_action(libc::SIGSEGV, move || {}) };
116 assert_eq!(res.unwrap_err().kind(), io::ErrorKind::InvalidInput);
117
118 let res = unsafe { ylong_signal::register_signal_action(libc::SIGTERM, move || {}) };
119 assert!(res.is_ok());
120 let res = unsafe { ylong_signal::register_signal_action(libc::SIGTERM, move || {}) };
121 assert_eq!(res.unwrap_err().kind(), io::ErrorKind::AlreadyExists);
122
123 let res = ylong_signal::deregister_signal_hook(libc::SIGTERM);
124 assert!(res.is_ok());
125
126 let res = ylong_signal::deregister_signal_action(libc::SIGSEGV);
127 assert!(res.is_ok());
128
129 let res = ylong_signal::deregister_signal_hook(libc::SIGSEGV);
130 assert!(res.is_ok());
131 }
132
133 /// SDV cases for signal register when there is already an existing handler
134 ///
135 /// # Brief
136 /// 1. Registers a signal handler using libc syscall
137 /// 2. Registers a signal handler using ylong_signal::register_signal_action
138 /// 3. Manually raises the signal
139 /// 4. Checks if the the new action get executed correctly
140 /// 5. Deregisters the signal action
141 /// 6. Manually raises the signal
142 /// 7. Checks if the old handler gets executed correctly
143 /// 8. Deregister the hook.
sdv_signal_register_with_old()144 fn sdv_signal_register_with_old() {
145 #[cfg(not(windows))]
146 {
147 let mut new_act: libc::sigaction = unsafe { std::mem::zeroed() };
148 new_act.sa_sigaction = test_handler as usize;
149 unsafe {
150 libc::sigaction(libc::SIGINT, &new_act, std::ptr::null_mut());
151 }
152 }
153
154 #[cfg(windows)]
155 {
156 unsafe {
157 libc::signal(libc::SIGINT, test_handler as usize);
158 }
159 }
160
161 let res = unsafe {
162 ylong_signal::register_signal_action(libc::SIGINT, move || {
163 let global = Global::get_instance();
164 assert_eq!(global.value.load(Ordering::Relaxed), 0);
165 global.value.fetch_add(2, Ordering::Relaxed);
166 })
167 };
168 assert!(res.is_ok());
169
170 unsafe {
171 libc::raise(libc::SIGINT);
172 }
173
174 let global = Global::get_instance();
175 assert_eq!(global.value.load(Ordering::Relaxed), 2);
176
177 let res = ylong_signal::deregister_signal_action(libc::SIGINT);
178 assert!(res.is_ok());
179
180 unsafe {
181 libc::raise(libc::SIGINT);
182 }
183 assert_eq!(global.value.load(Ordering::Relaxed), 3);
184 let res = ylong_signal::deregister_signal_hook(libc::SIGINT);
185 assert!(res.is_ok());
186 }
187
188 pub struct Global {
189 value: AtomicUsize,
190 }
191
192 impl Global {
get_instance() -> &'static Global193 fn get_instance() -> &'static Global {
194 static mut GLOBAL: MaybeUninit<Global> = MaybeUninit::uninit();
195 static ONCE: Once = Once::new();
196
197 unsafe {
198 ONCE.call_once(|| {
199 GLOBAL = MaybeUninit::new(Global {
200 value: AtomicUsize::new(0),
201 });
202 });
203 &*GLOBAL.as_ptr()
204 }
205 }
206 }
207
test_handler(_sig_num: c_int)208 extern "C" fn test_handler(_sig_num: c_int) {
209 let global = Global::get_instance();
210 global.value.fetch_add(1, Ordering::Relaxed);
211 }
212
213 /// SDV cases for signal register in multi-thread env
214 ///
215 /// # Brief
216 /// 1. Registers a signal handler
217 /// 2. Spawns another thread to raise the signal
218 /// 3. Raises the same signal on the main thread
219 /// 4. All execution should return OK
220 #[cfg(not(windows))]
sdv_signal_register_multi()221 fn sdv_signal_register_multi() {
222 for i in 0..1000 {
223 let res = unsafe {
224 ylong_signal::register_signal_action(libc::SIGCHLD, move || {
225 let mut data = 100;
226 data += i;
227 assert_eq!(data, 100 + i);
228 })
229 };
230 std::thread::spawn(move || {
231 unsafe { libc::raise(libc::SIGCHLD) };
232 });
233 assert!(res.is_ok());
234 unsafe {
235 libc::raise(libc::SIGCHLD);
236 }
237
238 let res = ylong_signal::deregister_signal_action(libc::SIGCHLD);
239 assert!(res.is_ok());
240
241 unsafe {
242 libc::raise(libc::SIGCHLD);
243 }
244 }
245 }
246