diff mbox series

tools: use systemd to schedule reboot

Message ID 20241103084851.118639-1-oss@braunwarth.dev
State Changes Requested
Headers show
Series tools: use systemd to schedule reboot | expand

Commit Message

Daniel Braunwarth Nov. 3, 2024, 8:48 a.m. UTC
On systems which are using systemd we should not use the reboot()
syscall to reboot the system. This is bypassing systemd.

Signed-off-by: Daniel Braunwarth <oss@braunwarth.dev>
---
 tools/swupdate-ipc.c      | 28 +++++++++++++++++++++++-----
 tools/swupdate-progress.c | 15 ++++++++++++++-
 2 files changed, 37 insertions(+), 6 deletions(-)

Comments

Stefano Babic Nov. 3, 2024, 11:14 a.m. UTC | #1
Hi Daniel,

On 03.11.24 09:48, Daniel Braunwarth wrote:
> On systems which are using systemd we should not use the reboot()
> syscall to reboot the system. This is bypassing systemd.
>

The reason for this is there are use cases where it is wanted to bypass
any user space tool and ask the kernel to reboot. Instead of executing
systemctl, user space should then run shutdown, that it unaware about
init manager, and for systemd based devices it is just a link to systemctl.

The use case you want is supported with :

- swupdate-progress is started without -r
- swudpate is started with -e (exec script), and this can run systemctl
or better shutdown.

If this appears hidden. we could add a command line parm to configure
the reboot command by distinguish between system call or external command.

Best regards,
Stefano

> Signed-off-by: Daniel Braunwarth <oss@braunwarth.dev>
> ---
>   tools/swupdate-ipc.c      | 28 +++++++++++++++++++++++-----
>   tools/swupdate-progress.c | 15 ++++++++++++++-
>   2 files changed, 37 insertions(+), 6 deletions(-)
>
> diff --git a/tools/swupdate-ipc.c b/tools/swupdate-ipc.c
> index 0dfaeac..3b17d23 100644
> --- a/tools/swupdate-ipc.c
> +++ b/tools/swupdate-ipc.c
> @@ -535,6 +535,28 @@ static void restart_system(unsigned int ndevs)
>   	}
>   }
>
> +static void reboot_device(void)
> +{
> +	int ret;
> +
> +#ifdef CONFIG_SYSTEMD
> +	ret = system("systemctl reboot");
> +	if (ret == 0) {
> +		/* systemctl reboot is non-blocking and returns after the reboot
> +		   is initiated */
> +		while(true);
> +	}
> +#else
> +	sleep(5);
> +	sync();
> +	ret = reboot(RB_AUTOBOOT);
> +#endif
> +
> +	if (ret < 0) { /* Should never happen. */
> +		fprintf(stdout, "Please reset the board.\n");
> +	}
> +}
> +
>   static int sysrestart(cmd_t  __attribute__((__unused__)) *cmd, int argc, char *argv[]) {
>   	int connfd;
>   	struct progress_msg msg;
> @@ -651,11 +673,7 @@ static int sysrestart(cmd_t  __attribute__((__unused__)) *cmd, int argc, char *a
>   		case SUCCESS:
>   			fprintf(stdout, "Ready to reboot !\n");
>   			restart_system(ndevs);
> -			sleep(5);
> -			sync();
> -			if (reboot(RB_AUTOBOOT) < 0) { /* It should never happen */
> -				fprintf(stdout, "Please reset the board.\n");
> -			}
> +			reboot_device();
>   			break;
>   		case FAILURE:
>   			ndevs = 0;
> diff --git a/tools/swupdate-progress.c b/tools/swupdate-progress.c
> index 5e37162..6df3a29 100644
> --- a/tools/swupdate-progress.c
> +++ b/tools/swupdate-progress.c
> @@ -191,9 +191,22 @@ static void fill_progress_bar(char *bar, size_t size, unsigned int percent)
>
>   static void reboot_device(void)
>   {
> +	int ret;
> +
> +#ifdef CONFIG_SYSTEMD
> +	ret = system("systemctl reboot");
> +	if (ret == 0) {
> +		/* systemctl reboot is non-blocking and returns after the reboot
> +		   is initiated */
> +		while(true);
> +	}
> +#else
>   	sleep(5);
>   	sync();
> -	if (reboot(RB_AUTOBOOT) < 0) { /* Should never happen. */
> +	ret = reboot(RB_AUTOBOOT);
> +#endif
> +
> +	if (ret < 0) { /* Should never happen. */
>   		fprintf(stdout, "Please reset the board.\n");
>   	}
>   }
Daniel Braunwarth Nov. 3, 2024, 12:15 p.m. UTC | #2
Hi Stefano

On 03.11.24 12:14, Stefano Babic wrote:
> Hi Daniel,
> 
> On 03.11.24 09:48, Daniel Braunwarth wrote:
>> On systems which are using systemd we should not use the reboot()
>> syscall to reboot the system. This is bypassing systemd.
>>
> 
> The reason for this is there are use cases where it is wanted to bypass
> any user space tool and ask the kernel to reboot. Instead of executing
> systemctl, user space should then run shutdown, that it unaware about
> init manager, and for systemd based devices it is just a link to systemctl.

I can hardly imagine a valid use case to bypass systemd here. Do you 
have any example?

 From my point of view it is a high risk bypassing systemd here. This 
will sooner or later cause problems.

> 
> The use case you want is supported with :
> 
> - swupdate-progress is started without -r
> - swudpate is started with -e (exec script), and this can run systemctl
> or better shutdown.

What flag to you mean by "-e"? I'm only aware of the following:
-e, --select <software>,<mode> : Select software images set and source


How would I implement a use case were updates with and without the 
"no-reboot" can be applied to the target?

Until now we are using a postupdate command (something like 
"systemd-timer ... systemctl reboot"), but this prevents the use case 
where no reboot is required.

> 
> If this appears hidden. we could add a command line parm to configure
> the reboot command by distinguish between system call or external command.

That's something I could add additionally.


Regards
Daniel
Stefano Babic Nov. 3, 2024, 12:50 p.m. UTC | #3
Hi Daniel,

On 03.11.24 13:15, Daniel Braunwarth wrote:
> Hi Stefano
>
> On 03.11.24 12:14, Stefano Babic wrote:
>> Hi Daniel,
>>
>> On 03.11.24 09:48, Daniel Braunwarth wrote:
>>> On systems which are using systemd we should not use the reboot()
>>> syscall to reboot the system. This is bypassing systemd.
>>>
>>
>> The reason for this is there are use cases where it is wanted to bypass
>> any user space tool and ask the kernel to reboot. Instead of executing
>> systemctl, user space should then run shutdown, that it unaware about
>> init manager, and for systemd based devices it is just a link to
>> systemctl.
>
> I can hardly imagine a valid use case to bypass systemd here. Do you
> have any example?
>
>  From my point of view it is a high risk bypassing systemd here. This
> will sooner or later cause problems.

By many systems, a power cut can happens at any time and the way to
restart the system is just a way to power cut, and a reboot via system
call is the most close to it. Complex systems will need a shutdown and
they are backed up in case of power cut. I see the opposite, if a device
will require a systemd shutdown, it is not safe enough against power cut
and could not boot again when it is powered again. In such cases,
devices should have enough time to shut down before power is away (like
servers / pcs).

There are use cases, too, where a reboot is initiated by stopping to
trigger the watchdog. This is wanted to increase tests and be sure that
everything is fine if a power cut happens.

>
>>
>> The use case you want is supported with :
>>
>> - swupdate-progress is started without -r
>> - swudpate is started with -e (exec script), and this can run systemctl
>> or better shutdown.
>
> What flag to you mean by "-e"? I'm only aware of the following:
> -e, --select <software>,<mode> : Select software images set and source

Usage ./tools/swupdate-progress [OPTION]
  -c, --color             : Use colors to show results
  -e, --exec <script>     : call the script with the result of update


>
>
> How would I implement a use case were updates with and without the "no-
> reboot" can be applied to the target?

mmhhh...you're right. This case is not supported, it is not passed to
the script.


>
> Until now we are using a postupdate command (something like "systemd-
> timer ... systemctl reboot"), but this prevents the use case where no
> reboot is required.

Understood. In fact, to get this information, the progress interface
must be implemented, like swupdate-progress is doing.

But I will still maintain a way to bypass systemd for cases where this
is forced. I would also avoid using CONFIG_ like CONFIG_SYSTEMD in
tools, as the same tools are sometimes installed on devices with systemd
(production software) and without (the recovery system). It cna be
checked if systemctl is available instead.

>
>>
>> If this appears hidden. we could add a command line parm to configure
>> the reboot command by distinguish between system call or external
>> command.
>
> That's something I could add additionally.

Ok

Regards,
Stefano
diff mbox series

Patch

diff --git a/tools/swupdate-ipc.c b/tools/swupdate-ipc.c
index 0dfaeac..3b17d23 100644
--- a/tools/swupdate-ipc.c
+++ b/tools/swupdate-ipc.c
@@ -535,6 +535,28 @@  static void restart_system(unsigned int ndevs)
 	}
 }
 
+static void reboot_device(void)
+{
+	int ret;
+
+#ifdef CONFIG_SYSTEMD
+	ret = system("systemctl reboot");
+	if (ret == 0) {
+		/* systemctl reboot is non-blocking and returns after the reboot
+		   is initiated */
+		while(true);
+	}
+#else
+	sleep(5);
+	sync();
+	ret = reboot(RB_AUTOBOOT);
+#endif
+
+	if (ret < 0) { /* Should never happen. */
+		fprintf(stdout, "Please reset the board.\n");
+	}
+}
+
 static int sysrestart(cmd_t  __attribute__((__unused__)) *cmd, int argc, char *argv[]) {
 	int connfd;
 	struct progress_msg msg;
@@ -651,11 +673,7 @@  static int sysrestart(cmd_t  __attribute__((__unused__)) *cmd, int argc, char *a
 		case SUCCESS:
 			fprintf(stdout, "Ready to reboot !\n");
 			restart_system(ndevs);
-			sleep(5);
-			sync();
-			if (reboot(RB_AUTOBOOT) < 0) { /* It should never happen */
-				fprintf(stdout, "Please reset the board.\n");
-			}
+			reboot_device();
 			break;
 		case FAILURE:
 			ndevs = 0;
diff --git a/tools/swupdate-progress.c b/tools/swupdate-progress.c
index 5e37162..6df3a29 100644
--- a/tools/swupdate-progress.c
+++ b/tools/swupdate-progress.c
@@ -191,9 +191,22 @@  static void fill_progress_bar(char *bar, size_t size, unsigned int percent)
 
 static void reboot_device(void)
 {
+	int ret;
+
+#ifdef CONFIG_SYSTEMD
+	ret = system("systemctl reboot");
+	if (ret == 0) {
+		/* systemctl reboot is non-blocking and returns after the reboot
+		   is initiated */
+		while(true);
+	}
+#else
 	sleep(5);
 	sync();
-	if (reboot(RB_AUTOBOOT) < 0) { /* Should never happen. */
+	ret = reboot(RB_AUTOBOOT);
+#endif
+
+	if (ret < 0) { /* Should never happen. */
 		fprintf(stdout, "Please reset the board.\n");
 	}
 }