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=