-
Notifications
You must be signed in to change notification settings - Fork 58
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
Support passing unix fds #54
Conversation
fb7941c
to
ea72f91
Compare
Yeah looks ok. Just a few notes.
|
Unfortunately, asyncio doesn't expose sendmsg/recvmsg coroutines :( What we could do is keep these as regular functions, at the cost of slightly ugly invocation. As for passing fds at any point, I'm pretty sure that all existing implementations do that while sending the initial fixed size header. Allowing fds to be passed along the body is somewhat problematic, because recvmsg is atomic, i.e. it truncates messages if buffer is too small, and has a buffer size limit. And dbus-next parses messages on the fly, so we would need to refactor the whole unmarshalling part to use a in-memory buffer. High level API and negotiation: ok. |
77f995b
to
8737508
Compare
0daf81e
to
a5dde99
Compare
Hi all, |
a5dde99
to
6364eb0
Compare
dbus_next/_private/unmarshaller.py
Outdated
|
||
if self.sock: | ||
try: | ||
msg, ancdata, *_ = self.sock.recvmsg(1, socket.CMSG_LEN(16 * unix_fds.itemsize), |
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 think we should replace 1
with the size of fixed message header. Also, why 16
? Let's at least extract magic number into a constant.
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.
Done
@@ -228,7 +250,8 @@ def _unmarshall(self): | |||
sender = header_fields.get(HeaderField.SENDER.name) | |||
signature = header_fields.get(HeaderField.SIGNATURE.name, '') | |||
signature_tree = SignatureTree(signature) | |||
unix_fds = header_fields.get(HeaderField.UNIX_FDS.name) | |||
# TODO: check unix_fds against the header |
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.
?
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 sure what to check here
dbus_next/aio/message_bus.py
Outdated
ancdata = [(socket.SOL_SOCKET, socket.SCM_RIGHTS, array.array("i", msg.unix_fds))] \ | ||
if msg.unix_fds else None | ||
|
||
await self.sock_sendmsg(self._sock, buf[:1], ancdata=ancdata) |
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.
Same here, send the whole fixed-size header first, then the rest.
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.
And done
@acrisci Did you have a chance to look at this? |
I'm getting a test failure on travis that I can reproduce locally. |
unix_fds = array.array("i") | ||
|
||
if self.sock: | ||
try: |
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.
This code path might run multiple times for a single unmarshalling of a message. That will happen in the case there was a blocking error. The previously read data will be read from a buffer cache instead of the socket.
One solution would be to cache the fds on the unmarshaller instance and then skip reading the fds from the socket in the case that the fd cache is present.
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 added that cache to unmarshaller, please have a look if this is what you had in mind.
Use |
dbus_next/service.py
Outdated
fds = [] | ||
|
||
def _replace(obj): | ||
if getattr(obj, "fileno", False): |
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.
There's hasattr
but better might be type(obj) is socket
.
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 changed this to use hasattr
. If I'm not mistaken this doesn't have to be a socket but could for example be an open file.
dbus_next/message.py
Outdated
@@ -135,7 +135,7 @@ def new_error(msg: 'Message', error_name: str, error_text: str) -> 'Message': | |||
body=[error_text]) | |||
|
|||
@staticmethod | |||
def new_method_return(msg: 'Message', signature: str = '', body: List[Any] = []) -> 'Message': | |||
def new_method_return(msg: 'Message', signature: str = '', body: List[Any] = [], fds: List[Any] = []) -> 'Message': |
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.
Be more specific than List[Any]
. I think you only accept a union of socket
and int
.
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.
This could be something else other than a socket so I changed it to List[Union[int, IO]]
dbus_next/auth.py
Outdated
if response != _AuthResponse.OK: | ||
raise AuthError(f'authentication failed: {response.value}: {args}') | ||
if response is _AuthResponse.OK: | ||
return "NEGOTIATE_UNIX_FD" |
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.
This command may only be sent on transports that support Unix file descriptor passing.
Some people use http transport with this even though it's deprecated.
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 reworked AuthResponse
so if fds negotiation fails with an ERROR we just continue without them (like before).
dbus_next/aio/message_bus.py
Outdated
@@ -222,6 +225,32 @@ def reply_handler(reply, err): | |||
|
|||
return future.result() | |||
|
|||
def sock_sendmsg(self, sock, *buffers, ancdata=None, flags=0): |
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.
This should be private.
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.
Changed
dbus_next/message.py
Outdated
@@ -232,3 +235,41 @@ def _marshall(self): | |||
header_block.marshall() | |||
header_block.align(8) | |||
return header_block.buffer + body_block.buffer | |||
|
|||
|
|||
def replace_fds(body_obj, children, replace_fn): |
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.
This should be private.
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.
Changed
import socket | ||
|
||
import pytest | ||
|
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.
Be sure these tests are good because someone might come along and do the glib implementation and need to modify this.
525ee77
to
f15f86e
Compare
Are you guys still working on this? It seems like a good feature. Let me know if I can help. |
Hi, It turned out that our target platform puts restrictions on SCM_RIGHTS, and we can't use it directly. We ended up with a workaround, passing path to an unix socket istead of a descriptor. So no, we're not actively working on that feature, sorry :( At least the PR is functional, we've tested it with ELL acting as a service side. |
f15f86e
to
cbfee90
Compare
cbfee90
to
f0fcbdc
Compare
Hi, |
Sorry for not getting to this sooner. There are just too many subtle issues here to do this in one PR. But what is here is basically good. I am going to merge this, make sure it's turned off so it doesn't affect any existing code and mark the feature as experimental so we can make breaking changes for awhile. I'll tag you on any PRs I make in the future. |
Sure thing, I'm just glad this code will not get lost :D |
Hi,
We've started working on #33. It's still very much in progress, but I wanted to give you heads up and get some feedback about the general approach.
On the other side, we'll be using https://git.kernel.org/pub/scm/libs/ell/ell.git/