add utils for passing file descriptor between processes#4019
add utils for passing file descriptor between processes#4019echoechoin wants to merge 21 commits into
Conversation
0f6a823 to
8ee8195
Compare
| } | ||
|
|
||
| /* get fd_to_send by open /proc/PID/fd/FD */ | ||
| fd_to_send = open(proc_path, O_RDWR); |
There was a problem hiding this comment.
open(2) works for an fd opening a regular file.
For fds opening peculiar objects like sockets, we can use pidfd_getfd as a fallback.
There was a problem hiding this comment.
How about using pidfd_getfd at first, then fall back to open if errno == ENOSYS || errno == EPERM.
There was a problem hiding this comment.
The semantics of calling open(/proc/$pid/fd/$fd) and of calling pidfd_getfd($fd) differ.
open makes a new open file object in the kernel.
pidfd_getfd doesn't. pidfd_getfd makes a new reference to the open file.
If a receiver process calls lseek on a received file descriptor, it affects on the open file behind the fd.
open(2)
[target process]<fd> ------ [file object] --- [dentry] --- [inode]
/
[receiver process]<fd> -----[file object] --/
pidfd_getfd(2)
[target process]<fd> ------ [file object] --- [dentry] --- [inode]
/
[receiver process]<fd> ----/
I would like to withdraw my comment.
I think users expect the behavior of open(2). So, using the open(2) method is better as the default method.
What about activating the pidfd_getfd(2) method only if --pidfd-getfd option is given?
There was a problem hiding this comment.
Now I believe it is better to activate the pidfd_getfd(2) method only if --pidfd-getfd option is given. No fallback.
|
It seems you need to add bash-completion files for your new commands. |
| opt_pid = strtopid_or_err(optarg, _("invalid pid")); | ||
| if (opt_pid < 1) | ||
| errx(EXIT_FAILURE, _("pid must be positive")); |
There was a problem hiding this comment.
You can also use this internal lib function. It would make way for a potential PID:inode support which could be interesting for fd{recv,send}, since it'll help avoid sending/receiving a fd from/to a wrong process (in cases where that is important of course).
| opt_pid = strtopid_or_err(optarg, _("invalid pid")); | |
| if (opt_pid < 1) | |
| errx(EXIT_FAILURE, _("pid must be positive")); | |
| opt_pid = ul_parse_pid_str_or_err(optarg, &opt_pid, NULL); |
There was a problem hiding this comment.
For some context in regards to PID:inode: #3953 (comment)
|
What about extending fdrecv to receive multiple fds? |
How about this: # Send multiple fds:
fdsend --block --fd 0,1,2 -b pipe_test
# Receive multiple fds:
fdrecv --fd 0,1,2 pipe_test --run cat # 0 <-> 0, 1 <-> 1, 2 <-> 2 (full mapping)
fdrecv --fd 0,1 pipe_test --run cat # 0 <-> 0, 1 <-> 1 (2 is ignored)
fdrecv --fd 0 pipe_test --run cat # 0 <-> 0 (1 and 2 are ignored)Also ensure that # Send multiple fds:
fdsend --block --fd 0,1,2 -b pipe_test
# Receive only first fd via --stdin, --stdout, or --stderr:
fdrecv --stdin pipe_test --run cat # 0 <-> stdin, ignore other fds. |
f7cc796 to
1931ac9
Compare
4f0da87 to
215f250
Compare
|
What about supporting an abstract Unix socket (https://man7.org/linux/man-pages/man7/unix.7.html)? |
How about add a new command option for abstract unix socket? fdsend --abstract --fd 1 sockspec
fdrecv --abstract --fd 1 sockspec run cator add an |
|
Adding Rationals:
For abstract sockets, we cannot use inotify to detect the appearance of a listening socket. So the usability of these commands with
|
|
In |
|
I don't have any other idea than the polling approach. |
| * Receiver: socket/bind/listen/accept, recvmsg with SCM_RIGHTS. | ||
| * On success sets *out_fd to the received fd and returns 0. | ||
| */ | ||
| static int fdrecv_accept_and_recv_fd(const char *sockpath, int *out_fd, int abstract) |
Check warning
Code scanning / CodeQL
Poorly documented large function Warning
7e2f180 to
b97707e
Compare
b97707e to
afd3921
Compare
f865d19 to
993f975
Compare
| FILE *out = stdout; | ||
|
|
||
| fputs(USAGE_HEADER, out); | ||
| fprintf(out, _(" %s [options] SOCKSPEC --run command args...\n"), program_invocation_short_name); |
There was a problem hiding this comment.
I find that --run is a bit unnecessary. Since SOCKSPEC can not be specified an arbitrary amount of time, we can simply design the CLI like this:
fdrecv [options] SOCKSPEC COMMAND
COMMAND will have an arbitrary amount of arguments, which is fine as these will be the remaining elements of argv and we'll just pass them to execvp(2).
What do you think ?
There was a problem hiding this comment.
If we extend fdrecv to accept fds from multiple sockets, the fdcecv command line can become complicated. In these cases, the --run option helps people understand the command line.
If the action that fdrecv takes after receiving all fds is only "run", we can choose -- (or -) instead of --run.
|
It would be nice if fdrecv could receive multiple fds from different sockets at once. terminal A terminal B terminal C trminal recv: |
|
This pull request sparks many ideas. I find an alternative action (Added after submitting this comment) Instead of adding |
2eb8f68 to
3d9f5c2
Compare
|
|
Please note that I am currently focusing on stabilizing the new release. I will work on this PR later. |
Review of the final stateOverall the idea is solid and the SCM_RIGHTS handling is correct. A few things CriticalCompilation error — ul_parse_pid_str_or_err(optarg, &opts.pid, NULL);The actual signature in extern void ul_parse_pid_str_or_err(char *pidstr, pid_t *pid_num, uint64_t *pfd_ino, int flags);Missing the 4th ul_parse_pid_str_or_err(optarg, &opts.pid, NULL, 0);fdrecv must use getopt_long()The hand-rolled The multi-socket syntax This matters for maintainability: every new flag will require hand-coding all Security / Correctness
Code quality nits
Man pages
Tests
|
This commit implements the fdsend and fdrecv utilities as proposed in Discussion util-linux#3683. It adds: - fdsend: send a file descriptor to a Unix domain socket (path or abstract). Supports blocking wait for receiver, optional pid/fd resolution, and pidfd_getfd(2) where available. - fdrecv: receive a file descriptor from a Unix domain socket and optionally exec a program with the received fd. Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Allow using pidfd_getfd(2) instead of open(/proc/PID/fd/FD) to obtain the file descriptor from the process. Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
…usage message for clarity Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
- Support multiple -f N SOCKSPEC (and -i/-o/-e SOCKSPEC) before --run CMD. - Add `relocate_conflicting_fds` to avoid target FD and received FD conflicts. - Add fdrecv doc update and tests. Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
Signed-off-by: WanBingjiang <wanbingjiang@webray.com.cn>
3d9f5c2 to
6b3deb6
Compare
| * If recv_fds[j] equals pairs[i].target_fd for some i != j and | ||
| * recv_fds[j] != pairs[j].target_fd, dup it to a fd number above | ||
| * all target fds so it cannot collide with any target. | ||
| */ |
Check notice
Code scanning / CodeQL
For loop variable changed in body Note
Based on the outcomes of Discussion #3683, I have developed this demo, with the relevant operation example as follows: