##// END OF EJS Templates
rust-chg: add future that handles "attachio" request...
Yuya Nishihara -
r40008:7a0ffdd4 default
parent child Browse files
Show More
@@ -0,0 +1,97 b''
1 // Copyright 2018 Yuya Nishihara <yuya@tcha.org>
2 //
3 // This software may be used and distributed according to the terms of the
4 // GNU General Public License version 2 or any later version.
5
6 //! Functions to send client-side fds over the command server channel.
7
8 use futures::{Async, Future, Poll};
9 use std::io;
10 use std::os::unix::io::AsRawFd;
11 use tokio_hglib::{Client, Connection};
12 use tokio_hglib::codec::ChannelMessage;
13 use tokio_hglib::protocol::MessageLoop;
14
15 use super::message;
16 use super::procutil;
17
18 /// Future to send client-side fds over the command server channel.
19 ///
20 /// This works as follows:
21 /// 1. Client sends "attachio" request.
22 /// 2. Server sends back 1-byte input request.
23 /// 3. Client sends fds with 1-byte dummy payload in response.
24 /// 4. Server returns the number of the fds received.
25 ///
26 /// If the stderr is omitted, it will be redirected to the stdout. This
27 /// allows us to attach the pager stdin to both stdout and stderr, and
28 /// dispose of the client-side handle once attached.
29 #[must_use = "futures do nothing unless polled"]
30 pub struct AttachIo<C, I, O, E>
31 where C: Connection,
32 {
33 msg_loop: MessageLoop<C>,
34 stdin: I,
35 stdout: O,
36 stderr: Option<E>,
37 }
38
39 impl<C, I, O, E> AttachIo<C, I, O, E>
40 where C: Connection + AsRawFd,
41 I: AsRawFd,
42 O: AsRawFd,
43 E: AsRawFd,
44 {
45 pub fn with_client(client: Client<C>, stdin: I, stdout: O, stderr: Option<E>)
46 -> AttachIo<C, I, O, E> {
47 let msg_loop = MessageLoop::start(client, b"attachio");
48 AttachIo { msg_loop, stdin, stdout, stderr }
49 }
50 }
51
52 impl<C, I, O, E> Future for AttachIo<C, I, O, E>
53 where C: Connection + AsRawFd,
54 I: AsRawFd,
55 O: AsRawFd,
56 E: AsRawFd,
57 {
58 type Item = Client<C>;
59 type Error = io::Error;
60
61 fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
62 loop {
63 let (client, msg) = try_ready!(self.msg_loop.poll());
64 match msg {
65 ChannelMessage::Data(b'r', data) => {
66 let fd_cnt = message::parse_result_code(data)?;
67 if fd_cnt == 3 {
68 return Ok(Async::Ready(client));
69 } else {
70 return Err(io::Error::new(io::ErrorKind::InvalidData,
71 "unexpected attachio result"));
72 }
73 }
74 ChannelMessage::Data(..) => {
75 // just ignore data sent to uninteresting (optional) channel
76 self.msg_loop = MessageLoop::resume(client);
77 }
78 ChannelMessage::InputRequest(1) => {
79 // this may fail with EWOULDBLOCK in theory, but the
80 // payload is quite small, and the send buffer should
81 // be empty so the operation will complete immediately
82 let sock_fd = client.as_raw_fd();
83 let ifd = self.stdin.as_raw_fd();
84 let ofd = self.stdout.as_raw_fd();
85 let efd = self.stderr.as_ref().map_or(ofd, |f| f.as_raw_fd());
86 procutil::send_raw_fds(sock_fd, &[ifd, ofd, efd])?;
87 self.msg_loop = MessageLoop::resume(client);
88 }
89 ChannelMessage::InputRequest(..) | ChannelMessage::LineRequest(..) |
90 ChannelMessage::SystemRequest(..) => {
91 return Err(io::Error::new(io::ErrorKind::InvalidData,
92 "unsupported request while attaching io"));
93 }
94 }
95 }
96 }
97 }
@@ -4,8 +4,12 b''
4 // GNU General Public License version 2 or any later version.
4 // GNU General Public License version 2 or any later version.
5
5
6 extern crate bytes;
6 extern crate bytes;
7 #[macro_use]
8 extern crate futures;
7 extern crate libc;
9 extern crate libc;
10 extern crate tokio;
8 extern crate tokio_hglib;
11 extern crate tokio_hglib;
9
12
13 pub mod attachio;
10 pub mod message;
14 pub mod message;
11 pub mod procutil;
15 pub mod procutil;
General Comments 0
You need to be logged in to leave comments. Login now