diff --git a/docs/TRANSIENT-SETTINGS.md b/docs/TRANSIENT-SETTINGS.md
index 218a43c6b7..b90aaa702e 100644
--- a/docs/TRANSIENT-SETTINGS.md
+++ b/docs/TRANSIENT-SETTINGS.md
@@ -139,6 +139,8 @@ All execution-related settings are available for transient units.
✓ TTYReset=
✓ TTYVHangup=
✓ TTYVTDisallocate=
+✓ TTYRows=
+✓ TTYColumns=
✓ SyslogIdentifier=
✓ SyslogFacility=
✓ SyslogLevel=
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml
index c9f71a6dca..93f2c90280 100644
--- a/man/org.freedesktop.systemd1.xml
+++ b/man/org.freedesktop.systemd1.xml
@@ -2689,6 +2689,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b TTYVTDisallocate = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly q TTYRows = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly q TTYColumns = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly i SyslogPriority = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s SyslogIdentifier = '...';
@@ -3230,6 +3234,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
+
+
+
+
@@ -3822,6 +3830,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
+
+
+
+
@@ -4550,6 +4562,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b TTYVTDisallocate = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly q TTYRows = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly q TTYColumns = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly i SyslogPriority = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s SyslogIdentifier = '...';
@@ -5117,6 +5133,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
+
+
+
+
@@ -5705,6 +5725,10 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2esocket {
+
+
+
+
@@ -6323,6 +6347,10 @@ node /org/freedesktop/systemd1/unit/home_2emount {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b TTYVTDisallocate = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly q TTYRows = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly q TTYColumns = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly i SyslogPriority = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s SyslogIdentifier = '...';
@@ -6818,6 +6846,10 @@ node /org/freedesktop/systemd1/unit/home_2emount {
+
+
+
+
@@ -7324,6 +7356,10 @@ node /org/freedesktop/systemd1/unit/home_2emount {
+
+
+
+
@@ -8063,6 +8099,10 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b TTYVTDisallocate = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly q TTYRows = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
+ readonly q TTYColumns = ...;
+ @org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly i SyslogPriority = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly s SyslogIdentifier = '...';
@@ -8544,6 +8584,10 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
+
+
+
+
@@ -9036,6 +9080,10 @@ node /org/freedesktop/systemd1/unit/dev_2dsda3_2eswap {
+
+
+
+
diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml
index 83a23ca9a5..c9cae4b9d3 100644
--- a/man/systemd.exec.xml
+++ b/man/systemd.exec.xml
@@ -2950,6 +2950,14 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy
TTYPath= before and after execution. Defaults to no.
+
+ TTYRows=
+ TTYColumns=
+
+ Configure the size of the TTY specified with TTYPath=. If unset or
+ set to the empty string, the kernel default is used.
+
+
TTYVTDisallocate=
diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c
index 33743f8c4d..5b8db55052 100644
--- a/src/basic/terminal-util.c
+++ b/src/basic/terminal-util.c
@@ -857,6 +857,39 @@ unsigned lines(void) {
return cached_lines;
}
+int terminal_set_size_fd(int fd, const char *ident, unsigned rows, unsigned cols) {
+ struct winsize ws;
+
+ if (rows == UINT_MAX && cols == UINT_MAX)
+ return 0;
+
+ if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+ return log_debug_errno(errno,
+ "TIOCGWINSZ ioctl for getting %s size failed, not setting terminal size: %m",
+ ident ?: "TTY");
+
+ if (rows == UINT_MAX)
+ rows = ws.ws_row;
+ else if (rows > USHRT_MAX)
+ rows = USHRT_MAX;
+
+ if (cols == UINT_MAX)
+ cols = ws.ws_col;
+ else if (cols > USHRT_MAX)
+ cols = USHRT_MAX;
+
+ if (rows == ws.ws_row && cols == ws.ws_col)
+ return 0;
+
+ ws.ws_row = rows;
+ ws.ws_col = cols;
+
+ if (ioctl(fd, TIOCSWINSZ, &ws) < 0)
+ return log_debug_errno(errno, "TIOCSWINSZ ioctl for setting %s size failed: %m", ident ?: "TTY");
+
+ return 0;
+}
+
/* intended to be used as a SIGWINCH sighandler */
void columns_lines_cache_reset(int signum) {
cached_columns = 0;
diff --git a/src/basic/terminal-util.h b/src/basic/terminal-util.h
index d327627b20..ce454bc1ae 100644
--- a/src/basic/terminal-util.h
+++ b/src/basic/terminal-util.h
@@ -120,6 +120,8 @@ int release_terminal(void);
int terminal_vhangup_fd(int fd);
int terminal_vhangup(const char *name);
+int terminal_set_size_fd(int fd, const char *ident, unsigned rows, unsigned cols);
+
int chvt(int vt);
int read_one_char(FILE *f, char *ret, usec_t timeout, bool *need_nl);
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
index 3645e680fe..afb2d8f10f 100644
--- a/src/core/dbus-execute.c
+++ b/src/core/dbus-execute.c
@@ -1230,6 +1230,8 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("TTYReset", "b", bus_property_get_bool, offsetof(ExecContext, tty_reset), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TTYVHangup", "b", bus_property_get_bool, offsetof(ExecContext, tty_vhangup), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("TTYVTDisallocate", "b", bus_property_get_bool, offsetof(ExecContext, tty_vt_disallocate), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("TTYRows", "q", bus_property_get_unsigned, offsetof(ExecContext, tty_rows), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("TTYColumns", "q", bus_property_get_unsigned, offsetof(ExecContext, tty_cols), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SyslogPriority", "i", bus_property_get_int, offsetof(ExecContext, syslog_priority), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SyslogIdentifier", "s", NULL, offsetof(ExecContext, syslog_identifier), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SyslogLevelPrefix", "b", bus_property_get_bool, offsetof(ExecContext, syslog_level_prefix), SD_BUS_VTABLE_PROPERTY_CONST),
@@ -1860,6 +1862,12 @@ int bus_exec_context_set_transient_property(
if (streq(name, "TTYVTDisallocate"))
return bus_set_transient_bool(u, name, &c->tty_vt_disallocate, message, flags, error);
+ if (streq(name, "TTYRows"))
+ return bus_set_transient_unsigned(u, name, &c->tty_rows, message, flags, error);
+
+ if (streq(name, "TTYColumns"))
+ return bus_set_transient_unsigned(u, name, &c->tty_cols, message, flags, error);
+
if (streq(name, "PrivateTmp"))
return bus_set_transient_bool(u, name, &c->private_tmp, message, flags, error);
diff --git a/src/core/execute.c b/src/core/execute.c
index 26f847c1b0..425e3e5a37 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -213,6 +213,9 @@ static void exec_context_tty_reset(const ExecContext *context, const ExecParamet
(void) reset_terminal(path);
}
+ if (p && p->stdin_fd >= 0)
+ (void) terminal_set_size_fd(p->stdin_fd, path, context->tty_rows, context->tty_cols);
+
if (context->tty_vt_disallocate && path)
(void) vt_disallocate(path);
}
@@ -466,6 +469,7 @@ static int setup_input(
const int named_iofds[static 3]) {
ExecInput i;
+ int r;
assert(context);
assert(params);
@@ -479,6 +483,7 @@ static int setup_input(
if (isatty(STDIN_FILENO)) {
(void) ioctl(STDIN_FILENO, TIOCSCTTY, context->std_input == EXEC_INPUT_TTY_FORCE);
(void) reset_terminal_fd(STDIN_FILENO, true);
+ (void) terminal_set_size_fd(STDIN_FILENO, NULL, context->tty_rows, context->tty_cols);
}
return STDIN_FILENO;
@@ -504,6 +509,10 @@ static int setup_input(
if (fd < 0)
return fd;
+ r = terminal_set_size_fd(fd, exec_context_tty_path(context), context->tty_rows, context->tty_cols);
+ if (r < 0)
+ return r;
+
return move_fd(fd, STDIN_FILENO, false);
}
@@ -757,6 +766,7 @@ static int chown_terminal(int fd, uid_t uid) {
}
static int setup_confirm_stdio(
+ const ExecContext *context,
const char *vc,
int *ret_saved_stdin,
int *ret_saved_stdout) {
@@ -787,6 +797,10 @@ static int setup_confirm_stdio(
if (r < 0)
return r;
+ r = terminal_set_size_fd(fd, vc, context->tty_rows, context->tty_cols);
+ if (r < 0)
+ return r;
+
r = rearrange_stdio(fd, fd, STDERR_FILENO); /* Invalidates 'fd' also on failure */
TAKE_FD(fd);
if (r < 0)
@@ -848,13 +862,13 @@ enum {
CONFIRM_EXECUTE = 1,
};
-static int ask_for_confirmation(const char *vc, Unit *u, const char *cmdline) {
+static int ask_for_confirmation(const ExecContext *context, const char *vc, Unit *u, const char *cmdline) {
int saved_stdout = -1, saved_stdin = -1, r;
_cleanup_free_ char *e = NULL;
char c;
/* For any internal errors, assume a positive response. */
- r = setup_confirm_stdio(vc, &saved_stdin, &saved_stdout);
+ r = setup_confirm_stdio(context, vc, &saved_stdin, &saved_stdout);
if (r < 0) {
write_confirm_error(r, vc, u);
return CONFIRM_EXECUTE;
@@ -3994,7 +4008,7 @@ static int exec_child(
return log_oom();
}
- r = ask_for_confirmation(vc, unit, cmdline);
+ r = ask_for_confirmation(context, vc, unit, cmdline);
if (r != CONFIRM_EXECUTE) {
if (r == CONFIRM_PRETEND_SUCCESS) {
*exit_status = EXIT_SUCCESS;
@@ -5066,6 +5080,8 @@ void exec_context_init(ExecContext *c) {
#if HAVE_SECCOMP
c->syscall_errno = SECCOMP_ERROR_NUMBER_KILL;
#endif
+ c->tty_rows = UINT_MAX;
+ c->tty_cols = UINT_MAX;
numa_policy_reset(&c->numa_policy);
}
@@ -5705,11 +5721,15 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
"%sTTYPath: %s\n"
"%sTTYReset: %s\n"
"%sTTYVHangup: %s\n"
- "%sTTYVTDisallocate: %s\n",
+ "%sTTYVTDisallocate: %s\n"
+ "%sTTYRows: %u\n"
+ "%sTTYColumns: %u\n",
prefix, c->tty_path,
prefix, yes_no(c->tty_reset),
prefix, yes_no(c->tty_vhangup),
- prefix, yes_no(c->tty_vt_disallocate));
+ prefix, yes_no(c->tty_vt_disallocate),
+ prefix, c->tty_rows,
+ prefix, c->tty_cols);
if (IN_SET(c->std_output,
EXEC_OUTPUT_KMSG,
diff --git a/src/core/execute.h b/src/core/execute.h
index 18a4316d01..b0da375def 100644
--- a/src/core/execute.h
+++ b/src/core/execute.h
@@ -232,6 +232,9 @@ struct ExecContext {
bool tty_vhangup;
bool tty_vt_disallocate;
+ unsigned tty_rows;
+ unsigned tty_cols;
+
bool ignore_sigpipe;
ExecKeyringMode keyring_mode;
diff --git a/src/core/load-fragment-gperf.gperf.in b/src/core/load-fragment-gperf.gperf.in
index 5ecba47e31..5315d2da88 100644
--- a/src/core/load-fragment-gperf.gperf.in
+++ b/src/core/load-fragment-gperf.gperf.in
@@ -41,6 +41,8 @@
{{type}}.TTYReset, config_parse_bool, 0, offsetof({{type}}, exec_context.tty_reset)
{{type}}.TTYVHangup, config_parse_bool, 0, offsetof({{type}}, exec_context.tty_vhangup)
{{type}}.TTYVTDisallocate, config_parse_bool, 0, offsetof({{type}}, exec_context.tty_vt_disallocate)
+{{type}}.TTYRows, config_parse_tty_size, 0, offsetof({{type}}, exec_context.tty_rows)
+{{type}}.TTYColumns, config_parse_tty_size, 0, offsetof({{type}}, exec_context.tty_cols)
{{type}}.SyslogIdentifier, config_parse_unit_string_printf, 0, offsetof({{type}}, exec_context.syslog_identifier)
{{type}}.SyslogFacility, config_parse_log_facility, 0, offsetof({{type}}, exec_context.syslog_priority)
{{type}}.SyslogLevel, config_parse_log_level, 0, offsetof({{type}}, exec_context.syslog_priority)
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
index 62cadaf228..8cf821cd22 100644
--- a/src/core/load-fragment.c
+++ b/src/core/load-fragment.c
@@ -6494,3 +6494,29 @@ int config_parse_watchdog_sec(
return 0;
}
+
+int config_parse_tty_size(
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *section,
+ unsigned section_line,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+
+ unsigned *sz = data;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+
+ if (isempty(rvalue)) {
+ *sz = UINT_MAX;
+ return 0;
+ }
+
+ return config_parse_unsigned(unit, filename, line, section, section_line, lvalue, ltype, rvalue, data, userdata);
+}
diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h
index 9ff550410f..ab2a0393fc 100644
--- a/src/core/load-fragment.h
+++ b/src/core/load-fragment.h
@@ -148,6 +148,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_bpf_foreign_program);
CONFIG_PARSER_PROTOTYPE(config_parse_cgroup_socket_bind);
CONFIG_PARSER_PROTOTYPE(config_parse_restrict_network_interfaces);
CONFIG_PARSER_PROTOTYPE(config_parse_watchdog_sec);
+CONFIG_PARSER_PROTOTYPE(config_parse_tty_size);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
index dbd33420be..d1068440f7 100644
--- a/src/shared/bus-unit-util.c
+++ b/src/shared/bus-unit-util.c
@@ -1022,7 +1022,9 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
if (streq(field, "LogRateLimitIntervalSec"))
return bus_append_parse_sec_rename(m, field, eq);
- if (streq(field, "LogRateLimitBurst"))
+ if (STR_IN_SET(field, "LogRateLimitBurst",
+ "TTYRows",
+ "TTYColumns"))
return bus_append_safe_atou(m, field, eq);
if (streq(field, "MountFlags"))
diff --git a/test/fuzz/fuzz-unit-file/directives-all.service b/test/fuzz/fuzz-unit-file/directives-all.service
index 494c7545e4..37a68708f7 100644
--- a/test/fuzz/fuzz-unit-file/directives-all.service
+++ b/test/fuzz/fuzz-unit-file/directives-all.service
@@ -931,6 +931,8 @@ TTYPath=
TTYReset=
TTYVHangup=
TTYVTDisallocate=
+TTYRows=
+TTYColumns=
TemporaryFileSystem=
TimerSlackNSec=
TrustedCertificateFile=
diff --git a/test/fuzz/fuzz-unit-file/directives.mount b/test/fuzz/fuzz-unit-file/directives.mount
index a7ee579a9a..67421444cc 100644
--- a/test/fuzz/fuzz-unit-file/directives.mount
+++ b/test/fuzz/fuzz-unit-file/directives.mount
@@ -195,6 +195,8 @@ TTYPath=
TTYReset=
TTYVHangup=
TTYVTDisallocate=
+TTYRows=
+TTYColumns=
TasksAccounting=
TasksMax=
TemporaryFileSystem=
diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service
index 4615a2229a..d2e2a120a0 100644
--- a/test/fuzz/fuzz-unit-file/directives.service
+++ b/test/fuzz/fuzz-unit-file/directives.service
@@ -332,6 +332,8 @@ TTYPath=
TTYReset=
TTYVHangup=
TTYVTDisallocate=
+TTYRows=
+TTYColumns=
TasksAccounting=
TasksMax=
TemporaryFileSystem=
diff --git a/test/fuzz/fuzz-unit-file/directives.socket b/test/fuzz/fuzz-unit-file/directives.socket
index df64142982..865fd83adc 100644
--- a/test/fuzz/fuzz-unit-file/directives.socket
+++ b/test/fuzz/fuzz-unit-file/directives.socket
@@ -243,6 +243,8 @@ TTYPath=
TTYReset=
TTYVHangup=
TTYVTDisallocate=
+TTYRows=
+TTYColumns=
TasksAccounting=
TasksMax=
TemporaryFileSystem=
diff --git a/test/fuzz/fuzz-unit-file/directives.swap b/test/fuzz/fuzz-unit-file/directives.swap
index 1dfdffb379..f538ba8b60 100644
--- a/test/fuzz/fuzz-unit-file/directives.swap
+++ b/test/fuzz/fuzz-unit-file/directives.swap
@@ -191,6 +191,8 @@ TTYPath=
TTYReset=
TTYVHangup=
TTYVTDisallocate=
+TTYRows=
+TTYColumns=
TasksAccounting=
TasksMax=
TemporaryFileSystem=