Project `qubes-completion` - awesome bash completion for Qubes OS commands

This post is about a new project that provides bash completion for Qubes OS commands (also called tab completion).

I hope that it will be a part of Qubes OS at some point (after testing, improvements, code reviews and etc.). But even now the script works incredibly well according to my estimation.
Awesome, really. But the code is huge, and we know: more code - more places to make mistakes.

Currently I would like some users to try it and provide feedback, so that obvious problems will be found.
If you know bash, it will be an advantage, as you would be able to review the code or provide feedback.

Be aware that for proper testing the script should be installed (by simple copying) in dom0, and it is very recommended not to run anything un-trusted there.

Feel free to try and provide feedback. Additional help would be appreciated (e.g. writing test-cases, or code reviewing)

You can also help in a different way - by reading documentation below and providing text improvements or, perhaps, ideas for better implementation based on provided examples.

Here is the current state of docs:


qubes-completion - Bash tab completion for Qubes OS commands

What the project does

Project qubes-completion provides typing completions and hints for commands specific to Qubes OS operation system by completing all flags and arguments, qube names, firewall rules, devices, prefs, features, tags, packages and etc.
Typing completion is provided for almost all commands starting with qvm- and qubes- prefixes and several others.

While the project is expected to be the most valuable for advanced users who frequently use terminal in Qubes OS to manage the system, it can also be useful for average users. To use bash completions user should press Tab key once or twice when typing commands in terminal that runs bash.

What is supported

The project supports completion for:

1. Qube names (VM names):

$ qvm-firewall sys-<TAB><TAB>

sys-firewall  sys-net  sys-whonix

Running states of qubes are respected, e.g. provide only halted qubes for qvm-start:

$ qvm-start <TAB><TAB>

work  sys-whonix  fedora-37

Also classes (types) of qubes are used, like TemplateVMs and DispVMs:

$ qvm-create --template <TAB><TAB>

debian-11  fedora-37  fedora-37-minimal

2. All flags of commands (long and short versions):

$ qvm-ls --raw-<TAB><TAB>

--raw-data  --raw-list
$ qvm-ls -<TAB><TAB>

--all         --help          -o            --spinner
-d            --help-columns  -O            -t
--disk        --help-formats  --paused      --tags
--exclude     -k              -q            --tree
--fields      --kernel        --quite       -v
--format      -n              --raw-data    --verbose
-h            --network       --raw-list    
--halted      --no-spinner    --running     

3. Predefined arguments (e.g. column names, colors of qubes and etc.):

$ qvm-ls --raw-data --fields=<TAB><TAB>

CLASS  FLAGS    MEMORY  PRIV-CURR  PRIV-USED  ROOT-MAX   STATE
DISK   GATEWAY  NAME    PRIV-MAX   ROOT-CURR  ROOT-USED
$ qvm-block <TAB><TAB>

attach  detach  list 

4. Firewall rule management (including rule numbers with hints)

$ qvm-firewall sys-firewall <TAB><TAB>

add    del    list   reset
$ qvm-firewall sys-firewall add <TAB><TAB>

action=    dst6=       dstports=   icmptype=   specialtarget=
dst4=      dsthost=    expire=     proto=
$ qvm-firewall sys-firewall del -<TAB>
=> qvm-firewall sys-firewall del --rule-no=
$ qvm-firewall sys-firewall del --rule-no=<TAB><TAB>

NO  ACTION  HOST                PROTOCOL  PORT(S)  SPECIAL TARGET  ICMP TYPE  EXPIRE   COMMENT
0   accept  1.1.1.1/32          tcp       443      -               -          -       -                      
1   accept  200.200.200.200/32  udp       443      -               -          -       -                      
2   accept  222.222.222.222/32  udp       80       -               -          -       -                      
3   drop    111.111.111.111/32  udp       443      -               -          -       -                      
4   drop    111.111.111.112/32  udp       443      -               -          -       -                      
5   drop    111.111.111.113/32  udp       443      -               -          -       -                      
6   drop    92.168.0.2/16       udp       443      -               -          -       -                      
7   drop    192.168.0.0/16      tcp       81-90    dns             -          -       -
8   drop    -                   icmp      -        dns             7          +1905s  -
9   drop    92.168.0.2/16       icmp      -        dns             6          +99905s -
10  drop    111.111.111.114/32  udp       443      -               -          -       -                      
11  drop    -                   -         -        -               -          -       -
$ qvm-firewall sys-firewall del --rule-no=1<TAB><TAB>

NO  ACTION  HOST                PROTOCOL  PORT(S)  SPECIAL TARGET  ICMP TYPE  EXPIRE   COMMENT
1   accept  200.200.200.200/32  udp       443      -               -          -       -                      
10  drop    111.111.111.114/32  udp       443      -               -          -       -                      
11  drop    -                   -         -        -               -          -       -

5. Device IDs with name hints for all types (block, usb, pci and mic).

$ qvm-block attach sys-usb <TAB><TAB>

dom0:sda        WDC_SOMEDISK ()   sys-usb (read-only=no, frontend-dev=xvdx)
dom0:sdb        WORK_SOMEDISK ()
dom0:sdb1       WORK_SOMEDISK ()  work (read-only=no)
cryptvm:dm-0    somecrypt1        work (read-only=no, frontend-dev=xvdf)
cryptvm:dm-4    somecrypt5        personal (read-only=no)
$ qvm-device block detach work <TAB><TAB>

dom0:sdb1       WORK_SOMEDISK ()  work (read-only=no)
cryptvm:dm-0    somecrypt1        work (read-only=no, frontend-dev=xvdf)

6. Property names (prefs), actual and common values for them

$ qvm-prefs personal <TAB><TAB>

audiovm             installed_by_rpm   name                 uuid
autostart           ip                 netvm                vcpus
backup_timestamp    ip6                provides_network     virt_mode
debug               kernel             qid                  visible_gateway
default_dispvm      kernelopts         qrexec_timeout       visible_gateway6
default_user        keyboard_layout    shutdown_timeout     visible_ip
dns                 klass              start_time           visible_ip6
gateway             label              stubdom_mem          visible_netmask
gateway6            mac                stubdom_xid          xid
guivm               management_dispvm  template             
icon                maxmem             template_for_dispvms 
include_in_backups  memory             updateable           
$ qvm-prefs personal label <TAB><TAB>

black  blue  --default  gray  green  orange  purple  red  yellow
$ qvm-prefs personal klass <TAB><TAB>

AdminVM  AppVM  --default  DispVM  StandaloneVM  TemplateVM

7. Common qube features

qvm-features work gui<TAB>

gui    gui-default-allow-utf8-titles    gui-emulated
$ qvm-features --<TAB><TAB>

--D   --default  --delete  --help  --quite  --unset  --verbose

8. Tags of qubes

$ qvm-tags sys-firewall <TAB><TAB>

add    del    list    set    unset
$ qvm-tags sys-firewall del <TAB><TAB>

audiovm-dom0   created-by-dom0   guivm-dom0

9. Pool names:

$ qvm-create -P <TAB><TAB>

linux-kernel   varlibqubes   vm-pool

Note that volume-completion features currently are not available because qvm-volume tool is not optimized and executes too slow for providing responsive completions. If it gets optimized it will be possible to provide proper completion for volumes, too.

10. Arguments and flags provided by dnf for qubes-dom0-update:

$ sudo qubes-dom0-update --action=<TAB><TAB>

alias          distro-sync   install     reinstall            search
autoremove     downgrade     list        remove               shell
check          group         makecache   repoinfo             swap
check-update   help          mark        repolist             updateinfo
clean          history       module      repoquery            upgrade
deplist        info          provides    repository-packages  upgrade-minimal
$ sudo qubes-dom0-update --<TAB><TAB>

--action=             --disableexcludes     --noplugins
--advisories          --disableplugin       --obsoletes
--advisory            --disablerepo         --preserve-terminal
--allowerasing        --downloaddir         --quiet
--assumeno            --downloadonly        --randomwait
--assumeyes           --enableplugin        --refresh
--best                --enablerepo          --releasever
--bugfix              --enhancement         --repo
--bz                  --errorlevel          --repofrompath
--cacheonly           --exclude             --repoid
--check-only          --excludepkgs         --rpmverbosity
--clean               --force-xen-upgrade   --sec-severity
--color               --gui                 --secseverity
--config              --help                --security
--console             --help-cmd            --setopt
--cve                 --installroot         --show-output
--debuglevel          --newpackage          --showduplicates
--debugsolver         --noautoremove        --skip-broken
--destdir             --nodocs              --verbose
--disableexcludepkgs  --nogpgcheck          --version
$ sudo qubes-dom0-update --en<TAB>

--enableplugin   --enablerepo   --enhancement

11. Packages for qubes-dom0-update (limited)

Note: Currently Qubes OS (as of R4.1) does not have dnf databases of packages and repos available for installation inside dom0, so it is not possible to provide completion of them (unlike inside other qubes).
If the situation with dnf databases in dom0 ever changes and completion of repos and packages for both dnf and qubes-dom0-update will start to work well out of the box.

Currently the project provides completion of packages that are possible to remove:

$ qubes-dom0-update --action=remove gtk-<TAB>

gtk-murrine-engine-0.98.2-18.fc32.x86_64
gtk-unico-engine-1.0.3-0.15.20140109bzr152.fc32.x86_64
gtk-update-icon-cache-3.24.28-2.fc32.x86_64
gtk-xfce-engine-3.2.0-11.fc32.x86_64

12. And more…

See yourself.

List of supported commands

Currently completion is provided for Qubes OS commands listed below.

Symbol * marks commands that have completion with limited implementation or a room for improvement.

  • qvm-ls
  • qvm-tags
  • qvm-start *
  • qvm-shutdown
  • qvm-kill
  • qvm-run
  • qvm-pause
  • qvm-unpause
  • qvm-create *
  • qvm-clone
  • qvm-remove
  • qvm-device
  • qvm-block
  • qvm-usb
  • qvm-pci
  • qvm-prefs
  • qvm-features
  • qvm-volume *
  • qvm-check
  • qvm-firewall
  • qvm-service
  • qvm-sync-appmenus
  • qvm-appmenus *
  • qvm-copy-to-vm
  • qvm-move-to-vm
  • qvm-copy
  • qvm-move
  • qvm-start-gui
  • qvm-start-daemon
  • qvm-xkill
  • qvm-sync-clock
  • qvm-get-image
  • qvm-get-tinted-image
  • qvm-console-dispvm
  • qubes-dom0-update *
  • qubes-prefs
  • qubes-guid
  • qubes-hcl-report
  • qubes-bug-report
  • qubes-policy
  • qubes-vm-settings
  • qubes-vm-clone
  • qubes-vm-boot-from-device
  • qubes-input-trigger
  • qubes-guivm-session
  • qubesctl *
  • qubesd-query *

How to install and use

1. Install bash-completion

The first step is to install bash-completion. To do so run in terminal of dom0:

$ sudo qubes-dom0-update -y bash-completion

Explanation: the project qubes-completion requires bash-completion. For some reason bash-completion package is not installed in dom0 by default in Qubes OS R4.0 and R4.1 (that may be changed in the future releases). Project bash-completion is great and installed in almost all GNU/Linux distributions out of box, so it worth installing it whether you plan to use qubes-completion or not.

2. Install qubes-completion

Currently qubes-completion does not belong to any package but can be copied as a file to dom0 and saved as
/etc/bash_completion.d/qubes-completion.sh.
To copy it from some qube user should run in terminal of dom0 something like:

$ qvm-run --pass-io qubename 'cat /home/user/Downloads/qubes-completion.sh' | sudo tee '/etc/bash_completion.d/qubes-completion.sh' | wc -l

Where:

  • qubename is a name of the qube used to download qubes-completion.sh script (e.g. personal).
  • /home/user/Downloads/qubes-completion.sh is an example of full path to the downloaded file inside qubename qube.

As the output user should see a number of lines that where copied and it should not be zero.

All done

Completions should start working in all new instances of bash terminals.

About the code

Code style

The code uses style guide from Google:
Shell Style Guide

It is used for everything except indentation which is 4 spaces and no tabs.

The code was verified with a great bash script checker ShellCheck using a FLOSS extension for Codium/VS Code, thanks to the authors, it helped a lot.

Notes on structure of the code

  • Shebang is not used for bash completion scripts, it is absent on purpose.

  • Execution is started with the function main() at the end of the script.

  • All completion functions are named after the commands, but with underscores.
    E.g. for completion of qvm-create the _qvm_create() function is called.
    All functions initialize specific Qubes OS completion model
    by calling __init_qubes_completion() function.

Tests

The project has tests that are performed by BATS
(Bash Automated Testing System).
Tests are located in test/tests sub-directory.

Testing bash completions in general is not that easy.
Unlike bash completion project that uses python and other third-party stuff
to run tests the current project has it in Bash.
The magic of test preparation and execution is done by functions in file
test/tests_common.sh that make Bash completion variables look the same as
they are prepared by Bash in the terminal (using GNU function in C).

The approach also allows to generate tests (see test/tests_generator.sh).
Note that there still can be differences in parsing, so created tests should
be checked manually before using.

Generation of tests and manual checks of the expected results is required.
If tests are created for all supported functions it will ensure that further
changes are not breaking things. It is important because completion logic
for Qubes OS is quite a complicated thing.

Debugging

The project has debug-related stuff. Debug mode is managed by
DEBUG_MODE variable that is set to “0” by default (debug turned off).
Setting it to “1” makes debug code executable, otherwise it is skipped.

File debug_overrides.sh and stubs for qvm-* tools

Main script on the execution checks the existence of debug_overrides.sh file,
and if it exists it is sourced. The example of debug_overrides.sh is provided.

This way if debug output to log is not needed user can remove or rename
debug_overrides.sh file and avoid modification of the source code.
Also copying the project script alone to other places will automatically turn
debug mode off as it is desired by default.

So, the proper way to turn debug mode on is not a direct modification of
the main script, but overriding in debug_overrides.sh file, that also can
have paths to stub versions of qvm- tools that do not exist outside of dom0.

Check out the provided example of debug_overrides.sh for better understanding.

Debug functions

The script has general logging functions that allow to debug the completion
using extensive step-by-step real-time logging to an external log file:

  • Log a message to the log file if debug mode is on:
    __debug_msg()

  • Log an array to the log file if debug mode is on
    (similar to declare -p but cleaner and works with refs):
    __debug_print_array()

  • Log all BASH completion variables to the log file if debug mode is on:
    __debug_log_env()

When arguments are changed for Qubes OS tools

When arguments or flags of Qubes OS tool were changed the completion script
should be changed accordingly. The starting point of making such modifications
should be the function that corresponds to the changed tool.
E.g. _qvm_create() function for qvm-create tool.

In most cases the strings with the list or arguments and flags are located
inside these functions where they are used, and not in one place at the top
of the file with some constant strings for all commands.

The rationality of this choice:

  • Despite the fact that idea of keeping all strings in one place looks appealing,
    it’s actually makes harder to support such code. The developer still needs to
    scroll to the place where the string is used and analyze how modification of
    flags or arguments will affect the code. So keeping the strings in one place
    of the file not only does not make it easier the support of the code, but also
    requires one to keep two distant places of the code coherent and consistent.

  • The better approach is to keep the strings in place responsible for
    the corresponding command that uses them - the completion function
    with almost the same name as a command changed.

  • Please note, that code still follows DRY principle and in some cases common
    strings are still moved away just to avoid redefinition and duplicates.

Note: look the chapter about tests in this document. Because tests should be
a great help for monitoring of correctness of such code modifications.

Additional notes

The project tries not to use short versions of flags and arguments of common
third-party terminal tools and Bash builtin commands.
It allows to be more explicit, readable and error-proof.

The completion is expected to be quite smart, but it is not trying to
completely validate the way user calls functions.
Is many cases the inappropriate or misplaced arguments or flags will not be
provided to prevent user from calling something wrong, but there are still
millions of ways to call the commands improperly.
So, consider this completion script as an assistant, not a master.

Qubes OS tools has a specific thing in their syntax:

  • The arguments for flags can be provided after both “=” char and space.
  • And the value of the argument also can contain “=” char (maybe multiple?).

It makes impossible to parse commands in usual and general way, e.g. consider:

qvm-example --foo bar=baz

It’s impossible to say if --foo is followed by bar=baz value or
option bar has baz value.
So, to parse the command line some additional info is required (e.g. from docs).
It’s a minor design problem and complicates the script a bit, but what can we do.
The better design would to avoid separation of name=something values with “=”,
replace that with “:” (as it is already used in some commands) or something else.


9 Likes

Can you make github repository public or provide a link to download this script?

As I said in the beginning of this topic, the point is that at first the script should be tested or reviewed at least by someone. To avoid simple mistakes for average users who may download the script and use it forever without updates.
That is why I am currently asking for help with testing a limited group of people whom I will give the current project and only after that publish repo or package or something for everybody.

I am keen on taking a look and providing feedback @balko. My preference would be in a GitHub (or similar) repo (public or not), so that it is easier to refer to specific areas of the code when commenting. Feel free to DM me of you want.

Side note on packaging: I have a scaffold set up to create signed RPM packages for dom0 using a tool called Tito. There are a few moving pieces, but if you can follow through the Makefiles, you might find it interesting:

1 Like

At face value, this surprises me. Some commands have subcommands, e.g. qvm-volume extend like Git’s git commit, which shouldn’t be ambiguous to a completion program (because the subcommands and their options are known).

I assume that’s not what you’re referring to @balko, can you point me to an example please?

Thinking aloud… I don’t think a completion tool must necessarily support all possible syntax variants, suffice that it always suggests a correct variant. YMMV I suppose.

Well, yes, that what I am talking about - you know that extend is a command with subcommands.This knowledge is required for completion script. That is why the script is big - it deals with almost all of know and documented flags, options and etc (look at feature-list).

It is true, as said in the readme above - the tool is not doing everything possible, but it does a lot.

Thank you, I will soon DM you and give access to the project on github.

1 Like

Guessing that the option --quite is not quite as --quiet as intended :wink:

And I can’t wait to use this @balko! I’d be very pleased to help with what I can. :raised_hands:

1 Like

Thank you for working on this. This will be handy. I guess, completion should also work with zsh?
Are there working different people on this? Or same team?

1 Like

That is true, that’s a kind of mistakes I would want to avoid for the first public release :slight_smile:

Thank you, any help will be appreciated.

Thanks, I love it myself (using it for a while). About zsh - I have doubts, the package is working similar way bash-completion project works. It is quite bash-syntax dependent.

About projects - the first your link is about this project.
The second link (by user taradiddles) is something different, it’s a way smaller project.

I gave you access to the repo. Please note that this repo is a temporary one, just for upload/sharing and does not have commits tree from my working tree. So, do not make it public yet. Later I will recreate a github repo and make it public after initial problems and typos are fixed.

The project supports tests (BATS framework is used). Also I wrote test generator and executor, but writing test rules is a thing that has to be finished. Help with it will be great too, maybe community will be able to help, because it does not even require to know BASH.

OK, now anybody can try it!

I made an improved version public, you can install it from Releases section of github:

The first post was modified accordingly.

Currently I would like some users to try it and provide feedback, so that obvious problems will be found.
If you know bash, it will be an advantage, as you would be able to review the code or provide feedback.

So, feel free to try and provide feedback. Additional help would be appreciated (e.g. writing test-cases, or code reviewing).

You can also help in a different way - by reading documentation below and providing text improvements or, perhaps, ideas for better implementation based on provided examples.

New release 1.0.2 of qubes-completion was published!

Give it a try: Release v1.0.2 · jamke/qubes-completion · GitHub

Change log:

  • Add support for qvm-backup and qvm-backup-restore
  • Update all basic tests
  • Remove third-party code
  • Avoid qube completion with dom0 for some unsuitable cases
  • Add submodules of BATS
  • Improve README
  • Minor typo fixes and improvements

Installation:

  1. install bash-completion package in dom0:
    sudo qubes-dom0-update bash-completion
  2. put bash script-file attached to the release qubes-completion.sh to /etc/bash_completion.d/ in dom0.

More details can be found in the README file.

Note:

This the release was made with a help of Qubes OS community!

Thanks to all contributors, here is a list of them on github:

2 Likes

I’ve been using this, it works very well (once I look past the fact that I shot myself in the foot with some of my VM naming conventions…I’ve changed a few things and now a qube name will complete with a lot less typed).

The only irritation I have is that in dom0, qvm-copy won’t complete to qvm-copy-to-vm. Yes, both commands technically exist, but qvm-copy doesn’t actually work in dom0, so it shouldn’t be considered a possible completion. (The reverse is true in domU, but at least when it “stops” to offer you a choice, you have qvm-copy which is complete.)

OK, so having said it works well, I decided I wanted it on my laptop.

Unfortunately qubes-dom0-update seems to be having trouble finding bash-completion at the moment–and the error message indicates it’s worse than that. "Failed to download metadata for repo qubes-dom0-current: cannot prepare internal mirrorlist: status code 404 for https://yum.qubes-os.org/r$releasever/current/dom0/fc32/repomd.xml.metalink (IP 147.75.102.29).

(Please ignore any obvious typos, I had to copy it over by read-and-shift-to-other-machine-and-type.)

To be clear, your issue is not connected to my completion project, of course.
Something is broken with qubes-dom0-update in your case. If it happens all the time, you should report it to Qubes OS issue tracker.

I checked the path from you log, there is no such file indeed, check file lists: Index of /r4.1/current/dom0/fc32/ and Index of /r4.2/current/dom0/fc32/

Similar to your problem:

You’re right it doesn’t stem from the Qubes completion. But I thought you might happen to know how to fix it because it affects Qubes completion.

What do you think about my other comment (re: qvm-copy-to-vm vs. qvm-copy)?

Sorry, I missed it.
The proper solution will be to remove qvm-copy from dom0. You can post an enhancement ticket here, I think it will be a right thing to do.

Currently qvm-copy is a almost empty script in /usr/bin/qvm-copy that only prints error message, check the content yourself. Completely useless for you. You can rename it, move it somewhere or remove it. It should solve your problem completely, but during Qubes OS update it may be recovered.

NOTE: This completion that bothers you (qvm-cop<TAB><TAB>) does not depend on qubes-completion and does NOT even depend on bash-completion package. It is done by bash shell itself without those packages.

7 posts were split to a new topic: Pre-selecting different qvm-copy targets each time

Happy to split out the three posts above to a new topic if that seems useful. Just let me know.

@SteveC I split the topic, please check if I got the new title close to what you meant, and adjust it to match your intention if needed! :slightly_smiling_face:

1 Like