Files
finit/doc/config/tty.md
T
Joachim Wiberg c584795202 doc: fix back-references and stale claims left by the conversion
Reference sections kept pointing at the line-based format they no longer
document.  `sysv` and `task` sent the reader to Services for "<COND>",
the cgroups chapter opened by listing three legacy directives and then
explained further down that only two of them exist here, and the logging
chapter still gave "log:prio:facility.level,tag:ident" as the full
syntax.

Some claims were wrong independent of the format:

  - a sysv is a supervised daemon, grouped with service in
    SVC_TYPE_DAEMON, not a variation on task
  - restart-max has no upper bound of 255, or any other
  - the built-in rescue fallback runs in 12345789, not 12345
  - conditional loading quotes system/10-hotplug.conf, not
    system/hotplug.conf
  - the key spells conflicts, not conflict
  - the built-in getty no longer wants TERM last, it is a key

`if` takes either a service name or, in angle brackets, a condition,
decided in svc_ifthen().  Only the examples showed this, so it is now
said.

Terminology follows the split index.md already draws: a block is the new
format, a stanza the line-based one.

src/rescue.conf was still line-based, missed because it sits in src/
rather than system/ or contrib/.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2026-07-30 15:23:35 +02:00

5.5 KiB

TTYs and Consoles

Syntax: tty NAME { device = DEV } -- built-in getty
tty NAME { command = "CMD ARGS" } -- external getty
tty NAME { notty = true }, or { rescue = true } -- bare shell, no device

The block title NAME is what initctl shows. The three variants differ in what they open: device runs the built-in getty on that TTY, and DEV may be the special keyword @console, expanded from /sys/class/tty/console/active, useful on embedded systems. command hands the TTY to an external getty. notty opens nothing at all.

Settings common to all three:

Setting Alias Description
runlevel Runlevels to run in, e.g. "12345"
conditions cond Conditions to wait for
noclear Do not clear the TTY after each session
nowait Do not wait for Enter before the login prompt
nologin Skip login, give a shell straight away

The device variant takes two more:

Setting Description
baud Baud rate, default 0, i.e., keep kernel default
term $TERM value, e.g. "vt220"

A tty block inherits runlevel, condition (and other feature) parsing from the service block. So TTYs can run in one or many runlevels and depend on any condition supported by Finit. This is useful e.g. to depend on <pid/elogind> before starting a TTY.

Example:

tty ttyAMA0 {
    runlevel = "12345"
    device   = "/dev/ttyAMA0"
    baud     = 115200
    term     = "vt220"
    noclear  = true
}

The second tty syntax variant is for using an external getty, like agetty or the BusyBox getty.

The third variant is for board bringup and the rescue boot mode. No device node is required, the same output the kernel uses is reused for stdio. With notty a shell is started (nologin, noclear, and nowait are implied); with rescue the bundled /libexec/finit/sulogin presents a bare-bones root login prompt. If the root (uid:0, gid:0) user does not have a password set, no rescue is possible. For more information, see the Rescue Mode section.

By default, the first two syntax variants clear the TTY and wait for the user to press enter before starting getty.

Example:

tty getty {
    runlevel = "12345"
    command  = "/sbin/getty -L 115200 /dev/ttyAMA0 vt100"
}

tty agetty {
    runlevel = "12345"
    command  = "/sbin/agetty -L ttyAMA0 115200 vt100"
    nowait   = true
}

The noclear setting disables clearing the TTY after each session. Clearing the TTY when a user logs out is usually preferable.

The nowait setting disables the press Enter to activate console message before actually starting the getty program. On small and embedded systems running multiple unused getty wastes both memory and CPU cycles, so wait is the preferred default.

The nologin setting disables getty and /bin/login, and gives the user a root (login) shell on the given TTY immediately. Needless to say, this is a rather insecure option, but can be very useful for developer builds, during board bringup, or similar.

Embedded systems may want to enable automatic DEV by supplying the special @console device. This works regardless whether the system uses ttyS0, ttyAMA0, ttyMXC0, or anything else. Finit figures it out by querying sysfs: /sys/class/tty/console/active. Leave baud out to keep the kernel default.

Most systems get by fine by just using console, which will evaluate to /dev/console. If you have to use @console to get any output, you may have some issue with your kernel config.

Example:

tty console {
    runlevel = "12345"
    device   = "@console"
    term     = "vt220"
    noclear  = true
}

On really bare bones systems, or for board bringup, Finit can give you a shell prompt as soon as bootstrap is done, without opening any device node:

tty board {
    runlevel = "12345789"
    notty    = true
}

This should of course not be enabled on production systems. Because it may give a user root access without having to log in. However, for board bringup and system debugging it can come in handy.

One can also use a service block to start a stand-alone shell:

service shell {
    runlevel = "12345"
    command  = "/bin/sh -l"
}

Controlling TTY for Services

The tty setting gives a run, task, or service a controlling terminal on the given device. The device is opened, set as the controlling terminal for the session (after setsid()), and connected to the process's stdin, stdout, and stderr. A default TERM environment variable is set based on the device type: vt102 for serial lines and linux for virtual terminals.

The value may be a device node like /dev/ttyS0, or the special keyword @console (see above). Note that @console expands only to the first console, not all.

When tty is combined with a log block, stdout and stderr are redirected to the log sink instead of the TTY, but stdin remains connected to the TTY device.

The tty setting is for run, task, and service blocks only. A tty block (for getty/login) is a different thing entirely, see above.

Example:

service foo {
    description = "Foo on serial console"
    runlevel    = "2345"
    tty         = "/dev/ttyS0"
    command     = "/usr/sbin/foo"
}

task setup {
    description = "Board bringup on console"
    runlevel    = "S"
    tty         = "@console"
    command     = "my-setup-script"
}