1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-06 17:18:12 +03:00

Introduce systemd-pty-forward

This allows running a command with tinted terminal background.
This commit is contained in:
Daan De Meyer 2024-12-26 22:58:33 +01:00
parent fd3ee47d8d
commit 8224a48da9
6 changed files with 312 additions and 0 deletions

View File

@ -1052,6 +1052,7 @@ manpages = [
'systemd-shutdown'],
''],
['systemd-pstore.service', '8', ['systemd-pstore'], 'ENABLE_PSTORE'],
['systemd-pty-forward', '1', [], ''],
['systemd-quotacheck.service',
'8',
['systemd-quotacheck'],

View File

@ -0,0 +1,81 @@
<?xml version='1.0'?> <!--*-nxml-*-->
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
<refentry id="systemd-pty-forward"
xmlns:xi="http://www.w3.org/2001/XInclude">
<refentryinfo>
<title>systemd-pty-forward</title>
<productname>systemd</productname>
</refentryinfo>
<refmeta>
<refentrytitle>systemd-pty-forward</refentrytitle>
<manvolnum>1</manvolnum>
</refmeta>
<refnamediv>
<refname>systemd-pty-forward</refname>
<refpurpose>Run a command with a custom terminal background color or title</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>systemd-pty-forward</command>
<arg choice="opt" rep="repeat">OPTIONS</arg>
<arg choice="req">COMMAND</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para><command>systemd-pty-forward</command> can be used to run a command with a custom terminal
background color or title.</para>
</refsect1>
<refsect1>
<title>Options</title>
<para>The following options are understood:</para>
<variablelist>
<varlistentry>
<term><option>--background=<replaceable>COLOR</replaceable></option></term>
<listitem><para>Change the terminal background color to the specified ANSI color as long as the
command runs. The color specified should be an ANSI X3.64 SGR background color, i.e. strings such as
<literal>40</literal>, <literal>41</literal>, …, <literal>47</literal>, <literal>48;2;…</literal>,
<literal>48;5;…</literal>. See <ulink
url="https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters">ANSI
Escape Code (Wikipedia)</ulink> for details.</para>
<para>Example: <literal>--background=44</literal> for a blue background.</para>
<xi:include href="version-info.xml" xpointer="v258"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--title=<replaceable>TITLE</replaceable></option></term>
<listitem><para>Change the terminal title to the specified string as long as the command runs.</para>
<xi:include href="version-info.xml" xpointer="v258"/>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--quiet</option></term>
<term><option>-q</option></term>
<listitem><para>Suppresses additional informational output while running.</para>
<xi:include href="version-info.xml" xpointer="v258"/></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help"/>
<xi:include href="standard-options.xml" xpointer="version"/>
</variablelist>
</refsect1>
</refentry>

View File

@ -2402,6 +2402,7 @@ subdir('src/pcrextend')
subdir('src/pcrlock')
subdir('src/portable')
subdir('src/pstore')
subdir('src/ptyfwd')
subdir('src/quotacheck')
subdir('src/random-seed')
subdir('src/rc-local-generator')

9
src/ptyfwd/meson.build Normal file
View File

@ -0,0 +1,9 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
executables += [
executable_template + {
'name' : 'systemd-pty-forward',
'public' : true,
'sources' : files('ptyfwd-tool.c'),
},
]

214
src/ptyfwd/ptyfwd-tool.c Normal file
View File

@ -0,0 +1,214 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <getopt.h>
#include <unistd.h>
#include "alloc-util.h"
#include "build.h"
#include "event-util.h"
#include "fd-util.h"
#include "main-func.h"
#include "pretty-print.h"
#include "ptyfwd.h"
#include "strv.h"
static bool arg_quiet = false;
static char *arg_background = NULL;
static char *arg_title = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_background, freep);
STATIC_DESTRUCTOR_REGISTER(arg_title, freep);
static int help(void) {
_cleanup_free_ char *link = NULL;
int r;
r = terminal_urlify_man("systemd-pty-forward", "1", &link);
if (r < 0)
return log_oom();
printf("%1$s [OPTIONS...] COMMAND ...\n"
"\n%5$sRun command with a custom terminal background color or title.%6$s\n"
"\n%3$sOptions:%4$s\n"
" -h --help Show this help\n"
" --version Print version\n"
" -q --quiet Suppress information messages during runtime\n"
" --background=COLOR Set ANSI color for background\n"
" --title=TITLE Set terminal title\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
ansi_underline(),
ansi_normal(),
ansi_highlight(),
ansi_normal());
return 0;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_BACKGROUND,
ARG_TITLE,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "quiet", no_argument, NULL, 'q' },
{ "background", required_argument, NULL, ARG_BACKGROUND },
{ "title", required_argument, NULL, ARG_TITLE },
{}
};
int c, r;
assert(argc >= 0);
assert(argv);
optind = 0;
while ((c = getopt_long(argc, argv, "+hq", options, NULL)) >= 0)
switch (c) {
case 'h':
return help();
case ARG_VERSION:
return version();
case 'q':
arg_quiet = true;
break;
case ARG_BACKGROUND:
r = free_and_strdup_warn(&arg_background, optarg);
if (r < 0)
return r;
break;
case ARG_TITLE:
r = free_and_strdup_warn(&arg_title, optarg);
if (r < 0)
return r;
break;
case '?':
return -EINVAL;
default:
assert_not_reached();
}
return 1;
}
static int pty_forward_handler(PTYForward *f, int rcode, void *userdata) {
sd_event *e = ASSERT_PTR(userdata);
assert(f);
if (rcode == -ECANCELED) {
log_debug_errno(rcode, "PTY forwarder disconnected.");
return sd_event_exit(e, EXIT_SUCCESS);
} else if (rcode < 0) {
(void) sd_event_exit(e, EXIT_FAILURE);
return log_error_errno(rcode, "Error on PTY forwarding logic: %m");
}
return 0;
}
static int helper_on_exit(sd_event_source *s, const siginfo_t *si, void *userdata) {
/* Add 128 to signal exit statuses to mimick shells. */
return sd_event_exit(sd_event_source_get_event(s), si->si_status + (si->si_code == CLD_EXITED ? 0 : 128));
}
static int run(int argc, char *argv[]) {
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
_cleanup_close_ int pty_fd = -EBADF, peer_fd = -EBADF;
_cleanup_(pty_forward_freep) PTYForward *forward = NULL;
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
_cleanup_(sd_event_source_unrefp) sd_event_source *exit_source = NULL;
int r;
log_setup();
assert_se(sigprocmask_many(SIG_BLOCK, /*ret_old_mask=*/ NULL, SIGCHLD) >= 0);
r = parse_argv(argc, argv);
if (r <= 0)
return r;
_cleanup_strv_free_ char **l = strv_copy(argv + optind);
if (!l)
return log_oom();
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to get event loop: %m");
(void) sd_event_set_signal_exit(event, true);
pty_fd = openpt_allocate(O_RDWR|O_NOCTTY|O_NONBLOCK|O_CLOEXEC, /*ret_peer=*/ NULL);
if (pty_fd < 0)
return log_error_errno(pty_fd, "Failed to acquire pseudo tty: %m");
peer_fd = pty_open_peer(pty_fd, O_RDWR|O_NOCTTY|O_CLOEXEC);
if (peer_fd < 0)
return log_error_errno(peer_fd, "Failed to open pty peer: %m");
if (!arg_quiet)
log_info("Press ^] three times within 1s to disconnect TTY.");
r = pty_forward_new(event, pty_fd, /*flags=*/ 0, &forward);
if (r < 0)
return log_error_errno(r, "Failed to create PTY forwarder: %m");
if (!isempty(arg_background)) {
r = pty_forward_set_background_color(forward, arg_background);
if (r < 0)
return log_error_errno(r, "Failed to set background color: %m");
}
if (shall_set_terminal_title() && !isempty(arg_title)) {
r = pty_forward_set_title(forward, arg_title);
if (r < 0)
return log_error_errno(r, "Failed to set title: %m");
}
pty_forward_set_handler(forward, pty_forward_handler, &event);
r = pidref_safe_fork_full(
"(sd-ptyfwd)",
(int[]) { peer_fd, peer_fd, peer_fd },
/* except_fds= */ NULL,
/* n_except_fds= */ 0,
/* flags= */ FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_REARRANGE_STDIO,
&pidref);
if (r < 0)
return log_error_errno(r, "Failed to fork child: %m");
if (r == 0) {
r = terminal_new_session();
if (r < 0)
return log_error_errno(r, "Failed to create new session: %m");
(void) execvp(l[0], l);
log_error_errno(errno, "Failed to execute %s: %m", l[0]);
_exit(EXIT_FAILURE);
}
peer_fd = safe_close(peer_fd);
r = event_add_child_pidref(event, &exit_source, &pidref, WEXITED, helper_on_exit, NULL);
if (r < 0)
return log_error_errno(r, "Failed to add child event source: %m");
r = sd_event_source_set_child_process_own(exit_source, true);
if (r < 0)
return log_error_errno(r, "Failed to take ownership of child process: %m");
return sd_event_loop(event);
}
DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run);

View File

@ -0,0 +1,6 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
set -eux
set -o pipefail
systemd-pty-forward --background 41 --title test echo foobar