Ignore:
Timestamp:
Jun 30, 2010 5:29:59 AM (14 years ago)
Author:
ming
Message:

Implemented CLI (re #1098)

  • pjlib-util:
    • implement telnet CLI
  • pjsip-apps/samples:
    • sample CLI demo application
  • build:
    • VS6 and Makefile
File:
1 edited

Legend:

Unmodified
Added
Removed
  • pjproject/branches/projects/cli/pjlib-util/src/pjlib-util/cli.c

    r3211 r3231  
    3030#include <pjlib-util/xml.h> 
    3131 
     32#define CMD_HASH_TABLE_SIZE 63  /* Hash table size */ 
     33 
     34#define CLI_CMD_CHANGE_LOG  30000 
     35#define CLI_CMD_EXIT        30001 
     36 
    3237#if 1 
    3338    /* Enable some tracing */ 
     
    3843#endif 
    3944 
    40 #define CMD_HASH_TABLE_SIZE 63          /* Hash table size */ 
    41  
    4245struct pj_cli_t 
    4346{ 
    44     pj_pool_t          *pool;          /* Pool to allocate memory from */ 
     47    pj_pool_t          *pool;           /* Pool to allocate memory from */ 
    4548    pj_cli_cfg          cfg;            /* CLI configuration */ 
    4649    pj_cli_cmd_spec     root;           /* Root of command tree structure */ 
    4750    pj_cli_front_end    fe_head;        /* List of front-ends */ 
    48     pj_hash_table_t    *hash;          /* Command hash table */ 
     51    pj_hash_table_t    *hash;           /* Command hash table */ 
    4952 
    5053    pj_bool_t           is_quitting; 
     
    6871} 
    6972 
     73PJ_DEF(void) pj_cli_write_log(pj_cli_t *cli, 
     74                              int level, 
     75                              const char *buffer, 
     76                              int len) 
     77{ 
     78    struct pj_cli_front_end *fe; 
     79 
     80    pj_assert(cli); 
     81 
     82    fe = cli->fe_head.next; 
     83    while (fe != &cli->fe_head) { 
     84        if (fe->op && fe->op->on_write_log) 
     85            (*fe->op->on_write_log)(fe, level, buffer, len); 
     86        fe = fe->next; 
     87    } 
     88} 
     89 
     90/* Command handler */ 
     91static pj_status_t cmd_handler(pj_cli_cmd_val *cval) 
     92{ 
     93    unsigned level; 
     94 
     95    switch(cval->cmd->id) { 
     96    case CLI_CMD_CHANGE_LOG: 
     97        level = pj_strtoul(&cval->argv[1]); 
     98        if (!level && cval->argv[1].slen > 0 && (cval->argv[1].ptr[0] < '0' || 
     99            cval->argv[1].ptr[0] > '9')) 
     100            return PJ_CLI_EINVARG; 
     101        cval->sess->log_level = level; 
     102        return PJ_SUCCESS; 
     103    case CLI_CMD_EXIT: 
     104        pj_cli_end_session(cval->sess); 
     105        return PJ_CLI_EEXIT; 
     106    default: 
     107        return PJ_SUCCESS; 
     108    } 
     109} 
     110 
    70111PJ_DEF(pj_status_t) pj_cli_create(pj_cli_cfg *cfg, 
    71112                                  pj_cli_t **p_cli) 
     
    73114    pj_pool_t *pool; 
    74115    pj_cli_t *cli; 
     116    unsigned i; 
     117    char* cmd_xmls[] = { 
     118     "<CMD name='log' id='30000' sc='' desc='Change log level'>" 
     119     "  <ARGS>" 
     120     "    <ARG name='level' type='int' desc='Log level'/>" 
     121     "  </ARGS>" 
     122     "</CMD>", 
     123     "<CMD name='exit' id='30001' sc='' desc='Exit session'>" 
     124     "</CMD>", 
     125    }; 
    75126 
    76127    PJ_ASSERT_RETURN(cfg && cfg->pf && p_cli, PJ_EINVAL); 
    77128 
    78     pool = pj_pool_create(cfg->pf, "cli", 1024, 1024, NULL); 
     129    pool = pj_pool_create(cfg->pf, "cli", PJ_CLI_POOL_SIZE,  
     130                          PJ_CLI_POOL_INC, NULL); 
     131    if (!pool) 
     132        return PJ_ENOMEM; 
    79133    cli = PJ_POOL_ZALLOC_T(pool, struct pj_cli_t); 
    80     if (!cli) 
    81         return PJ_ENOMEM; 
    82134 
    83135    pj_memcpy(&cli->cfg, cfg, sizeof(*cfg)); 
     
    89141    cli->root.sub_cmd = PJ_POOL_ZALLOC_T(pool, pj_cli_cmd_spec); 
    90142    pj_list_init(cli->root.sub_cmd); 
     143 
     144    /* Register some standard commands. */ 
     145    for (i = 0; i < sizeof(cmd_xmls)/sizeof(cmd_xmls[0]); i++) { 
     146        pj_str_t xml = pj_str(cmd_xmls[i]); 
     147 
     148        if (pj_cli_add_cmd_from_xml(cli, NULL, &xml, &cmd_handler, NULL) != 
     149            PJ_SUCCESS) 
     150            TRACE_((THIS_FILE, "Failed to add command #%d", i)); 
     151    } 
    91152 
    92153    *p_cli = cli; 
     
    167228    pj_assert(sess); 
    168229 
    169     if (sess->op->destroy) 
     230    if (sess->op && sess->op->destroy) 
    170231        (*sess->op->destroy)(sess); 
    171232} 
     
    184245                                            pj_cli_cmd_spec *p_cmd) 
    185246{ 
     247    #define ERROR_(STATUS) \ 
     248        do {status = STATUS; goto on_exit;} while(0) 
    186249    pj_pool_t *pool; 
    187250    pj_xml_node *root; 
     
    197260    /* Parse the xml */ 
    198261    pool = pj_pool_create(cli->cfg.pf, "xml", 1024, 1024, NULL); 
     262    if (!pool) 
     263        return PJ_ENOMEM; 
    199264    root = pj_xml_parse(pool, xml->ptr, xml->slen); 
    200265    if (!root) { 
    201266        TRACE_((THIS_FILE, "Error: unable to parse XML")); 
    202         status = PJ_EINVAL; 
    203         goto on_exit; 
    204     } 
    205  
    206     if (pj_stricmp2(&root->name, "CMD")) { 
    207         status = PJ_EINVAL; 
    208         goto on_exit; 
    209     } 
     267        ERROR_(PJ_CLI_EBADXML); 
     268    } 
     269 
     270    if (pj_stricmp2(&root->name, "CMD")) 
     271        ERROR_(PJ_EINVAL); 
    210272 
    211273    /* Initialize the command spec */ 
    212274    cmd = PJ_POOL_ZALLOC_T(cli->pool, struct pj_cli_cmd_spec); 
    213     if (!cmd) { 
    214         status = PJ_ENOMEM; 
    215         goto on_exit; 
    216     } 
    217275     
    218276    /* Get the command attributes */ 
     
    227285                            attr->value.slen, NULL)) 
    228286            { 
    229                 status = PJ_CLI_EBADNAME; 
    230                 goto on_exit; 
     287                ERROR_(PJ_CLI_EBADNAME); 
    231288            } 
    232289 
     
    267324            PJ_CATCH_ANY { 
    268325                pj_scan_fini(&scanner); 
    269                 status = PJ_GET_EXCEPTION(); 
    270                 goto on_exit; 
     326                ERROR_(PJ_GET_EXCEPTION()); 
    271327            } 
    272328            PJ_END; 
     
    290346                    pj_cli_arg_spec *arg; 
    291347 
    292                     if (cmd->arg_cnt >= PJ_CLI_MAX_ARGS) { 
    293                         status = PJ_CLI_ETOOMANYARGS; 
    294                         goto on_exit; 
    295                     } 
     348                    if (cmd->arg_cnt >= PJ_CLI_MAX_ARGS) 
     349                        ERROR_(PJ_CLI_ETOOMANYARGS); 
    296350                    arg = &args[cmd->arg_cnt]; 
    297351                    pj_bzero(arg, sizeof(*arg)); 
     
    323377    if (cmd->id == PJ_CLI_CMD_ID_GROUP) { 
    324378        /* Command group shouldn't have any shortcuts nor arguments */ 
    325         if (!cmd->sc_cnt || !cmd->arg_cnt) { 
    326             status = PJ_EINVAL; 
    327             goto on_exit; 
    328         } 
     379        if (!cmd->sc_cnt || !cmd->arg_cnt) 
     380            ERROR_(PJ_EINVAL); 
    329381        cmd->sub_cmd = PJ_POOL_ALLOC_T(cli->pool, struct pj_cli_cmd_spec); 
    330382        pj_list_init(cmd->sub_cmd); 
    331383    } 
    332384 
    333     if (!cmd->name.slen) { 
    334         status = PJ_CLI_EBADNAME; 
    335         goto on_exit; 
    336     } 
     385    if (!cmd->name.slen) 
     386        ERROR_(PJ_CLI_EBADNAME); 
    337387 
    338388    if (cmd->arg_cnt) { 
     
    341391        cmd->arg = (pj_cli_arg_spec *)pj_pool_zalloc(cli->pool, cmd->arg_cnt * 
    342392                                                     sizeof(pj_cli_arg_spec)); 
    343         if (!cmd->arg) { 
    344             status = PJ_ENOMEM; 
    345             goto on_exit; 
    346         } 
    347393        for (i = 0; i < cmd->arg_cnt; i++) { 
    348394            pj_strdup(cli->pool, &cmd->arg[i].name, &args[i].name); 
     
    357403        cmd->sc = (pj_str_t *)pj_pool_zalloc(cli->pool, cmd->sc_cnt * 
    358404                                             sizeof(pj_str_t)); 
    359         if (!cmd->sc) { 
    360             status = PJ_ENOMEM; 
    361             goto on_exit; 
    362         } 
    363405        for (i = 0; i < cmd->sc_cnt; i++) { 
    364406            pj_strdup(cli->pool, &cmd->sc[i], &sc[i]); 
    365             pj_hash_set(cli->pool, cli->hash, cmd->sc[i].ptr,  
    366                         cmd->sc[i].slen, 0, cmd); 
     407            pj_hash_set(cli->pool, cli->hash, sc[i].ptr,  
     408                        sc[i].slen, 0, cmd); 
    367409        } 
    368410    } 
     
    384426 
    385427    return status; 
     428#undef ERROR_ 
    386429} 
    387430 
     
    406449    /* Parse the command line. */ 
    407450    len = pj_ansi_strlen(cmdline); 
     451    if (len > 0 && cmdline[len - 1] == '\n') { 
     452        cmdline[--len] = 0; 
     453        if (len > 0 && cmdline[len - 1] == '\r') 
     454            cmdline[--len] = 0; 
     455    } 
    408456    pj_scan_init(&scanner, cmdline, len, PJ_SCAN_AUTOSKIP_WS,  
    409457                 &on_syntax_error); 
     
    441489         
    442490        if (!pj_scan_is_eof(&scanner)) { 
    443             pj_scan_get_newline(&scanner); 
    444             if (!pj_scan_is_eof(&scanner)) 
    445                 PJ_THROW(PJ_CLI_EINVARG); 
     491            PJ_THROW(PJ_CLI_EINVARG); 
    446492        } 
    447493    } 
     
    478524        info = &einfo; 
    479525    status = pj_cli_parse(sess, cmdline, &val, info); 
    480  
    481526    if (status != PJ_SUCCESS) 
    482527        return status; 
Note: See TracChangeset for help on using the changeset viewer.