1use std::{collections::HashSet, process};
2
3use crate::{error::Result, parent};
4use log::debug;
5use nix::unistd::{fork, ForkResult, Pid};
6use signal_hook::{
7 consts::{SIGCHLD, SIGINT, SIGQUIT, SIGTERM},
8 iterator::Signals,
9};
10
11pub struct Server<T> {
14 num_processes: u32,
15 resource: Option<T>,
16 child_init: Box<dyn Fn(u32, T)>,
17 child_num: u32,
18}
19
20impl<T> Server<T> {
21 pub fn from_resource(resource: T, child_init: Box<dyn Fn(u32, T)>, num_processes: u32) -> Self {
26 Self {
27 num_processes,
28 resource: Some(resource),
29 child_init,
30 child_num: 0,
31 }
32 }
33
34 pub fn prefork(&mut self) -> Result<Option<HashSet<Pid>>> {
38 let mut pids = HashSet::new();
39 let mut is_parent = true;
40 for _ in 0..self.num_processes {
41 let pid = self.fork()?;
42 if let Some(pid) = pid {
43 pids.insert(pid);
44 } else {
45 is_parent = false;
46 break;
47 }
48 }
49 if is_parent {
50 Ok(Some(pids))
51 } else {
52 Ok(None)
53 }
54 }
55
56 pub fn fork(&mut self) -> Result<Option<Pid>> {
60 match unsafe { fork()? } {
61 ForkResult::Parent { child } => {
62 self.child_num += 1;
63 Ok(Some(child))
64 }
65 ForkResult::Child => {
66 if let Some(resource) = self.resource.take() {
67 (self.child_init)(self.child_num, resource);
68 } else {
69 unreachable!("fork resource is empty");
70 }
71 Ok(None)
72 }
73 }
74 }
75}
76
77pub fn run_parent(mut pids: HashSet<Pid>) -> Result<()> {
78 let parent_pid = process::id();
79 debug!("Handling signals in parent {parent_pid}");
80 let mut signals = Signals::new([SIGINT, SIGTERM, SIGQUIT, SIGCHLD])?;
81 for signal in signals.forever() {
82 match signal {
83 SIGCHLD => {
84 parent::wait_process(&mut pids);
85 if pids.is_empty() {
86 break;
87 }
88 }
89 _ => break,
90 }
91 }
92 parent::kill_all(pids)
93}