Skip to content

Commit 25cc1e6

Browse files
authored
Merge pull request #468 from memorysafety/pvdrz-fork-and-exec
Use `fork` and `exec` for the command
2 parents 95093c4 + 1e8b055 commit 25cc1e6

File tree

4 files changed

+278
-90
lines changed

4 files changed

+278
-90
lines changed

sudo/lib/exec/backchannel.rs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ use std::{
44
mem::size_of,
55
os::{
66
fd::{AsRawFd, RawFd},
7-
unix::{net::UnixStream, process::ExitStatusExt},
7+
unix::net::UnixStream,
88
},
9-
process::ExitStatus,
109
};
1110

1211
use crate::system::{interface::ProcessId, signal::SignalNumber};
1312

14-
use super::signal_fmt;
13+
use super::{signal_fmt, ExitReason};
1514

1615
type Prefix = u8;
1716
type ParentData = c_int;
@@ -89,14 +88,11 @@ impl From<io::Error> for ParentMessage {
8988
}
9089
}
9190

92-
impl From<ExitStatus> for ParentMessage {
93-
fn from(status: ExitStatus) -> Self {
94-
if let Some(code) = status.code() {
95-
Self::CommandExit(code)
96-
} else {
97-
// `ExitStatus::code` docs state that it only returns `None` if the process was
98-
// terminated by a signal so this should always succeed.
99-
Self::CommandSignal(status.signal().unwrap())
91+
impl From<ExitReason> for ParentMessage {
92+
fn from(reason: ExitReason) -> Self {
93+
match reason {
94+
ExitReason::Code(code) => Self::CommandExit(code),
95+
ExitReason::Signal(signal) => Self::CommandSignal(signal),
10096
}
10197
}
10298
}

sudo/lib/exec/event.rs

Lines changed: 81 additions & 19 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,34 @@ 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
}
132-
}
133-
134-
if let Some(break_reason) = self.check_break() {
135-
return break_reason;
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+
}
136200
}
137201
}
138202
}
139203

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),
204+
/// Unregister all the handlers created by the dispatcher.
205+
pub(super) fn unregister_handlers(self) {
206+
for handler in self.signal_handlers {
207+
handler.unregister();
146208
}
147209
}
148210
}

0 commit comments

Comments
 (0)