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:
parent
fd3ee47d8d
commit
8224a48da9
@ -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'],
|
||||
|
81
man/systemd-pty-forward.xml
Normal file
81
man/systemd-pty-forward.xml
Normal 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>
|
@ -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
9
src/ptyfwd/meson.build
Normal 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
214
src/ptyfwd/ptyfwd-tool.c
Normal 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);
|
6
test/units/TEST-74-AUX-UTILS.pty-forward.sh
Executable file
6
test/units/TEST-74-AUX-UTILS.pty-forward.sh
Executable 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
|
Loading…
Reference in New Issue
Block a user