diff mbox series

[4/5] create ipc sockets below /run on systemd

Message ID 20231023022529.15081-5-felix.moessbauer@siemens.com
State Changes Requested
Headers show
Series Rework socket creation to better integrate with systemd | expand

Commit Message

Felix Moessbauer Oct. 23, 2023, 2:25 a.m. UTC
When running on a systemd bootet system, the IPC sockets must be created
below /run to be in sync with sockets provided by systemd units.

Signed-off-by: Felix Moessbauer <felix.moessbauer@siemens.com>
---
 ipc/network_ipc.c  | 10 +++++++++-
 ipc/progress_ipc.c | 10 +++++++++-
 2 files changed, 18 insertions(+), 2 deletions(-)

Comments

Stefano Babic Oct. 23, 2023, 9:28 a.m. UTC | #1
Hi Felix,

On 23.10.23 04:25, 'Felix Moessbauer' via swupdate wrote:
> When running on a systemd bootet system, the IPC sockets must be created
> below /run to be in sync with sockets provided by systemd units.
> 
> Signed-off-by: Felix Moessbauer <felix.moessbauer@siemens.com>
> ---
>   ipc/network_ipc.c  | 10 +++++++++-
>   ipc/progress_ipc.c | 10 +++++++++-
>   2 files changed, 18 insertions(+), 2 deletions(-)
> 
> diff --git a/ipc/network_ipc.c b/ipc/network_ipc.c
> index 20332b3..435a579 100644
> --- a/ipc/network_ipc.c
> +++ b/ipc/network_ipc.c
> @@ -17,6 +17,10 @@
>   #include "network_ipc.h"
>   #include "compat.h"
>   
> +#ifdef CONFIG_SYSTEMD
> +#include <systemd/sd-daemon.h>
> +#endif
> +
>   #ifdef CONFIG_SOCKET_CTRL_PATH
>   static char* SOCKET_CTRL_PATH = (char*)CONFIG_SOCKET_CTRL_PATH;
>   #else
> @@ -30,7 +34,11 @@ char *get_ctrl_socket(void) {
>   		const char *tmpdir = getenv("TMPDIR");
>   		if (!tmpdir)
>   			tmpdir = "/tmp";
> -
> +// on systemd systems /run is available and used for sockets created by systemd
> +#ifdef CONFIG_SYSTEMD
> +		if(sd_booted())
> +			tmpdir = "/run";
> +#endif

No, this does not cover all use cases.

Think about we have multiple instances of SWUpdate on the same device. 
This is a pattern when the device acts with different roles (gateway vs 
device is one of them), or when the update is split into domains, and an 
instance of SWUpdate can be set with lower rights if it is in charge to 
update just a part of the system. We need two or multiple sets of 
sockets, and they should never conflict. This is currently done by 
setting TMPDIR before launching SWUpdate, and in this case letting 
SWUpdate to create the sockets itself.

Or an instance is maybe getting the sockets from systemd, the second one 
can create the sockets. It is still configurable, but if sockets have 
the same names, your change breaks this use case.

Nevertheless, why do we need to hardcode the path into the code when we 
can set it before launching ? We can just set TMPDIR as /run in the 
service unit, and this is ok in most cases (swupdate -i will create the 
sockets in /tmp).

It is also what systemd is doing, it uses env vars to pass the fds: it 
sets LISTEN_FDS to retrieve the list of fds.

Best regards,
Stefano

>   		if (asprintf(&SOCKET_CTRL_PATH, "%s/%s", tmpdir, SOCKET_CTRL_DEFAULT) == -1)
>   			return (char *)"/tmp/"SOCKET_CTRL_DEFAULT;
>   	}
> diff --git a/ipc/progress_ipc.c b/ipc/progress_ipc.c
> index 745dc44..a0def5c 100644
> --- a/ipc/progress_ipc.c
> +++ b/ipc/progress_ipc.c
> @@ -14,6 +14,10 @@
>   #include <unistd.h>
>   #include <stdbool.h>
>   
> +#ifdef CONFIG_SYSTEMD
> +#include <systemd/sd-daemon.h>
> +#endif
> +
>   #include <progress_ipc.h>
>   
>   #ifdef CONFIG_SOCKET_PROGRESS_PATH
> @@ -29,7 +33,11 @@ char *get_prog_socket(void) {
>   		const char *tmpdir = getenv("TMPDIR");
>   		if (!tmpdir)
>   			tmpdir = "/tmp";
> -
> +// on systemd systems /run is available and used for sockets created by systemd
> +#ifdef CONFIG_SYSTEMD
> +		if(sd_booted())
> +			tmpdir = "/run";
> +#endif
>   		if (asprintf(&SOCKET_PROGRESS_PATH, "%s/%s", tmpdir, SOCKET_PROGRESS_DEFAULT) == -1)
>   			return (char *)"/tmp/"SOCKET_PROGRESS_DEFAULT;
>   	}
Felix Moessbauer Oct. 23, 2023, 10:34 a.m. UTC | #2
On Mon, 2023-10-23 at 11:28 +0200, Stefano Babic wrote:
> Hi Felix,
> 
> On 23.10.23 04:25, 'Felix Moessbauer' via swupdate wrote:
> > When running on a systemd bootet system, the IPC sockets must be
> > created
> > below /run to be in sync with sockets provided by systemd units.
> > 
> > Signed-off-by: Felix Moessbauer <felix.moessbauer@siemens.com>
> > ---
> >   ipc/network_ipc.c  | 10 +++++++++-
> >   ipc/progress_ipc.c | 10 +++++++++-
> >   2 files changed, 18 insertions(+), 2 deletions(-)
> > 
> > diff --git a/ipc/network_ipc.c b/ipc/network_ipc.c
> > index 20332b3..435a579 100644
> > --- a/ipc/network_ipc.c
> > +++ b/ipc/network_ipc.c
> > @@ -17,6 +17,10 @@
> >   #include "network_ipc.h"
> >   #include "compat.h"
> >   
> > +#ifdef CONFIG_SYSTEMD
> > +#include <systemd/sd-daemon.h>
> > +#endif
> > +
> >   #ifdef CONFIG_SOCKET_CTRL_PATH
> >   static char* SOCKET_CTRL_PATH = (char*)CONFIG_SOCKET_CTRL_PATH;
> >   #else
> > @@ -30,7 +34,11 @@ char *get_ctrl_socket(void) {
> >                 const char *tmpdir = getenv("TMPDIR");
> >                 if (!tmpdir)
> >                         tmpdir = "/tmp";
> > -
> > +// on systemd systems /run is available and used for sockets
> > created by systemd
> > +#ifdef CONFIG_SYSTEMD
> > +               if(sd_booted())
> > +                       tmpdir = "/run";
> > +#endif
> 
> No, this does not cover all use cases.
> 
> Think about we have multiple instances of SWUpdate on the same
> device. 
> This is a pattern when the device acts with different roles (gateway
> vs 
> device is one of them), or when the update is split into domains, and
> an 
> instance of SWUpdate can be set with lower rights if it is in charge
> to 
> update just a part of the system. We need two or multiple sets of 
> sockets, and they should never conflict. This is currently done by 
> setting TMPDIR before launching SWUpdate, and in this case letting 
> SWUpdate to create the sockets itself.
> 
> Or an instance is maybe getting the sockets from systemd, the second
> one 
> can create the sockets. It is still configurable, but if sockets have
> the same names, your change breaks this use case.
> 
> Nevertheless, why do we need to hardcode the path into the code when
> we 
> can set it before launching ? We can just set TMPDIR as /run in the 
> service unit, and this is ok in most cases (swupdate -i will create
> the 
> sockets in /tmp).

Hi Stefano, that's a valid point I overlooked so far.

Unfortunately we cannot simply set TMPDIR freely, as this has quite
some side effects: When doing an update with a compressed image, the
image is first decompressed to TMPDIR and then flashed. However, this
artifact might be quite big and should never be stored in a tmpfs
(usually we run a bit low on memory anyways). Basically all tooling
which is used as part of the update process (including mktemp) uses
TMPDIR to find a location to store temporary artifacts.

This why I started this whole overhaul in the first place: I had to set
TMPDIR to /var/tmp (which is persistent storage in my case), as /tmp is
a tmpfs where we simply do not have enough space. And by setting it, I
went down the rabbit hole with improper cleanup, silently failing the
swupdate-progress service (forgot to set TMPDIR there as well), no
reboots on update, ...

We need a mechanism to control where the sockets are created, which
does not depend on the TMPDIR.

How about a new variable? We could even make that backwards compatible
by first checking for the new variable, then for the TMPDIR.

Best regards,
Felix

> 
> It is also what systemd is doing, it uses env vars to pass the fds:
> it 
> sets LISTEN_FDS to retrieve the list of fds.
> 
> Best regards,
> Stefano
> 
> >                 if (asprintf(&SOCKET_CTRL_PATH, "%s/%s", tmpdir,
> > SOCKET_CTRL_DEFAULT) == -1)
> >                         return (char *)"/tmp/"SOCKET_CTRL_DEFAULT;
> >         }
> > diff --git a/ipc/progress_ipc.c b/ipc/progress_ipc.c
> > index 745dc44..a0def5c 100644
> > --- a/ipc/progress_ipc.c
> > +++ b/ipc/progress_ipc.c
> > @@ -14,6 +14,10 @@
> >   #include <unistd.h>
> >   #include <stdbool.h>
> >   
> > +#ifdef CONFIG_SYSTEMD
> > +#include <systemd/sd-daemon.h>
> > +#endif
> > +
> >   #include <progress_ipc.h>
> >   
> >   #ifdef CONFIG_SOCKET_PROGRESS_PATH
> > @@ -29,7 +33,11 @@ char *get_prog_socket(void) {
> >                 const char *tmpdir = getenv("TMPDIR");
> >                 if (!tmpdir)
> >                         tmpdir = "/tmp";
> > -
> > +// on systemd systems /run is available and used for sockets
> > created by systemd
> > +#ifdef CONFIG_SYSTEMD
> > +               if(sd_booted())
> > +                       tmpdir = "/run";
> > +#endif
> >                 if (asprintf(&SOCKET_PROGRESS_PATH, "%s/%s",
> > tmpdir, SOCKET_PROGRESS_DEFAULT) == -1)
> >                         return (char
> > *)"/tmp/"SOCKET_PROGRESS_DEFAULT;
> >         }
>
Stefano Babic Oct. 23, 2023, 11:01 a.m. UTC | #3
Hi Felix,

On 23.10.23 12:34, 'MOESSBAUER, Felix' via swupdate wrote:
> On Mon, 2023-10-23 at 11:28 +0200, Stefano Babic wrote:
>> Hi Felix,
>>
>> On 23.10.23 04:25, 'Felix Moessbauer' via swupdate wrote:
>>> When running on a systemd bootet system, the IPC sockets must be
>>> created
>>> below /run to be in sync with sockets provided by systemd units.
>>>
>>> Signed-off-by: Felix Moessbauer <felix.moessbauer@siemens.com>
>>> ---
>>>    ipc/network_ipc.c  | 10 +++++++++-
>>>    ipc/progress_ipc.c | 10 +++++++++-
>>>    2 files changed, 18 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/ipc/network_ipc.c b/ipc/network_ipc.c
>>> index 20332b3..435a579 100644
>>> --- a/ipc/network_ipc.c
>>> +++ b/ipc/network_ipc.c
>>> @@ -17,6 +17,10 @@
>>>    #include "network_ipc.h"
>>>    #include "compat.h"
>>>    
>>> +#ifdef CONFIG_SYSTEMD
>>> +#include <systemd/sd-daemon.h>
>>> +#endif
>>> +
>>>    #ifdef CONFIG_SOCKET_CTRL_PATH
>>>    static char* SOCKET_CTRL_PATH = (char*)CONFIG_SOCKET_CTRL_PATH;
>>>    #else
>>> @@ -30,7 +34,11 @@ char *get_ctrl_socket(void) {
>>>                  const char *tmpdir = getenv("TMPDIR");
>>>                  if (!tmpdir)
>>>                          tmpdir = "/tmp";
>>> -
>>> +// on systemd systems /run is available and used for sockets
>>> created by systemd
>>> +#ifdef CONFIG_SYSTEMD
>>> +               if(sd_booted())
>>> +                       tmpdir = "/run";
>>> +#endif
>>
>> No, this does not cover all use cases.
>>
>> Think about we have multiple instances of SWUpdate on the same
>> device.
>> This is a pattern when the device acts with different roles (gateway
>> vs
>> device is one of them), or when the update is split into domains, and
>> an
>> instance of SWUpdate can be set with lower rights if it is in charge
>> to
>> update just a part of the system. We need two or multiple sets of
>> sockets, and they should never conflict. This is currently done by
>> setting TMPDIR before launching SWUpdate, and in this case letting
>> SWUpdate to create the sockets itself.
>>
>> Or an instance is maybe getting the sockets from systemd, the second
>> one
>> can create the sockets. It is still configurable, but if sockets have
>> the same names, your change breaks this use case.
>>
>> Nevertheless, why do we need to hardcode the path into the code when
>> we
>> can set it before launching ? We can just set TMPDIR as /run in the
>> service unit, and this is ok in most cases (swupdate -i will create
>> the
>> sockets in /tmp).
> 
> Hi Stefano, that's a valid point I overlooked so far.
> 
> Unfortunately we cannot simply set TMPDIR freely, as this has quite
> some side effects: When doing an update with a compressed image, the
> image is first decompressed to TMPDIR and then flashed. However, this
> artifact might be quite big and should never be stored in a tmpfs
> (usually we run a bit low on memory anyways).

True, but SWUpdate supports zero-copy, and this is usually set for large 
artifacts. Nothing is stored temporarily.

> Basically all tooling
> which is used as part of the update process (including mktemp) uses
> TMPDIR to find a location to store temporary artifacts.
> 
> This why I started this whole overhaul in the first place: I had to set
> TMPDIR to /var/tmp (which is persistent storage in my case), as /tmp is
> a tmpfs where we simply do not have enough space.

Ok

> And by setting it, I
> went down the rabbit hole with improper cleanup, silently failing the
> swupdate-progress service (forgot to set TMPDIR there as well), no
> reboots on update, ...

Of course, everything should be consistent then.

Internally, consistency is guaranteed because processes that wants to 
communicate call first get_ctrl_socket() to get the right path. But this 
cannot be guaranteed for the external processes.

> 
> We need a mechanism to control where the sockets are created, which
> does not depend on the TMPDIR.

So you want to have sockets and temporary files into different directories.

> 
> How about a new variable? 

Possible, I am just careful to avoid to use a lot of envs. But ther eis 
currently only TMPDIR, so we can introduce a new one with fallback to 
TMPDIR if not set.

> We could even make that backwards compatible
> by first checking for the new variable, then for the TMPDIR.

Ok -fine.

So I will apply 1-3 (this is cleanup and better code understanding), and 
drop 4-5 for the moment.

Best regards,
Stefano

> 
> Best regards,
> Felix
> 
>>
>> It is also what systemd is doing, it uses env vars to pass the fds:
>> it
>> sets LISTEN_FDS to retrieve the list of fds.
>>
>> Best regards,
>> Stefano
>>
>>>                  if (asprintf(&SOCKET_CTRL_PATH, "%s/%s", tmpdir,
>>> SOCKET_CTRL_DEFAULT) == -1)
>>>                          return (char *)"/tmp/"SOCKET_CTRL_DEFAULT;
>>>          }
>>> diff --git a/ipc/progress_ipc.c b/ipc/progress_ipc.c
>>> index 745dc44..a0def5c 100644
>>> --- a/ipc/progress_ipc.c
>>> +++ b/ipc/progress_ipc.c
>>> @@ -14,6 +14,10 @@
>>>    #include <unistd.h>
>>>    #include <stdbool.h>
>>>    
>>> +#ifdef CONFIG_SYSTEMD
>>> +#include <systemd/sd-daemon.h>
>>> +#endif
>>> +
>>>    #include <progress_ipc.h>
>>>    
>>>    #ifdef CONFIG_SOCKET_PROGRESS_PATH
>>> @@ -29,7 +33,11 @@ char *get_prog_socket(void) {
>>>                  const char *tmpdir = getenv("TMPDIR");
>>>                  if (!tmpdir)
>>>                          tmpdir = "/tmp";
>>> -
>>> +// on systemd systems /run is available and used for sockets
>>> created by systemd
>>> +#ifdef CONFIG_SYSTEMD
>>> +               if(sd_booted())
>>> +                       tmpdir = "/run";
>>> +#endif
>>>                  if (asprintf(&SOCKET_PROGRESS_PATH, "%s/%s",
>>> tmpdir, SOCKET_PROGRESS_DEFAULT) == -1)
>>>                          return (char
>>> *)"/tmp/"SOCKET_PROGRESS_DEFAULT;
>>>          }
>>
>
diff mbox series

Patch

diff --git a/ipc/network_ipc.c b/ipc/network_ipc.c
index 20332b3..435a579 100644
--- a/ipc/network_ipc.c
+++ b/ipc/network_ipc.c
@@ -17,6 +17,10 @@ 
 #include "network_ipc.h"
 #include "compat.h"
 
+#ifdef CONFIG_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+
 #ifdef CONFIG_SOCKET_CTRL_PATH
 static char* SOCKET_CTRL_PATH = (char*)CONFIG_SOCKET_CTRL_PATH;
 #else
@@ -30,7 +34,11 @@  char *get_ctrl_socket(void) {
 		const char *tmpdir = getenv("TMPDIR");
 		if (!tmpdir)
 			tmpdir = "/tmp";
-
+// on systemd systems /run is available and used for sockets created by systemd
+#ifdef CONFIG_SYSTEMD
+		if(sd_booted())
+			tmpdir = "/run";
+#endif
 		if (asprintf(&SOCKET_CTRL_PATH, "%s/%s", tmpdir, SOCKET_CTRL_DEFAULT) == -1)
 			return (char *)"/tmp/"SOCKET_CTRL_DEFAULT;
 	}
diff --git a/ipc/progress_ipc.c b/ipc/progress_ipc.c
index 745dc44..a0def5c 100644
--- a/ipc/progress_ipc.c
+++ b/ipc/progress_ipc.c
@@ -14,6 +14,10 @@ 
 #include <unistd.h>
 #include <stdbool.h>
 
+#ifdef CONFIG_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+
 #include <progress_ipc.h>
 
 #ifdef CONFIG_SOCKET_PROGRESS_PATH
@@ -29,7 +33,11 @@  char *get_prog_socket(void) {
 		const char *tmpdir = getenv("TMPDIR");
 		if (!tmpdir)
 			tmpdir = "/tmp";
-
+// on systemd systems /run is available and used for sockets created by systemd
+#ifdef CONFIG_SYSTEMD
+		if(sd_booted())
+			tmpdir = "/run";
+#endif
 		if (asprintf(&SOCKET_PROGRESS_PATH, "%s/%s", tmpdir, SOCKET_PROGRESS_DEFAULT) == -1)
 			return (char *)"/tmp/"SOCKET_PROGRESS_DEFAULT;
 	}