1#[cfg(any(feature = "debug_signal_handler", feature = "debug_channels"))]
11use log::debug;
12use log::error;
13#[cfg(any(target_os = "linux", feature = "debug_signal_handler", not(test)))]
14use log::info;
15
16#[cfg(target_os = "linux")]
17use nix::unistd::gettid;
18
19use crate::launch::channels::{AquaReceiver, AquaSender};
20use crate::launch::execution_config::ExecutionConfig;
21use crate::utilities::channel_content::InternalCommand;
22pub(crate) use crate::utilities::signal_handler_channels::SignalHandlerChannels;
23use spin_sleep::SpinSleeper;
24use std::sync::atomic::{AtomicBool, Ordering};
25use std::{sync::Arc, time::Duration};
26
27fn send_command(
43 command: InternalCommand,
44 tx_to_thread_opt: Option<&mut AquaSender<InternalCommand>>,
45 rx_from_thread_opt: Option<&mut AquaReceiver<bool>>,
46 thread_name: &str,
47) {
48 match tx_to_thread_opt {
49 Some(tx_to_thread) => {
50 match tx_to_thread.send(command) {
51 Ok(_) => {
52 #[cfg(feature = "debug_signal_handler")]
53 debug!(target: module_path!(), "QUIT sent to {}", thread_name);
54
55 match rx_from_thread_opt {
56 Some(rx_from_thread) => match rx_from_thread.recv() {
57 Ok(_) => {
58 #[cfg(feature = "debug_signal_handler")]
59 debug!(target: module_path!(), "received acknowledgement from {}", thread_name);
60 }
61 Err(e) => {
62 error!(
63 target: module_path!(),
64 "could not receive answer from {thread_name} thread {e:?}"
65 );
66 }
67 },
68 None => { }
69 };
70 }
71 Err(e) => {
72 error!(
73 target: module_path!(),
74 "channel communication to {thread_name} thread failed when sending Quit command ({e:?})"
75 );
76 }
77 }
78 }
79 None => {
80 #[cfg(feature = "debug_signal_handler")]
82 {
83 let message = format!("not sending QUIT to {thread_name}");
84 debug!(target: module_path!(), "{message}");
85 }
86 }
87 }
88}
89
90#[cfg_attr(doc, aquamarine::aquamarine)]
111pub fn handle_signals(
146 term: Arc<AtomicBool>,
147 mut signal_handler_channels: SignalHandlerChannels,
148 execution_config: ExecutionConfig,
149) {
150 #[cfg(target_os = "linux")]
151 info!(target: module_path!(), "Thread started with TID: {}", gettid());
152
153 if signal_handler_channels
154 .tx_signal_handler_to_tcp_opt
155 .is_some()
156 != signal_handler_channels
157 .rx_signal_handler_from_tcp_opt
158 .is_some()
159 {
160 panic!(
161 "{}: inconsistent channel parameters for TcpCommunication provided",
162 module_path!()
163 );
164 }
165 let use_simulator = signal_handler_channels
166 .tx_signal_handler_to_tcp_opt
167 .is_some();
168
169 let spin_sleeper = SpinSleeper::default();
170 let sleep_interval_millis = 100;
171 let sleep_duration_hundred_milli_sec = Duration::from_millis(sleep_interval_millis);
172
173 for signal in signal_hook::consts::TERM_SIGNALS {
174 match signal_hook::flag::register(*signal, Arc::clone(&term)) {
175 Ok(_) => {
176 #[cfg(feature = "debug_signal_handler")]
177 info!(
178 target: module_path!(),
179 "registered hook for signal {}",
180 signal
181 );
182 }
183 Err(e) => {
184 panic!("{}: Could not hook SIGTERM routine: {e:?}", module_path!());
185 }
186 }
187 }
188
189 while !term.load(Ordering::Relaxed) {
190 spin_sleeper.sleep(sleep_duration_hundred_milli_sec);
191 }
192 #[cfg(not(test))]
193 info!(
194 target: module_path!(),
195 "received termination signal from operating system",
196 );
197
198 if execution_config.data_logger {
201 send_command(
202 InternalCommand::Quit,
203 Some(&mut signal_handler_channels.tx_signal_handler_to_data_logger),
204 Some(&mut signal_handler_channels.rx_signal_handler_from_data_logger),
205 "data_logger",
206 );
207 }
208 send_command(
209 InternalCommand::Quit,
210 signal_handler_channels
211 .tx_signal_handler_to_messaging_opt
212 .as_mut(),
213 signal_handler_channels
214 .rx_signal_handler_from_messaging_opt
215 .as_mut(),
216 "messaging",
217 );
218 #[cfg(feature = "debug_signal_handler")]
219 debug!(
220 target: module_path!(),
221 "phase 1 of termination concluded",
222 );
223
224 if execution_config.refill {
227 send_command(
228 InternalCommand::Quit,
229 Some(&mut signal_handler_channels.tx_signal_handler_to_refill),
230 Some(&mut signal_handler_channels.rx_signal_handler_from_refill),
231 "refill",
232 );
233 }
234 if execution_config.heating {
235 send_command(
236 InternalCommand::Quit,
237 Some(&mut signal_handler_channels.tx_signal_handler_to_heating),
238 Some(&mut signal_handler_channels.rx_signal_handler_from_heating),
239 "heating",
240 );
241 }
242 if execution_config.ventilation {
243 send_command(
244 InternalCommand::Quit,
245 Some(&mut signal_handler_channels.tx_signal_handler_to_ventilation),
246 Some(&mut signal_handler_channels.rx_signal_handler_from_ventilation),
247 "ventilation",
248 );
249 }
250 if execution_config.feed {
251 send_command(
252 InternalCommand::Quit,
253 Some(&mut signal_handler_channels.tx_signal_handler_to_feed),
254 Some(&mut signal_handler_channels.rx_signal_handler_from_feed),
255 "feed",
256 );
257 }
258 if execution_config.balling {
259 send_command(
260 InternalCommand::Quit,
261 Some(&mut signal_handler_channels.tx_signal_handler_to_balling),
262 Some(&mut signal_handler_channels.rx_signal_handler_from_balling),
263 "balling",
264 );
265 }
266 #[cfg(feature = "debug_signal_handler")]
267 debug!(
268 target: module_path!(),
269 "phase 2 of termination concluded",
270 );
271
272 if execution_config.tank_level_switch {
275 send_command(
276 InternalCommand::Quit,
277 Some(&mut signal_handler_channels.tx_signal_handler_to_tank_level_switch),
278 Some(&mut signal_handler_channels.rx_signal_handler_from_tank_level_switch),
279 "tank_level_switch",
280 );
281 }
282 if !use_simulator && execution_config.atlas_scientific {
283 send_command(
284 InternalCommand::Quit,
285 Some(&mut signal_handler_channels.tx_signal_handler_to_atlas_scientific),
286 Some(&mut signal_handler_channels.rx_signal_handler_from_atlas_scientific),
287 "atlas_scientific",
288 );
289 }
290 if execution_config.sensor_manager {
291 send_command(
292 InternalCommand::Quit,
293 Some(&mut signal_handler_channels.tx_signal_handler_to_sensor_manager),
294 Some(&mut signal_handler_channels.rx_signal_handler_from_sensor_manager),
295 "ambient",
296 );
297 }
298 if execution_config.schedule_check {
299 send_command(
300 InternalCommand::Quit,
301 Some(&mut signal_handler_channels.tx_signal_handler_to_schedule_check),
302 Some(&mut signal_handler_channels.rx_signal_handler_from_schedule_check),
303 "schedule_check",
304 );
305 }
306 #[cfg(feature = "debug_signal_handler")]
307 debug!(
308 target: module_path!(),
309 "phase 3 of termination concluded",
310 );
311
312 #[cfg(all(feature = "target_hw", target_os = "linux"))]
314 {
315 if execution_config.i2c_interface {
316 send_command(
317 InternalCommand::Quit,
318 Some(&mut signal_handler_channels.tx_signal_handler_to_i2c_interface),
319 None,
320 "i2c_interface",
321 );
322 }
323 if execution_config.dht {
324 send_command(
325 InternalCommand::Quit,
326 Some(&mut signal_handler_channels.tx_signal_handler_to_dht),
327 None,
328 "dht",
329 );
330 }
331 }
332 if execution_config.relay_manager {
333 send_command(
334 InternalCommand::Quit,
335 Some(&mut signal_handler_channels.tx_signal_handler_to_relay_manager),
336 Some(&mut signal_handler_channels.rx_signal_handler_from_relay_manager),
337 "relay_manager",
338 );
339 }
340 send_command(
341 InternalCommand::Quit,
342 signal_handler_channels
343 .tx_signal_handler_to_tcp_opt
344 .as_mut(),
345 signal_handler_channels
346 .rx_signal_handler_from_tcp_opt
347 .as_mut(),
348 "tcp",
349 );
350 if execution_config.monitors {
351 send_command(
352 InternalCommand::Quit,
353 Some(&mut signal_handler_channels.tx_signal_handler_to_monitors),
354 Some(&mut signal_handler_channels.rx_signal_handler_from_monitors),
355 "monitors",
356 );
357 }
358 #[cfg(feature = "debug_signal_handler")]
359 debug!(
360 target: module_path!(),
361 "phase 4 of termination concluded",
362 );
363
364 if execution_config.data_logger {
366 send_command(
367 InternalCommand::Terminate,
368 Some(&mut signal_handler_channels.tx_signal_handler_to_data_logger),
369 None,
370 "data_logger",
371 );
372 }
373 #[cfg(feature = "debug_signal_handler")]
374 debug!(
375 target: module_path!(),
376 "phase 5 of termination concluded",
377 );
378
379 if execution_config.heating {
381 send_command(
382 InternalCommand::Terminate,
383 Some(&mut signal_handler_channels.tx_signal_handler_to_heating),
384 None,
385 "heating",
386 );
387 }
388 if execution_config.ventilation {
389 send_command(
390 InternalCommand::Terminate,
391 Some(&mut signal_handler_channels.tx_signal_handler_to_ventilation),
392 None,
393 "ventilation",
394 );
395 }
396 if execution_config.feed {
397 send_command(
398 InternalCommand::Terminate,
399 Some(&mut signal_handler_channels.tx_signal_handler_to_feed),
400 None,
401 "feed",
402 );
403 }
404 if execution_config.refill {
405 send_command(
406 InternalCommand::Terminate,
407 Some(&mut signal_handler_channels.tx_signal_handler_to_refill),
408 None,
409 "refill",
410 );
411 }
412 if execution_config.balling {
413 send_command(
414 InternalCommand::Terminate,
415 Some(&mut signal_handler_channels.tx_signal_handler_to_balling),
416 None,
417 "balling",
418 );
419 }
420 #[cfg(feature = "debug_signal_handler")]
421 debug!(
422 target: module_path!(),
423 "phase 6 of termination concluded",
424 );
425
426 if execution_config.ds18b20 {
428 send_command(
429 InternalCommand::Quit,
430 Some(&mut signal_handler_channels.tx_signal_handler_to_ds18b20),
431 None,
432 "ds18b20",
433 );
434 }
435 #[cfg(all(feature = "target_hw", target_os = "linux"))]
436 {
437 if execution_config.atlas_scientific {
438 send_command(
439 InternalCommand::Terminate,
440 Some(&mut signal_handler_channels.tx_signal_handler_to_atlas_scientific),
441 None,
442 "atlas_scientific",
443 );
444 }
445 }
446
447 if execution_config.sensor_manager {
448 send_command(
449 InternalCommand::Terminate,
450 Some(&mut signal_handler_channels.tx_signal_handler_to_sensor_manager),
451 None,
452 "ambient",
453 );
454 }
455 if execution_config.tank_level_switch {
456 send_command(
457 InternalCommand::Terminate,
458 Some(&mut signal_handler_channels.tx_signal_handler_to_tank_level_switch),
459 None,
460 "tank_level_switch",
461 );
462 }
463 #[cfg(feature = "debug_signal_handler")]
464 debug!(
465 target: module_path!(),
466 "phase 7 of termination concluded",
467 );
468
469 if execution_config.watchdog {
471 send_command(
472 InternalCommand::Terminate,
473 Some(&mut signal_handler_channels.tx_signal_handler_to_watchdog),
474 Some(&mut signal_handler_channels.rx_signal_handler_from_watchdog),
475 "watchdog",
476 );
477 }
478 send_command(
479 InternalCommand::Terminate,
480 signal_handler_channels
481 .tx_signal_handler_to_messaging_opt
482 .as_mut(),
483 None,
484 "messaging",
485 );
486 send_command(
487 InternalCommand::Quit,
488 Some(&mut signal_handler_channels.tx_signal_handler_to_memory),
489 None,
490 "memory",
491 );
492
493 println!("Termination of application completed");
494
495 #[cfg(feature = "debug_channels")]
496 debug!(target: module_path!(), "{signal_handler_channels}");
497}
498
499#[cfg(test)]
500pub mod tests {
501 use crate::launch::channels::Channels;
502 use crate::launch::execution_config::ExecutionConfig;
503 use crate::utilities::channel_content::InternalCommand;
504 use crate::utilities::signal_handler::handle_signals;
505 use spin_sleep::SpinSleeper;
506 use std::sync::atomic::{AtomicBool, Ordering};
507 use std::time::Duration;
508 use std::{process::Command, sync::Arc, thread};
509
510 fn send_signal(pid: u32, signal: i32) {
512 Command::new("kill")
513 .args(&[pid.to_string().as_str()])
514 .output()
515 .expect("Failed to send signal");
516 println!("Sent signal {} to {}", signal, pid);
517 }
518
519 #[test]
521 fn test_signal_handling() {
522 let term = Arc::new(AtomicBool::new(false));
523 let term_clone = Arc::clone(&term);
524
525 let mut channels = Channels::new_for_test();
526
527 let join_handle_test_object = thread::Builder::new()
529 .name("test_object".to_string())
530 .spawn(move || {
531 handle_signals(
532 term_clone,
533 channels.signal_handler,
534 ExecutionConfig::default(),
535 );
536 })
537 .unwrap();
538
539 let pid = std::process::id();
541
542 let spin_sleeper = SpinSleeper::default();
543 let sleep_duration_one_second = Duration::from_secs(1);
544
545 spin_sleeper.sleep(sleep_duration_one_second);
546
547 send_signal(pid, signal_hook::consts::signal::SIGABRT);
548
549 assert_eq!(
550 channels
551 .data_logger
552 .rx_data_logger_from_signal_handler
553 .recv()
554 .unwrap(),
555 InternalCommand::Quit
556 );
557 _ = channels
558 .data_logger
559 .tx_data_logger_to_signal_handler
560 .send(true);
561
562 assert_eq!(
563 channels
564 .messaging
565 .rx_messaging_from_signal_handler
566 .recv()
567 .unwrap(),
568 InternalCommand::Quit
569 );
570 _ = channels.messaging.tx_messaging_to_signal_handler.send(true);
571
572 assert_eq!(
573 channels
574 .refill
575 .rx_refill_from_signal_handler
576 .recv()
577 .unwrap(),
578 InternalCommand::Quit
579 );
580 _ = channels.refill.tx_refill_to_signal_handler.send(true);
581
582 assert_eq!(
583 channels
584 .heating
585 .rx_heating_from_signal_handler
586 .recv()
587 .unwrap(),
588 InternalCommand::Quit
589 );
590 _ = channels.heating.tx_heating_to_signal_handler.send(true);
591
592 assert_eq!(
593 channels
594 .ventilation
595 .rx_ventilation_from_signal_handler
596 .recv()
597 .unwrap(),
598 InternalCommand::Quit
599 );
600 _ = channels
601 .ventilation
602 .tx_ventilation_to_signal_handler
603 .send(true);
604
605 assert_eq!(
606 channels.feed.rx_feed_from_signal_handler.recv().unwrap(),
607 InternalCommand::Quit
608 );
609 _ = channels.feed.tx_feed_to_signal_handler.send(true);
610
611 assert_eq!(
612 channels
613 .balling
614 .rx_balling_from_signal_handler
615 .recv()
616 .unwrap(),
617 InternalCommand::Quit
618 );
619 _ = channels.balling.tx_balling_to_signal_handler.send(true);
620
621 assert_eq!(
622 channels
623 .tank_level_switch
624 .rx_tank_level_switch_from_signal_handler
625 .recv()
626 .unwrap(),
627 InternalCommand::Quit
628 );
629 _ = channels
630 .tank_level_switch
631 .tx_tank_level_switch_to_signal_handler
632 .send(true);
633 #[cfg(all(feature = "target_hw", target_os = "linux"))]
634 {
635 assert_eq!(
636 channels
637 .atlas_scientific
638 .rx_atlas_scientific_from_signal_handler
639 .recv()
640 .unwrap(),
641 InternalCommand::Quit
642 );
643 _ = channels
644 .atlas_scientific
645 .tx_atlas_scientific_to_signal_handler
646 .send(true);
647 }
648 assert_eq!(
649 channels
650 .sensor_manager
651 .rx_sensor_manager_from_signal_handler
652 .recv()
653 .unwrap(),
654 InternalCommand::Quit
655 );
656 _ = channels
657 .sensor_manager
658 .tx_sensor_manager_to_signal_handler
659 .send(true);
660 assert_eq!(
661 channels
662 .schedule_check
663 .rx_schedule_check_from_signal_handler
664 .recv()
665 .unwrap(),
666 InternalCommand::Quit
667 );
668 _ = channels
669 .schedule_check
670 .tx_schedule_check_to_signal_handler
671 .send(true);
672 #[cfg(all(target_os = "linux", feature = "target_hw"))]
673 assert_eq!(
674 channels
675 .i2c_interface
676 .rx_i2c_interface_from_signal_handler
677 .recv()
678 .unwrap(),
679 InternalCommand::Quit
680 );
681 #[cfg(all(target_os = "linux", feature = "target_hw"))]
684 assert_eq!(
685 channels.dht.rx_dht_from_signal_handler.recv().unwrap(),
686 InternalCommand::Quit
687 );
688 assert_eq!(
691 channels
692 .relay_manager
693 .rx_relay_manager_from_signal_handler
694 .recv()
695 .unwrap(),
696 InternalCommand::Quit
697 );
698 _ = channels
699 .relay_manager
700 .tx_relay_manager_to_signal_handler
701 .send(true);
702
703 assert_eq!(
704 channels
705 .tcp_communication
706 .rx_tcp_communication_from_signal_handler
707 .recv()
708 .unwrap(),
709 InternalCommand::Quit
710 );
711 _ = channels
712 .tcp_communication
713 .tx_tcp_communication_to_signal_handler
714 .send(true);
715
716 assert_eq!(
717 channels
718 .monitors
719 .rx_monitors_from_signal_handler
720 .recv()
721 .unwrap(),
722 InternalCommand::Quit
723 );
724 _ = channels.monitors.tx_monitors_to_signal_handler.send(true);
725
726 assert_eq!(
727 channels
728 .data_logger
729 .rx_data_logger_from_signal_handler
730 .recv()
731 .unwrap(),
732 InternalCommand::Terminate
733 );
734 assert_eq!(
737 channels
738 .heating
739 .rx_heating_from_signal_handler
740 .recv()
741 .unwrap(),
742 InternalCommand::Terminate
743 );
744 assert_eq!(
747 channels
748 .ventilation
749 .rx_ventilation_from_signal_handler
750 .recv()
751 .unwrap(),
752 InternalCommand::Terminate
753 );
754 assert_eq!(
757 channels.feed.rx_feed_from_signal_handler.recv().unwrap(),
758 InternalCommand::Terminate
759 );
760 assert_eq!(
763 channels
764 .refill
765 .rx_refill_from_signal_handler
766 .recv()
767 .unwrap(),
768 InternalCommand::Terminate
769 );
770 assert_eq!(
773 channels
774 .balling
775 .rx_balling_from_signal_handler
776 .recv()
777 .unwrap(),
778 InternalCommand::Terminate
779 );
780 assert_eq!(
783 channels
784 .ds18b20
785 .rx_ds18b20_from_signal_handler
786 .recv()
787 .unwrap(),
788 InternalCommand::Quit
789 );
790 #[cfg(all(feature = "target_hw", target_os = "linux"))]
793 {
794 assert_eq!(
795 channels
796 .atlas_scientific
797 .rx_atlas_scientific_from_signal_handler
798 .recv()
799 .unwrap(),
800 InternalCommand::Terminate
801 );
802 }
803 assert_eq!(
806 channels
807 .sensor_manager
808 .rx_sensor_manager_from_signal_handler
809 .recv()
810 .unwrap(),
811 InternalCommand::Terminate
812 );
813 assert_eq!(
816 channels
817 .tank_level_switch
818 .rx_tank_level_switch_from_signal_handler
819 .recv()
820 .unwrap(),
821 InternalCommand::Terminate
822 );
823 assert_eq!(
826 channels
827 .watchdog
828 .rx_watchdog_from_signal_handler
829 .recv()
830 .unwrap(),
831 InternalCommand::Terminate
832 );
833 _ = channels.watchdog.tx_watchdog_to_signal_handler.send(true);
834
835 assert_eq!(
836 channels
837 .messaging
838 .rx_messaging_from_signal_handler
839 .recv()
840 .unwrap(),
841 InternalCommand::Terminate
842 );
843 join_handle_test_object
846 .join()
847 .expect("Test object thread did not finish.");
848
849 assert!(term.load(Ordering::Acquire));
850 }
851}