GNU bug report logs - #51417
[PATCH] environment: Suggest command upon 'execlp' failure.

Previous Next

Package: guix-patches;

Reported by: Ludovic Courtès <ludo <at> gnu.org>

Date: Tue, 26 Oct 2021 17:46:02 UTC

Severity: normal

Tags: patch

Done: Ludovic Courtès <ludo <at> gnu.org>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 51417 in the body.
You can then email your comments to 51417 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to guix-patches <at> gnu.org:
bug#51417; Package guix-patches. (Tue, 26 Oct 2021 17:46:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Ludovic Courtès <ludo <at> gnu.org>:
New bug report received and forwarded. Copy sent to guix-patches <at> gnu.org. (Tue, 26 Oct 2021 17:46:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: guix-patches <at> gnu.org
Cc: Ludovic Courtès <ludo <at> gnu.org>
Subject: [PATCH] environment: Suggest command upon 'execlp' failure.
Date: Tue, 26 Oct 2021 19:45:26 +0200
* guix/scripts/environment.scm (launch-environment): Call
'primitive-_exit' upon 'system-error.
(suggest-command-name, validate-exit-status): New procedures.
(launch-environment/fork): Call 'validate-exit-status'.
(launch-environment/container)[exit/status*]: New procedure.
Use it instead of 'exit/status'.
---
 guix/scripts/environment.scm | 48 +++++++++++++++++++++++++++++++++---
 1 file changed, 45 insertions(+), 3 deletions(-)

Hi!

Here’s the idea:

--8<---------------cut here---------------start------------->8---
$ ./pre-inst-env guix shell coreutils -C -- name
guix shell: error: name: command not found
hint: Did you mean 'uname'?

$ ./pre-inst-env guix shell ungoogled-chromium -- ungoogled-chromium
guix shell: error: ungoogled-chromium: command not found
hint: Did you mean 'chromium'?

$ ./pre-inst-env guix shell supertuxkart -- supertuxcart
guix shell: error: supertuxcart: command not found
hint: Did you mean 'supertuxkart'?

--8<---------------cut here---------------end--------------->8---

Thoughts?

Ludo’.

diff --git a/guix/scripts/environment.scm b/guix/scripts/environment.scm
index 7b97a8e39a..fc55151254 100644
--- a/guix/scripts/environment.scm
+++ b/guix/scripts/environment.scm
@@ -34,6 +34,7 @@ (define-module (guix scripts environment)
   #:use-module (guix scripts)
   #:use-module (guix scripts build)
   #:use-module (guix transformations)
+  #:autoload   (ice-9 ftw) (scandir)
   #:autoload   (gnu build linux-container) (call-with-container %namespaces
                                             user-namespace-supported?
                                             unprivileged-user-namespace-supported?
@@ -401,7 +402,12 @@ (define* (launch-environment command profile manifest
 
   (match command
     ((program . args)
-     (apply execlp program program args))))
+     (catch 'system-error
+       (lambda ()
+         (apply execlp program program args))
+       (lambda _
+         ;; Following established convention, exit with 127 upon ENOENT.
+         (primitive-_exit 127))))))
 
 (define (child-shell-environment shell profile manifest)
   "Create a child process, load PROFILE and MANIFEST, and then run SHELL in
@@ -552,6 +558,38 @@ (define-syntax-rule (warn exp ...)
         (info (G_ "All is good!  The shell gets correct environment \
 variables.~%")))))
 
+(define (suggest-command-name profile command)
+  "COMMAND was not found in PROFILE so display a hint suggesting the closest
+command name."
+  (define not-dot?
+    (match-lambda
+      ((or "." "..") #f)
+      (_ #t)))
+
+  (match (scandir (string-append profile "/bin") not-dot?)
+    (() #f)
+    (available
+     (match command
+       ((executable _ ...)
+        ;; Look for a suggestion with a high threshold: a suggestion is
+        ;; usually better than no suggestion.
+        (let ((closest (string-closest executable available
+                                       #:threshold 12)))
+          (unless (or (not closest) (string=? closest executable))
+            (display-hint (format #f (G_ "Did you mean '~a'?~%")
+                                  closest)))))))))
+
+(define (validate-exit-status profile command status)
+  "When STATUS, an integer as returned by 'waitpid', is 127, raise a \"command
+not found\" error.  Otherwise return STATUS."
+  ;; Most likely, exit value 127 means ENOENT.
+  (when (eqv? (status:exit-val status) 127)
+    (report-error (G_ "~a: command not found~%")
+                  (first command))
+    (suggest-command-name profile command)
+    (exit 1))
+  status)
+
 (define* (launch-environment/fork command profile manifest
                                   #:key pure? (white-list '()))
   "Run COMMAND in a new process with an environment containing PROFILE, with
@@ -563,7 +601,8 @@ (define* (launch-environment/fork command profile manifest
                            #:pure? pure?
                            #:white-list white-list))
     (pid (match (waitpid pid)
-           ((_ . status) status)))))
+           ((_ . status)
+            (validate-exit-status profile command status))))))
 
 (define* (launch-environment/container #:key command bash user user-mappings
                                        profile manifest link-profile? network?
@@ -584,6 +623,9 @@ (define (optional-mapping->fs mapping)
     (and (file-exists? (file-system-mapping-source mapping))
          (file-system-mapping->bind-mount mapping)))
 
+  (define (exit/status* status)
+    (exit/status (validate-exit-status profile command status)))
+
   (mlet %store-monad ((reqs (inputs->requisites
                              (list (direct-store-path bash) profile))))
     (return
@@ -640,7 +682,7 @@ (define (optional-mapping->fs mapping)
                                       '())
                                   (map file-system-mapping->bind-mount
                                        mappings))))
-       (exit/status
+       (exit/status*
         (call-with-container file-systems
           (lambda ()
             ;; Setup global shell.

base-commit: 0a42998a50e8bbe9e49142b21a570db00efe7491
-- 
2.33.0





Reply sent to Ludovic Courtès <ludo <at> gnu.org>:
You have taken responsibility. (Sat, 06 Nov 2021 22:24:02 GMT) Full text and rfc822 format available.

Notification sent to Ludovic Courtès <ludo <at> gnu.org>:
bug acknowledged by developer. (Sat, 06 Nov 2021 22:24:02 GMT) Full text and rfc822 format available.

Message #10 received at 51417-done <at> debbugs.gnu.org (full text, mbox):

From: Ludovic Courtès <ludo <at> gnu.org>
To: 51417-done <at> debbugs.gnu.org
Subject: Re: bug#51417: [PATCH] environment: Suggest command upon 'execlp'
 failure.
Date: Sat, 06 Nov 2021 23:23:37 +0100
Ludovic Courtès <ludo <at> gnu.org> skribis:

> * guix/scripts/environment.scm (launch-environment): Call
> 'primitive-_exit' upon 'system-error.
> (suggest-command-name, validate-exit-status): New procedures.
> (launch-environment/fork): Call 'validate-exit-status'.
> (launch-environment/container)[exit/status*]: New procedure.
> Use it instead of 'exit/status'.

Pushed in 5d2d87fed7efcb8f0cee040125f7768bab3df3f4.

Ludo’.




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Sun, 05 Dec 2021 12:24:06 GMT) Full text and rfc822 format available.

This bug report was last modified 2 years and 139 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.