Skip to content
This repository was archived by the owner on Jan 9, 2020. It is now read-only.

Commit 1fb2475

Browse files
author
Vishvananda Ishaya Abrams
committed
Create a new console and pass it to console-socket
1 parent 0e63fe4 commit 1fb2475

File tree

1 file changed

+84
-33
lines changed

1 file changed

+84
-33
lines changed

src/main.rs

Lines changed: 84 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ use nix::poll::{poll, PollFd, POLLIN, POLLHUP, POLLNVAL, EventFlags};
4242
use nix::sched::{setns, unshare, CloneFlags};
4343
use nix::sched::{CLONE_NEWNS, CLONE_NEWPID, CLONE_NEWUSER, CLONE_NEWUTS};
4444
use 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};
4547
use nix::sys::signal::{SigSet, Signal};
4648
use nix::sys::stat::{Mode, fstat};
4749
use 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

Comments
 (0)