Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/config/service-opts.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Other run/task/service options are:
* `cgroup.NAME[,opts]` or `cgroup:opts` -- see the [Cgroups](cgroups.md) section
* `env:[-]/path/to/env` -- see the [Service Environment](service-env.md) section
* `log:...` -- see [Redirecting Output](logging.md#redirecting-output)
* `tty:<dev>` -- see [Controlling TTY](tty.md#controlling-tty)
* `nowarn` -- see [Conditional Loading](services.md#conditional-loading)
* `notify:...` -- see [Service Synchronization](service-sync.md)
* `if:...` -- see [Conditional Execution](services.md#conditional-execution)
Expand Down
29 changes: 28 additions & 1 deletion doc/config/tty.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,31 @@ board bringup and system debugging it can come in handy.

One can also use the `service` stanza to start a stand-alone shell:

service [12345] /bin/sh -l
service [12345] /bin/sh -l

Controlling TTY for Services
----------------------------

The `tty:<dev>` option 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.

`<dev>` 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 `log:`, stdout and stderr are redirected
to the log sink instead of the TTY, but stdin remains connected to the
TTY device.

> The `tty:<dev>` option is for `run`, `task`, and `service` stanzas only.
> The `tty` directive itself (for getty/login) has its own syntax, see
> above.

**Example:**

service [2345] tty:/dev/ttyS0 /usr/sbin/foo -- Foo on serial console
task [S] tty:@console my-setup-script -- Board bringup on console
3 changes: 2 additions & 1 deletion doc/switchroot.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ For more complex setups (LUKS, LVM, etc.):

```
# Unlock LUKS volume
run [S] name:cryptsetup /sbin/cryptsetup open /dev/sda2 cryptroot -- Unlocking encrypted root
# The tty:@console stanza is required so cryptsetup can prompt for a passphrase
run [S] name:cryptsetup tty:@console /sbin/cryptsetup open /dev/sda2 cryptroot -- Unlocking encrypted root

# Activate LVM
run [S] name:lvm /sbin/lvm vgchange -ay -- Activating LVM volumes
Expand Down
31 changes: 31 additions & 0 deletions man/finit.conf.5
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,37 @@ is
and the default
.Cm tag
identity is the basename of the service or run/task command.
.It Cm tty:\&<dev>
Give the
.Cm run , task ,
or
.Cm service
a controlling terminal on
.Cm <dev> .
The device is opened, set as the controlling terminal for the session
(after
.Fn setsid ) ,
and connected to stdin, stdout, and stderr.
A default
.Ev TERM
variable is set based on the device type:
.Cm vt102
for serial lines and
.Cm linux
for virtual terminals.
.Cm <dev>
may be a device node like
.Pa /dev/ttyS0 ,
or the special keyword
.Cm @console
(see TTYs and Consoles above).
When combined with
.Cm log: ,
stdout and stderr are redirected to the log sink instead, but stdin
remains connected to the TTY device.
.Bd -unfilled -offset indent
service [2345] tty:/dev/ttyS0 /usr/sbin/foo -- Foo on serial console
.Ed
.El
.Sh RESCUE MODE
Finit supports a rescue mode which is activated by the
Expand Down
66 changes: 66 additions & 0 deletions src/service.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@
#include <sched.h> /* sched_yield() */
#include <string.h>
#include <sys/reboot.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <linux/vt.h>
#include <net/if.h>
#ifdef _LIBITE_LITE
# include <libite/lite.h>
Expand Down Expand Up @@ -387,6 +389,43 @@ static int lredirect(svc_t *svc)
return close(pipefd[1]);
}

/*
* Set up a controlling TTY for run/task/service.
* Opens the TTY device, dups to 0/1/2, resets termios to
* sane defaults, sets TERM env, and updates procname.
*/
static void svc_prepare_ctty(const char *tty, const char *procname, int log)
{
int fd, dummy;

fd = open(tty, O_RDWR);
if (fd < 0) {
logit(LOG_ERR, "Failed opening %s: %s", tty, strerror(errno));
_exit(1);
}

/* Acquire as controlling terminal for the session */
ioctl(fd, TIOCSCTTY, 0);

dup2(fd, STDIN_FILENO);
if (!log) {
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
}

/* Set default TERM */
if (ioctl(fd, VT_OPENQRY, &dummy) == -1)
setenv("TERM", "vt102", 1); /* likely a serial line */
else
setenv("TERM", "linux", 1);

/* Reset to sane defaults */
stty(fd, B0);
close(fd);

prctl(PR_SET_NAME, procname, 0, 0, 0);
}

/*
* Handle redirection of process output, if enabled
*/
Expand Down Expand Up @@ -946,6 +985,14 @@ static int service_start(svc_t *svc)

sig_unblock();

/*
* If the stanza declared tty:<dev>, set up a controlling
* terminal on that device now that we're a session leader.
* Must run after setsid() to acquire the controlling TTY.
*/
if (!svc_is_tty(svc) && svc_has_ctty(svc))
svc_prepare_ctty(svc->log.ctty, svc->cmd, !!svc->log.enabled);

Comment thread
Ollie-Gutierrez marked this conversation as resolved.
if (svc_is_runtask(svc))
status = exec_runtask(args[0], &args[1]);
else if (svc_is_tty(svc))
Expand Down Expand Up @@ -1795,6 +1842,7 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
char ident[MAX_IDENT_LEN];
char *ifstmt = NULL;
char *notify = NULL;
char *ctty = NULL;
struct tty tty = { 0 };
char *dev = NULL;
int respawn = 0;
Expand Down Expand Up @@ -1911,6 +1959,8 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
env = arg;
else if (MATCH_CMD(cmd, "caps:", arg))
caps = arg;
else if (MATCH_CMD(cmd, "tty:", arg))
ctty = arg;
/* catch both cgroup: and cgroup. handled in parse_cgroup() */
else if (MATCH_CMD(cmd, "cgroup", arg))
cgroup = arg;
Expand Down Expand Up @@ -2160,6 +2210,22 @@ int service_register(int type, char *cfg, struct rlimit rlimit[], char *file)
parse_caps(svc, caps);
else
memset(svc->capabilities, 0, sizeof(svc->capabilities));

if (!svc_is_tty(svc) && ctty) {
char *dev = ctty;

/* NOTE: @console expands only to first console, not all */
if (tty_isatcon(ctty))
dev = tty_atcon();
dev = tty_canonicalize(dev);
if (dev)
strlcpy(svc->log.ctty, dev, sizeof(svc->log.ctty));
else
logit(LOG_WARNING, "%s: tty: %s not found, skipping ctty", svc_ident(svc, NULL, 0), ctty);
} else if (!svc_is_tty(svc)) {
memset(svc->log.ctty, 0, sizeof(svc->log.ctty));
}
Comment thread
Ollie-Gutierrez marked this conversation as resolved.

if (file)
strlcpy(svc->file, file, sizeof(svc->file));
else
Expand Down
2 changes: 2 additions & 0 deletions src/svc.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ typedef struct svc {
char file[64];
char prio[20];
char ident[20];
char ctty[32];
} log;

/* Only for TTY type services */
Expand Down Expand Up @@ -291,6 +292,7 @@ static inline int svc_in_runlevel (svc_t *svc, int runlevel) { return svc && IS
static inline int svc_has_pidfile (svc_t *svc) { return svc_is_daemon(svc) && svc->pidfile[0] != 0 && svc->pidfile[0] != '!'; }
static inline int svc_has_pre (svc_t *svc) { return svc->pre_script[0]; }
static inline int svc_has_post (svc_t *svc) { return svc->post_script[0]; }
static inline int svc_has_ctty (svc_t *svc) { return svc && svc->log.ctty[0]; }
static inline int svc_has_ready (svc_t *svc) { return svc->ready_script[0];}
static inline int svc_has_cleanup (svc_t *svc) { return svc->cleanup_script[0]; }

Expand Down
Loading