@@ -42,6 +42,8 @@ use nix::poll::{poll, PollFd, POLLIN, POLLHUP, POLLNVAL, EventFlags};
4242use nix:: sched:: { setns, unshare, CloneFlags } ;
4343use nix:: sched:: { CLONE_NEWNS , CLONE_NEWPID , CLONE_NEWUSER , CLONE_NEWUTS } ;
4444use nix:: sched:: { CLONE_NEWIPC , CLONE_NEWNET , CLONE_NEWCGROUP } ;
45+ use nix:: sys:: socket:: { ControlMessage , MsgFlags , socket, connect, sendmsg} ;
46+ use nix:: sys:: socket:: { SockAddr , UnixAddr , AddressFamily , SockType , SockFlag } ;
4547use nix:: sys:: signal:: { SigSet , Signal } ;
4648use nix:: sys:: stat:: { Mode , fstat} ;
4749use nix:: sys:: wait:: { waitpid, WaitStatus , WNOHANG } ;
@@ -311,7 +313,7 @@ fn run() -> Result<()> {
311313 )
312314 . arg (
313315 Arg :: with_name ( "console-socket" )
314- . help ( "console socket (unsupported) " )
316+ . help ( "socket to pass master of console " )
315317 . long ( "console-socket" )
316318 . takes_value ( true ) ,
317319 )
@@ -529,9 +531,17 @@ fn finish_create(id: &str, dir: &str, matches: &ArgMatches) -> Result<()> {
529531 chdir ( & * dir) . chain_err (
530532 || format ! ( "failed to chdir to {}" , & dir) ,
531533 ) ?;
534+ // NOTE: There are certain configs where we will not be able to create a
535+ // console during start, so this could potentially create the
536+ // console during init and pass to the process via sendmsg. This
537+ // would also allow us to write debug data from the init process
538+ // to the console and allow us to pass stdoutio from init to the
539+ // process, fixing the lack of stdout collection if -t is not
540+ // specified when using docker run.
532541 let csocket = matches. value_of ( "console-socket" ) . unwrap_or_default ( ) ;
533542 if csocket != "" {
534- bail ! ( "Console socket unsupported. Try running without -t" ) ;
543+ let lnk = format ! ( "{}/console-socket" , dir) ;
544+ symlink ( & csocket, lnk) ?;
535545 }
536546
537547 let console = matches. value_of ( "c" ) . unwrap_or_default ( ) ;
@@ -542,7 +552,7 @@ fn finish_create(id: &str, dir: &str, matches: &ArgMatches) -> Result<()> {
542552 let pidfile = matches. value_of ( "p" ) . unwrap_or_default ( ) ;
543553
544554 let child_pid =
545- run_container ( id, & rootfs, & spec, -1 , true , true , true , -1 ) ?;
555+ run_container ( id, & rootfs, & spec, -1 , true , true , true , -1 , - 1 ) ?;
546556 if child_pid != -1 {
547557 debug ! ( "writing init pid file {}" , child_pid) ;
548558 let mut f = File :: create ( INIT_PID ) ?;
@@ -617,6 +627,20 @@ fn cmd_start(id: &str, state_dir: &str, matches: &ArgMatches) -> Result<()> {
617627 || format ! ( "failed to load {}" , CONFIG ) ,
618628 ) ?;
619629
630+ let csocket = format ! ( "{}/console-socket" , dir) ;
631+ let mut csocketfd =
632+ socket ( AddressFamily :: Unix , SockType :: Stream , SockFlag :: empty ( ) , 0 ) ?;
633+ csocketfd =
634+ match connect ( csocketfd, & SockAddr :: Unix ( UnixAddr :: new ( & * csocket) ?) ) {
635+ Err ( e) => {
636+ if e. errno ( ) != Errno :: ENOENT {
637+ let msg = format ! ( "failed to open {}" , csocket) ;
638+ return Err ( e) . chain_err ( || msg) ?;
639+ }
640+ -1
641+ }
642+ Ok ( ( ) ) => csocketfd,
643+ } ;
620644 let console = format ! ( "{}/console" , dir) ;
621645 let consolefd = match open ( & * console, O_NOCTTY | O_RDWR , Mode :: empty ( ) ) {
622646 Err ( e) => {
@@ -645,6 +669,7 @@ fn cmd_start(id: &str, state_dir: &str, matches: &ArgMatches) -> Result<()> {
645669 init,
646670 false ,
647671 true ,
672+ csocketfd,
648673 consolefd,
649674 ) ?;
650675 if child_pid != -1 {
@@ -800,6 +825,7 @@ fn cmd_run(id: &str, matches: &ArgMatches) -> Result<()> {
800825 matches. is_present ( "o" ) ,
801826 matches. is_present ( "d" ) ,
802827 -1 ,
828+ -1 ,
803829 ) ?;
804830 info ! ( "Container running with pid {}" , child_pid) ;
805831 Ok ( ( ) )
@@ -873,7 +899,8 @@ fn run_container(
873899 mut init : bool ,
874900 mut init_only : bool ,
875901 daemonize : bool ,
876- consolefd : RawFd ,
902+ csocketfd : RawFd ,
903+ mut consolefd : RawFd ,
877904) -> Result < ( i32 ) > {
878905 if let Err ( e) = prctl:: set_dumpable ( false ) {
879906 bail ! ( format!( "set dumpable returned {}" , e) ) ;
@@ -1003,35 +1030,6 @@ fn run_container(
10031030 sethostname ( & spec. hostname ) ?;
10041031 }
10051032
1006- // NOTE: if we are running without a supplied console, then
1007- // stdout and stderr will not be properly passed to
1008- // docker since the start command has different stdout
1009- // than the init command. In order to make this work
1010- // we would need init to make a pseudoterminal and copy
1011- // data back and forth, or pass the stdio file discriptors
1012- // over a socket of some sort.
1013- if consolefd != -1 {
1014- setsid ( ) ?;
1015- if unsafe { libc:: ioctl ( consolefd, libc:: TIOCSCTTY ) } < 0 {
1016- warn ! ( "could not TIOCSCTTY" ) ;
1017- } ;
1018- dup2 ( consolefd, 0 ) . chain_err (
1019- || "could not dup tty to stdin" ,
1020- ) ?;
1021- dup2 ( consolefd, 1 ) . chain_err (
1022- || "could not dup tty to stdout" ,
1023- ) ?;
1024- dup2 ( consolefd, 2 ) . chain_err (
1025- || "could not dup tty to stderr" ,
1026- ) ?;
1027-
1028- // NOTE: we may need to fix up the mount of /dev/console
1029- } else if daemonize && !init_only {
1030- close ( 0 ) . chain_err ( || "could not close stdin" ) ?;
1031- close ( 1 ) . chain_err ( || "could not close stdout" ) ?;
1032- close ( 2 ) . chain_err ( || "could not close stderr" ) ?;
1033- }
1034-
10351033 if cf. contains ( CLONE_NEWNS ) {
10361034 mounts:: init_rootfs ( spec, rootfs, & cpath, bind_devices)
10371035 . chain_err ( || "failed to init rootfs" ) ?;
@@ -1065,8 +1063,61 @@ fn run_container(
10651063 // NOTE: apparently criu has problems if pointing to an fd outside
10661064 // the filesystem namespace.
10671065 reopen_dev_null ( ) ?;
1066+ }
1067+
1068+ if csocketfd != -1 {
1069+ let mut slave: libc:: c_int = unsafe { std:: mem:: uninitialized ( ) } ;
1070+ let mut master: libc:: c_int = unsafe { std:: mem:: uninitialized ( ) } ;
1071+ let ret = unsafe {
1072+ libc:: openpty (
1073+ & mut master,
1074+ & mut slave,
1075+ std:: ptr:: null_mut ( ) ,
1076+ std:: ptr:: null_mut ( ) ,
1077+ std:: ptr:: null_mut ( ) ,
1078+ )
1079+ } ;
1080+ Errno :: result ( ret) . chain_err ( || "could not openpty" ) ?;
1081+ defer ! ( close( master) . unwrap( ) ) ;
1082+ let data: & [ u8 ] = b"/dev/ptmx" ;
1083+ let iov = [ nix:: sys:: uio:: IoVec :: from_slice ( data) ] ;
1084+ //let fds = [master.as_raw_fd()];
1085+ let fds = [ master] ;
1086+ let cmsg = ControlMessage :: ScmRights ( & fds) ;
1087+ warn ! ( "debug sending master fd to socket" ) ;
1088+ sendmsg ( csocketfd, & iov, & [ cmsg] , MsgFlags :: empty ( ) , None ) ?;
1089+ consolefd = slave;
1090+ }
1091+ // NOTE: if we are running without a supplied console, then
1092+ // stdout and stderr will not be properly passed to
1093+ // docker since the start command has different stdout
1094+ // than the init command. In order to make this work
1095+ // we would need init to pass the file discriptors from
1096+ // init over a socket of some sort.
1097+ if consolefd != -1 {
1098+ warn ! ( "setting up slave console" ) ;
1099+ setsid ( ) ?;
1100+ if unsafe { libc:: ioctl ( consolefd, libc:: TIOCSCTTY ) } < 0 {
1101+ warn ! ( "could not TIOCSCTTY" ) ;
1102+ } ;
1103+ dup2 ( consolefd, 0 ) . chain_err (
1104+ || "could not dup tty to stdin" ,
1105+ ) ?;
1106+ dup2 ( consolefd, 1 ) . chain_err (
1107+ || "could not dup tty to stdout" ,
1108+ ) ?;
1109+ dup2 ( consolefd, 2 ) . chain_err (
1110+ || "could not dup tty to stderr" ,
1111+ ) ?;
10681112
1113+ // NOTE: we may need to fix up the mount of /dev/console
1114+ } else if daemonize && !init_only {
1115+ close ( 0 ) . chain_err ( || "could not close stdin" ) ?;
1116+ close ( 1 ) . chain_err ( || "could not close stdout" ) ?;
1117+ close ( 2 ) . chain_err ( || "could not close stderr" ) ?;
1118+ }
10691119
1120+ if cf. contains ( CLONE_NEWNS ) {
10701121 mounts:: finish_rootfs ( spec) . chain_err (
10711122 || "failed to finish rootfs" ,
10721123 ) ?;
0 commit comments