/etc/bash_command_not_found is utterly broken

Bug #1479805 reported by CarstenHey on 2015-07-30
10
This bug affects 1 person
Affects Status Importance Assigned to Milestone
command-not-found (Ubuntu)
Low
Zygmunt Krynicki

Bug Description

In a nutshell, if the package command-not-found is removed, but not purged, sourcing this file leads to silent failures if a command is not found.

I don't use Ubuntu, but I assume that the file bash_command_not_found in the bzr repository is installed to /etc

./x is your bash_command_not_found with /usr/ replaced by /ur/ to simulate that the packages is removed but not purged.

I use a self written printexitvalue (see tcsh and zsh), which prints the line "bash: exit $?" via $PROMPT_COMMAND - this is unrelated to the bug, but shown in the output below.

$ unset -f command_not_found_handle
$ cat x
command_not_found_handle() {
  if [ -x /ur/lib/command-not-found ]; then
     /ur/lib/command-not-found -- "$1"
     return $?
  else
     return 127
  fi
}
$ foo
bash: foo: command not found
bash: exit 127
$ . ./x
$ foo
bash: exit 127
$

As you can see, trying to run the non-available command foo results in no output (except of the one I do in $PROMPT_COMMAND) and correctly fails with exit code 127. It does not print "bash: foo: command not found", as it should.

command_not_found_handle() is not command_not_found_handler() (note the trailing 'r' in the word handler, that's not in the word handle). man bash (for the former function) and man zshall (for the latter function) contain the glory details how both functions work and how they differ. If /usr/lib/command-not-found is available, your command_not_found_handle() implementation behaves as it should, but if /usr/lib/command-not-found is not available it behaves as zsh's command_not_found_handler() should, but not as a command_not_found_handle() should.

An untested, but presumably correct implementation, except of the unconditional overwriting of the function (I don't know how to prevent this in bash properly), and returning 127 when it should return 126 (see man p exec), based on /etc/zsh_command_not_found, is shown below.

Please replace two leading spaces with a tab, four leading spaces with two tabs and so on; and don't replace "|| return $?" with "\nreturn $?", in case someone uses set -e interactively. I quoted the words return and builtin to guard against weird alias definitions (I did not do this in the zsh snipppet because, in general, I expect zsh users to have at least basic shell knowledge, and sometimes zsh users do ugly things on purpose.

# (c) Zygmunt Krynicki 2007,
# Licensed under GPL, see COPYING for the whole text
#
# This script will look-up command in the database and suggest
# installation of packages available from the repository

if [[ -x /usr/lib/command-not-found ]] ; then
  command_not_found_handle() {
    [[ -x /usr/lib/command-not-found ]] || {
      \builtin printf >&2 'bash: %scommand not found\n' ${1+"$1: "}
      \return 127
    }
    /usr/lib/command-not-found -- ${1+"$1"} || \return $?
  }
fi

Related branches

CarstenHey (c.hey) wrote :

A possible changelog entry is:

  * /etc/bash_command_not_found: print error message if a command
    is not found, and the package has been removed but not purged.

I think an indentation with four spaces is better for this file (in order to avoid line wrapping on 80x25 terminals).
This file is indented accordingly and I have replaced ']] ;' with ']];', since, unlike the zsh variant, it contains no ')) ;' :

  http://stateful.de/~carsten/tmp/150730QuR6xwuv7Z0/bash_command_not_found

Testing the function, i.e., w/o the “if [[ -x /usr/lib/command-not-found ]] ; then” guard around the function definition, before committing seems to be a good idea. This requires an Ubuntu installation (which I do not have) and should be done with the package installed, and with the package removed but not purged.

Btw., \builtin print avoids functions and aliases named printf, which could lead to a command-not-found-error in command_not_found_handle() - not in a bulletproof way, but for this we'd need a new POSIX release.

CarstenHey (c.hey) wrote :

  * /etc/bash_command_not_found: print an error message if a command
    is not found, and the package has been removed but not purged.

CarstenHey (c.hey) wrote :

I also added a check to both shell snippets (i.e., for zsh and for bash) that prevents defining the handler function if the shell is run interactively. Ideally, all users would set up their shell rc files in a way that does not source the snippets for interactive shells, but users are not perfect …. I can imagine rare cases where command-not-found is used for debugging, and where a user might want to enable it even for non-interactive shells. To ease this, $PS1 is checked, and not $-. The outer if condition now is for both shells: [[ -n "${PS1-}" && -x /usr/lib/command-not-found ]]

I also replaced all tabs with four spaces each in the zsh snippet in order to let the file fit on an 80 characters wide terminal w/o line wrapping.

There is still one issue that could be fixed in these files: LP:#559060. Don't expect a patch from me for this one, although I might send one, if I stumble over a clean solution.

http://stateful.de/~carsten/tmp/150730jFFWrFL4qo4/bash_command_not_found
http://stateful.de/~carsten/tmp/150730jFFWrFL4qo4/zsh_command_not_found

  * bash_command_not_found:
      - Print an error message if a command is not found, and the package
        has been removed but not purged. (LP: #1479805)
      - Do not define the handler function if the package has been removed.
      - Do not define the handler function if the shell is interactive.
  * zsh_command_not_found:
      - Do not define the handler function if the shell is not interactive.
      - Reindent file in order to fit on an 80 characters wide terminal without
        line wrapping.

CarstenHey (c.hey) wrote :

  * bash_command_not_found:
      - Print an error message if a command is not found, and the package
        has been removed but not purged. (LP: #1479805)
      - Don't run command-not-found if bash_completion is used.

CarstenHey (c.hey) wrote :

  * zsh_command_not_found:
      - Guard against command-not-found accidentally returning 0.

Hey.

Can you please make those patches into merge requests. I basically
stopped supporting c-n-f out of lack of time. The closer you get that
to trunk, the easier it will be to merge and release to ubuntu.

Thanks
ZK

On Thu, Aug 13, 2015 at 12:32 PM, CarstenHey <email address hidden> wrote:
> * zsh_command_not_found:
> - Guard against command-not-found accidentally returning 0.
>
> ** Attachment added: "zsh_command_not_found"
> https://bugs.launchpad.net/ubuntu/+source/command-not-found/+bug/1479805/+attachment/4444080/+files/zsh_command_not_found
>
> --
> You received this bug notification because you are subscribed to
> command-not-found in Ubuntu.
> https://bugs.launchpad.net/bugs/1479805
>
> Title:
> /etc/bash_command_not_found is utterly broken
>
> To manage notifications about this bug go to:
> https://bugs.launchpad.net/ubuntu/+source/command-not-found/+bug/1479805/+subscriptions

Daniel Holbach (dholbach) wrote :

Thanks a lot Carsten. I turned the scripts you provided into a merge proposal over at https://code.launchpad.net/~dholbach/command-not-found/1479805/+merge/275678

Maybe you can both take a look.

Changed in command-not-found (Ubuntu):
status: New → In Progress
Changed in command-not-found (Ubuntu):
importance: Undecided → Medium
CarstenHey (c.hey) wrote :

* Mathew Hodson [2015-11-03 20:09 -0000]:
> ** Changed in: command-not-found (Ubuntu)
> Importance: Undecided => Medium

'Medium' might be too high (if anybody cares if it is low or medium):

I wrote in my initial mail:
| I don't use Ubuntu, but I assume that the file bash_command_not_found
| in the bzr repository is installed to /etc

Looks like this assumption was wrong:

<http://packages.ubuntu.com/search?searchon=contents&keywords=_command_not_found&mode=&suite=xenial&arch=any>:
| You have searched for paths that end with _command_not_found in suite xenial, all sections, and all architectures. Found 1 results.
|
| File Packages
| /etc/zsh_command_not_found command-not-found
|

Hence, the actual bug only affects upstream, but not the Ubuntu package,
but Ubuntu would benefit from the minor zsh improvements.

Shipping (a fixed) bash_command_not_found in the source package and
providing it in the upstream repository is still useful, for example,
a sysadmin might disable command-not-found in the system wide
bash.bashrc and source /etc/bash_command_not_found if it exists in the
skeleton .bashrc.

Carsten

Changed in command-not-found (Ubuntu):
importance: Medium → Low
Iain Lane (laney) wrote :

Hi zyga, could you look at this please?

Iain Lane (laney) on 2016-01-19
Changed in command-not-found (Ubuntu):
assignee: nobody → Zygmunt Krynicki (zyga)
Steve Langasek (vorlon) wrote :

It doesn't appear to be true that this bug is currently in progress.

However, I'm also not sure if there's anything further that needs doing for Ubuntu. The bzr history shows:

committer: Michael Vogt <email address hidden>
timestamp: Mon 2015-07-20 15:08:23 +0200
message:
  merge /etc/zsh_command_not_found: from Carsten Hey

But the timestamp on this is before the patch that was posted here. And the committed result is somewhat different than the one attached to this bug report.

Carsten, are any further changes needed to the zsh file?

Changed in command-not-found (Ubuntu):
status: In Progress → Incomplete
CarstenHey (c.hey) wrote :
Download full text (5.1 KiB)

Hi,

Ubuntu's zsh c-n-f foo is fine now and c-n-f integration for bash is
done in the bash package, which is fine too (maybe this bash completion
c-n-f thing documented in the bash completion package could be added,
but I don't know how to trigger this specific bug which runs the c-n-f
magic in the bash completion - only how to prevent it).

* Steve Langasek [2016-06-17 14:57 -0000]:
> It doesn't appear to be true that this bug is currently in progress.
>
> However, I'm also not sure if there's anything further that needs doing
> for Ubuntu. The bzr history shows:
>
> committer: Michael Vogt <email address hidden>
> timestamp: Mon 2015-07-20 15:08:23 +0200
> message:
> merge /etc/zsh_command_not_found: from Carsten Hey
>
> But the timestamp on this is before the patch that was posted here.

Two different patches doing roughly the same were posted in different
ways ... I don't think that the reason is relevant, but I could explain
it if you really want to know.

> And the committed result is somewhat different than the one attached
> to this bug report.

Both patches are good, but not perfect (reaching perfection would be
quite tricky and the gain would be very small, for instance, a useless
stat(2) could be avoided).

I did not read the two patches now, anyway the later one contains very
very very minor improvements, either by catching some really weird
corner cases, or by being shorter, or by being more readable, or ... but
this is hardly a reason to invest any time in merging.

> Carsten, are any further changes needed to the zsh file?

No related changes are needed in Ubuntu.

> ** Changed in: command-not-found (Ubuntu)
> Status: In Progress => Incomplete
>
> --
> You received this bug notification because you are subscribed to the bug
> report.
> https://bugs.launchpad.net/bugs/1479805
>
> Title:
> /etc/bash_command_not_found is utterly broken
>
> Status in command-not-found package in Ubuntu:
> Incomplete
>
> Bug description:
> In a nutshell, if the package command-not-found is removed, but not
> purged, sourcing this file leads to silent failures if a command is
> not found.
>
> I don't use Ubuntu, but I assume that the file bash_command_not_found
> in the bzr repository is installed to /etc
>
> ./x is your bash_command_not_found with /usr/ replaced by /ur/ to
> simulate that the packages is removed but not purged.
>
> I use a self written printexitvalue (see tcsh and zsh), which prints
> the line "bash: exit $?" via $PROMPT_COMMAND - this is unrelated to
> the bug, but shown in the output below.
>
> $ unset -f command_not_found_handle
> $ cat x
> command_not_found_handle() {
> if [ -x /ur/lib/command-not-found ]; then
> /ur/lib/command-not-found -- "$1"
> return $?
> else
> return 127
> fi
> }
> $ foo
> bash: foo: command not found
> bash: exit 127
> $ . ./x
> $ foo
> bash: exit 127
> $
>
> As you can see, trying to run the non-available command foo results in
> no output (except of the one I do in $PROMPT_COMMAND) and correctly
> fails with exit code 127. It does not print "bash: foo: command not
> found", as it should.
>
> c...

Read more...

Steve Langasek (vorlon) wrote :

Thanks for the feedback, marking as resolved.

Changed in command-not-found (Ubuntu):
status: Incomplete → Fix Released
To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers