libcli Developer's Reference

Table of Contents

  1. Introduction
  2. Authentication
  3. Tutorial
  4. Function Reference
    1. cli_init()
    2. cli_done()
    3. cli_register_command()
    4. cli_unregister_command()
    5. cli_loop()
    6. cli_set_auth_callback()(char *, char *))
    7. cli_allow_user()
    8. cli_deny_user()
    9. cli_set_banner()

1.0 Introduction

libcli provides a telnet command-line environment which can be embedded in other programs. This environment includes useful features such as automatic authentication, history, and command-line editing.

This guide should show you everything you need to embed libcli into your program. If you have any corrections, suggestions or modifications, please E-mail David Parrish <david@dparrish.com>.

2.0 Authentication

Two methods of authentcation are supported by libcli - internal and callback.

Internal authentication is based on a list of username / password combinations that are set up before cli_loop() is called. This list is checked with strcasecmp() for the username and strcmp() for the password.

Callback based authentication calls a callback with the username & password that the user enters, and must return a CLI_OK or CLI_ERROR. This can be used for checking passwords against some other database, e.g. LDAP.

If neither cli_set_auth_callback or cli_allow_user have been called before cli_loop(), then authentication will be disabled and the user will not be prompted for a username / password combination.

3.0 Tutorial

This section will guide you through implementing libcli in a basic server.
  1. Create a file libclitest.c.
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <libcli.h>
    
    int main(int argc, char *argv[])
    {
    	struct sockaddr_in servaddr;
    	struct cli_command *c;
    	struct cli_def *cli;
    	int on = 1, x, s;
    
    	// Must be called first to setup data structures
    	cli = cli_init();
    
    	// Set the greeting
    	cli_set_banner(cli, "Welcome to the CLI test program.");
    
    	// Enable 2 username / password combinations
    	cli_allow_user(cli, "fred", "nerk");
    	cli_allow_user(cli, "foo", "bar");
    
    	// Set up a few simple one-level commands
    	cli_register_command(cli, NULL, "test", cmd_test, NULL);
    	cli_register_command(cli, NULL, "simple", cmd_test, NULL);
    	cli_register_command(cli, NULL, "simon", NULL, NULL);
    
    	// This command takes arguments
    	cli_register_command(cli, NULL, "set", cmd_set, NULL);
    
    	// Set up 2 commands "show counters" and "show junk"
    	c = cli_register_command(cli, NULL, "show", NULL, NULL);
    	// Note how we store the previous command and use it as the parent for this one.
    	cli_register_command(cli, c, "junk", cmd_test, NULL);
    	// This one has some help text
    	cli_register_command(cli, c, "counters", cmd_test, "Show the counters that the system uses");
    
    	// Create a socket
    	s = socket(AF_INET, SOCK_STREAM, 0);
    	setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    
    	// Listen on port 12345
    	memset(&servaddr, 0, sizeof(servaddr));
    	servaddr.sin_family = AF_INET;
    	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    	servaddr.sin_port = htons(12345);
    	bind(s, (struct sockaddr *)&servaddr, sizeof(servaddr));
    
    	// Wait for a connection
    	listen(s, 50);
    
    	while ((x = accept(s, NULL, 0)))
    	{
    		// Pass the connection off to libcli
    		cli_loop(cli, x, "cli> ");
    		close(x);
    	}
    
    	// Free data structures
    	cli_done(cli);
    
    	return 0;
    }
    

    This code snippet is all that's required to enable a libcli program. However it's not yet compilable because we haven't created the callback functions.

    A few commands have been created:

    Note that simon isn't on this list because callback was NULL when it was registered, so the command will not be available.

    Also, the standard libcli commands help, exit, logout, quit and history are also available automatically.

  2. Make this program complete by adding the callback functions.
    int cmd_test(struct cli_def *cli, FILE *client, char *command, char *argv[], int argc)
    {
        int i;
        fprintf(client, "called %s with %s\r\n", __FUNCTION__, command);
        return CLI_OK;
    }
    
    int cmd_set(struct cli_def *cli, FILE *client, char *command, char *argv[], int argc)
    {
        if (argc < 2)
        {
    	fprintf(client, "Specify a variable to set\r\n");
    	return CLI_OK;
        }
        fprintf(client, "Setting %s to %s\r\n", argv[0], argv[1]);
        return CLI_OK;
    }
    

    2 callback functions are defined here, cmd_test and cmd_set. cmd_test is called by many of the commands defined in the tutorial, although in reality you would usually use a callback for a single command.

    cmd_test simply echos the command entered back to the client. Note that it shows the full expanded command, so you can enter "te" at the prompt and it will print back "called with test".

    cmd_set handles the arguments given on the command line. This allows you to use a single callback to handle lots of arguments like:

  3. Compile the code
    gcc libclitest.c -o libclitest -lcli
    
    You can now run the program with ./libclitest and telnet to port 12345 to see your work in action.

4.0 Function Reference

4.1 cli_init()

This must be called before any other cli_xxx function. It sets up the internal data structures used for command-line processing.

Returns a struct cli_def * which must be passed to all other cli_xxx functions.

4.2 cli_done(struct cli_def *cli)

This is optional, but it's a good idea to call this when you are finished with libcli. This frees memory used by libcli.

4.3 cli_register_command(struct cli_def *cli, struct cli_command *parent, char *command, int (*callback)(FILE *, char *, char **, int), char *help)

Add a command to the internal command tree. Returns a struct cli_command *, which you can pass as parent to another call to cli_register_command.

When the command has been entered by the user, callback is checked. If it is not NULL, then the callback is called with:

  1. FILE * - the output stream
  2. char * - the entire command which was entered. This is after command expansion.
  3. char ** - the list of arguments entered
  4. char ** - the number of arguments entered

The callback must return CLI_OK if the command was successful, CLI_ERROR if processing wasn't successful and the next matching command should be tried (if any), or CLI_QUIT to drop the connection (e.g. on a fatal error).

If parent is NULL, the command is added to the top level of commands.

If help is provided, it is given to the user when the use the help command or press ?.

4.4 cli_unregister_command(struct cli_def *cli, char *command)

Remove a command command and all children. There is not provision yet for removing commands at lower than the top level.

4.5 cli_loop(struct cli_def *cli, int sockfd, char *prompt)

The main loop of the command-line environment. This must be called with the FD of a socket open for bi-directional communication (sockfd) and the string that the user will be prompted with.

cli_loop() handles the telnet negotiation and authentication. It returns only when the connection is finished, either by a server or client disconnect.

Returns CLI_OK.

4.6 cli_set_auth_callback(struct cli_def *cli, int (*auth_callback)(char *, char *))

Enables or disables callback based authentication.

If auth_callback is not NULL, then authentication will be required on connection. auth_callback will be called with the username and password that the user enters.

auth_callback must return CLI_OK if authentication is successful, and CLI_ERROR if not.

If auth_callback is NULL, then callback based authentication will be disabled.

4.7 cli_allow_user(struct cli_def *cli, char *username, char *password)

Enables internal authentication, and adds username/password to the list of allowed users.

The internal list of users will be checked before callback based authentication is tried.

4.8 cli_deny_user(struct cli_def *cli, char *username)

Removes username/password from the list of allowed users.

If this is the last combination in the list, then internal authentication will be disabled.

4.9 cli_set_banner(struct cli_def *cli, char *banner)

Sets the greeting that clients will be presented with when they connect. This may be a security warning for example.

If banner is NULL, then no banner will be presented (the default).

David Parrish <david@dparrish.com> 2003-04-23