Clamav杀毒软件源码分析笔记[二]

 

  Clamav杀毒软件源码分析笔记[二]

刺猬@http://blog.csdn.net/littlehedgehog

[读取命令行参数]

开始正儿八经地分析源码了. 从哪里入手呢,当然还是按照书上的来,我们先来看看Clamd的程序如何从生到死,Clamd是杀毒软件的服务端,我们在客户端提交要查杀的信息,然后Clamd服务端进行按照我们指令办事,然后把结果返回给我们。这个貌似是unix程序的一个经典构架了,所谓的客户端直接跟用户打交道,然后解析用户输入,把真正要做的工作再传给服务端完成。服务端其实是一个无名英雄了。 话说回来这里第一步我们就要处理命令行下琐碎的各个参数选项.为了让Clamd服务端按照我们要求来运行,这貌似是个相当麻烦的工作,比女人都麻烦,呵呵. 不过还好 上有不少linux fans 都写过相应的博文了.  还是先来看源码吧: [./clamd/options.c]

  1. /* 这个是clamd的主函数 文件名为options,那么这个主函数就主要在处理这个选项(参数选项)问题了*/int main(int argc, char **argv){    int ret, opt_index, i, len;    struct optstruct *opt;    const char *getopt_parameters = “hc:V”;    static struct option long_options[] =    {        {“help”00‘h’},        {“config-file”10‘c’},        {“version”00‘V’},        {“debug”000},        {0000}    };#if defined(C_LINUX) && defined(CL_DEBUG)    /* njh@bandsman.co.uk: create a dump if needed */    struct rlimit rlim;    rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;    if (setrlimit(RLIMIT_CORE, &rlim) < 0)        perror(“setrlimit”);#endif    opt=(struct optstruct*)mcalloc(1, sizeof(struct optstruct));    opt->optlist = NULL;    opt->filename = NULL;    while (1)    {        opt_index=0;        ret=getopt_long(argc, argv, getopt_parameters, long_options, &opt_index);        if (ret == -1)            break;        switch (ret)        {        case 0:            register_long_option(opt, long_options[opt_index].name);            break;        default:            if (strchr(getopt_parameters, ret))                register_char_option(opt, ret);            else            {                fprintf(stderr, “ERROR: Unknown option passed./n”);                free_opt(opt);                exit(40);            }        }    }    if (optind < argc)    {        len=0;        /* count length of non-optin arguments 下面是处理非选项参数*/        for (i=optind; i<argc; i++)            len+=strlen(argv[i]);        len=len+argc-optind-1/* add spaces between arguments */        opt->filename=(char*)mcalloc(len + 256, sizeof(char));        for (i=optind; i<argc; i++)        {            strncat(opt->filename, argv[i], strlen(argv[i]));            if (i != argc-1)                strncat(opt->filename, ” “1);        }    }    clamd(opt);    free_opt(opt);    cli_dbgmsg(“exit main()”);    return(0);}

代码不长,逻辑也不复杂. 不过在理解GNU提供给我们截获参数选项操作之前,我们确实应该先来明确一下处理的关键点在哪里.比如说有如下命令行 command -h   这是我们来获取command使用帮助的时候键入的命令. 也就是说按照约定,我们键入-h 就是表明要显示出帮助手册,但是在linux/unix里,我们有时候也键入全称来输入参数,比如说command –help 两横加上全称,同样也是表明我们需要帮助手册.为了能处理以上两种情况,我们需要做更多的准备.  好了,下面的文字是 上拷来的,呵呵,这也是代码复用的思想…

============================================

摘自冰火天地 《getopt_long及其使用》

========================================

Linux系统下,需要大量的命令行选项,如果自己手动解析他们的话实在是有违软件复用的思想,不过还好,GNU C library留给我们一个解析命令行的接口(X/Open规范),好好使用它可以使你的程序改观不少。

使用getopt_long()需要引入头文件

#include <getopt.h>

     现在我们使用一个例子来说明它的使用。

一个应用程序需要如下的短选项和长选项。

      短选项               长选项                           作用

      -h                      –help                           输出程序命令行参数说明然后退出
      -o filename        –output filename      给定输出文件名
      -v                      –version                       显示程序当前版本后退后

为了使用getopt_long函数,我们需要先确定两个结构:

1.一个字符串,包括所需要的短选项字符,如果选项后有参数,字符后加一个”:”符 。本例中,这个字符串应该为”ho:v”。(因为-o后面有参数filename,所以字符后面要加”:”)

2.一个包含长选项字符串的结构体数组,每一个结构体包含4个域,第一个域为长选项字符串,第二个域是一个标识,只能为0或1,分别代表没有、有。第三个域永远为NULL。第四个域为对应的短选项字符串。结构体数组的最后一个元素全部为NULL和0,标识结束。在本例中,它应该像一下的样子:

     const struct option long_options[] = {
         { “help”,        0, NULL, ‘h’ },
         { “output”,      1, NULL, ‘o’ },
         { “version”, 0, NULL, ‘v’ },
         { NULL,          0, NULL, 0}
      };

调用时需要把main的两个参数argc和argv以及上述两个数据结构传给getopt_long。
每次调用getopt_long,它会解析一个符 ,返回相应的短选项字符,如果解析完毕返回-1。所以需要使用一个循环来处理所有的参数,而相应的循环里会使用switch语句进行选择。如果getopt_long遇到一个无效的选项字符,它会打印一个错误消息并且返回’,很多程序会打印出帮助信息并且中止运行;当getopt_long解析到一个长选项并且发现后面没有参数则返回’:’,表示缺乏参数。当处理一个参数时,全局变量optarg指向下一个要处理的变量。当getopt_long处理完所有的选项后,全局变量optind指向第一个未知的选项索引。

这一个例子代码为下:

  1. //编译使用gcc -o getopt_long getopt_long.c#include <getopt.h>#include <stdio.h>#include <stdlib.h>/*程序的名字*/const char* program_name;/* 打印程序参数 */void print_usage (FILE* stream, int exit_code){fprintf (stream, “Usage: %s options [ inputfile … ]/n”, program_name);fprintf (stream, ” -h –help                       显示这个帮助信息./n”                             ” -o –output filename 将输出定位到文件./n”                             ” -v –version                  打印版本信息./n”);exit (exit_code);}/* 主程序 */int main (int argc, char* argv[]){int next_option;//下一个要处理的参数符 int haveargv = 0;//是否有我们要的正确参数,一个标识           /* 包含短选项字符的字符串,注意这里的‘:’ */          const char* const short_options = “ho:v”;              /* 标识长选项和对应的短选项的数组 */              const struct option long_options[] = {                   { “help”,        0, NULL, ‘h’ },                   { “output”,      1, NULL, ‘o’ },                   { “version”0, NULL, ‘v’ },                   { NULL,         0, NULL, 0     }};//最后一个元素标识为NULL                        /* 此参数用于承放指定的参数,默认为空 */const char* output_filename = NULL;/* 一个标志,是否显示版本  */int verbose = 0;/* argv[0]始终指向可执行的文件文件名 */                             program_name = argv[0];    do{    next_option = getopt_long (argc, argv, short_options, long_options, NULL);    switch (next_option)    {     case ‘h’:     /* -h or –help */           haveargv = 1;       print_usage (stdout, 0);     case ‘o’:     /* -o or –output */         /* 此时optarg指向–output后的filename */      output_filename = optarg;      haveargv = 1;      break;     case ‘v’:     /* -v or    –version */      verbose = 1;      haveargv = 1;      break;     case ‘:’:     /* 缺乏长选项内容 */      break;     case ‘:     /* 出现一个未指定的参数*/      print_usage (stderr, 1);     case -1:      /* 处理完毕后返回-1 */             if (!haveargv)             {                   print_usage (stderr, 1);             }      break;     default:      /* 未指定的参数出现,出错处理 */      print_usage (stderr, 1);                                  break;    }}while (next_option !=-1);                            if (verbose){    int i;    for (i = optind; i < argc; ++i)    printf (“Argument: %s/n”, argv[i]);}                                                   return 0;}

============================================

文章节录完

============================================

  1. /*短选项加入链表 比如–config_file=test 注意 这里虽然是个长选项 但是它含有短字符代表,即是字符c    {“config-file”, 1, 0, ‘c’}    */void register_char_option(struct optstruct *opt, char ch){    struct optnode *newnode;    newnode = (struct optnode *) mmalloc(sizeof(struct optnode));    newnode->optchar = ch;    if (optarg != NULL)    {        newnode->optarg = (char *) mcalloc(strlen(optarg) + 1sizeof(char));   //参数值,这里就是test        strcpy(newnode->optarg, optarg);    }    else        newnode->optarg = NULL;    newnode->optname = NULL;        newnode->next = opt->optlist;   //连入链表    opt->optlist = newnode;}

opt和newnode的结构如下所示:

  1. struct optnode{    char optchar;   //短选项名 比如文中举例的 h 选项     char *optarg;   //参数值, 有时候我们对参数要设定值的,比如 -p 23 这里23就是参数值    char *optname; //长选项名, 文中举例的help这个字符串就应该放在这里,跟前面h对应    struct optnode *next; //下一个节点呗,我们要连成链表阿};struct opt

    声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2008年9月5日
下一篇 2008年9月6日

相关推荐