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