module

Krikri::ControllingTty

Gives the CURRENT (plugin) process a controlling terminal, so that anything it spawns afterwards can open("/dev/tty").

Why this exists: real ansible-core's ssh connection plugin asks for a remote pty on essentially every module invocation - see plugins/connection/ssh.py:

use_tty = self.get_option('use_tty')
...
if not in_data and sudoable and use_tty:
    args = ('-tt', self.host, cmd)

and sudoable is True for ordinary module dispatch (it is only forced False for the internal dd-based put_file/fetch_file helpers). So under real Ansible the whole remote process tree - the module, and whatever subprocess a command:/shell: task spawns - inherits a controlling terminal, and /dev/tty is openable there.

This engine's SSHManager never passes -t/-tt, deliberately: the same channel carries each plugin's JSON PluginResult back to the controller, and a pty would merge stderr into stdout, translate LF to CRLF, and change buffering - i.e. it would risk corrupting the result protocol for all 100+ plugins, on every path (one-shot exec, the bash -s batch script, and the length-prefixed persistent daemon pipe alike). So instead of asking ssh for a tty, the process that actually needs one manufactures its own, entirely on the target host and entirely off the transport:

posix_openpt -> setsid -> TIOCSCTTY on the pty slave

Nothing about the ssh channel changes: stdin/stdout/stderr of this process are untouched (still the pipes the transport handed us), so the JSON result travels back byte-for-byte as before. The pty is a side channel that only /dev/tty opens reach.

Found via the Galaxy role imntreal.smallstep_ca, whose command: step ca init ... task fails under this engine with "error allocating terminal: open /dev/tty: no such device or address" while real ansible-playbook succeeds - the step CLI opens /dev/tty unconditionally to render a banner, even when (as there) every credential comes from --password-file and it never actually reads from it.

Constants

O_NOCTTY = 256

Linux O_NOCTTY - opening a terminal with it must never make that terminal the caller's controlling one as a side effect (only the explicit TIOCSCTTY below is allowed to do that).

TIOCSCTTY = 21518_u64

Linux asm-generic/ioctls.h. arg: 0 = don't steal the tty from another session (impossible for a pty we just allocated anyway). Deliberately namespaced here rather than at top level: the expect plugin defines its own top-level TIOCSCTTY and both land in the same fat-plugin compilation unit.

VTIME = 5

Crystal's own LibC bindings define VMIN but not VTIME (termios.cr) - Linux's c_cc index for it.

Class methods

ensure

Idempotent: safe to call before every spawn, and in particular safe in the --persistent-daemon remote half, where one resident process serves many tasks and must only ever do this once.

Returns true if this process has a controlling terminal when it returns. Never raises and never changes behavior on failure: if anything here is unavailable (no /dev/ptmx, no permission, already a session leader of a session that lost its tty, a non-Linux target), the caller simply proceeds exactly as it did before this existed.

Source
present?

True if this process already has a controlling terminal - the normal case for ansible_connection=local runs from an interactive shell, where the plugin binary inherits the user's own terminal and there is nothing to do.

Source