[bug#73494,v4,1/3] services: activation: Continue on exceptions.

Message ID 597361035150296c6051f6f78fc8cc8ff3af8b61.1745911065.git.maxim.cournoyer@gmail.com
State New
Headers
Series [bug#73494,v4,1/3] services: activation: Continue on exceptions. |

Commit Message

Maxim Cournoyer April 29, 2025, 7:17 a.m. UTC
From: Hilton Chain <hako@ultrarare.space>

Use ‘invoke’ for backtraces and avoid changing environment.

* gnu/services.scm (activation-script): Execute activation scripts in
‘invoke’.
Warn about failed activation scripts.

Change-Id: I89be31433fbb46d0c4a9dc6115ab167910840b6f
Signed-off-by: Maxim Cournoyer <maxim.cournoyer@gmail.com>
---
 gnu/services.scm | 32 +++++++++++++++++++++++++++-----
 1 file changed, 27 insertions(+), 5 deletions(-)


base-commit: 2b4680c6c17bd6470f78a1f39d2f7af8b05e279f
  

Comments

Hilton Chain April 29, 2025, 4:19 p.m. UTC | #1
On Tue, 29 Apr 2025 15:17:43 +0800,
Maxim Cournoyer wrote:
>
> From: Hilton Chain <hako@ultrarare.space>
>
> Use ‘invoke’ for backtraces and avoid changing environment.
>
> * gnu/services.scm (activation-script): Execute activation scripts in
> ‘invoke’.
> Warn about failed activation scripts.
>
> Change-Id: I89be31433fbb46d0c4a9dc6115ab167910840b6f
> Signed-off-by: Maxim Cournoyer <maxim.cournoyer@gmail.com>
> ---
>  gnu/services.scm | 32 +++++++++++++++++++++++++++-----
>  1 file changed, 27 insertions(+), 5 deletions(-)

I think this patch needs updating for 76698.  Will take a look later.
  
Ludovic Courtès May 2, 2025, 4:17 p.m. UTC | #2
Hello,

Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:

> From: Hilton Chain <hako@ultrarare.space>
>
> Use ‘invoke’ for backtraces and avoid changing environment.
>
> * gnu/services.scm (activation-script): Execute activation scripts in
> ‘invoke’.
> Warn about failed activation scripts.
>
> Change-Id: I89be31433fbb46d0c4a9dc6115ab167910840b6f
> Signed-off-by: Maxim Cournoyer <maxim.cournoyer@gmail.com>

[...]

>                        ;; Run the services' activation snippets.
> -                      ;; TODO: Use 'load-compiled'.
> -                      (for-each primitive-load '#$actions)))))
> +                      (for-each (lambda (action)
> +                                  ;; Don't block activation process when one
> +                                  ;; action fails.
> +                                  (catch #t
> +                                    (lambda ()
> +                                      (invoke action))
> +                                    (lambda _
> +                                      (warning
> +                                       (G_ "failed to activate '~a'.~%")
> +                                       action))))
> +                                '#$actions)))))

As I suggested upthread (or elsewhere? I’m lost!), I think it would be
enough to keep loading these files in the main process (as opposed to
spawning one short-lived process for each activation) and simply catch
exceptions:

  (for-each (lambda (file)
              (guard (c …)
                (save-module-excursion
                  …)))
            '#$actions)

I think it would do what we want while avoiding the overhead.  I don’t
think it qualifies as “premature optimization” because it’s already in
place and the semantics are well-defined.

Thoughts?

Ludo’.
  
Hilton Chain May 3, 2025, 2:44 p.m. UTC | #3
On Sat, 03 May 2025 00:17:23 +0800,
Ludovic Courtès wrote:
>
> Hello,
>
> Maxim Cournoyer <maxim.cournoyer@gmail.com> writes:
>
> > From: Hilton Chain <hako@ultrarare.space>
> >
> > Use ‘invoke’ for backtraces and avoid changing environment.
> >
> > * gnu/services.scm (activation-script): Execute activation scripts in
> > ‘invoke’.
> > Warn about failed activation scripts.
> >
> > Change-Id: I89be31433fbb46d0c4a9dc6115ab167910840b6f
> > Signed-off-by: Maxim Cournoyer <maxim.cournoyer@gmail.com>
>
> [...]
>
> >                        ;; Run the services' activation snippets.
> > -                      ;; TODO: Use 'load-compiled'.
> > -                      (for-each primitive-load '#$actions)))))
> > +                      (for-each (lambda (action)
> > +                                  ;; Don't block activation process when one
> > +                                  ;; action fails.
> > +                                  (catch #t
> > +                                    (lambda ()
> > +                                      (invoke action))
> > +                                    (lambda _
> > +                                      (warning
> > +                                       (G_ "failed to activate '~a'.~%")
> > +                                       action))))
> > +                                '#$actions)))))
>
> As I suggested upthread (or elsewhere? I’m lost!), I think it would be
> enough to keep loading these files in the main process (as opposed to
> spawning one short-lived process for each activation) and simply catch
> exceptions:
>
>   (for-each (lambda (file)
>               (guard (c …)
>                 (save-module-excursion
>                   …)))
>             '#$actions)
>
> I think it would do what we want while avoiding the overhead.  I don’t
> think it qualifies as “premature optimization” because it’s already in
> place and the semantics are well-defined.

Implemented in v6.
  

Patch

diff --git a/gnu/services.scm b/gnu/services.scm
index 8a4002e0724..ed17981ffdd 100644
--- a/gnu/services.scm
+++ b/gnu/services.scm
@@ -692,15 +692,28 @@  (define* (activation-service->script service)
 (define (activation-script gexps)
   "Return the system's activation script, which evaluates GEXPS."
   (define actions
-    (map (cut program-file "activate-service.scm" <>) gexps))
+    (map (lambda (action)
+           (program-file "activate-service.scm"
+                         (with-imported-modules (source-module-closure
+                                                 '((gnu build activation)
+                                                   (guix build utils)))
+                           #~(begin
+                               (use-modules (gnu build activation)
+                                            (guix build utils))
+                               #$action))))
+         gexps))
 
   (program-file "activate.scm"
                 (with-imported-modules (source-module-closure
                                         '((gnu build activation)
-                                          (guix build utils)))
+                                          (guix build utils)
+                                          (guix diagnostics)
+                                          (guix i18n)))
                   #~(begin
                       (use-modules (gnu build activation)
-                                   (guix build utils))
+                                   (guix build utils)
+                                   (guix diagnostics)
+                                   (guix i18n))
 
                       (mkdir-p "/var/run")
                       ;; Make sure the user accounting database exists.  If it
@@ -719,8 +732,17 @@  (define (activation-script gexps)
                       (activate-current-system)
 
                       ;; Run the services' activation snippets.
-                      ;; TODO: Use 'load-compiled'.
-                      (for-each primitive-load '#$actions)))))
+                      (for-each (lambda (action)
+                                  ;; Don't block activation process when one
+                                  ;; action fails.
+                                  (catch #t
+                                    (lambda ()
+                                      (invoke action))
+                                    (lambda _
+                                      (warning
+                                       (G_ "failed to activate '~a'.~%")
+                                       action))))
+                                '#$actions)))))
 
 (define (gexps->activation-gexp gexps)
   "Return a gexp that runs the activation script containing GEXPS."