-
Notifications
You must be signed in to change notification settings - Fork 460
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
termux-usb: fix issue with usb fd being closed before sent over socket #644
Conversation
Thanks for the pull. Some details for clarity... The
Previously, in
termux-api/app/src/main/java/com/termux/api/util/ResultReturner.java Lines 173 to 181 in 3c1a6be
Previously, before 3c1a6be, the
Note that calling You should passed Also fix typo in "otherwise we mysterious socket issues", preferably remove that part, and replace it completely with "Make sure we send the file descriptors before closing them by flushing them and data (
You should also follow commit guidelines for format, check git history and https://github.com/termux/termux-app?tab=readme-ov-file#commit-messages-guidelines Will need to update the commit title and description as well with this new info, probably should link this pull in the message. |
Hi, when can this be merged? It breaks at least one downstream project. |
This is a minor refactor of the WithAncillaryFd ResultReturner that achieves two goals: 1. The usb fd is no longer closed before the message is sent over the socket. This resolves termux#643 2. We queue the fds to be sent (using setFileDescriptorsForSend) and send and flush the output message in one operation to ensure the correct ordering Explanation per @agnostic-apollo: Previously, in UsbApi, the fd was only temporarily stored in WithAncillaryFd, but not actually sent or passed to LocalSocket, and then a @ was written, possibly in attempts to send it, even though like I said, it wasn't passed to LocalSocket with setFileDescriptorsForSend() yet, so it was a pointless write, but worked due to autoflush on close() (check below). After this, when writeResult() returned, then fd was passed to LocalSocket, but still not sent, since no data was written after it. Previously, before 3c1a6be, the PrintWriter in ResultReturner was using java try-with-resources, which automatically closes when try gets out of scope. Additionally, when new PrintWriter(outputSocket.getOutputStream() is called, it uses a BufferedWriter internally, which when closed, automatically flushes. What this would do is actually send the socket that was previously passed to LocalSocket. Moreover, PrintWriter was also closed before fd was closed since try-with-resources finished before, but after the commit, it was closed afterwards, causing your issue. See termux#644
5e23b09
to
cd43150
Compare
I made the changes requested and verified that the fix still works. |
This is a minor refactor of the WithAncillaryFd ResultReturner that achieves two goals: 1. The usb fd is no longer closed before the message is sent over the socket. This resolves termux#643 2. We queue the fds to be sent (using setFileDescriptorsForSend) and send and flush the output message in one operation to ensure the correct ordering Explanation per @agnostic-apollo: Previously, in UsbApi, the fd was only temporarily stored in WithAncillaryFd, but not actually sent or passed to LocalSocket, and then a @ was written, possibly in attempts to send it, even though like I said, it wasn't passed to LocalSocket with setFileDescriptorsForSend() yet, so it was a pointless write, but worked due to autoflush on close() (check below). After this, when writeResult() returned, then fd was passed to LocalSocket, but still not sent, since no data was written after it. Previously, before 3c1a6be, the PrintWriter in ResultReturner was using java try-with-resources, which automatically closes when try gets out of scope. Additionally, when new PrintWriter(outputSocket.getOutputStream() is called, it uses a BufferedWriter internally, which when closed, automatically flushes. What this would do is actually send the socket that was previously passed to LocalSocket. Moreover, PrintWriter was also closed before fd was closed since try-with-resources finished before, but after the commit, it was closed afterwards, causing your issue. See termux#644
cd43150
to
63a9abf
Compare
Looks good, thanks. Will merge it soon. |
Hi, any particular reason this hasn't been merged yet? Thanks. |
Sorry, got unexpectedly busy with family stuff and then got sick, am back now, will merge it within next couple of days. Thanks for the ping. |
63a9abf
to
418e977
Compare
This is a minor refactor of the WithAncillaryFd ResultReturner that achieves two goals: 1. The usb fd is no longer closed before the message is sent over the socket. This resolves termux#643 2. We queue the fds to be sent (using setFileDescriptorsForSend) and send and flush the output message in one operation to ensure the correct ordering Explanation per @agnostic-apollo: Previously, in UsbApi, the fd was only temporarily stored in WithAncillaryFd, but not actually sent or passed to LocalSocket, and then a @ was written, possibly in attempts to send it, even though like I said, it wasn't passed to LocalSocket with setFileDescriptorsForSend() yet, so it was a pointless write, but worked due to autoflush on close() (check below). After this, when writeResult() returned, then fd was passed to LocalSocket, but still not sent, since no data was written after it. Previously, before 3c1a6be, the PrintWriter in ResultReturner was using java try-with-resources, which automatically closes when try gets out of scope. Additionally, when new PrintWriter(outputSocket.getOutputStream() is called, it uses a BufferedWriter internally, which when closed, automatically flushes. What this would do is actually send the socket that was previously passed to LocalSocket. Moreover, PrintWriter was also closed before fd was closed since try-with-resources finished before, but after the commit, it was closed afterwards, causing your issue. See termux#644
This is a minor refactor of the WithAncillaryFd ResultReturner that achieves two goals: 1. The usb fd is no longer closed before the message is sent over the socket. This resolves termux#643 2. We queue the fd to be sent (using setFileDescriptorsForSend) and then write `@` as data and then flush the output message in a single operation so that fd is actually sent. Writing `@` is required because as per docs "The file descriptors will be sent with the next write of normal data, and will be delivered in a single ancillary message.". The `@` character is not special, it is just the chosen character expected as the message by the native `termux-api` command when a fd is sent. - https://developer.android.com/reference/android/net/LocalSocket#setFileDescriptorsForSend(java.io.FileDescriptor[]) - https://github.com/termux/termux-api-package/blob/e62bdadea3f26b60430bb85248f300fee68ecdcc/termux-api.c#L358 Explanation for why this got broken in 3c1a6be by @agnostic-apollo at termux#644 (comment): Previously, in `UsbApi`, the fd was only temporarily stored in `WithAncillaryFd`, but not actually sent or passed to `LocalSocket`, and then a `@` was written, possibly in attempts to send it, even though like I said, it wasn't passed to `LocalSocket` with `setFileDescriptorsForSend()` yet, so it was a pointless write, but worked due to autoflush on `close()` (check below). After this, when `writeResult()` returned, then `fd` was passed to `LocalSocket`, but still not sent, since no data was written after it. - https://github.com/termux/termux-api/blob/3bea194249586a7dcb143e66b46c1694cb6ca21a/app/src/main/java/com/termux/api/apis/UsbAPI.java#L69-L70 - https://github.com/termux/termux-api/blob/3c1a6be86ff0768fa8be029267fbe96dd7fbfb7f/app/src/main/java/com/termux/api/util/ResultReturner.java#L173-L181 Previously, before termux@3c1a6be, the `PrintWriter` in `ResultReturner` was using java `try-with-resources`, which automatically closes when `try` gets out of scope. Additionally, when `new PrintWriter(outputSocket.getOutputStream()` is called, it uses a `BufferedWriter` internally, which when closed, automatically flushes. What this would do is actually send the socket that was previously passed to `LocalSocket`. Moreover, `PrintWriter` was also closed before `fd` was closed since `try-with-resources` finished before, but after the commit, it was closed afterwards, causing the issue. - https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:libcore/ojluni/src/main/java/java/io/PrintWriter.java;l=164 - https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:libcore/ojluni/src/main/java/java/io/BufferedWriter.java;l=268 - https://cs.android.com/android/platform/superproject/main/+/main:libcore/ojluni/src/main/java/java/io/Writer.java;l=412 Closes termux#643
418e977
to
4c6a519
Compare
Thanks for working on the fix. |
This is a minor refactor of the WithAncillaryFd ResultReturner that achieves two goals:
The usb fd is no longer closed before the message is sent over the socket. This resolves termux-usb -e broken with empty non-fd response #643
We queue the fds to be sent (using setFileDescriptorsForSend) Before printing an output message to ensure the order is correct. This was working before by virtue of the buffering mechanism, but would break if there were no buffering.