Command Line Arguments Parsing in glibc

Many Linux programs are command line based and sometimes the options can be complicated. Luckily, the GNU C library glibc provides some APIs to simplify the command line option parsing.

Specifically, there’re two methods for parsing the commands, getopt and getopt_long. getopt() is used to parse the single character option, and getopt_long() works with both long options and single-character options. It’s recommended to use getopt_long().

Explanation of getopt_long() Prototype

The function getopt_long has the following prototype,

int getopt_long (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *indexptr)

argc and argv are the argument count and argument vector, the same as the two input parameters in the standard main prototype.

shortopts is a string specifying the option characters that are valid as input options. An option character can be followed by a colon (‘:’) to indicate it takes a required argument. The character can also be followed by two colons (‘::’) to indicate the argument is optional. If nothing follows the option character, the option doesn’t take any arguments.

longopts describes the long options to accept, which is an array of struct option. longopts must be terminated by a struct option of all 0s.

The struct option describes a single long option, which is defined as below,

struct option {

const char *name;    //the name of the option

int has_arg;  //can be no_argument, required_argument, or optional_argument

int *flag; //for flag option only, set to NULL for non-flag options

int val;  //for flag, val is the value to store in the flag when the flag option is seen;

              //for other options, val is a value that is uniquely identify the long option

};

indexptr takes address of an integer variable, and getopt_long will store the index of the matched long option in the array longopts into it.

The Command Line Parse in Mini Firewall

#include <stdio.h>

#include <stdlib.h>

#include <getopt.h>

 

#define print_value(x) (x==NULL?"-" : x)

 

static struct mf_rule_struct {

    int in_out;

    char *src_ip;

    char *src_netmask;

    char *src_port;            //default to -1 

    char *dest_ip;

    char *dest_netmask;

    char *dest_port;

    char *proto;

    char *action;

} mf_rule;

 

static struct mf_delete_struct {

    char *cmd;

    char *row;

} mf_delete;

 

void send_rule_to_proc()

{

    printf("TODO: send_rule_to_procn");

}

 

void send_delete_to_proc()

{

    printf("TODO: send_delete_to_procn");

}

 

void print_rule()

{

    printf("TODO: print_rulen");

    return;

}

 

int main(int argc, char **argv)

{

    int c; int action = 1;    //1: new rule; 2: print; 3: delete

    mf_rule.in_out = -1; mf_rule.src_ip = NULL; mf_rule.src_netmask = NULL; mf_rule.src_port = NULL;

    mf_rule.dest_ip = NULL; mf_rule.dest_netmask = NULL; mf_rule.dest_port = NULL;mf_rule.proto = NULL;

    mf_rule.action = NULL;

    while (1) 

    {

        static struct option long_options[] = 

        {

        /*set a flag*/

            {"in", no_argument, &mf_rule.in_out, 1},

            {"out", no_argument, &mf_rule.in_out, 0},

        /*These options don't set a flag.

            We distinguish them by their indices.*/

            {"print", no_argument, 0, 'o'},

            {"delete", required_argument, 0, 'd'},

            {"srcip", required_argument, 0, 's'},

            {"srcnetmask", required_argument, 0, 'm'},

            {"srcport", required_argument, 0, 'p'},

            {"destip", required_argument, 0, 't'},

            {"destnetmask", required_argument, 0, 'n'},

            {"destport", required_argument, 0, 'q'},

            {"proto", required_argument, 0, 'c'},

            {"action", required_argument, 0, 'a'},

            {0, 0, 0, 0}

        };

        int option_index = 0;

        c = getopt_long(argc, argv, "od:s:m:p:t:n:q:c:a:", long_options, &option_index);

        /*Detect the end of the options. */

        if (c == -1)

            break;

        action = 1;

        switch (c)

        {

            case 0:

              printf("flag option: %s, mf_rule.in_out = %dn", long_options[option_index].name, mf_rule.in_out);

              break;

            case 'o':

                action = 2;    //print

              break;

            case 'd':

              action = 3;       //delete

              mf_delete.cmd = (char *)long_options[option_index].name;

              mf_delete.row = optarg;

              break;

            case 's':

              mf_rule.src_ip = optarg;  //src ip

              break; 

            case 'm':

              mf_rule.src_netmask = optarg; //srcnetmask:

              break;

            case 'p':

              mf_rule.src_port = optarg;    //srcport:

              break;

            case 't':

              mf_rule.dest_ip = optarg;     //destip:

              break;

            case 'n':

              mf_rule.dest_netmask = optarg;    //destnetmask

              break;

            case 'q':

              mf_rule.dest_port = optarg;    //destport

              break;

            case 'c':

              mf_rule.proto = optarg; //proto

              break;

            case 'a':

              mf_rule.action = optarg;//action

              break;

            case '?':

              /* getopt_long printed an error message. */

              break;

            default:

              abort();

        }

    if (c != 0)

        printf("%s = %sn",  long_options[option_index].name, optarg);

    }

    if (action == 1) {

        send_rule_to_proc();

    } else if (action == 2) {

        print_rule();

    } else if (action == 3) {

        send_delete_to_proc();

    }

    if (optind < argc)

    {

        printf("non-option ARGV-elements: ");

        while (optind < argc)

        printf("%s ", argv[optind++]);

        putchar('n');

    }

}

 

In order to understand the code above, you may need to read the section below,

How getopt_long Matches the Input Options

For short option, getopt_long returns the character code for the option, and stores the option’s argument (if it has one) in optarg.

For long option, getopt_long takes action based on flag and val fields of the option matched.

If the flag is NULL, meaning the option is not a flag. getopt_long returns val. Normally you can use the short option’s character code in val if long option is equivalent to the short option.

If the flag is not NULL, meaning it’s a flag option. getopt_long will return 0, and the flag value will be set accordingly.

For all options, getopt_long stores the matched option’s index in array longopts to *indexptr. You can access the name of the option by longopts[*indexptr].name.

getopt_long will also put the argument value in optarg if the option has one. Otherwise, optarg is set to NULL.

When getopt_long has no more options to handle, it returns –1. The index of next remaining argument in argv is stored in variable optind.

Sample Input and Output of the Mini Firewall Command Line Parsing

Firstly, save the program as cmd.c and compile the code using the command below,

gcc -o cmd cmd.c

Input 1:

./cmd --in --srcip 10.2.10.2 --srcnetmask 255.255.255.0 --srcport 1000 --destip 72.23.13.23 --destnetmask 255.255.0.0 --destport 80 --proto 6 --action 0

Output 1:

flag option: in, mf_rule.in_out = 1

srcip = 10.2.10.2

srcnetmask = 255.255.255.0

srcport = 1000

destip = 72.23.13.23

destnetmask = 255.255.0.0

destport = 80

proto = 6

action = 0

TODO: send_rule_to_proc

Input 2:

./cmd --delete 1

Output 2:

delete = 1

TODO: send_delete_to_proc

Note:

This post is part of the tutorial: How to write a Linux Firewall in Less than 1000 Lines

Part 1: Overview

Part 2: Command Line Arguments Parsing in glibc

Part 3.1: Linux Kernel Module Basics and Hello World

Part 3.2: Linux Kernel Programming – Linked List

Part 3.3 Linux Kernel Programming – Memory Allocation

Part 4.1: How to Filter Network Packets using Netfilter – Part 1 Netfilter Hooks

Part 4.2 How to Filter Network Packets using Netfilter – Part 2 Implement the Hook Function

Part 5: Linux procfs Virtual File System

Part 6: Put Everything Together

Reference:

http://www.gnu.org/s/libc/manual/html_node/Getopt.html

Leave a Reply

Your email address will not be published. Required fields are marked *