How to correctly provide cli suggestions for a binary behind `systemd-run`?

I have the following wrapper at the moment:

environment = {
        systemPackages =
          let
            cscliWrapper = pkgs.symlinkJoin {
              name = "cscli";
              paths = [
                # `--working-directory=/var/lib/crowdsec/data/hub`: Because `cscli hubtest` needs to be in the `hub` directory.
                (pkgs.writeShellScriptBin "cscli" ''
                  exec systemd-run \
                    --quiet \
                    --pty \
                    --wait \
                    --collect \
                    --pipe \
                    --working-directory=/var/lib/crowdsec/data/hub \
                    --property=ExecPaths="${cfg.settings.config.config_paths.plugin_dir}" \
                    --property=ExecPaths="${cfg.package}/bin/crowdsec" \
                    --property=User=${cfg.user} \
                    --property=Group=${cfg.group} \
                    --property=DynamicUser=true \
                    --property=StateDirectory="crowdsec" \
                    --property=StateDirectoryMode="0750" \
                    --property=ConfigurationDirectory="crowdsec" \
                    --property=ConfigurationDirectoryMode="0750" \
                    -- \
                    ${lib.getExe configuredCscli} "$@"
                '')
                (pkgs.runCommand "cscli-completions" { } ''
                  mkdir -p $out/share
                  ln -s ${cfg.package}/share/bash-completion $out/share/bash-completion
                  ln -s ${cfg.package}/share/zsh $out/share/zsh
                  ln -s ${cfg.package}/share/fish $out/share/fish
                '')
              ];
            };
          in
          [ cscliWrapper ];
   # ...
};

The problem is, that the autocompletion for cscli isn’t working correctly: The user gets asked its password everytime it tries to get the suggestions with tab. Any ideas how to fix that?

1 Like

I would guess that the completions invoke the cscli command, and run some kind of subcommand ad-hoc to return the completions.

Presumably this means you need to patch the completion files to point them at the “real” cscli binary.

In either case, you’ll find your answer interpreting the completion files.

Do you know any other packages which have a similar case like my one (trying to get completion for systemd-run wrappers working) which I could look at how they solved this problem?

I’m only getting this issue for non-root users if they are in a fish or zsh shell. bash works fine.

1 Like

No, but if it helps, I suspect the solution will be something like:

(pkgs.runCommand "cscli-completions" { } ''
  mkdir -p $out/share
  ln -s ${cfg.package}/share/bash-completion $out/share/bash-completion

  sed 's/cscli/${lib.getExe cfg.package}/g' '${cfg.package}/share/zsh' > "$out/share/zsh"

  sed 's/cscli/${lib.getExe cfg.package}/g' '${cfg.package}/share/fish' > "$out/share/fish"
'')

That’s likely overzealous though, and will modify things that shouldn’t be. You might need a patch file or a more specific sed. Read the files, it should help figure out what’s going on.

Ok, after some further investigations, I could narrow it down to the following line of the completion file for fish:

    # Disable ActiveHelp which is not supported for fish shell
    set -l requestComp "CSCLI_ACTIVE_HELP=0 $args[1] __complete $args[2..-1] $lastArg"

$args[1] is cscli here which is wrapped behind systemd-run, hence not being able to provide the completion output.

When I tried to replicate this line locally:

CSCLI_ACTIVE_HELP=0 cscli __complete ''

The following conditions needed to be fulfilled:

  1. The config file needs to be accessible /etc/crowdsec/config.yaml (which is not possible, because it’s in the namespace of the crowdsec service)
  2. The state directory /var/lib/crowdsec/data needs to be accessible (also not really possible, beacuse it’s in the namespace of the crowdsec service)

So… what I may think of is to create something like another cscli wrapper, which gets config.yaml with a random config- and state-directory which it can read and write to, just for the completions. Otherwise, I’m out of ideas how to make that work.

Presumably the fish completions try to access these to be able to autocomplete stateful things, e.g. which rules you have installed. A random directory won’t be fully featured.

You can either:

  • Create a modified config that mostly matches the real one, but points at an empty state directory, and patch this invocation to add a config flag that points at that config.
    • This will be incomplete feature-wise, but probably alright to use.
  • Accept that fish completions cannot be supported if you’re not executing cscli as the correct user and simply don’t install those completions for non-root users.

This isn’t on you, it’s just meh CLI design. The completions are generated by a go module, it’s likely the authors don’t actively use them themselves.

  • Accept that fish completions cannot be supported if you’re not executing cscli as the correct user and simply don’t install those completions for non-root users.

I think, I’d like to go this route. At least for now.
The PR is already big and I can add a note-section for its wiki page so that other users are aware of it (and maybe someone else comes up with a better idea).

1 Like