cttyhack/tty/initramfs problem (take a number)

Denys Vlasenko vda.linux at googlemail.com
Mon Dec 7 00:20:46 UTC 2009

On Sunday 06 December 2009 02:53, Ersin Akinci wrote:
> Aha, thank you for clarifying.  I wasn't aware that virtual terminals
> were different from pseudo-terminals.  That makes more sense.
> I still haven't been able to solve my problem, however.  /dev/tty1
> exists, and I went ahead and made /dev/tty0, as well.  All permissions
> are correct.  I also tried your suggestion to do exec </dev/tty1
> >/dev/tty1 2>/dev/tty1, but that didn't work, either.

What does it show?

> >Just for kicks, 
> I tried /bin/sh /dev/tty1, which leaves me promptless but allows me to
> enter commands nevertheless (without job control, however).
> "strace cttyhack sh" pulls up some interesting results.  I can't copy
> and paste the entire thing, but I will write out the relevant parts:
> / # strace cttyhack sh
> execve("/bin/cttyhack", [cttyhack", "sh"], [/* 7 vars *]) = 0
> <snip>
> ioctl(0, TIOCGSERIAL, 0xbfa39200)     = -1 EINVAL (Invalid argument)
> ioctl(0, VT_GETSTATE, 0xbfa39200)     = 0

ctty determined that fd 0 is a virtual tty #1. Now it opens that:

> open("/dev/tty1", O_RDWR)     = 3

See docs/ctty.htm, "Getting a controlling tty":

"How does one get a controlling terminal? Nobody knows, this is a great mystery.

The System V approach is that the first tty opened by the process becomes
its controlling tty.

The BSD approach is that one has to explicitly call

    ioctl(fd, TIOCSCTTY, 0/1);

to get a controlling tty.

Linux tries to be compatible with both, as always, and this results
in a very obscure complex of conditions. Roughly:
The TIOCSCTTY ioctl will give us a controlling tty, provided that
(i) the current process is a session leader, and
(ii) it does not yet have a controlling tty, and
(iii) maybe the tty should not already control some other session;
if it does it is an error if we aren't root, or we steal the tty
if we are all-powerful. [vda: correction: third parameter controls this:
if 1, we steal tty from any such session, if 0, we don't steal]

Opening some terminal will give us a controlling tty, provided that
(i) the current process is a session leader, and
(ii) it does not yet have a controlling tty, and
(iii) the tty does not already control some other session, and
(iv) the open did not have the O_NOCTTY flag, and
(v) the tty is not the foreground VT, and
(vi) the tty is not the console, and
(vii) maybe the tty should not be master or slave pty."

So, this open should give you ctty already.

> dup2(3, 0)     = 0
> dup2(3, 1)     = 1
> dup2(3, 2)     = 2
> close(3)     = 0
> ioctl(0, TIOCSCTTY)     = -1 EPERM (Operation not permitted)

but we try harder anyway... and fail?!?

Kernel's code:

static int tiocsctty(struct tty_struct *tty, int arg)
        int ret = 0;
        if (current->signal->leader && (task_session(current) == tty->session))
                return ret;

         * The process must be a session leader and
         * not have a controlling tty already.
        if (!current->signal->leader || current->signal->tty) {
                ret = -EPERM;
if EPERM comes from here,
then try "strace setsid sh"
to ensure sh *is* a session leader
                goto unlock;

        if (tty->session) {
                 * This tty is already the controlling
                 * tty for another session group!
                if (arg == 1 && capable(CAP_SYS_ADMIN)) {
                         * Steal it away
                } else {
                        ret = -EPERM;
if EPERM comes from here,
then you don't have CAP_SYS_ADMIN,
since cttyhack does pass arg = 1.
                        goto unlock;
        proc_set_tty(current, tty);
        return ret;

> <snip, trying and failing to execute standard sh locations>
> execve("/bin/sh", ["sh"], [/* 7 vars */]) = 0
> open("/dev/tty", O_RDWR)     = -1 ENXIO (No such device or address)
> ioctl(2, SNDCTL_TMR_TIMEBASE or TCGETS, {B38400 opost isig icanon echo ...}) = 0
> fcntl(2, F_DUPFD, 10)     = 10
> fcntl(10, F_SETFD, FD_CLOEXEC)     = 0
> ioctl(10, TIOCGPGRP, [1464])     = -1 ENOTTY (Inappropriate ioctl for device)
> <snip>
> Strange.  /dev/tty definitely exists with the correct major/minor
> number pair (5, 0), I made sure to create that beforehand with mknod.

open("/dev/tty") failing is normal. It means you have no ctty.
The question is: "why opening /dev/tty1 didn't give you ctty?"

> Some more testing: I tried running /bin/sh /dev/tty2,

This just tells sh to run /dev/tty2 as if it is a shell script.
I bit strnage way of "running shell on a tty" if you ask me.

> and when I 
> switch to virtual terminal 2 it executes all my commands and outputs
> to...um, whatever it's outputting to when I first boot my computer
> (/dev/console?  /dev/tty0?)

You need to redirect all three fds.


More information about the busybox mailing list