Skip to content

Commit c40ec5a

Browse files
Add unstable env::set and env::remove
1 parent 1b3a5f2 commit c40ec5a

File tree

8 files changed

+171
-66
lines changed

8 files changed

+171
-66
lines changed

library/std/src/env.rs

+83-23
Original file line numberDiff line numberDiff line change
@@ -298,19 +298,48 @@ impl Error for VarError {
298298
}
299299
}
300300

301+
#[unstable(feature = "unsafe_env", issue = "none")]
301302
/// Sets the environment variable `key` to the value `value` for the currently running
302303
/// process.
303304
///
304-
/// Note that while concurrent access to environment variables is safe in Rust,
305-
/// some platforms only expose inherently unsafe non-threadsafe APIs for
306-
/// inspecting the environment. As a result, extra care needs to be taken when
307-
/// auditing calls to unsafe external FFI functions to ensure that any external
308-
/// environment accesses are properly synchronized with accesses in Rust.
305+
/// # Safety
309306
///
310-
/// Discussion of this unsafety on Unix may be found in:
307+
/// This function must not be called in the presence of concurrent threads that
308+
/// may simultaneously read or write the environment. Also, it's not allowed
309+
/// to use this function in functions marked with `#[test]` attribute because
310+
/// test harness uses threads internally.
311311
///
312-
/// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188)
313-
/// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2)
312+
/// # Panics
313+
///
314+
/// This function may panic if `key` is empty, contains an ASCII equals sign `'='`
315+
/// or the NUL character `'\0'`, or when `value` contains the NUL character.
316+
///
317+
/// # Examples
318+
///
319+
/// ```
320+
/// #![feature(unsafe_env)]
321+
///
322+
/// use std::env;
323+
///
324+
/// let key = "KEY";
325+
/// unsafe {
326+
/// env::set(key, "VALUE");
327+
/// }
328+
/// assert_eq!(env::var(key), Ok("VALUE".to_string()));
329+
/// ```
330+
pub unsafe fn set<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
331+
_set_var(key.as_ref(), value.as_ref(), |k, v| os_imp::setenv(k, v))
332+
}
333+
334+
/// Sets the environment variable `key` to the value `value` for the currently running
335+
/// process.
336+
///
337+
/// # Safety
338+
///
339+
/// This function must not be called in the presence of concurrent threads that
340+
/// may simultaneously read or write the environment. Also, it's not allowed
341+
/// to use this function in functions marked with `#[test]` attribute because
342+
/// test harness uses threads internally.
314343
///
315344
/// # Panics
316345
///
@@ -328,27 +357,59 @@ impl Error for VarError {
328357
/// ```
329358
#[stable(feature = "env", since = "1.0.0")]
330359
pub fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(key: K, value: V) {
331-
_set_var(key.as_ref(), value.as_ref())
360+
_set_var(key.as_ref(), value.as_ref(), |k, v| unsafe { os_imp::setenv_locking(k, v) })
332361
}
333362

334-
fn _set_var(key: &OsStr, value: &OsStr) {
335-
os_imp::setenv(key, value).unwrap_or_else(|e| {
363+
fn _set_var<F: Fn(&OsStr, &OsStr) -> io::Result<()>>(key: &OsStr, value: &OsStr, f: F) {
364+
f(key, value).unwrap_or_else(|e| {
336365
panic!("failed to set environment variable `{:?}` to `{:?}`: {}", key, value, e)
337366
})
338367
}
339368

369+
#[unstable(feature = "unsafe_env", issue = "none")]
340370
/// Removes an environment variable from the environment of the currently running process.
341371
///
342-
/// Note that while concurrent access to environment variables is safe in Rust,
343-
/// some platforms only expose inherently unsafe non-threadsafe APIs for
344-
/// inspecting the environment. As a result extra care needs to be taken when
345-
/// auditing calls to unsafe external FFI functions to ensure that any external
346-
/// environment accesses are properly synchronized with accesses in Rust.
372+
/// # Safety
373+
///
374+
/// This function must not be called in the presence of concurrent threads that
375+
/// may simultaneously read or write the environment. Also, it's not allowed
376+
/// to use this function in functions marked with `#[test]` attribute because
377+
/// test harness uses threads internally.
378+
///
379+
/// # Panics
380+
///
381+
/// This function may panic if `key` is empty, contains an ASCII equals sign
382+
/// `'='` or the NUL character `'\0'`, or when the value contains the NUL
383+
/// character.
384+
///
385+
/// # Examples
386+
///
387+
/// ```
388+
/// #![feature(unsafe_env)]
389+
///
390+
/// use std::env;
391+
///
392+
/// let key = "KEY";
393+
/// env::set_var(key, "VALUE");
394+
/// assert_eq!(env::var(key), Ok("VALUE".to_string()));
395+
///
396+
/// unsafe {
397+
/// env::remove(key);
398+
/// }
399+
/// assert!(env::var(key).is_err());
400+
/// ```
401+
pub unsafe fn remove<K: AsRef<OsStr>>(key: K) {
402+
_remove_var(key.as_ref(), |n| os_imp::unsetenv(n))
403+
}
404+
405+
/// Removes an environment variable from the environment of the currently running process.
347406
///
348-
/// Discussion of this unsafety on Unix may be found in:
407+
/// # Safety
349408
///
350-
/// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188)
351-
/// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2)
409+
/// This function must not be called in the presence of concurrent threads that
410+
/// may simultaneously read or write the environment. Also, it's not allowed
411+
/// to use this function in functions marked with `#[test]` attribute because
412+
/// test harness uses threads internally.
352413
///
353414
/// # Panics
354415
///
@@ -370,12 +431,11 @@ fn _set_var(key: &OsStr, value: &OsStr) {
370431
/// ```
371432
#[stable(feature = "env", since = "1.0.0")]
372433
pub fn remove_var<K: AsRef<OsStr>>(key: K) {
373-
_remove_var(key.as_ref())
434+
_remove_var(key.as_ref(), |n| unsafe { os_imp::unsetenv_locking(n) })
374435
}
375436

376-
fn _remove_var(key: &OsStr) {
377-
os_imp::unsetenv(key)
378-
.unwrap_or_else(|e| panic!("failed to remove environment variable `{:?}`: {}", key, e))
437+
fn _remove_var<F: Fn(&OsStr) -> io::Result<()>>(key: &OsStr, f: F) {
438+
f(key).unwrap_or_else(|e| panic!("failed to remove environment variable `{:?}`: {}", key, e))
379439
}
380440

381441
/// An iterator that splits an environment variable into paths according to

library/std/src/sys/hermit/os.rs

+13-9
Original file line numberDiff line numberDiff line change
@@ -144,18 +144,22 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
144144
unsafe { ENV.as_ref().unwrap().lock().unwrap().get_mut(k).cloned() }
145145
}
146146

147-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
148-
unsafe {
149-
let (k, v) = (k.to_owned(), v.to_owned());
150-
ENV.as_ref().unwrap().lock().unwrap().insert(k, v);
151-
}
147+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
148+
setenv_locking(k, v)
149+
}
150+
151+
pub unsafe fn setenv_locking(k: &OsStr, v: &OsStr) -> io::Result<()> {
152+
let (k, v) = (k.to_owned(), v.to_owned());
153+
ENV.as_ref().unwrap().lock().unwrap().insert(k, v);
152154
Ok(())
153155
}
154156

155-
pub fn unsetenv(k: &OsStr) -> io::Result<()> {
156-
unsafe {
157-
ENV.as_ref().unwrap().lock().unwrap().remove(k);
158-
}
157+
pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
158+
unsetenv_locking(k)
159+
}
160+
161+
pub unsafe fn unsetenv_locking(k: &OsStr) -> io::Result<()> {
162+
ENV.as_ref().unwrap().lock().unwrap().remove(k);
159163
Ok(())
160164
}
161165

library/std/src/sys/sgx/os.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,21 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
110110
get_env_store().and_then(|s| s.lock().unwrap().get(k).cloned())
111111
}
112112

113-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
113+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
114+
setenv_locking(k, v)
115+
}
116+
117+
pub unsafe fn setenv_locking(k: &OsStr, v: &OsStr) -> io::Result<()> {
114118
let (k, v) = (k.to_owned(), v.to_owned());
115119
create_env_store().lock().unwrap().insert(k, v);
116120
Ok(())
117121
}
118122

119-
pub fn unsetenv(k: &OsStr) -> io::Result<()> {
123+
pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
124+
unsetenv_locking(k)
125+
}
126+
127+
pub unsafe fn unsetenv_locking(k: &OsStr) -> io::Result<()> {
120128
if let Some(env) = get_env_store() {
121129
env.lock().unwrap().remove(k);
122130
}

library/std/src/sys/solid/os.rs

+15-10
Original file line numberDiff line numberDiff line change
@@ -151,23 +151,28 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
151151
}
152152
}
153153

154-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
154+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
155155
let k = CString::new(k.as_bytes())?;
156156
let v = CString::new(v.as_bytes())?;
157+
cvt_env(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
158+
}
157159

158-
unsafe {
159-
let _guard = ENV_LOCK.write();
160-
cvt_env(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
161-
}
160+
pub unsafe fn setenv_locking(k: &OsStr, v: &OsStr) -> io::Result<()> {
161+
let k = CString::new(k.as_bytes())?;
162+
let v = CString::new(v.as_bytes())?;
163+
let _guard = ENV_LOCK.write();
164+
cvt_env(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
162165
}
163166

164-
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
167+
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
165168
let nbuf = CString::new(n.as_bytes())?;
169+
cvt_env(libc::unsetenv(nbuf.as_ptr())).map(drop)
170+
}
166171

167-
unsafe {
168-
let _guard = ENV_LOCK.write();
169-
cvt_env(libc::unsetenv(nbuf.as_ptr())).map(drop)
170-
}
172+
pub unsafe fn unsetenv_locking(n: &OsStr) -> io::Result<()> {
173+
let nbuf = CString::new(n.as_bytes())?;
174+
let _guard = ENV_LOCK.write();
175+
cvt_env(libc::unsetenv(nbuf.as_ptr())).map(drop)
171176
}
172177

173178
/// In kmclib, `setenv` and `unsetenv` don't always set `errno`, so this

library/std/src/sys/unix/os.rs

+15-10
Original file line numberDiff line numberDiff line change
@@ -541,23 +541,28 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
541541
}
542542
}
543543

544-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
544+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
545545
let k = CString::new(k.as_bytes())?;
546546
let v = CString::new(v.as_bytes())?;
547+
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
548+
}
547549

548-
unsafe {
549-
let _guard = ENV_LOCK.write();
550-
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
551-
}
550+
pub unsafe fn setenv_locking(k: &OsStr, v: &OsStr) -> io::Result<()> {
551+
let k = CString::new(k.as_bytes())?;
552+
let v = CString::new(v.as_bytes())?;
553+
let _guard = ENV_LOCK.write();
554+
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
552555
}
553556

554-
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
557+
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
555558
let nbuf = CString::new(n.as_bytes())?;
559+
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
560+
}
556561

557-
unsafe {
558-
let _guard = ENV_LOCK.write();
559-
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
560-
}
562+
pub unsafe fn unsetenv_locking(n: &OsStr) -> io::Result<()> {
563+
let nbuf = CString::new(n.as_bytes())?;
564+
let _guard = ENV_LOCK.write();
565+
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
561566
}
562567

563568
#[cfg(not(target_os = "espidf"))]

library/std/src/sys/unsupported/os.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,19 @@ pub fn getenv(_: &OsStr) -> Option<OsString> {
8080
None
8181
}
8282

83-
pub fn setenv(_: &OsStr, _: &OsStr) -> io::Result<()> {
83+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
84+
setenv_locking(k, v)
85+
}
86+
87+
pub unsafe fn setenv_locking(_: &OsStr, _: &OsStr) -> io::Result<()> {
8488
Err(io::Error::new_const(io::ErrorKind::Unsupported, &"cannot set env vars on this platform"))
8589
}
8690

87-
pub fn unsetenv(_: &OsStr) -> io::Result<()> {
91+
pub unsafe fn unsetenv(k: &OsStr) -> io::Result<()> {
92+
unsetenv_locking(k)
93+
}
94+
95+
pub unsafe fn unsetenv_locking(_: &OsStr) -> io::Result<()> {
8896
Err(io::Error::new_const(io::ErrorKind::Unsupported, &"cannot unset env vars on this platform"))
8997
}
9098

library/std/src/sys/wasi/os.rs

+13-6
Original file line numberDiff line numberDiff line change
@@ -187,18 +187,25 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
187187
}
188188
}
189189
}
190+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
191+
let k = CString::new(k.as_bytes())?;
192+
let v = CString::new(v.as_bytes())?;
193+
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
194+
}
190195

191-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
196+
pub unsafe fn setenv_locking(k: &OsStr, v: &OsStr) -> io::Result<()> {
192197
let k = CString::new(k.as_bytes())?;
193198
let v = CString::new(v.as_bytes())?;
199+
let _guard = env_lock();
200+
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
201+
}
194202

195-
unsafe {
196-
let _guard = env_lock();
197-
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
198-
}
203+
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
204+
let nbuf = CString::new(n.as_bytes())?;
205+
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
199206
}
200207

201-
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
208+
pub unsafe fn unsetenv_locking(n: &OsStr) -> io::Result<()> {
202209
let nbuf = CString::new(n.as_bytes())?;
203210

204211
unsafe {

library/std/src/sys/windows/os.rs

+12-4
Original file line numberDiff line numberDiff line change
@@ -262,16 +262,24 @@ pub fn getenv(k: &OsStr) -> Option<OsString> {
262262
.ok()
263263
}
264264

265-
pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
265+
pub unsafe fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
266+
setenv_locking(k, v)
267+
}
268+
269+
pub unsafe fn setenv_locking(k: &OsStr, v: &OsStr) -> io::Result<()> {
266270
let k = to_u16s(k)?;
267271
let v = to_u16s(v)?;
268272

269-
cvt(unsafe { c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr()) }).map(drop)
273+
cvt(c::SetEnvironmentVariableW(k.as_ptr(), v.as_ptr())).map(drop)
274+
}
275+
276+
pub unsafe fn unsetenv(n: &OsStr) -> io::Result<()> {
277+
unsetenv_locking(n)
270278
}
271279

272-
pub fn unsetenv(n: &OsStr) -> io::Result<()> {
280+
pub unsafe fn unsetenv_locking(n: &OsStr) -> io::Result<()> {
273281
let v = to_u16s(n)?;
274-
cvt(unsafe { c::SetEnvironmentVariableW(v.as_ptr(), ptr::null()) }).map(drop)
282+
cvt(c::SetEnvironmentVariableW(v.as_ptr(), ptr::null())).map(drop)
275283
}
276284

277285
pub fn temp_dir() -> PathBuf {

0 commit comments

Comments
 (0)