From c44ec9cece23b3ef4b7403dafed8cdf54b924437 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Fri, 24 Nov 2017 13:46:51 +0100 Subject: [PATCH] merge lxc-console improvements from stable branch Signed-off-by: Wolfgang Bumiller --- ...art-after-a-potential-syslog.service.patch | 2 +- ...lxcnetaddbr-when-instantiating-veths.patch | 2 +- ...003-deny-rw-mounting-of-sys-and-proc.patch | 2 +- ...iting-from-the-namespaced-cgroup-roo.patch | 2 +- ...make-cgroupns-separation-level-confi.patch | 2 +- ...ame-cgroup-namespace-directory-to-ns.patch | 2 +- ...run-lxc-monitord-as-a-regular-daemon.patch | 2 +- .../0008-Make-lxc-.service-forking.patch | 2 +- .../0009-console-non-functional-changes.patch | 116 +++++++++++++++ .../0010-console-non-functional-changes.patch | 135 ++++++++++++++++++ ...011-console-fix-console-info-message.patch | 121 ++++++++++++++++ ...sole-report-detach-message-on-demand.patch | 36 +++++ ...le-use-correct-escape-sequence-check.patch | 26 ++++ debian/patches/series | 5 + 14 files changed, 447 insertions(+), 8 deletions(-) create mode 100644 debian/patches/0009-console-non-functional-changes.patch create mode 100644 debian/patches/0010-console-non-functional-changes.patch create mode 100644 debian/patches/0011-console-fix-console-info-message.patch create mode 100644 debian/patches/0012-console-report-detach-message-on-demand.patch create mode 100644 debian/patches/0013-console-use-correct-escape-sequence-check.patch diff --git a/debian/patches/0001-lxc.service-start-after-a-potential-syslog.service.patch b/debian/patches/0001-lxc.service-start-after-a-potential-syslog.service.patch index 3050937..6162efc 100644 --- a/debian/patches/0001-lxc.service-start-after-a-potential-syslog.service.patch +++ b/debian/patches/0001-lxc.service-start-after-a-potential-syslog.service.patch @@ -1,7 +1,7 @@ From 92f2489b28e79f7a67f45bc698f1d61785a6537d Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Fri, 10 Feb 2017 09:13:40 +0100 -Subject: [PATCH 1/8] lxc.service: start after a potential syslog.service +Subject: [PATCH 01/13] lxc.service: start after a potential syslog.service Signed-off-by: Wolfgang Bumiller --- diff --git a/debian/patches/0002-pve-run-lxcnetaddbr-when-instantiating-veths.patch b/debian/patches/0002-pve-run-lxcnetaddbr-when-instantiating-veths.patch index a767784..cac7bd0 100644 --- a/debian/patches/0002-pve-run-lxcnetaddbr-when-instantiating-veths.patch +++ b/debian/patches/0002-pve-run-lxcnetaddbr-when-instantiating-veths.patch @@ -1,7 +1,7 @@ From 6aecf604cf28c5164f3d957b0ad33bf03527fa26 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Fri, 10 Feb 2017 09:15:37 +0100 -Subject: [PATCH 2/8] pve: run lxcnetaddbr when instantiating veths +Subject: [PATCH 02/13] pve: run lxcnetaddbr when instantiating veths FIXME: Why aren't we using regular up-scripts? diff --git a/debian/patches/0003-deny-rw-mounting-of-sys-and-proc.patch b/debian/patches/0003-deny-rw-mounting-of-sys-and-proc.patch index ffb903e..3ec25b2 100644 --- a/debian/patches/0003-deny-rw-mounting-of-sys-and-proc.patch +++ b/debian/patches/0003-deny-rw-mounting-of-sys-and-proc.patch @@ -1,7 +1,7 @@ From 8c695baaff8d18a87233ffc119e8fd0495819dbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= Date: Wed, 9 Nov 2016 09:14:26 +0100 -Subject: [PATCH 3/8] deny rw mounting of /sys and /proc +Subject: [PATCH 03/13] deny rw mounting of /sys and /proc this would allow root in a privileged container to change the permissions of /sys on the host, which could lock out diff --git a/debian/patches/0004-separate-the-limiting-from-the-namespaced-cgroup-roo.patch b/debian/patches/0004-separate-the-limiting-from-the-namespaced-cgroup-roo.patch index fd6f4be..52db64f 100644 --- a/debian/patches/0004-separate-the-limiting-from-the-namespaced-cgroup-roo.patch +++ b/debian/patches/0004-separate-the-limiting-from-the-namespaced-cgroup-roo.patch @@ -1,7 +1,7 @@ From 6ebdc24c00b4dee75aebef3136469a5297e1d9ee Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Tue, 15 Nov 2016 09:20:24 +0100 -Subject: [PATCH 4/8] separate the limiting from the namespaced cgroup root +Subject: [PATCH 04/13] separate the limiting from the namespaced cgroup root When cgroup namespaces are enabled a privileged container with mixed cgroups has full write access to its own root diff --git a/debian/patches/0005-start-initutils-make-cgroupns-separation-level-confi.patch b/debian/patches/0005-start-initutils-make-cgroupns-separation-level-confi.patch index 01166e3..4beb179 100644 --- a/debian/patches/0005-start-initutils-make-cgroupns-separation-level-confi.patch +++ b/debian/patches/0005-start-initutils-make-cgroupns-separation-level-confi.patch @@ -1,7 +1,7 @@ From ef58cfcf70fbe666acee0c407f77a22eeb1eec4f Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Wed, 16 Nov 2016 09:53:42 +0100 -Subject: [PATCH 5/8] start/initutils: make cgroupns separation level +Subject: [PATCH 05/13] start/initutils: make cgroupns separation level configurable Adds a new global config variable `lxc.cgroup.separate` diff --git a/debian/patches/0006-rename-cgroup-namespace-directory-to-ns.patch b/debian/patches/0006-rename-cgroup-namespace-directory-to-ns.patch index 3f5a5c8..9a570bc 100644 --- a/debian/patches/0006-rename-cgroup-namespace-directory-to-ns.patch +++ b/debian/patches/0006-rename-cgroup-namespace-directory-to-ns.patch @@ -1,7 +1,7 @@ From 1341290e8af87aab15e844abb1a1451cb21ec275 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Fri, 23 Dec 2016 15:57:24 +0100 -Subject: [PATCH 6/8] rename cgroup namespace directory to ns +Subject: [PATCH 06/13] rename cgroup namespace directory to ns Signed-off-by: Wolfgang Bumiller --- diff --git a/debian/patches/0007-possibility-to-run-lxc-monitord-as-a-regular-daemon.patch b/debian/patches/0007-possibility-to-run-lxc-monitord-as-a-regular-daemon.patch index 63ef5d2..1eb458e 100644 --- a/debian/patches/0007-possibility-to-run-lxc-monitord-as-a-regular-daemon.patch +++ b/debian/patches/0007-possibility-to-run-lxc-monitord-as-a-regular-daemon.patch @@ -1,7 +1,7 @@ From 6811fb42be10c4eaf026be35914c546a95520b9e Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Mon, 20 Nov 2017 10:49:41 +0100 -Subject: [PATCH 7/8] possibility to run lxc-monitord as a regular daemon +Subject: [PATCH 07/13] possibility to run lxc-monitord as a regular daemon lxc-monitord instances are spawned on demand and, if this happens from a service, the daemon is considered part of diff --git a/debian/patches/0008-Make-lxc-.service-forking.patch b/debian/patches/0008-Make-lxc-.service-forking.patch index 7431ca1..32e7b70 100644 --- a/debian/patches/0008-Make-lxc-.service-forking.patch +++ b/debian/patches/0008-Make-lxc-.service-forking.patch @@ -1,7 +1,7 @@ From 2001f560675efca7d6dcabe8fb8b376442d5d6d0 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Mon, 20 Nov 2017 10:51:36 +0100 -Subject: [PATCH 8/8] Make lxc@.service forking +Subject: [PATCH 08/13] Make lxc@.service forking Previously the init process' output was dumped into the log files since the service used Type=simple and diff --git a/debian/patches/0009-console-non-functional-changes.patch b/debian/patches/0009-console-non-functional-changes.patch new file mode 100644 index 0000000..13c2cab --- /dev/null +++ b/debian/patches/0009-console-non-functional-changes.patch @@ -0,0 +1,116 @@ +From 105ec17dbcad4fc48183a824df13b825974848bb Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Mon, 23 Oct 2017 13:41:33 +0200 +Subject: [PATCH 09/13] console: non-functional changes + +Signed-off-by: Christian Brauner +--- + src/lxc/console.c | 39 +++++++++++++++++++++++---------------- + 1 file changed, 23 insertions(+), 16 deletions(-) + +diff --git a/src/lxc/console.c b/src/lxc/console.c +index c8e545eb..3592662b 100644 +--- a/src/lxc/console.c ++++ b/src/lxc/console.c +@@ -498,9 +498,13 @@ out: + + void lxc_console_delete(struct lxc_console *console) + { +- if (console->tios && console->peer >= 0 && +- tcsetattr(console->peer, TCSAFLUSH, console->tios)) +- WARN("failed to set old terminal settings"); ++ int ret; ++ ++ if (console->tios && console->peer >= 0) { ++ ret = tcsetattr(console->peer, TCSAFLUSH, console->tios); ++ if (ret < 0) ++ WARN("%s - Failed to set old terminal settings", strerror(errno)); ++ } + free(console->tios); + console->tios = NULL; + +@@ -509,7 +513,6 @@ void lxc_console_delete(struct lxc_console *console) + close(console->slave); + if (console->log_fd >= 0) + close(console->log_fd); +- + console->peer = -1; + console->master = -1; + console->slave = -1; +@@ -518,57 +521,61 @@ void lxc_console_delete(struct lxc_console *console) + + int lxc_console_create(struct lxc_conf *conf) + { ++ int ret, saved_errno; + struct lxc_console *console = &conf->console; +- int ret; + + if (!conf->rootfs.path) { +- INFO("container does not have a rootfs, console device will be shared with the host"); ++ INFO("Container does not have a rootfs. The console will be " ++ "shared with the host"); + return 0; + } + + if (console->path && !strcmp(console->path, "none")) { +- INFO("no console requested"); ++ INFO("No console was requested"); + return 0; + } + + process_lock(); + ret = openpty(&console->master, &console->slave, console->name, NULL, NULL); ++ saved_errno = errno; + process_unlock(); + if (ret < 0) { +- SYSERROR("failed to allocate a pty"); ++ ERROR("%s - Failed to allocate a pty", strerror(saved_errno)); + return -1; + } + +- if (fcntl(console->master, F_SETFD, FD_CLOEXEC)) { +- SYSERROR("failed to set console master to close-on-exec"); ++ ret = fcntl(console->master, F_SETFD, FD_CLOEXEC); ++ if (ret < 0) { ++ SYSERROR("Failed to set FD_CLOEXEC flag on console master"); + goto err; + } + +- if (fcntl(console->slave, F_SETFD, FD_CLOEXEC)) { +- SYSERROR("failed to set console slave to close-on-exec"); ++ ret = fcntl(console->slave, F_SETFD, FD_CLOEXEC); ++ if (ret < 0) { ++ SYSERROR("Failed to set FD_CLOEXEC flag on console slave"); + goto err; + } + + ret = lxc_console_peer_default(console); + if (ret < 0) { +- ERROR("failed to allocate peer tty device"); ++ ERROR("Failed to allocate a peer pty device"); + goto err; + } + + if (console->log_path) { + console->log_fd = lxc_unpriv(open(console->log_path, O_CLOEXEC | O_RDWR | O_CREAT | O_APPEND, 0600)); + if (console->log_fd < 0) { +- SYSERROR("failed to open console log file \"%s\"", console->log_path); ++ SYSERROR("Failed to open console log file \"%s\"", console->log_path); + goto err; + } +- DEBUG("using \"%s\" as console log file", console->log_path); ++ DEBUG("Using \"%s\" as console log file", console->log_path); + } + + return 0; + + err: + lxc_console_delete(console); +- return -1; ++ return -ENODEV; + } + + int lxc_console_set_stdfds(int fd) +-- +2.11.0 + diff --git a/debian/patches/0010-console-non-functional-changes.patch b/debian/patches/0010-console-non-functional-changes.patch new file mode 100644 index 0000000..ff2724a --- /dev/null +++ b/debian/patches/0010-console-non-functional-changes.patch @@ -0,0 +1,135 @@ +From 7394b5c772b562f239b4138b11ecc8493b4c30b4 Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Thu, 26 Oct 2017 17:33:10 +0200 +Subject: [PATCH 10/13] console: non-functional changes + +Signed-off-by: Christian Brauner +--- + src/lxc/console.c | 61 +++++++++++++++++++++++++++++-------------------------- + 1 file changed, 32 insertions(+), 29 deletions(-) + +diff --git a/src/lxc/console.c b/src/lxc/console.c +index 3592662b..eda5be62 100644 +--- a/src/lxc/console.c ++++ b/src/lxc/console.c +@@ -678,18 +678,16 @@ int lxc_console(struct lxc_container *c, int ttynum, + istty = isatty(stdinfd); + if (istty) { + ret = lxc_setup_tios(stdinfd, &oldtios); +- if (ret) { +- ERROR("failed to setup terminal properties"); ++ if (ret < 0) + return -1; +- } + } else { +- INFO("fd %d does not refer to a tty device", stdinfd); ++ INFO("File descriptor %d does not refer to a tty device", stdinfd); + } + + ttyfd = lxc_cmd_console(c->name, &ttynum, &masterfd, c->config_path); + if (ttyfd < 0) { + ret = ttyfd; +- goto err1; ++ goto restore_tios; + } + + fprintf(stderr, "\n" +@@ -699,13 +697,13 @@ int lxc_console(struct lxc_container *c, int ttynum, + ttynum, 'a' + escape - 1); + + ret = setsid(); +- if (ret) +- INFO("already group leader"); ++ if (ret < 0) ++ TRACE("Process is already group leader"); + + ts = lxc_console_sigwinch_init(stdinfd, masterfd); + if (!ts) { + ret = -1; +- goto err2; ++ goto close_fds; + } + ts->escape = escape; + ts->winch_proxy = c->name; +@@ -719,52 +717,57 @@ int lxc_console(struct lxc_container *c, int ttynum, + + ret = lxc_mainloop_open(&descr); + if (ret) { +- ERROR("failed to create mainloop"); +- goto err3; ++ ERROR("Failed to create mainloop"); ++ goto sigwinch_fini; + } + + if (ts->sigfd != -1) { + ret = lxc_mainloop_add_handler(&descr, ts->sigfd, +- lxc_console_cb_sigwinch_fd, ts); +- if (ret) { +- ERROR("failed to add handler for SIGWINCH fd"); +- goto err4; ++ lxc_console_cb_sigwinch_fd, ts); ++ if (ret < 0) { ++ ERROR("Failed to add SIGWINCH handler"); ++ goto close_mainloop; + } + } + + ret = lxc_mainloop_add_handler(&descr, ts->stdinfd, + lxc_console_cb_tty_stdin, ts); +- if (ret) { +- ERROR("failed to add handler for stdinfd"); +- goto err4; ++ if (ret < 0) { ++ ERROR("Failed to add stdin handler"); ++ goto close_mainloop; + } + + ret = lxc_mainloop_add_handler(&descr, ts->masterfd, + lxc_console_cb_tty_master, ts); +- if (ret) { +- ERROR("failed to add handler for masterfd"); +- goto err4; ++ if (ret < 0) { ++ ERROR("Failed to add master handler"); ++ goto close_mainloop; + } + + ret = lxc_mainloop(&descr, -1); +- if (ret) { +- ERROR("mainloop returned an error"); +- goto err4; ++ if (ret < 0) { ++ ERROR("The mainloop returned an error"); ++ goto close_mainloop; + } + + ret = 0; + +-err4: ++close_mainloop: + lxc_mainloop_close(&descr); +-err3: ++ ++sigwinch_fini: + lxc_console_sigwinch_fini(ts); +-err2: ++ ++close_fds: + close(masterfd); + close(ttyfd); +-err1: ++ ++restore_tios: + if (istty) { +- if (tcsetattr(stdinfd, TCSAFLUSH, &oldtios) < 0) +- WARN("failed to reset terminal properties: %s.", strerror(errno)); ++ istty = tcsetattr(stdinfd, TCSAFLUSH, &oldtios); ++ if (istty < 0) ++ WARN("%s - Failed to restore terminal properties", ++ strerror(errno)); + } + + return ret; +-- +2.11.0 + diff --git a/debian/patches/0011-console-fix-console-info-message.patch b/debian/patches/0011-console-fix-console-info-message.patch new file mode 100644 index 0000000..ace2a38 --- /dev/null +++ b/debian/patches/0011-console-fix-console-info-message.patch @@ -0,0 +1,121 @@ +From c8a9c9c9f6c189164a2eedd0d1e99905cb0a8672 Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Fri, 10 Nov 2017 19:07:38 +0100 +Subject: [PATCH 11/13] console: fix console info message + +Now it will be displayed nicely formatted: + +a1 login: chb@conventiont|~ +> lxc console a1 + +Connected to tty 0 +Type to exit the console, to enter Ctrl+a itself + +Ubuntu 17.10 a1 console + +a1 login: + +Signed-off-by: Christian Brauner +--- + src/lxc/console.c | 54 ++++++++++++++++++++++++++---------------------------- + 1 file changed, 26 insertions(+), 28 deletions(-) + +diff --git a/src/lxc/console.c b/src/lxc/console.c +index eda5be62..e95f5c54 100644 +--- a/src/lxc/console.c ++++ b/src/lxc/console.c +@@ -675,26 +675,9 @@ int lxc_console(struct lxc_container *c, int ttynum, + struct lxc_tty_state *ts; + int istty = 0; + +- istty = isatty(stdinfd); +- if (istty) { +- ret = lxc_setup_tios(stdinfd, &oldtios); +- if (ret < 0) +- return -1; +- } else { +- INFO("File descriptor %d does not refer to a tty device", stdinfd); +- } +- + ttyfd = lxc_cmd_console(c->name, &ttynum, &masterfd, c->config_path); +- if (ttyfd < 0) { +- ret = ttyfd; +- goto restore_tios; +- } +- +- fprintf(stderr, "\n" +- "Connected to tty %1$d\n" +- "Type to exit the console, " +- " to enter Ctrl+%2$c itself\n", +- ttynum, 'a' + escape - 1); ++ if (ttyfd < 0) ++ return -1; + + ret = setsid(); + if (ret < 0) +@@ -710,9 +693,12 @@ int lxc_console(struct lxc_container *c, int ttynum, + ts->winch_proxy_lxcpath = c->config_path; + ts->stdoutfd = stdoutfd; + ++ istty = isatty(stdinfd); + if (istty) { + lxc_console_winsz(stdinfd, masterfd); + lxc_cmd_console_winch(ts->winch_proxy, ts->winch_proxy_lxcpath); ++ } else { ++ INFO("File descriptor %d does not refer to a tty device", stdinfd); + } + + ret = lxc_mainloop_open(&descr); +@@ -744,14 +730,34 @@ int lxc_console(struct lxc_container *c, int ttynum, + goto close_mainloop; + } + ++ fprintf(stderr, "\n" ++ "Connected to tty %1$d\n" ++ "Type to exit the console, " ++ " to enter Ctrl+%2$c itself\n", ++ ttynum, 'a' + escape - 1); ++ ++ if (istty) { ++ ret = lxc_setup_tios(stdinfd, &oldtios); ++ if (ret < 0) ++ goto close_mainloop; ++ } ++ + ret = lxc_mainloop(&descr, -1); + if (ret < 0) { + ERROR("The mainloop returned an error"); +- goto close_mainloop; ++ goto restore_tios; + } + + ret = 0; + ++restore_tios: ++ if (istty) { ++ istty = tcsetattr(stdinfd, TCSAFLUSH, &oldtios); ++ if (istty < 0) ++ WARN("%s - Failed to restore terminal properties", ++ strerror(errno)); ++ } ++ + close_mainloop: + lxc_mainloop_close(&descr); + +@@ -762,13 +768,5 @@ close_fds: + close(masterfd); + close(ttyfd); + +-restore_tios: +- if (istty) { +- istty = tcsetattr(stdinfd, TCSAFLUSH, &oldtios); +- if (istty < 0) +- WARN("%s - Failed to restore terminal properties", +- strerror(errno)); +- } +- + return ret; + } +-- +2.11.0 + diff --git a/debian/patches/0012-console-report-detach-message-on-demand.patch b/debian/patches/0012-console-report-detach-message-on-demand.patch new file mode 100644 index 0000000..2765a1a --- /dev/null +++ b/debian/patches/0012-console-report-detach-message-on-demand.patch @@ -0,0 +1,36 @@ +From 1e47ecd9a9367e6cf166c872d3cec26eb4c77aba Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Fri, 10 Nov 2017 19:51:57 +0100 +Subject: [PATCH 12/13] console: report detach message on demand + +When users pass -1 there's there won't be an escape sequence to exit the +console so no need to print a misleading info message about how to detach. + +Signed-off-by: Christian Brauner +--- + src/lxc/console.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/src/lxc/console.c b/src/lxc/console.c +index e95f5c54..8f6203f0 100644 +--- a/src/lxc/console.c ++++ b/src/lxc/console.c +@@ -730,11 +730,14 @@ int lxc_console(struct lxc_container *c, int ttynum, + goto close_mainloop; + } + +- fprintf(stderr, "\n" ++ if (ts->escape >= 1) { ++ fprintf(stderr, ++ "\n" + "Connected to tty %1$d\n" + "Type to exit the console, " + " to enter Ctrl+%2$c itself\n", + ttynum, 'a' + escape - 1); ++ } + + if (istty) { + ret = lxc_setup_tios(stdinfd, &oldtios); +-- +2.11.0 + diff --git a/debian/patches/0013-console-use-correct-escape-sequence-check.patch b/debian/patches/0013-console-use-correct-escape-sequence-check.patch new file mode 100644 index 0000000..4885de4 --- /dev/null +++ b/debian/patches/0013-console-use-correct-escape-sequence-check.patch @@ -0,0 +1,26 @@ +From b50f5a815258541b6da0c772388d93ea303a4f3d Mon Sep 17 00:00:00 2001 +From: Christian Brauner +Date: Sat, 11 Nov 2017 23:05:33 +0100 +Subject: [PATCH 13/13] console: use correct escape sequence check + +Signed-off-by: Christian Brauner +--- + src/lxc/console.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/lxc/console.c b/src/lxc/console.c +index 8f6203f0..72c01b19 100644 +--- a/src/lxc/console.c ++++ b/src/lxc/console.c +@@ -616,7 +616,7 @@ int lxc_console_cb_tty_stdin(int fd, uint32_t events, void *cbdata, + if (lxc_read_nointr(ts->stdinfd, &c, 1) <= 0) + return 1; + +- if (ts->escape != -1) { ++ if (ts->escape >= 1) { + /* we want to exit the console with Ctrl+a q */ + if (c == ts->escape && !ts->saw_escape) { + ts->saw_escape = 1; +-- +2.11.0 + diff --git a/debian/patches/series b/debian/patches/series index 3ff7181..c5af9e5 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -6,3 +6,8 @@ 0006-rename-cgroup-namespace-directory-to-ns.patch 0007-possibility-to-run-lxc-monitord-as-a-regular-daemon.patch 0008-Make-lxc-.service-forking.patch +0009-console-non-functional-changes.patch +0010-console-non-functional-changes.patch +0011-console-fix-console-info-message.patch +0012-console-report-detach-message-on-demand.patch +0013-console-use-correct-escape-sequence-check.patch -- 2.39.2