Skip to content

Commit 3418eb3

Browse files
committed
Introduce exit reasons for the event loop
1 parent 372315f commit 3418eb3

File tree

3 files changed

+125
-52
lines changed

3 files changed

+125
-52
lines changed

sudo/lib/exec/event.rs

Lines changed: 77 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{io, ops::ControlFlow, os::fd::AsRawFd};
1+
use std::{io, os::fd::AsRawFd};
22

33
use crate::log::dev_error;
44
use crate::system::{
@@ -9,8 +9,14 @@ use crate::system::{
99
use signal_hook::consts::*;
1010

1111
pub(super) trait EventClosure: Sized {
12-
/// Reason why the event loop should break. This is the return type of [`EventDispatcher::event_loop`].
12+
/// Reason why the event loop should break.
13+
///
14+
/// See [`EventDispatcher::set_break`] for more information.
1315
type Break;
16+
/// Reason why the event loop should exit.
17+
///
18+
/// See [`EventDispatcher::set_exit`] for more information.
19+
type Exit;
1420
/// Operation that the closure must do when a signal arrives.
1521
fn on_signal(&mut self, info: SignalInfo, dispatcher: &mut EventDispatcher<Self>);
1622
}
@@ -38,7 +44,7 @@ macro_rules! define_signals {
3844
})?,)*],
3945
poll_set: PollSet::new(),
4046
callbacks: Vec::with_capacity(SIGNALS.len()),
41-
status: ControlFlow::Continue(()),
47+
status: Status::Continue,
4248
};
4349

4450
$(
@@ -73,6 +79,53 @@ define_signals! {
7379
SIGWINCH = 11,
7480
}
7581

82+
enum Status<T: EventClosure> {
83+
Continue,
84+
Stop(StopReason<T>),
85+
}
86+
87+
impl<T: EventClosure> Status<T> {
88+
fn is_break(&self) -> bool {
89+
matches!(self, Self::Stop(StopReason::Break(_)))
90+
}
91+
92+
fn take_stop(&mut self) -> Option<StopReason<T>> {
93+
// If the status ends up to be `Continue`, we are replacing it by another `Continue`.
94+
let status = std::mem::replace(self, Self::Continue);
95+
match status {
96+
Status::Continue => None,
97+
Status::Stop(reason) => Some(reason),
98+
}
99+
}
100+
101+
fn take_break(&mut self) -> Option<T::Break> {
102+
match self.take_stop()? {
103+
StopReason::Break(break_reason) => Some(break_reason),
104+
reason @ StopReason::Exit(_) => {
105+
// Replace back the status because it was not a `Break`.
106+
*self = Self::Stop(reason);
107+
None
108+
}
109+
}
110+
}
111+
112+
fn take_exit(&mut self) -> Option<T::Exit> {
113+
match self.take_stop()? {
114+
reason @ StopReason::Break(_) => {
115+
// Replace back the status because it was not an `Exit`.
116+
*self = Self::Stop(reason);
117+
None
118+
}
119+
StopReason::Exit(exit_reason) => Some(exit_reason),
120+
}
121+
}
122+
}
123+
124+
pub(super) enum StopReason<T: EventClosure> {
125+
Break(T::Break),
126+
Exit(T::Exit),
127+
}
128+
76129
#[derive(PartialEq, Eq, Hash, Clone)]
77130
struct EventId(usize);
78131

@@ -83,7 +136,7 @@ pub(super) struct EventDispatcher<T: EventClosure> {
83136
signal_handlers: [SignalHandler; SIGNALS.len()],
84137
poll_set: PollSet<EventId>,
85138
callbacks: Vec<Callback<T>>,
86-
status: ControlFlow<T::Break>,
139+
status: Status<T>,
87140
}
88141

89142
impl<T: EventClosure> EventDispatcher<T> {
@@ -107,7 +160,13 @@ impl<T: EventClosure> EventDispatcher<T> {
107160
///
108161
/// This means that the event loop will stop even if other events are ready.
109162
pub(super) fn set_break(&mut self, reason: T::Break) {
110-
self.status = ControlFlow::Break(reason);
163+
self.status = Status::Stop(StopReason::Break(reason));
164+
}
165+
166+
/// Stop the event loop when the callbacks for the events that are ready by now have been
167+
/// dispatched and set a reason for it.
168+
pub(super) fn set_exit(&mut self, reason: T::Exit) {
169+
self.status = Status::Stop(StopReason::Exit(reason));
111170
}
112171

113172
/// Return whether a break reason has been set already. This function will return `false` after
@@ -118,31 +177,27 @@ impl<T: EventClosure> EventDispatcher<T> {
118177

119178
/// Run the event loop for this handler.
120179
///
121-
/// The event loop will continue indefinitely unless [`EventDispatcher::set_break`] is called.
122-
pub(super) fn event_loop(&mut self, state: &mut T) -> T::Break {
180+
/// The event loop will continue indefinitely unless you call [`EventDispatcher::set_break`] or
181+
/// [`EventDispatcher::set_exit`].
182+
pub(super) fn event_loop(&mut self, state: &mut T) -> StopReason<T> {
123183
loop {
124184
if let Ok(ids) = self.poll_set.poll() {
125185
for EventId(id) in ids {
126186
self.callbacks[id](state, self);
127187

128-
if let Some(break_reason) = self.check_break() {
129-
return break_reason;
188+
if let Some(reason) = self.status.take_break() {
189+
return StopReason::Break(reason);
130190
}
131191
}
192+
if let Some(reason) = self.status.take_exit() {
193+
return StopReason::Exit(reason);
194+
}
195+
} else {
196+
// FIXME: maybe we shout return the IO error instead.
197+
if let Some(reason) = self.status.take_stop() {
198+
return reason;
199+
}
132200
}
133-
134-
if let Some(break_reason) = self.check_break() {
135-
return break_reason;
136-
}
137-
}
138-
}
139-
140-
pub(super) fn check_break(&mut self) -> Option<T::Break> {
141-
// This is OK as we are swapping `Continue(())` by other `Continue(())` if the status is
142-
// not `Break`.
143-
match std::mem::replace(&mut self.status, ControlFlow::Continue(())) {
144-
ControlFlow::Continue(()) => None,
145-
ControlFlow::Break(reason) => Some(reason),
146201
}
147202
}
148203
}

sudo/lib/exec/monitor.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ fn is_self_terminating(
308308

309309
impl<'a> EventClosure for MonitorClosure<'a> {
310310
type Break = ();
311+
type Exit = ();
311312

312313
fn on_signal(&mut self, info: SignalInfo, dispatcher: &mut EventDispatcher<Self>) {
313314
dev_info!(
@@ -327,9 +328,7 @@ impl<'a> EventClosure for MonitorClosure<'a> {
327328
SIGCHLD => {
328329
self.handle_sigchld(command_pid);
329330
if self.command_pid.is_none() {
330-
// FIXME: This is what ogsudo calls a `loopexit`. Meaning that we should still
331-
// dispatch the remaining events to their callbacks and exit nicely.
332-
dispatcher.set_break(());
331+
dispatcher.set_exit(());
333332
}
334333
}
335334
// Skip the signal if it was sent by the user and it is self-terminating.

sudo/lib/exec/parent.rs

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::system::wait::{waitpid, WaitError, WaitOptions};
1212
use crate::system::{chown, fork, Group, User};
1313
use crate::system::{getpgid, interface::ProcessId, signal::SignalInfo};
1414

15-
use super::event::{EventClosure, EventDispatcher};
15+
use super::event::{EventClosure, EventDispatcher, StopReason};
1616
use super::monitor::exec_monitor;
1717
use super::{
1818
backchannel::{BackchannelPair, MonitorMessage, ParentBackchannel, ParentMessage},
@@ -154,15 +154,10 @@ impl ParentClosure {
154154
}
155155

156156
fn run(mut self, dispatcher: &mut EventDispatcher<Self>) -> io::Result<ExitReason> {
157-
let exit_reason = match dispatcher.event_loop(&mut self) {
158-
ParentMessage::IoError(code) => return Err(io::Error::from_raw_os_error(code)),
159-
ParentMessage::CommandExit(code) => ExitReason::Code(code),
160-
ParentMessage::CommandSignal(signal) => ExitReason::Signal(signal),
161-
// We never set this event as the last event
162-
ParentMessage::CommandPid(_) => unreachable!(),
163-
};
164-
165-
Ok(exit_reason)
157+
match dispatcher.event_loop(&mut self) {
158+
StopReason::Break(err) | StopReason::Exit(ParentExit::Backchannel(err)) => Err(err),
159+
StopReason::Exit(ParentExit::Command(exit_reason)) => Ok(exit_reason),
160+
}
166161
}
167162

168163
/// Read an event from the backchannel and return the event if it should break the event loop.
@@ -173,9 +168,15 @@ impl ParentClosure {
173168
// Failed to read command status. This means that something is wrong with the socket
174169
// and we should stop.
175170
Err(err) => {
176-
dev_error!("could not receive message from monitor: {err}");
177-
if !dispatcher.got_break() {
178-
dispatcher.set_break(err.into());
171+
// If we get EOF the monitor exited or was killed
172+
if err.kind() == io::ErrorKind::UnexpectedEof {
173+
dev_info!("parent received EOF from backchannel");
174+
dispatcher.set_exit(err.into());
175+
} else {
176+
dev_error!("could not receive message from monitor: {err}");
177+
if !dispatcher.got_break() {
178+
dispatcher.set_break(err);
179+
}
179180
}
180181
}
181182
Ok(event) => {
@@ -185,26 +186,24 @@ impl ParentClosure {
185186
ParentMessage::CommandPid(pid) => {
186187
dev_info!("received command PID ({pid}) from monitor");
187188
self.command_pid = pid.into();
188-
// Do not set `CommandPid` as a break reason.
189-
return;
190189
}
191190
// The command terminated or the monitor was not able to spawn it. We should stop
192191
// either way.
193-
ParentMessage::CommandExit(_code) => {
194-
dev_info!("command exited with status code {_code}");
192+
ParentMessage::CommandExit(code) => {
193+
dev_info!("command exited with status code {code}");
194+
dispatcher.set_exit(ExitReason::Code(code).into());
195195
}
196196

197-
ParentMessage::CommandSignal(_signal) => {
198-
dev_info!("command was terminated by {}", signal_fmt(_signal))
197+
ParentMessage::CommandSignal(signal) => {
198+
dev_info!("command was terminated by {}", signal_fmt(signal));
199+
dispatcher.set_exit(ExitReason::Signal(signal).into());
199200
}
200-
ParentMessage::IoError(_code) => {
201-
dev_info!(
202-
"received error ({_code}) for monitor: {}",
203-
io::Error::from_raw_os_error(_code)
204-
)
201+
ParentMessage::IoError(code) => {
202+
let err = io::Error::from_raw_os_error(code);
203+
dev_info!("received error ({code}) for monitor: {err}",);
204+
dispatcher.set_break(err);
205205
}
206206
}
207-
dispatcher.set_break(event);
208207
}
209208
}
210209
}
@@ -253,7 +252,7 @@ impl ParentClosure {
253252
Err(err) if err.kind() == io::ErrorKind::BrokenPipe => {
254253
dev_error!("broken pipe while writing to monitor over backchannel");
255254
// FIXME: maybe we need a different event for backchannel errors.
256-
dispatcher.set_break(err.into());
255+
dispatcher.set_break(err);
257256
}
258257
// Non critical error, we can retry later.
259258
Err(_) => {}
@@ -300,8 +299,28 @@ impl ParentClosure {
300299
}
301300
}
302301

302+
enum ParentExit {
303+
/// Error while reading from the backchannel.
304+
Backchannel(io::Error),
305+
/// The command exited.
306+
Command(ExitReason),
307+
}
308+
309+
impl From<io::Error> for ParentExit {
310+
fn from(err: io::Error) -> Self {
311+
Self::Backchannel(err)
312+
}
313+
}
314+
315+
impl From<ExitReason> for ParentExit {
316+
fn from(reason: ExitReason) -> Self {
317+
Self::Command(reason)
318+
}
319+
}
320+
303321
impl EventClosure for ParentClosure {
304-
type Break = ParentMessage;
322+
type Break = io::Error;
323+
type Exit = ParentExit;
305324

306325
fn on_signal(&mut self, info: SignalInfo, _dispatcher: &mut EventDispatcher<Self>) {
307326
dev_info!(

0 commit comments

Comments
 (0)