-
Notifications
You must be signed in to change notification settings - Fork 48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Close logpipe
input file descriptor after dup2()
#116
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -835,28 +835,31 @@ extern "C" fn ANativeActivity_onCreate( | |
) { | ||
abort_on_panic(|| { | ||
// Maybe make this stdout/stderr redirection an optional / opt-in feature?... | ||
unsafe { | ||
let file = unsafe { | ||
let mut logpipe: [RawFd; 2] = Default::default(); | ||
libc::pipe(logpipe.as_mut_ptr()); | ||
libc::pipe2(logpipe.as_mut_ptr(), libc::O_CLOEXEC); | ||
libc::dup2(logpipe[1], libc::STDOUT_FILENO); | ||
libc::dup2(logpipe[1], libc::STDERR_FILENO); | ||
std::thread::spawn(move || { | ||
let tag = CStr::from_bytes_with_nul(b"RustStdoutStderr\0").unwrap(); | ||
let file = File::from_raw_fd(logpipe[0]); | ||
let mut reader = BufReader::new(file); | ||
let mut buffer = String::new(); | ||
loop { | ||
buffer.clear(); | ||
if let Ok(len) = reader.read_line(&mut buffer) { | ||
if len == 0 { | ||
break; | ||
} else if let Ok(msg) = CString::new(buffer.clone()) { | ||
android_log(Level::Info, tag, &msg); | ||
} | ||
libc::close(logpipe[1]); | ||
|
||
File::from_raw_fd(logpipe[0]) | ||
}; | ||
|
||
std::thread::spawn(move || { | ||
let tag = CStr::from_bytes_with_nul(b"RustStdoutStderr\0").unwrap(); | ||
let mut reader = BufReader::new(file); | ||
let mut buffer = String::new(); | ||
loop { | ||
buffer.clear(); | ||
if let Ok(len) = reader.read_line(&mut buffer) { | ||
Comment on lines
+852
to
+854
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably drive-by fix the error handling, because this will There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like that EOF case is handled afterwards with At least it looks like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah that's exactly the |
||
if len == 0 { | ||
break; | ||
} else if let Ok(msg) = CString::new(buffer.clone()) { | ||
android_log(Level::Info, tag, &msg); | ||
} | ||
} | ||
}); | ||
} | ||
} | ||
}); | ||
|
||
log::trace!( | ||
"Creating: {:p}, saved_state = {:p}, save_state_size = {}", | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using
OwnedFd
might be even nicer: it'll close the file descriptors automatically and has a (safe)impl From<OwnedFd> for File
.Even though it is FFI-compatible I'm not sure if we're allowed to rely on the fact that they are initially uninitialized, or perhaps
libc::pipe()
not initializing them at all or to-1
on error?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I think we should switch to
nix
orrustix
: no code below is validating the error codes returned bylibc
, while those APIs do.On quick inspection I prefer
rustix
: they returnOwnedFd
frompipe()
rather thannix
' returningRawFd
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's pretty cool:
Unfortunately converting this whole file to
rustix
is a hassle :(There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ping @rib :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm half wondering whether we should be setting
O_CLOSEXEC
viapipe2()
also but actually I suppose it would maybe make sense that if you fork + exec for some reason then you'd want stdout/err for the thing you exec to also get directed to this same IO thread.I guess it'd make sense to use
O_CLOSEXEC
on the just the read end maybe.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sunfishcode hence perhaps using
rustix
in a popular boilerplate Rust+Android crate will give it the testing it deserves?We even used to run
ndk-glue
in an emulator, but I don't think that was ported toandroid-activity
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyway, let's move/continue discussions to #139 if there needs to be any.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks, yeah, I guess I forgot how many letters from 'close' to keep - I'd guess even that the 'O' might be for 'on', who knows :)
hmm, no,
dup2()
doesn't really make an existing file descriptor point to the same file object - though it does give the illusion that that's what it does.dup2()
here effectively (atomically)close()s
the originalstdin/err
(deleting the original FDs) and then creates new FDs that don't have flags set (such asO_CLOEXEC
). The new FD created bydup2
hold a reference to the same file object that theoldfd
points at.So if you think of an FD as only the integer then it does give that illusion that an existing FD is being made to point at another file - but the FD really has some associated state (including the flags such as
O_CLOEXEC
) so it's quite an important distinction to note thatdup2
is creating a new FD and the newstdin/err
file descriptors fromdup2()
won't haveO_CLOEXEC
set.This is effectively why there's now a
dup3()
to close the same race condition that existed with other system calls that create new file descriptors withoutO_CLOEXEC
already set.The wording is very particular here and the devil is in the details. It's saying it "uses the file descriptor number", not the actual, pre-existing file descriptor. An FD is more than just the "number" that gets passed around in userspace and the FD is something that's also separate from the file object that it references.
Some file state is common to the referenced file object and some other state like the
O_CLOEXEC
is unique to each FD.dup2()
closes one FD, then creates a new FD that will also use the same number (such asstdout/err
) that was previously associated with the FD which has been closed - and then makes that new FD reference the same file object as the other FD passed todup2()
.I guess at least the rustix binding for
dup2
looked pretty explicit that it would map to thedup2
system call. I guess I would expect that not every API is implemented with such an explicit 1:1 mapping to system calls though and its those slightly higher-level APIs I'd maybe worry about.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't want to go to the depths of discussing that a light-weight file descriptor integer in userspace must (obviously) have a whole representation on the kernel side before even pointing to any sort of file object.
However, for a completely distinct case, it is perhaps worth mentioning it, starting with this quote from
dup3()
docs:We already deduced above that FD flags affect the FD (or FD number as it were), and won't be "copied" over from
oldfd
tonewfd
. However, noting thatnewfd
gets its whole, for the theoretical case thatstdin
/stdout
haveO_CLOEXEC
set, FD replaced without any sharing is still useful: in this case the resulting FD (which the FD number points to) won't haveO_CLOEXEC
set anymore.As such, if there is a case where an Android app may set
O_CLOEXEC
onstdin
/stderr
before handing control over toandroid-activity
, we'll undo that with ourdup2()
. Should we haveF_GETFD
to check if it is set, and if so usedup3()
to add it back?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not worried too much if we're translating existing
libc::
calls to their 1:1 equivalents inrustix
, unless we cannot find that or stumble upon a more high-level abstraction over it, in which case we will have to check whether it does the same.However, I doubt
android-activity
does much if anything special with syscalls.