dparrish / libcli

Libcli provides a shared library for including a Cisco-like command-line interface into other software. It's a telnet interface which supports command-line editing, history, authentication and callbacks for a user-definable function tree.
https://dparrish.com/link/libcli
GNU Lesser General Public License v2.1
296 stars 148 forks source link

how to append the command after arugment? #97

Open PFtyrant opened 4 months ago

PFtyrant commented 4 months ago

Hi there! stuck in the following situations...

  1. How do I append a command after an argument? example:

    cmd looks like: ip igmp snooping vlan <VLAN-LIST> mrouter interfaces <INTERFACE-LIST>
    - ip
    - igmp
      - snooping
        - vlan
          - VLAN-LIST                     --> argument here
            - mrouter
              - interfaces
                - INTERFACES-LIST         --> argument here
  2. command and argument can't be at the same depth? example:

    • VLAN
      • --> this is arguments for VLAN
      • default-vlan --> this is a sub command of VLAN command

I would appreciate any feedback! Thank you!

filka55533 commented 4 months ago

Hello! You can dispatch it only with very hard validation callback on snooping command. I think, you have very hard command hierarchy. You can define vlan config mode, where you can store context within all required data about vlan, and declare commands, that would be enable on vlan config mode.

#define MODE_CONFIG_VLAN 0x02
struct vlan_context {
  // Add all fields that you need for vlan
};

int config_vlan(struct cli_def *cli, const char *command, char *argv[], int argc);

void initialize_commands(struct cli_def *cli) {
  struct cli_def *c;
  c = cli_register_command(cli, NULL, "vlan", config_vlan, PRIVILEGE_PRIVILEGED, MODE_CONFIG,
                           "Configure VLAN");

  c = cli_register_command(cli, c, "ip", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG, "ip help");
  c = cli_register_command(cli, c, "igmp", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG,
                           "igmp help");
  c = cli_register_command(cli, c, "snooping", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG,
                           "snooping help");
  c = cli_register_command(cli, c, "mrouter", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG_VLAN,
                           "mrouter help");
  c = cli_register_command(cli, c, "interfaces", NULL, PRIVILEGE_PRIVILEGED, MODE_CONFIG_VLAN,
                           "interfaces help");
}

int config_vlan(struct cli_def *cli, const char *command, char *argv[], int argc) {
  static struct vlan_context ctx;
  // Your validation routine, if no argument should be use -- set default vlan id
  ...
  // If all is ok
  cli_set_context(cli, (void *)&ctx);
  cli_set_configmode(cli, MODE_CONFIG_VLAN, "vlan");
}

And than you can config it as

your_proj> enable 
your_proj# configure terminal
your_proj(config)# vlan 12
your_proj(config-vlan)# ip igmp snooping mrouter interfaces <INTERFACE-LIST>

For my opinion this would be much easier, than when you would write snooping command callback.

PFtyrant commented 4 months ago

Thanks for your reply, @filka55533. it is a good opinion. Unfortunately, we don't want to modify the CLI command hierarchy. So, regarding question 2, is there any way to solve it?

filka55533 commented 4 months ago

Just look on example with perimeter command on clitest.c . On optarg shape you can see function shape_transient_eval, where according to the shape (rectangle or triangle) should be setting new MODE_CONFIG_* mode. You can use same technique, where you should be enable your new mode (f.e. MODE_CONFIG_VLAN) and register other commands (mrouter and interfaces) only for this new mode like on the clitest.c file with side_1-4 arguments. But this solution would be harder to scale for new command if compare it with my previous solution, keep in mind it.