Skip to content

Commit 23e8b7e

Browse files
committed
[diagnostic] extend from_env function
1 parent ca812d9 commit 23e8b7e

File tree

6 files changed

+112
-52
lines changed

6 files changed

+112
-52
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ edition = "2018"
1414
[target.'cfg(unix)'.dependencies]
1515
libc = "0.2.50"
1616

17+
[features]
18+
check_pipe = []
19+
1720
[dev-dependencies]
1821
futures = "0.1"
1922
num_cpus = "1.0"

src/lib.rs

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
//!
3030
//! // See API documentation for why this is `unsafe`
3131
//! let client = match unsafe { Client::from_env() } {
32-
//! Some(client) => client,
33-
//! None => panic!("client not configured"),
32+
//! Ok(client) => client,
33+
//! Err(_) => panic!("client not configured"),
3434
//! };
3535
//! ```
3636
//!
@@ -151,6 +151,22 @@ struct HelperInner {
151151
consumer_done: bool,
152152
}
153153

154+
/// Error type for `from_env` function.
155+
#[derive(Debug)]
156+
pub enum ErrFromEnv {
157+
/// There isn't env var, that describes jobserver to inherit.
158+
IsNotConfigured,
159+
/// Cannot connect following this process's environment.
160+
PlatformSpecific {
161+
/// Error.
162+
err: imp::ErrFromEnv,
163+
/// Name of gotten env var.
164+
env: &'static str,
165+
/// Value of gotten env var.
166+
var: String,
167+
},
168+
}
169+
154170
impl Client {
155171
/// Creates a new jobserver initialized with the given parallelism limit.
156172
///
@@ -197,8 +213,9 @@ impl Client {
197213
/// # Return value
198214
///
199215
/// If a jobserver was found in the environment and it looks correct then
200-
/// `Some` of the connected client will be returned. If no jobserver was
201-
/// found then `None` will be returned.
216+
/// `Ok` of the connected client will be returned. If no relevant env var
217+
/// was found then `Err(IsNotConfigured)` will be returned. In other cases
218+
/// `Err(PlatformSpecific)` will be returned.
202219
///
203220
/// Note that on Unix the `Client` returned **takes ownership of the file
204221
/// descriptors specified in the environment**. Jobservers on Unix are
@@ -211,6 +228,9 @@ impl Client {
211228
/// with `CLOEXEC` so they're not automatically inherited by spawned
212229
/// children.
213230
///
231+
/// There is a feature to configure, will this function check if provided
232+
/// files are actually pipes.
233+
///
214234
/// # Safety
215235
///
216236
/// This function is `unsafe` to call on Unix specifically as it
@@ -227,28 +247,24 @@ impl Client {
227247
///
228248
/// Note, though, that on Windows it should be safe to call this function
229249
/// any number of times.
230-
pub unsafe fn from_env() -> Option<Client> {
231-
let var = match env::var("CARGO_MAKEFLAGS")
232-
.or_else(|_| env::var("MAKEFLAGS"))
233-
.or_else(|_| env::var("MFLAGS"))
234-
{
235-
Ok(s) => s,
236-
Err(_) => return None,
237-
};
238-
let mut arg = "--jobserver-fds=";
239-
let pos = match var.find(arg) {
240-
Some(i) => i,
241-
None => {
242-
arg = "--jobserver-auth=";
243-
match var.find(arg) {
244-
Some(i) => i,
245-
None => return None,
246-
}
247-
}
248-
};
250+
pub unsafe fn from_env() -> Result<Client, ErrFromEnv> {
251+
let (env, var) = ["CARGO_MAKEFLAGS", "MAKEFLAGS", "MFLAGS"]
252+
.iter()
253+
.map(|&env| env::var(env).map(|var| (env, var)))
254+
.find_map(|p| p.ok())
255+
.ok_or(ErrFromEnv::IsNotConfigured)?;
256+
257+
let (arg, pos) = ["--jobserver-fds=", "--jobserver-auth="]
258+
.iter()
259+
.map(|&arg| var.find(arg).map(|pos| (arg, pos)))
260+
.find_map(|pos| pos)
261+
.ok_or(ErrFromEnv::IsNotConfigured)?;
249262

250263
let s = var[pos + arg.len()..].split(' ').next().unwrap();
251-
imp::Client::open(s).map(|c| Client { inner: Arc::new(c) })
264+
match imp::Client::open(s) {
265+
Ok(c) => Ok(Client { inner: Arc::new(c) }),
266+
Err(err) => Err(ErrFromEnv::PlatformSpecific { err, env, var }),
267+
}
252268
}
253269

254270
/// Acquires a token from this jobserver client.

src/unix.rs

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@ pub struct Acquired {
2525
byte: u8,
2626
}
2727

28+
#[derive(Debug)]
29+
pub enum ErrFromEnv {
30+
ParseEnvVar,
31+
OpenFile(String),
32+
InvalidDescriptor(i32, i32),
33+
}
34+
2835
impl Client {
2936
pub fn new(mut limit: usize) -> io::Result<Client> {
3037
let client = unsafe { Client::mk()? };
@@ -81,61 +88,66 @@ impl Client {
8188
Ok(Client::from_fds(pipes[0], pipes[1]))
8289
}
8390

84-
pub unsafe fn open(s: &str) -> Option<Client> {
85-
Client::from_fifo(s).or_else(|| Client::from_pipe(s))
91+
pub unsafe fn open(s: &str) -> Result<Client, ErrFromEnv> {
92+
match (Self::from_fifo(s), Self::from_pipe(s)) {
93+
(Some(result), None) | (None, Some(result)) => result,
94+
(None, None) => Err(ErrFromEnv::ParseEnvVar),
95+
(Some(_), Some(_)) => unreachable!(),
96+
}
8697
}
8798

8899
/// `--jobserver-auth=fifo:PATH`
89-
fn from_fifo(s: &str) -> Option<Client> {
100+
fn from_fifo(s: &str) -> Option<Result<Client, ErrFromEnv>> {
90101
let mut parts = s.splitn(2, ':');
91102
if parts.next().unwrap() != "fifo" {
92103
return None;
93104
}
94105
let path = match parts.next() {
95106
Some(p) => Path::new(p),
96-
None => return None,
107+
None => return Some(Err(ErrFromEnv::ParseEnvVar)),
97108
};
98109
let file = match OpenOptions::new().read(true).write(true).open(path) {
99110
Ok(f) => f,
100-
Err(_) => return None,
111+
Err(e) => return Some(Err(ErrFromEnv::OpenFile(e.to_string()))),
101112
};
102-
Some(Client::Fifo {
113+
Some(Ok(Client::Fifo {
103114
file,
104115
path: path.into(),
105-
})
116+
}))
106117
}
107118

108119
/// `--jobserver-auth=R,W`
109-
unsafe fn from_pipe(s: &str) -> Option<Client> {
120+
unsafe fn from_pipe(s: &str) -> Option<Result<Client, ErrFromEnv>> {
110121
let mut parts = s.splitn(2, ',');
111122
let read = parts.next().unwrap();
112123
let write = match parts.next() {
113124
Some(s) => s,
114-
None => return None,
125+
None => return Some(Err(ErrFromEnv::ParseEnvVar)),
115126
};
116127

117128
let read = match read.parse() {
118129
Ok(n) => n,
119-
Err(_) => return None,
130+
Err(_) => return Some(Err(ErrFromEnv::ParseEnvVar)),
120131
};
121132
let write = match write.parse() {
122133
Ok(n) => n,
123-
Err(_) => return None,
134+
Err(_) => return Some(Err(ErrFromEnv::ParseEnvVar)),
124135
};
125136

126137
// Ok so we've got two integers that look like file descriptors, but
127138
// for extra sanity checking let's see if they actually look like
128-
// instances of a pipe before we return the client.
139+
// instances of a pipe if feature enabled or valid files otherwise
140+
// before we return the client.
129141
//
130142
// If we're called from `make` *without* the leading + on our rule
131143
// then we'll have `MAKEFLAGS` env vars but won't actually have
132144
// access to the file descriptors.
133-
if is_valid_fd(read) && is_valid_fd(write) {
145+
if check_fd(read) && check_fd(write) {
134146
drop(set_cloexec(read, true));
135147
drop(set_cloexec(write, true));
136-
Some(Client::from_fds(read, write))
148+
Some(Ok(Client::from_fds(read, write)))
137149
} else {
138-
None
150+
Some(Err(ErrFromEnv::InvalidDescriptor(read, write)))
139151
}
140152
}
141153

@@ -207,7 +219,7 @@ impl Client {
207219
return Err(io::Error::new(
208220
io::ErrorKind::Other,
209221
"early EOF on jobserver pipe",
210-
))
222+
));
211223
}
212224
Err(e) => match e.kind() {
213225
io::ErrorKind::WouldBlock => { /* fall through to polling */ }
@@ -326,7 +338,7 @@ pub(crate) fn spawn_helper(
326338
client: client.inner.clone(),
327339
data,
328340
disabled: false,
329-
}))
341+
}));
330342
}
331343
Err(e) => break f(Err(e)),
332344
Ok(None) if helper.producer_done() => break,
@@ -385,8 +397,26 @@ impl Helper {
385397
}
386398
}
387399

388-
fn is_valid_fd(fd: c_int) -> bool {
389-
unsafe { libc::fcntl(fd, libc::F_GETFD) != -1 }
400+
fn check_fd(fd: c_int) -> bool {
401+
#[cfg(feature = "check_pipe")]
402+
unsafe {
403+
let mut stat = mem::zeroed();
404+
if libc::fstat(fd, &mut stat) == 0 {
405+
// On android arm and i686 mode_t is u16 and st_mode is u32,
406+
// this generates a type mismatch when S_IFIFO (declared as mode_t)
407+
// is used in operations with st_mode, so we use this workaround
408+
// to get the value of S_IFIFO with the same type of st_mode.
409+
let mut s_ififo = stat.st_mode;
410+
s_ififo = libc::S_IFIFO as _;
411+
stat.st_mode & s_ififo == s_ififo
412+
} else {
413+
false
414+
}
415+
}
416+
#[cfg(not(feature = "check_pipe"))]
417+
unsafe {
418+
libc::fcntl(fd, libc::F_GETFD) != -1
419+
}
390420
}
391421

392422
fn set_cloexec(fd: c_int, set: bool) -> io::Result<()> {

src/wasm.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ struct Inner {
1717
#[derive(Debug)]
1818
pub struct Acquired(());
1919

20+
#[derive(Debug)]
21+
pub enum ErrFromEnv {
22+
UnavailableOnTarget,
23+
}
24+
2025
impl Client {
2126
pub fn new(limit: usize) -> io::Result<Client> {
2227
Ok(Client {
@@ -27,8 +32,8 @@ impl Client {
2732
})
2833
}
2934

30-
pub unsafe fn open(_s: &str) -> Option<Client> {
31-
None
35+
pub unsafe fn open(_s: &str) -> Result<Client, ErrFromEnv> {
36+
Err(ErrFromEnv::UnavailableOnTarget)
3237
}
3338

3439
pub fn acquire(&self) -> io::Result<Acquired> {

src/windows.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ pub struct Client {
1414
#[derive(Debug)]
1515
pub struct Acquired;
1616

17+
#[derive(Debug)]
18+
pub enum ErrFromEnv {
19+
IsNotCString,
20+
CannotAcquireSemaphore,
21+
}
22+
1723
type BOOL = i32;
1824
type DWORD = u32;
1925
type HANDLE = *mut u8;
@@ -127,17 +133,17 @@ impl Client {
127133
))
128134
}
129135

130-
pub unsafe fn open(s: &str) -> Option<Client> {
136+
pub unsafe fn open(s: &str) -> Result<Client, ErrFromEnv> {
131137
let name = match CString::new(s) {
132138
Ok(s) => s,
133-
Err(_) => return None,
139+
Err(_) => return Err(ErrFromEnv::IsNotCString),
134140
};
135141

136142
let sem = OpenSemaphoreA(SYNCHRONIZE | SEMAPHORE_MODIFY_STATE, FALSE, name.as_ptr());
137143
if sem.is_null() {
138-
None
144+
Err(CannotAcquireSemaphore)
139145
} else {
140-
Some(Client {
146+
Ok(Client {
141147
sem: Handle(sem),
142148
name: s.to_string(),
143149
})

tests/client.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,23 @@ const TESTS: &[Test] = &[
3535
make_args: &[],
3636
rule: &|me| me.to_string(),
3737
f: &|| {
38-
assert!(unsafe { Client::from_env().is_none() });
38+
assert!(unsafe { Client::from_env().is_err() });
3939
},
4040
},
4141
Test {
4242
name: "no j args with plus",
4343
make_args: &[],
4444
rule: &|me| format!("+{}", me),
4545
f: &|| {
46-
assert!(unsafe { Client::from_env().is_none() });
46+
assert!(unsafe { Client::from_env().is_err() });
4747
},
4848
},
4949
Test {
5050
name: "j args with plus",
5151
make_args: &["-j2"],
5252
rule: &|me| format!("+{}", me),
5353
f: &|| {
54-
assert!(unsafe { Client::from_env().is_some() });
54+
assert!(unsafe { Client::from_env().is_ok() });
5555
},
5656
},
5757
Test {

0 commit comments

Comments
 (0)