1//! Global initialization and retrieval of command line arguments.
2//!
3//! On some platforms these are stored during runtime startup,
4//! and on some they are retrieved from the system on demand.
5
6#![allow(dead_code)] // runtime init functions not used during testing
7
8use crate::ffi::{CStr, OsString};
9use crate::os::unix::ffi::OsStringExt;
10use crate::{fmt, vec};
11
12/// One-time global initialization.
13pub unsafe fn init(argc: isize, argv: *const *const u8) {
14 imp::init(argc, argv)
15}
16
17/// Returns the command line arguments
18pub fn args() -> Args {
19 let (argc, argv) = imp::argc_argv();
20
21 let mut vec = Vec::with_capacity(argc as usize);
22
23 for i in 0..argc {
24 // SAFETY: `argv` is non-null if `argc` is positive, and it is
25 // guaranteed to be at least as long as `argc`, so reading from it
26 // should be safe.
27 let ptr = unsafe { argv.offset(i).read() };
28
29 // Some C commandline parsers (e.g. GLib and Qt) are replacing already
30 // handled arguments in `argv` with `NULL` and move them to the end.
31 //
32 // Since they can't directly ensure updates to `argc` as well, this
33 // means that `argc` might be bigger than the actual number of
34 // non-`NULL` pointers in `argv` at this point.
35 //
36 // To handle this we simply stop iterating at the first `NULL`
37 // argument. `argv` is also guaranteed to be `NULL`-terminated so any
38 // non-`NULL` arguments after the first `NULL` can safely be ignored.
39 if ptr.is_null() {
40 // NOTE: On Apple platforms, `-[NSProcessInfo arguments]` does not
41 // stop iterating here, but instead `continue`, always iterating
42 // up until it reached `argc`.
43 //
44 // This difference will only matter in very specific circumstances
45 // where `argc`/`argv` have been modified, but in unexpected ways,
46 // so it likely doesn't really matter which option we choose.
47 // See the following PR for further discussion:
48 // <https://github.com/rust-lang/rust/pull/125225>
49 break;
50 }
51
52 // SAFETY: Just checked that the pointer is not NULL, and arguments
53 // are otherwise guaranteed to be valid C strings.
54 let cstr = unsafe { CStr::from_ptr(ptr) };
55 vec.push(OsStringExt::from_vec(cstr.to_bytes().to_vec()));
56 }
57
58 Args { iter: vec.into_iter() }
59}
60
61pub struct Args {
62 iter: vec::IntoIter<OsString>,
63}
64
65impl !Send for Args {}
66impl !Sync for Args {}
67
68impl fmt::Debug for Args {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 self.iter.as_slice().fmt(f)
71 }
72}
73
74impl Iterator for Args {
75 type Item = OsString;
76 fn next(&mut self) -> Option<OsString> {
77 self.iter.next()
78 }
79 fn size_hint(&self) -> (usize, Option<usize>) {
80 self.iter.size_hint()
81 }
82}
83
84impl ExactSizeIterator for Args {
85 fn len(&self) -> usize {
86 self.iter.len()
87 }
88}
89
90impl DoubleEndedIterator for Args {
91 fn next_back(&mut self) -> Option<OsString> {
92 self.iter.next_back()
93 }
94}
95
96#[cfg(any(
97 target_os = "linux",
98 target_os = "android",
99 target_os = "freebsd",
100 target_os = "dragonfly",
101 target_os = "netbsd",
102 target_os = "openbsd",
103 target_os = "cygwin",
104 target_os = "solaris",
105 target_os = "illumos",
106 target_os = "emscripten",
107 target_os = "haiku",
108 target_os = "l4re",
109 target_os = "fuchsia",
110 target_os = "redox",
111 target_os = "vxworks",
112 target_os = "horizon",
113 target_os = "aix",
114 target_os = "nto",
115 target_os = "hurd",
116 target_os = "rtems",
117 target_os = "nuttx",
118))]
119mod imp {
120 use crate::ffi::c_char;
121 use crate::ptr;
122 use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering};
123
124 // The system-provided argc and argv, which we store in static memory
125 // here so that we can defer the work of parsing them until its actually
126 // needed.
127 //
128 // Note that we never mutate argv/argc, the argv array, or the argv
129 // strings, which allows the code in this file to be very simple.
130 static ARGC: AtomicIsize = AtomicIsize::new(0);
131 static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut());
132
133 unsafe fn really_init(argc: isize, argv: *const *const u8) {
134 // These don't need to be ordered with each other or other stores,
135 // because they only hold the unmodified system-provide argv/argc.
136 ARGC.store(argc, Ordering::Relaxed);
137 ARGV.store(argv as *mut _, Ordering::Relaxed);
138 }
139
140 #[inline(always)]
141 pub unsafe fn init(argc: isize, argv: *const *const u8) {
142 // on GNU/Linux if we are main then we will init argv and argc twice, it "duplicates work"
143 // BUT edge-cases are real: only using .init_array can break most emulators, dlopen, etc.
144 really_init(argc, argv);
145 }
146
147 /// glibc passes argc, argv, and envp to functions in .init_array, as a non-standard extension.
148 /// This allows `std::env::args` to work even in a `cdylib`, as it does on macOS and Windows.
149 #[cfg(all(target_os = "linux", target_env = "gnu"))]
150 #[used]
151 #[unsafe(link_section = ".init_array.00099")]
152 static ARGV_INIT_ARRAY: extern "C" fn(
153 crate::os::raw::c_int,
154 *const *const u8,
155 *const *const u8,
156 ) = {
157 extern "C" fn init_wrapper(
158 argc: crate::os::raw::c_int,
159 argv: *const *const u8,
160 _envp: *const *const u8,
161 ) {
162 unsafe {
163 really_init(argc as isize, argv);
164 }
165 }
166 init_wrapper
167 };
168
169 pub fn argc_argv() -> (isize, *const *const c_char) {
170 // Load ARGC and ARGV, which hold the unmodified system-provided
171 // argc/argv, so we can read the pointed-to memory without atomics or
172 // synchronization.
173 //
174 // If either ARGC or ARGV is still zero or null, then either there
175 // really are no arguments, or someone is asking for `args()` before
176 // initialization has completed, and we return an empty list.
177 let argv = ARGV.load(Ordering::Relaxed);
178 let argc = if argv.is_null() { 0 } else { ARGC.load(Ordering::Relaxed) };
179
180 // Cast from `*mut *const u8` to `*const *const c_char`
181 (argc, argv.cast())
182 }
183}
184
185// Use `_NSGetArgc` and `_NSGetArgv` on Apple platforms.
186//
187// Even though these have underscores in their names, they've been available
188// since the first versions of both macOS and iOS, and are declared in
189// the header `crt_externs.h`.
190//
191// NOTE: This header was added to the iOS 13.0 SDK, which has been the source
192// of a great deal of confusion in the past about the availability of these
193// APIs.
194//
195// NOTE(madsmtm): This has not strictly been verified to not cause App Store
196// rejections; if this is found to be the case, the previous implementation
197// of this used `[[NSProcessInfo processInfo] arguments]`.
198#[cfg(target_vendor = "apple")]
199mod imp {
200 use crate::ffi::{c_char, c_int};
201
202 pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
203 // No need to initialize anything in here, `libdyld.dylib` has already
204 // done the work for us.
205 }
206
207 pub fn argc_argv() -> (isize, *const *const c_char) {
208 unsafe extern "C" {
209 // These functions are in crt_externs.h.
210 fn _NSGetArgc() -> *mut c_int;
211 fn _NSGetArgv() -> *mut *mut *mut c_char;
212 }
213
214 // SAFETY: The returned pointer points to a static initialized early
215 // in the program lifetime by `libdyld.dylib`, and as such is always
216 // valid.
217 //
218 // NOTE: Similar to `_NSGetEnviron`, there technically isn't anything
219 // protecting us against concurrent modifications to this, and there
220 // doesn't exist a lock that we can take. Instead, it is generally
221 // expected that it's only modified in `main` / before other code
222 // runs, so reading this here should be fine.
223 let argc = unsafe { _NSGetArgc().read() };
224 // SAFETY: Same as above.
225 let argv = unsafe { _NSGetArgv().read() };
226
227 // Cast from `*mut *mut c_char` to `*const *const c_char`
228 (argc as isize, argv.cast())
229 }
230}
231
232#[cfg(any(target_os = "espidf", target_os = "vita"))]
233mod imp {
234 use crate::ffi::c_char;
235 use crate::ptr;
236
237 #[inline(always)]
238 pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
239
240 pub fn argc_argv() -> (isize, *const *const c_char) {
241 (0, ptr::null())
242 }
243}
244