Search code examples
cadmincommand-line-interfacedaemon

Are there any libraries that provide an administrative CLI for a C/C++ daemon?


I'm looking for some kind of embedded shell we can stick in a C/C++ daemon. The idea is that we can telnet to a port that's being listened on, and change configuration/view status/etc.

Something like the CLI that Quagga/Zebra has (which is modeled on IOS) or similar. Ideally has readline-like support for ease of use, and easily extensible for us to add new features to it.

I've thought about using something like embedded Python or Lua to provide a shell in that language, but I've never actually seen anyone else do this.

Anyone else mind chiming in with how they've accomplished this before?


Solution

  • libcli will do what you want. It also features authentication, works over the command line, telnet and you can easily build clients to work however you want it too. Very cisco like with automatic completion, sub commands etc. You can find it here : https://github.com/dparrish/libcli

    Here's a tiny example implementing the command "hello" and letting you telnet to it on port 12345:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    
    #include <libcli.h>
    
    int call_hello(struct cli_def *cli, char *name, char **argv, int argc) {
        cli_print(cli, "hello world");
        return 0;
    }
    
    int main(void) {
        struct cli_def *cli;
        int sockfd;
        int acceptfd;
        struct sockaddr_in saddr, raddr;
        unsigned int on = 1;
        unsigned int rlen = sizeof(struct sockaddr_in);
        cli = cli_init();
        cli_register_command(cli, NULL, "hello", call_hello,
                             PRIVILEGE_UNPRIVILEGED, MODE_ANY, "runs hello world");
    
        if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) == -1) {
             perror("socket");
             exit(-1);
        }
        saddr.sin_port = htons(12345);
        saddr.sin_family = AF_INET;
        saddr.sin_addr.s_addr = INADDR_ANY;
        memset(&raddr, 0, sizeof(raddr));
        raddr.sin_family=AF_INET;
        if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0) {
            perror("setsockopt");
            exit(-1);
        }
        if(bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)) == -1) {
             perror("bind");
             exit(-1);
        }
        if(listen(sockfd, 12) != 0) {
             perror("listen");
             exit(-1);
        }
        while((acceptfd=accept(sockfd, (struct sockaddr*)&raddr, &rlen)) > 0) {    
             while(cli_loop(cli, acceptfd) == 0);
        }
        return 0;
    }