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 14use std::io; 15use std::mem::MaybeUninit; 16use std::os::raw::c_int; 17use std::sync::atomic::{AtomicUsize, Ordering}; 18use 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] 27fn 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. 47fn 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 114fn 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. 144fn 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 188pub struct Global { 189 value: AtomicUsize, 190} 191 192impl Global { 193 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 208extern "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))] 221fn 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