rust/chg/src/attachio.rs
changeset 44848 1be605526c34
parent 44690 6bef9d43cc55
child 44849 cb5822e6e545
equal deleted inserted replaced
44847:e9e44e61042b 44848:1be605526c34
     3 // This software may be used and distributed according to the terms of the
     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.
     4 // GNU General Public License version 2 or any later version.
     5 
     5 
     6 //! Functions to send client-side fds over the command server channel.
     6 //! Functions to send client-side fds over the command server channel.
     7 
     7 
     8 use futures::{try_ready, Async, Future, Poll};
       
     9 use std::io;
     8 use std::io;
    10 use std::os::unix::io::AsRawFd;
     9 use std::os::unix::io::AsRawFd;
    11 use tokio_hglib::codec::ChannelMessage;
    10 use tokio_hglib::codec::ChannelMessage;
    12 use tokio_hglib::protocol::MessageLoop;
    11 use tokio_hglib::{Connection, Protocol};
    13 use tokio_hglib::{Client, Connection};
       
    14 
    12 
    15 use crate::message;
    13 use crate::message;
    16 use crate::procutil;
    14 use crate::procutil;
    17 
    15 
    18 /// Future to send client-side fds over the command server channel.
    16 /// Sends client-side fds over the command server channel.
    19 ///
    17 ///
    20 /// This works as follows:
    18 /// This works as follows:
    21 /// 1. Client sends "attachio" request.
    19 /// 1. Client sends "attachio" request.
    22 /// 2. Server sends back 1-byte input request.
    20 /// 2. Server sends back 1-byte input request.
    23 /// 3. Client sends fds with 1-byte dummy payload in response.
    21 /// 3. Client sends fds with 1-byte dummy payload in response.
    24 /// 4. Server returns the number of the fds received.
    22 /// 4. Server returns the number of the fds received.
    25 ///
    23 ///
    26 /// If the stderr is omitted, it will be redirected to the stdout. This
    24 /// 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
    25 /// allows us to attach the pager stdin to both stdout and stderr, and
    28 /// dispose of the client-side handle once attached.
    26 /// dispose of the client-side handle once attached.
    29 #[must_use = "futures do nothing unless polled"]
    27 pub async fn attach_io(
    30 pub struct AttachIo<C, I, O, E>
    28     proto: &mut Protocol<impl Connection + AsRawFd>,
    31 where
    29     stdin: impl AsRawFd,
    32     C: Connection,
    30     stdout: impl AsRawFd,
    33 {
    31     stderr: Option<impl AsRawFd>,
    34     msg_loop: MessageLoop<C>,
    32 ) -> io::Result<()> {
    35     stdin: I,
    33     // TODO: unindent
    36     stdout: O,
    34     {
    37     stderr: Option<E>,
    35         proto.send_command("attachio").await?;
    38 }
       
    39 
       
    40 impl<C, I, O, E> AttachIo<C, I, O, E>
       
    41 where
       
    42     C: Connection + AsRawFd,
       
    43     I: AsRawFd,
       
    44     O: AsRawFd,
       
    45     E: AsRawFd,
       
    46 {
       
    47     pub fn with_client(
       
    48         client: Client<C>,
       
    49         stdin: I,
       
    50         stdout: O,
       
    51         stderr: Option<E>,
       
    52     ) -> AttachIo<C, I, O, E> {
       
    53         let msg_loop = MessageLoop::start(client, b"attachio");
       
    54         AttachIo {
       
    55             msg_loop,
       
    56             stdin,
       
    57             stdout,
       
    58             stderr,
       
    59         }
       
    60     }
       
    61 }
       
    62 
       
    63 impl<C, I, O, E> Future for AttachIo<C, I, O, E>
       
    64 where
       
    65     C: Connection + AsRawFd,
       
    66     I: AsRawFd,
       
    67     O: AsRawFd,
       
    68     E: AsRawFd,
       
    69 {
       
    70     type Item = Client<C>;
       
    71     type Error = io::Error;
       
    72 
       
    73     fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
       
    74         loop {
    36         loop {
    75             let (client, msg) = try_ready!(self.msg_loop.poll());
    37             match proto.fetch_response().await? {
    76             match msg {
       
    77                 ChannelMessage::Data(b'r', data) => {
    38                 ChannelMessage::Data(b'r', data) => {
    78                     let fd_cnt = message::parse_result_code(data)?;
    39                     let fd_cnt = message::parse_result_code(data)?;
    79                     if fd_cnt == 3 {
    40                     if fd_cnt == 3 {
    80                         return Ok(Async::Ready(client));
    41                         return Ok(());
    81                     } else {
    42                     } else {
    82                         return Err(io::Error::new(
    43                         return Err(io::Error::new(
    83                             io::ErrorKind::InvalidData,
    44                             io::ErrorKind::InvalidData,
    84                             "unexpected attachio result",
    45                             "unexpected attachio result",
    85                         ));
    46                         ));
    86                     }
    47                     }
    87                 }
    48                 }
    88                 ChannelMessage::Data(..) => {
    49                 ChannelMessage::Data(..) => {
    89                     // just ignore data sent to uninteresting (optional) channel
    50                     // just ignore data sent to uninteresting (optional) channel
    90                     self.msg_loop = MessageLoop::resume(client);
       
    91                 }
    51                 }
    92                 ChannelMessage::InputRequest(1) => {
    52                 ChannelMessage::InputRequest(1) => {
    93                     // this may fail with EWOULDBLOCK in theory, but the
    53                     // this may fail with EWOULDBLOCK in theory, but the
    94                     // payload is quite small, and the send buffer should
    54                     // payload is quite small, and the send buffer should
    95                     // be empty so the operation will complete immediately
    55                     // be empty so the operation will complete immediately
    96                     let sock_fd = client.as_raw_fd();
    56                     let sock_fd = proto.as_raw_fd();
    97                     let ifd = self.stdin.as_raw_fd();
    57                     let ifd = stdin.as_raw_fd();
    98                     let ofd = self.stdout.as_raw_fd();
    58                     let ofd = stdout.as_raw_fd();
    99                     let efd = self.stderr.as_ref().map_or(ofd, |f| f.as_raw_fd());
    59                     let efd = stderr.as_ref().map_or(ofd, |f| f.as_raw_fd());
   100                     procutil::send_raw_fds(sock_fd, &[ifd, ofd, efd])?;
    60                     procutil::send_raw_fds(sock_fd, &[ifd, ofd, efd])?;
   101                     self.msg_loop = MessageLoop::resume(client);
       
   102                 }
    61                 }
   103                 ChannelMessage::InputRequest(..)
    62                 ChannelMessage::InputRequest(..)
   104                 | ChannelMessage::LineRequest(..)
    63                 | ChannelMessage::LineRequest(..)
   105                 | ChannelMessage::SystemRequest(..) => {
    64                 | ChannelMessage::SystemRequest(..) => {
   106                     return Err(io::Error::new(
    65                     return Err(io::Error::new(