diff mbox series

[OpenWrt-Devel,procd,2/2] state: fix reboot causing shutdown inside LXC container

Message ID 20200121083509.24067-2-ynezz@true.cz
State Accepted
Delegated to: Petr Štetiar
Headers show
Series [OpenWrt-Devel,procd,1/2] instance: provide error feedback if ujail binary is missing | expand

Commit Message

Petr Štetiar Jan. 21, 2020, 8:35 a.m. UTC
Executing `reboot` command in OpenWrt system runing inside LXC container
results in a shutdown of the container instead of rebooting the
container.

This appears to have been caused by commit 832369078d81 ("state: fix
shutdown when running in a container (FS#2425)"), which exits the pid
einz instead of the reboot().

While at it, refactor the halting code into separate function to shorten
the switch/case block and make it clearer, decrease the indentation
level by reversing the container if condition, replace magic 0 with
EXIT_SUCCESS constant in exit() and make it wait 1s for reboot message
delivery in both container/host cases as well.

Ref: FS#2666
Cc: Paul Spooren <mail@aparcar.org>
Fixes: 832369078d81 ("state: fix shutdown when running in a container (FS#2425)")
Tested-by: Baptiste Jonglez <lede@bitsofnetworks.org>
Signed-off-by: Petr Štetiar <ynezz@true.cz>
---
 state.c | 52 ++++++++++++++++++++++++++++++----------------------
 1 file changed, 30 insertions(+), 22 deletions(-)

Comments

Paul Oranje Jan. 22, 2020, 10:09 a.m. UTC | #1
A few small remarks, see in-line, regards,
Paul

> Op 21 jan. 2020, om 09:35 heeft Petr Štetiar <ynezz@true.cz> het volgende geschreven:
> 
> Executing `reboot` command in OpenWrt system runing inside LXC container
> results in a shutdown of the container instead of rebooting the
> container.
> 
> This appears to have been caused by commit 832369078d81 ("state: fix
> shutdown when running in a container (FS#2425)"), which exits the pid
> einz instead of the reboot().
> 
> While at it, refactor the halting code into separate function to shorten
> the switch/case block and make it clearer, decrease the indentation
> level by reversing the container if condition, replace magic 0 with
> EXIT_SUCCESS constant in exit() and make it wait 1s for reboot message
> delivery in both container/host cases as well.
> 
> Ref: FS#2666
> Cc: Paul Spooren <mail@aparcar.org>
> Fixes: 832369078d81 ("state: fix shutdown when running in a container (FS#2425)")
> Tested-by: Baptiste Jonglez <lede@bitsofnetworks.org>
> Signed-off-by: Petr Štetiar <ynezz@true.cz>
> ---
> state.c | 52 ++++++++++++++++++++++++++++++----------------------
> 1 file changed, 30 insertions(+), 22 deletions(-)
> 
> diff --git a/state.c b/state.c
> index 4737d0121ad0..e117ea302f93 100644
> --- a/state.c
> +++ b/state.c
> @@ -94,6 +94,34 @@ static void set_console(void)
> 		set_stdio(tty);
> }
> 
> +static void perform_halt()
> +{
> +	if (reboot_event == RB_POWER_OFF)
> +		LOG("- power down -\n");
> +	else
> +		LOG("- reboot -\n");
> +
> +	/* Allow time for last message to reach serial console, etc */
> +	sleep(1);
> +
> +	if (is_container()) {
> +		reboot(reboot_event);
When reboot returns, hasn't something gone wrong then ?
> +		exit(EXIT_SUCCESS);
The return below after exit() can never be reached.
> +		return;
> +	}
> +
> +	/* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS)
> +	 * in linux/kernel/sys.c, which can cause the machine to panic when
> +	 * the init process exits... */
> +	if (!vfork()) { /* child */
> +		reboot(reboot_event);
When reboot returns, hasn't something gone wrong then ?
> +		_exit(EXIT_SUCCESS);
> +	}
> +
> +	while (1)
> +		sleep(1);
> +}
> +
> static void state_enter(void)
> {
> 	char ubus_cmd[] = "/sbin/ubusd";
> @@ -153,29 +181,9 @@ static void state_enter(void)
> 		sync();
> 		sleep(1);
> #ifndef DISABLE_INIT
> -		if (reboot_event == RB_POWER_OFF)
> -			LOG("- power down -\n");
> -		else
> -			LOG("- reboot -\n");
> -
> -		if (!is_container()) {
> -			/* Allow time for last message to reach serial console, etc */
> -			sleep(1);
> -
> -			/* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS)
> -			 * in linux/kernel/sys.c, which can cause the machine to panic when
> -			 * the init process exits... */
> -			if (!vfork( )) { /* child */
> -				reboot(reboot_event);
> -				_exit(EXIT_SUCCESS);
> -			}
> -
> -			while (1)
> -				sleep(1);
> -		} else
> -			exit(0);
> +		perform_halt();
> #else
> -		exit(0);
> +		exit(EXIT_SUCCESS);
> #endif
> 		break;
> 
> 
> _______________________________________________
> openwrt-devel mailing list
> openwrt-devel@lists.openwrt.org
> https://lists.openwrt.org/mailman/listinfo/openwrt-devel
Petr Štetiar Jan. 23, 2020, 9:28 a.m. UTC | #2
Paul Oranje <por@oranjevos.nl> [2020-01-22 11:09:22]:

Hi,

thanks for review.

> > +	if (is_container()) {
> > +		reboot(reboot_event);
> When reboot returns, hasn't something gone wrong then ?

What do you suggest? 

I dont know how that behaves in all environments in order to answer that
question and I've following "However, in containers reboot() call is
ignored"[1] in my head since I've discovered it.

> > +		exit(EXIT_SUCCESS);
> The return below after exit() can never be reached.

What do you suggest? 

Does that additional return hurts that much? I mean, if we keep it, it's
clear, that the code bellow the return cant be ever reached, omitting it
leaves some possibility. Debugging this stuff is PITA. 

1. https://git.busybox.net/busybox/tree/init/init.c#n750

-- ynezz
Paul Oranje Jan. 23, 2020, 1:04 p.m. UTC | #3
Op 23 jan. 2020, om 10:28 heeft Petr Štetiar <ynezz@true.cz> het volgende geschreven:
> 
> Paul Oranje <por@oranjevos.nl> [2020-01-22 11:09:22]:
> 
> Hi,
> 
> thanks for review.
> 
>>> +	if (is_container()) {
>>> +		reboot(reboot_event);
>> When reboot returns, hasn't something gone wrong then ?
> 
> What do you suggest?
Falling through to exit() seems arbitrary, the return statement after that is a double whammy.
Suggestion: log of this unexpected condition or even stronger, an assert().

> I dont know how that behaves in all environments in order to answer that
> question and I've following "However, in containers reboot() call is
> ignored"[1] in my head since I've discovered it.
Ah, so the code builds on that busybox implementation. An assert() would kill 

>>> +		exit(EXIT_SUCCESS);
>> The return below after exit() can never be reached.
> 
> What do you suggest? 
Suggestion: remove the superfluous return statement or replace it with a comment like /* NEVER REACHED */.
Decorating perform_halt() with the _Noreturn function specifier might be an idea as it allows the compiler to warn when the function would possibly return (as of "c11 6.7.4 Function specifiers", or is an older version of the C standard to be used ?).

> Does that additional return hurts that much? I mean, if we keep it, it's
> clear, that the code bellow the return cant be ever reached, omitting it
> leaves some possibility. Debugging this stuff is PITA. 
Of coarse it does not really hurt, it's a bit like an if that always returns with an else clause, in that it is meaningless.
Besides, when code coverage analysis is used, these won't dirty the analysis.

> 
> 1. https://git.busybox.net/busybox/tree/init/init.c#n750


Bye,
Paul
Petr Štetiar Jan. 23, 2020, 2:58 p.m. UTC | #4
On January 23, 2020 1:04:16 PM UTC, Paul Oranje <por@oranjevos.nl> wrote:

>Suggestion: log of this unexpected condition or even stronger, an
>assert().

All logging is killed already, logging just to serial console won't cut it, assert is overkill, which needs compiler flags/ifdef fiddling and who knows how that would behave in different environments.

I could split it into separate functions if that implicit return would be more acceptable for you. It just seems overkill to me, just to make both of us satisfied :-)

>Decorating perform_halt() with the _Noreturn function specifier might

Ok, I like this one.
Paul Oranje Jan. 23, 2020, 6:19 p.m. UTC | #5
Op 23 jan. 2020, om 15:58 heeft Petr Štetiar <ynezz@true.cz> het volgende geschreven:
> 
> On January 23, 2020 1:04:16 PM UTC, Paul Oranje <por@oranjevos.nl> wrote:
> 
>> Suggestion: log of this unexpected condition or even stronger, an
>> assert().
> 
> All logging is killed already, logging just to serial console won't cut it, assert is overkill, which needs compiler flags/ifdef fiddling and who knows how that would behave in different environments.
Okay, did not think about the impact of this stdclib call.

> I could split it into separate functions if that implicit return would be more acceptable for you. It just seems overkill to me, just to make both of us satisfied :-)
Right, it's not really worth the trouble, it's just kind of aesthetic thing.

> 
>> Decorating perform_halt() with the _Noreturn function specifier might
> 
> Ok, I like this one.
>
diff mbox series

Patch

diff --git a/state.c b/state.c
index 4737d0121ad0..e117ea302f93 100644
--- a/state.c
+++ b/state.c
@@ -94,6 +94,34 @@  static void set_console(void)
 		set_stdio(tty);
 }
 
+static void perform_halt()
+{
+	if (reboot_event == RB_POWER_OFF)
+		LOG("- power down -\n");
+	else
+		LOG("- reboot -\n");
+
+	/* Allow time for last message to reach serial console, etc */
+	sleep(1);
+
+	if (is_container()) {
+		reboot(reboot_event);
+		exit(EXIT_SUCCESS);
+		return;
+	}
+
+	/* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS)
+	 * in linux/kernel/sys.c, which can cause the machine to panic when
+	 * the init process exits... */
+	if (!vfork()) { /* child */
+		reboot(reboot_event);
+		_exit(EXIT_SUCCESS);
+	}
+
+	while (1)
+		sleep(1);
+}
+
 static void state_enter(void)
 {
 	char ubus_cmd[] = "/sbin/ubusd";
@@ -153,29 +181,9 @@  static void state_enter(void)
 		sync();
 		sleep(1);
 #ifndef DISABLE_INIT
-		if (reboot_event == RB_POWER_OFF)
-			LOG("- power down -\n");
-		else
-			LOG("- reboot -\n");
-
-		if (!is_container()) {
-			/* Allow time for last message to reach serial console, etc */
-			sleep(1);
-
-			/* We have to fork here, since the kernel calls do_exit(EXIT_SUCCESS)
-			 * in linux/kernel/sys.c, which can cause the machine to panic when
-			 * the init process exits... */
-			if (!vfork( )) { /* child */
-				reboot(reboot_event);
-				_exit(EXIT_SUCCESS);
-			}
-
-			while (1)
-				sleep(1);
-		} else
-			exit(0);
+		perform_halt();
 #else
-		exit(0);
+		exit(EXIT_SUCCESS);
 #endif
 		break;