Mercurial > hg
comparison rust/chg/src/attachio.rs @ 39972:7a0ffdd4af78
rust-chg: add future that handles "attachio" request
This is the sequence to send client-side stdio and pager stdin to the
server.
author | Yuya Nishihara <yuya@tcha.org> |
---|---|
date | Mon, 24 Sep 2018 16:59:12 +0900 |
parents | |
children | ce088b38f92b |
comparison
equal
deleted
inserted
replaced
39971:b1d8acd82d60 | 39972:7a0ffdd4af78 |
---|---|
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 } |