KLone APIs | Modules | Data Structures | File List | Data Fields | Globals

main.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2005, 2006 by KoanLogic s.r.l. <http://www.koanlogic.com>
00003  * All rights reserved.
00004  *
00005  * This file is part of KLone, and as such it is subject to the license stated
00006  * in the LICENSE file which you have received as part of this distribution.
00007  *
00008  * $Id: main.c,v 1.44 2008/10/18 17:23:32 tat Exp $
00009  */
00010 
00011 #include "klone_conf.h"
00012 #include <sys/stat.h>
00013 #ifdef HAVE_SYS_DIR
00014 #include <sys/dir.h>
00015 #endif
00016 #include <sys/types.h>
00017 #include <dirent.h>
00018 #include <stdlib.h>
00019 #include <ctype.h>
00020 #include <stdio.h>
00021 #include <fcntl.h>
00022 #ifdef HAVE_UNISTD
00023 #include <unistd.h>
00024 #endif
00025 #ifdef HAVE_GETOPT
00026 #include <getopt.h>
00027 #endif
00028 #include <u/libu.h>
00029 #include <klone/os.h>
00030 #include <klone/request.h>
00031 #include <klone/response.h>
00032 #include <klone/translat.h>
00033 #include <klone/utils.h>
00034 #include <klone/run.h>
00035 #include <klone/mime_map.h>
00036 #include <klone/version.h>
00037 #include "pm.h"
00038 
00039 int facility = LOG_LOCAL0;
00040 
00041 /* command list enums */
00042 enum command_e { CMD_UNKNOWN, CMD_TRANS, CMD_IMPORT };
00043 
00044 /* runtime flags */
00045 enum flags_e { FLAG_NONE, FLAG_VERBOSE };
00046 
00047 typedef struct 
00048 {
00049     char *file_in, *file_out;   /* [trans] input, output file       */
00050     char *depend_out;           /* [trans] depend output file       */
00051     char *uri;                  /* [trans] translated file uri      */
00052     int verbose;                /* >0 when verbose mode is on       */
00053     char **arg;                 /* argv                             */
00054     size_t narg;                /* argc                             */
00055     int cmd;                    /* command to run                   */
00056     char *base_uri;             /* site base uri                    */
00057     int encrypt;                /* >0 when encryption is enabled    */
00058     int compress;               /* >0 when compress is enabled      */
00059     pm_t *comp_patt;            /* compress file pattern            */
00060     pm_t *enc_patt;             /* encrypt file pattern             */
00061     pm_t *excl_patt;            /* exclude file pattern             */
00062     char *key_file;             /* encryption key file name         */
00063     io_t *iom, *iod, *ior;      /* io makefile, io deps             */
00064     size_t ndir, nfile;         /* dir and file count               */
00065     size_t nexcl;               /* # of excluded files (-x)         */
00066 } context_t;
00067 
00068 context_t *ctx;
00069 
00070 #define KL1_FILE_FMT "pg_%s.%s"
00071 
00072 #define usage_if(expr)  if(expr) usage(); 
00073 
00074 static void usage(void)
00075 {
00076     static const char * us = 
00077 "Usage: klone [-hvV] -c COMMAND OPTIONS ARGUMENTS\n"
00078 "Version: %s - Copyright (c) 2005-2008 KoanLogic s.r.l.\n"
00079 "All rights reserved.\n"
00080 "\n"
00081 "       -h            display this help\n"
00082 "       -v            verbose mode\n"
00083 "       -V            print KLone version and exit\n"
00084 "       -c command    command to execute (see COMMAND LIST)\n"
00085 "\n"
00086 "\n"
00087 "    COMMAND LIST:\n"
00088 "       import        import a directory tree in the embedded filesystem\n"
00089 "       translate     convert a file or a dynamic web page to a C file\n"
00090 "\n"
00091 "\n"
00092 "    COMMANDS SYNTAX:\n"
00093 "\n"
00094 "       import OPTIONS dir\n"
00095 "         -b URI      base URI\n"
00096 "         -x pattern  exclude all files whose URI match the given pattern (*)\n"
00097 #ifdef HAVE_LIBOPENSSL
00098 "         -e pattern  encrypt all files whose URI match the given pattern (*)\n"
00099 "         -k key_file encryption key filename\n"
00100 #endif
00101 #ifdef HAVE_LIBZ
00102 "         -z          compress all compressable content (based on MIME types)\n"
00103 "         -Z pattern  compress all files whose URI match the given pattern (*)\n"
00104 #endif
00105 "         dir         directory tree path\n"
00106 "\n"
00107 "         (*) may be used more then once\n"
00108 "\n"
00109 "       translate OPTIONS\n"
00110 #ifdef HAVE_LIBOPENSSL
00111 "         -E          encrypt file content\n"
00112 #endif
00113 "         -i file     input file\n"
00114 #ifdef HAVE_LIBOPENSSL
00115 "         -k key_file encryption key filename\n"
00116 #endif
00117 "         -o file     output file\n"
00118 "         -u URI      URI of translated page\n"
00119 "                     (KLONE_CIPHER_KEY environ var is used if not provided)\n"
00120 #ifdef HAVE_LIBZ
00121 "         -z          compress file content\n"
00122 #endif
00123 "\n";
00124 
00125     fprintf(stderr, us, klone_version());
00126 
00127     exit(EXIT_FAILURE);
00128 }
00129 
00130 static void remove_trailing_slash(char *s)
00131 {
00132     size_t len;
00133     
00134     dbg_ifb (s == NULL) return;
00135     
00136     len = strlen(s);
00137     if(len && s[len - 1] == '/')
00138         s[len - 1] = 0;
00139 }
00140 
00141 static int parse_opt(int argc, char **argv)
00142 {
00143     int ret;
00144     char opts[512];
00145 
00146     if(argc == 1)
00147         usage();
00148 
00149     /* common switches */
00150     strcpy(opts, "hvVx:b:i:o:u:c:d:");
00151 
00152     /* encryption switches */
00153 #ifdef HAVE_LIBOPENSSL
00154     strcat(opts, "k:e:E");
00155 #endif
00156 
00157     /* compression switches */
00158 #ifdef HAVE_LIBZ
00159     strcat(opts, "zZ:");
00160 #endif
00161 
00162     while((ret = getopt(argc, argv, opts)) != -1)
00163     {
00164         switch(ret)
00165         {
00166         case 'v': /* verbose on */
00167             ctx->verbose++;
00168             break;
00169         case 'V': /* print name/version info and exit */
00170             u_print_version_and_exit();
00171             break;
00172 
00173 #ifdef HAVE_LIBOPENSSL
00174         case 'E': /* encryption on */
00175             ctx->encrypt = 1;
00176             break;
00177         case 'e': /* encrypt file pattern */
00178             dbg_err_if(pm_add(ctx->enc_patt, u_strdup(optarg)));
00179             break;
00180         case 'k': /* encryption key filename */
00181             ctx->key_file = u_strdup(optarg);
00182             warn_err_if(ctx->key_file == NULL);
00183             break;
00184 #endif
00185 
00186 #ifdef HAVE_LIBZ
00187         case 'Z': /* compress file pattern */
00188             ctx->compress = 1;
00189             dbg_err_if(pm_add(ctx->comp_patt, u_strdup(optarg)));
00190             break;
00191         case 'z': /* compress */
00192             ctx->compress = 1;
00193             break;
00194 #endif
00195         case 'c': /* command */
00196             if(!strcasecmp(optarg, "import"))
00197                 ctx->cmd = CMD_IMPORT;
00198             else if(!strcasecmp(optarg, "translate"))
00199                 ctx->cmd = CMD_TRANS;
00200             else
00201                 con_err("unknown command: %s", optarg);
00202             break;
00203         case 'i': /* input file */
00204             ctx->file_in = u_strdup(optarg);
00205             warn_err_if(ctx->file_in == NULL);
00206             break;
00207         case 'x': /* exclude pattern */
00208             dbg_err_if(pm_add(ctx->excl_patt, u_strdup(optarg)));
00209             break;
00210         case 'b': /* base_uri */
00211             ctx->base_uri = u_strdup(optarg);
00212             warn_err_if(ctx->base_uri == NULL);
00213 
00214             if(ctx->base_uri[0] != '/')
00215                 klone_die("base URI must be absolute "
00216                           "(i.e. must start with a '/')");
00217 
00218             remove_trailing_slash(ctx->base_uri);
00219 
00220             break;
00221         case 'o': /* output file */
00222             ctx->file_out = u_strdup(optarg);
00223             warn_err_if(ctx->file_out == NULL);
00224             break;
00225         case 'd': /* kld depend output file */
00226             ctx->depend_out = u_strdup(optarg);
00227             warn_err_if(ctx->depend_out == NULL);
00228             break;
00229         case 'u': /* translated page uri */
00230             /* skip the first char to avoid MSYS path translation bug
00231              * (see klone-site.c) */
00232             ctx->uri = u_strdup(1+optarg);
00233             warn_err_if(ctx->uri == NULL);
00234 
00235             if(ctx->uri[0] != '/')
00236                 klone_die("URI must be absolute (i.e. must start with a '/')");
00237 
00238             remove_trailing_slash(ctx->uri);
00239 
00240             break;
00241         default:
00242         case 'h': 
00243             usage();
00244         }
00245     }
00246 
00247     klone_die_if(ctx->cmd == 0, "missing command argument (-c)");
00248     ctx->narg = argc - optind;  /* # of args left */
00249     ctx->arg = argv + optind;   
00250 
00251     return 0;
00252 err:
00253     return ~0;
00254 }
00255 
00256 static int set_key_from_file(trans_info_t *pti, const char *key_file)
00257 {
00258     io_t *io = NULL;
00259 
00260     dbg_err_if (pti == NULL);
00261     dbg_err_if (key_file == NULL);
00262     
00263     dbg_err_if(u_file_open(key_file, O_RDONLY, &io));
00264 
00265     dbg_err_if(io_read(io, pti->key, CODEC_CIPHER_KEY_SIZE) <= 0);
00266     
00267     io_free(io);
00268 
00269     return 0;
00270 err:
00271     return ~0;
00272 }
00273 
00274 static int command_trans(void)
00275 {
00276     trans_info_t ti;
00277     const mime_map_t *mm;
00278     struct stat st;
00279     char *key_env;
00280     int key_found = 0;
00281 
00282     if(ctx->narg != 0)
00283         usage();    /* no argument allowed */
00284 
00285     memset(&ti, 0, sizeof(trans_info_t));
00286 
00287     klone_die_if(!ctx->file_in, "input file name required (-i file)");
00288     klone_die_if(!ctx->file_out, "output file name required (-o file)");
00289     klone_die_if(!ctx->uri, "translated page URI required (-u uri)");
00290 
00291     if(ctx->verbose)
00292         con("translating %s to %s (uri: %s)", ctx->file_in, ctx->file_out, 
00293             ctx->uri);
00294 
00295     /* input file */
00296     strncpy(ti.file_in, ctx->file_in, U_FILENAME_MAX);
00297 
00298     /* output file */
00299     strncpy(ti.file_out, ctx->file_out, U_FILENAME_MAX);
00300 
00301     /* kld depend file */
00302     if(ctx->depend_out)
00303         strncpy(ti.depend_out, ctx->depend_out, U_FILENAME_MAX);
00304 
00305     /* uri */
00306     strncpy(ti.uri, ctx->uri, URI_BUFSZ);
00307 
00308     /* zero out the key (some byte could not be overwritten with small keys) */
00309     memset(ti.key, 0, CODEC_CIPHER_KEY_SIZE);
00310 
00311     /* sanity checks */
00312     con_err_ifm(ctx->key_file && !ctx->encrypt, "-k used but -E is missing");
00313 
00314     /* encryption key */
00315     key_env = getenv("KLONE_CIPHER_KEY");
00316     if(key_env && strlen(key_env))
00317     {
00318         key_found = 1;
00319         strncpy(ti.key, key_env, CODEC_CIPHER_KEY_SIZE);
00320     }
00321 
00322     /* if -k has been used the overwrite KLONE_CIPHER_KEY env var (if present)*/
00323     if(ctx->key_file)
00324     {
00325         key_found = 1;
00326         con_err_ifm(set_key_from_file(&ti, ctx->key_file), 
00327             "error reading key file [%s]", ctx->key_file);
00328     }
00329 
00330     if(ctx->encrypt)
00331     {
00332         if(!key_found)
00333             con_err("encryption key required (use -k or KLONE_CIPHER_KEY "
00334                     "environ variable)");
00335         ti.encrypt = 1;
00336     }
00337 
00338     /* set MIME type */
00339     if((mm = u_get_mime_map(ctx->file_in)) != NULL)
00340         strncpy(ti.mime_type, mm->mime_type, MIME_BUFSZ);
00341     else
00342         strncpy(ti.mime_type, "application/octet-stream", MIME_BUFSZ);
00343 
00344     /* compress if requested and the file is compressable (by MIME type) */
00345     if(ctx->compress)
00346         ti.comp = 1;
00347 
00348     /* be sure that the input file exists */
00349     klone_die_if(stat(ctx->file_in, &st), "input file not found");
00350 
00351     ti.file_size = st.st_size;
00352     ti.mtime = st.st_mtime; 
00353 
00354     /* translate it */
00355     dbg_err_if(translate(&ti));
00356 
00357     return 0;
00358 err:
00359     /* delete output file on error */
00360     u_remove(ti.file_out);
00361     con(" ");
00362     return ~0;
00363 }
00364 
00365 static int is_cpp(const char *file_in)
00366 {
00367     size_t l;
00368 
00369     dbg_err_if (file_in == NULL);
00370 
00371     l = strlen(file_in);
00372     if(l < 4)
00373         return 0;
00374 
00375     /* if the file name ends with "[Cc][Cc]" consider it a c++ file */
00376     if(tolower(file_in[--l]) == 'c' && tolower(file_in[--l]) == 'c')
00377         return 1; /* c++ */
00378 
00379 err:
00380     return 0;
00381 }
00382 
00383 
00384 static int cb_file(struct dirent *de, const char *path , void *arg)
00385 {
00386     static const char *prefix = "$(srcdir)";
00387     const mime_map_t *mm;
00388     char uri_md5[MD5_DIGEST_BUFSZ];
00389     char file_in[U_FILENAME_MAX], uri[URI_BUFSZ], *base_uri = (char*)arg;
00390     char fullpath[U_FILENAME_MAX];
00391     const char *ext;
00392     int is_a_script, enc = 0, zip = 0;
00393 
00394     dbg_err_if (de == NULL);
00395     dbg_err_if (path == NULL);
00396     dbg_err_if (arg == NULL);
00397 
00398     dbg_err_if(u_snprintf(fullpath, U_FILENAME_MAX, "%s/%s", path, de->d_name));
00399 
00400     /* input filename (makefile-style) */
00401     dbg_err_if(translate_makefile_filepath(fullpath, prefix, file_in, 
00402         sizeof(file_in)));
00403 
00404     /* base uri */
00405     dbg_err_if(u_snprintf(uri, URI_BUFSZ, "%s/%s", base_uri, de->d_name));
00406     dbg_err_if(u_md5(uri, strlen(uri), uri_md5));
00407 
00408     /* if the URI match the given exclude pattern then skip it */
00409     if(!pm_is_empty(ctx->excl_patt) && pm_match(ctx->excl_patt, uri))
00410     {
00411         if(ctx->verbose)
00412             con("%s skipped", uri);
00413 
00414         ctx->nexcl++;
00415 
00416         return 0; /* skip it */
00417     } 
00418 
00419     is_a_script = translate_is_a_script(de->d_name);
00420 
00421     ctx->nfile++;
00422 
00423     /* if the URI match the given encrypt pattern then encrypt it */
00424     if(!pm_is_empty(ctx->enc_patt) && pm_match(ctx->enc_patt, uri))
00425         enc = 1;
00426 
00427     if(ctx->compress) /* -z or -Z have been used */
00428     {
00429         /* if the URI match the given compress pattern then compress it */
00430         if(!pm_is_empty(ctx->comp_patt))
00431         {   /* compression enabled basing on file URI pattern */
00432             if(pm_match(ctx->comp_patt, uri))
00433                 zip = 1;
00434         } else {
00435             /* compression enabled basing on URI MIME types */
00436             if((mm = u_get_mime_map(uri)) != NULL)
00437                 zip = mm->comp;
00438         }
00439     }
00440 
00441     /* print out some info */
00442     if(ctx->verbose == 1)
00443         con("%s (encrypted: %s, compressed: %s)", 
00444             uri, enc ? "yes" : "no", zip ? "yes" : "no");
00445     else if(ctx->verbose > 1)
00446         con("%s -> %s (encrypted: %s, compressed: %s)", 
00447             file_in + strlen(prefix), uri, 
00448             enc ? "yes" : "no", zip ? "yes" : "no");
00449 
00450     ext = u_match_ext(file_in, "klx") ? "cc" : "c";
00451 
00452     dbg_err_if(io_printf(ctx->iom, " \\\n" KL1_FILE_FMT, uri_md5, ext) < 0);
00453 
00454     dbg_err_if(io_printf(ctx->ior, "KLONE_REGISTER(action,%s);\n", uri_md5) <0);
00455 
00456     /* we're adding a '/' before the uri (that will be removed by klone.exe) 
00457      * to avoid MSYS (win32) automatic path translation oddity */
00458     dbg_err_if(io_printf(ctx->iod, 
00459             "\n" KL1_FILE_FMT 
00460             ": %s\n\t$(KLONE) -c translate -i $< -o $@ %s -u /%s %s %s %s %s\n"
00461             "%s", /* compiler depend-file generation command */
00462             uri_md5, ext, file_in, 
00463             is_a_script ? "-d $@.kld" : "",
00464             uri, 
00465             zip ? "-z" : "",
00466             enc ? "-E" : "", 
00467             enc && ctx->key_file ? "-k" : "", 
00468             enc && ctx->key_file ? ctx->key_file  : "",
00469             is_a_script ? "\t$(MKDEP) -f $@.d $(CFLAGS) -a $@\n" : ""
00470             ) < 0);
00471 
00472     /* include depend files */
00473     if(is_a_script)
00474     {
00475         dbg_err_if(io_printf(ctx->iod, "\n-include " KL1_FILE_FMT ".kld\n", 
00476             uri_md5, ext) < 0);
00477         dbg_err_if(io_printf(ctx->iod, "\n-include " KL1_FILE_FMT ".d\n", 
00478             uri_md5, ext) < 0);
00479     }
00480 
00481     return 0;
00482 err:
00483     return ~0;
00484 }
00485 
00486 static int cb_dir(struct dirent *de, const char *path , void *arg)
00487 {
00488     char dir[U_FILENAME_MAX], base_uri[URI_BUFSZ], *cur_uri = (char*)arg;
00489 
00490     dbg_err_if (de == NULL);
00491     dbg_err_if (path == NULL);
00492     dbg_err_if (arg == NULL);
00493     
00494     ctx->ndir++;
00495 
00496     dbg_err_if(u_snprintf(dir, U_FILENAME_MAX, "%s/%s", path, de->d_name));
00497 
00498     dbg_err_if(u_snprintf(base_uri, URI_BUFSZ, "%s/%s", cur_uri, de->d_name));
00499 
00500     u_foreach_dir_item(dir, S_IFREG, cb_file, (void*)base_uri);
00501 
00502     u_foreach_dir_item(dir, S_IFDIR, cb_dir, (void*)base_uri);
00503 
00504     return 0;
00505 err:
00506     return ~0;
00507 }
00508 
00509 static int print_register_header(io_t *out)
00510 {
00511     dbg_err_if (out == NULL);
00512  
00513     dbg_err_if(io_printf(out, "#include <klone_conf.h>\n") < 0);
00514     dbg_err_if(io_printf(out, "#include <klone/hook.h>\n") < 0);
00515     dbg_err_if(io_printf(out, "static void do_register(int);\n") < 0);
00516     dbg_err_if(io_printf(out, "void unregister_pages(void);\n") < 0);
00517     dbg_err_if(io_printf(out, "void register_pages(void);\n") < 0);
00518 
00519     dbg_err_if(io_printf(out, 
00520         "void unregister_pages(void) { \n"
00521         "do_register(0); }\n"
00522         ) < 0);
00523     dbg_err_if(io_printf(out, 
00524         "void register_pages(void) { \n"
00525         "do_register(1);\n"
00526         "#ifdef ENABLE_HOOKS\n"
00527         "    hooks_setup(); \n"
00528         "#endif \n"
00529         "}\n") < 0);
00530     dbg_err_if(io_printf(out, 
00531         "static void do_register(int action) {\n") < 0);
00532     dbg_err_if(io_printf(out,
00533         "#define KLONE_REGISTER(a, md5)     \\\n"
00534         "    do {                           \\\n"
00535         "    void module_init_##md5(void);  \\\n"
00536         "    void module_term_##md5(void);  \\\n"
00537         "    if(a) module_init_##md5();     \\\n"
00538         "    else module_term_##md5();      \\\n"
00539         "    } while(0)                     \n") < 0);
00540 
00541     return 0;
00542 err:
00543     return ~0;
00544 }
00545 
00546 static int print_register_footer(io_t *out)
00547 {
00548     dbg_err_if (out == NULL);
00549 
00550     dbg_err_if(io_printf(out, "#undef KLONE_REGISTER\n") < 0);
00551     dbg_err_if(io_printf(out, "}\n") < 0);
00552 
00553     return 0;
00554 err:
00555     return ~0;
00556 }
00557 
00558 static int trans_site(char *root_dir, char *base_uri)
00559 {
00560     dbg_err_if (root_dir == NULL);
00561     dbg_err_if (base_uri == NULL);
00562     
00563     /* makefile */
00564     dbg_err_if(u_file_open("autogen.mk", O_CREAT | O_TRUNC | O_WRONLY, 
00565                 &ctx->iom));
00566     dbg_err_if(u_file_open("autogen.dps", O_CREAT | O_TRUNC | O_WRONLY, 
00567                 &ctx->iod));
00568 
00569     dbg_err_if(u_file_open("register.c", O_CREAT | O_TRUNC | O_WRONLY, 
00570                 &ctx->ior));
00571 
00572     dbg_err_if(print_register_header(ctx->ior));
00573 
00574     dbg_err_if(io_printf(ctx->iom, "embfs_rootdir=%s\n", root_dir) < 0);
00575     dbg_err_if(io_printf(ctx->iom, "autogen_src= ") < 0);
00576 
00577     /* for each file call cb_file */
00578     u_foreach_dir_item(root_dir, S_IFREG, cb_file, base_uri);
00579     /* for each directory call cb_dir */
00580     u_foreach_dir_item(root_dir, S_IFDIR, cb_dir, base_uri);
00581 
00582     dbg_err_if(print_register_footer(ctx->ior));
00583 
00584     io_free(ctx->ior);
00585     io_free(ctx->iod);
00586     io_free(ctx->iom);
00587 
00588     return 0;
00589 err:
00590     if(ctx->ior)
00591         io_free(ctx->ior);
00592     if(ctx->iod)
00593         io_free(ctx->iod);
00594     if(ctx->iom)
00595         io_free(ctx->iom);
00596     return ~0;
00597 }
00598 
00599 static int command_import(void)
00600 {
00601     char *root_dir, *base_uri;
00602 
00603     if(ctx->narg != 1)
00604         usage();    /* just on directory expected */
00605 
00606     root_dir = ctx->arg[0];
00607     dbg_err_if(root_dir == NULL);
00608 
00609     if((base_uri = ctx->base_uri) == NULL)
00610     {
00611         base_uri = u_strdup("");
00612         dbg_err_if (base_uri == NULL);
00613     }
00614 
00615     dbg_err_if(trans_site(root_dir, base_uri));
00616 
00617     con("%lu dirs and %lu files imported, %lu files skipped", 
00618             ctx->ndir, ctx->nfile, ctx->nexcl);
00619 
00620     return 0;
00621 err:
00622     con("import error");
00623     return ~0;
00624 }
00625 
00626 static int dispatch_command(void)
00627 {
00628     switch(ctx->cmd)
00629     {
00630     case CMD_TRANS:
00631         dbg_err_if(command_trans());
00632         break;
00633     case CMD_IMPORT:
00634         dbg_err_if(command_import());
00635         break;
00636     default:
00637         con_err("unknown command");
00638     }
00639 
00640     return 0;
00641 err:
00642     return ~0;
00643 }
00644 
00645 int main(int argc, char **argv)
00646 {
00647     context_t context;
00648 
00649     ctx = &context;
00650 
00651     /* zero-out the context */
00652     memset(ctx, 0, sizeof(context_t));
00653 
00654     /* init pattern matching objects */
00655     dbg_err_if(pm_create(&ctx->comp_patt));
00656     dbg_err_if(pm_create(&ctx->enc_patt));
00657     dbg_err_if(pm_create(&ctx->excl_patt));
00658 
00659     /* parse command line switches and set ctx->cmd and params */
00660     dbg_err_if(parse_opt(argc, argv));
00661 
00662     /* run the command */
00663     dbg_err_if(dispatch_command());
00664 
00665     return EXIT_SUCCESS;
00666 err:
00667     return EXIT_FAILURE;
00668 }