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