libvirt-virsh参数解析代码解读
来源:互联网 发布:映射的端口号怎么看 编辑:程序博客网 时间:2024/05/20 14:20
virsh是libvirt的一个命令行工具。libvirt想知道用户是在请求什么操作,都是通过分析出入的参数来确定的。virsh中解析参数的函数是virshParseArgv。该函数的具体调用时virshParseArgv(ctl, argc, argv)。ctl是一个全局的结构体变量,argc是传入的参数个数,argv是参数指针数组。virshParseArgv函数的代码如下:
static boolvirshParseArgv(vshControl *ctl, int argc, char **argv){ int arg, len, debug, keepalive; size_t i; int longindex = -1; virshControlPtr priv = ctl->privData; struct option opt[] = { {"connect", required_argument, NULL, 'c'}, {"debug", required_argument, NULL, 'd'}, {"escape", required_argument, NULL, 'e'}, {"help", no_argument, NULL, 'h'}, {"keepalive-interval", required_argument, NULL, 'k'}, {"keepalive-count", required_argument, NULL, 'K'}, {"log", required_argument, NULL, 'l'}, {"quiet", no_argument, NULL, 'q'}, {"readonly", no_argument, NULL, 'r'}, {"timing", no_argument, NULL, 't'}, {"version", optional_argument, NULL, 'v'}, {NULL, 0, NULL, 0} }; /* Standard (non-command) options. The leading + ensures that no * argument reordering takes place, so that command options are * not confused with top-level virsh options. */ while ((arg = getopt_long(argc, argv, "+:c:d:e:hk:K:l:qrtvV", opt, &longindex)) != -1) { switch (arg) { case 'c': VIR_FREE(ctl->connname); ctl->connname = vshStrdup(ctl, optarg); break; case 'd': if (virStrToLong_i(optarg, NULL, 10, &debug) < 0) { vshError(ctl, _("option %s takes a numeric argument"), longindex == -1 ? "-d" : "--debug"); exit(EXIT_FAILURE); } if (debug < VSH_ERR_DEBUG || debug > VSH_ERR_ERROR) vshError(ctl, _("ignoring debug level %d out of range [%d-%d]"), debug, VSH_ERR_DEBUG, VSH_ERR_ERROR); else ctl->debug = debug; break; case 'e': len = strlen(optarg); if ((len == 2 && *optarg == '^' && virshAllowedEscapeChar(optarg[1])) || (len == 1 && *optarg != '^')) { priv->escapeChar = optarg; } else { vshError(ctl, _("Invalid string '%s' for escape sequence"), optarg); exit(EXIT_FAILURE); } break; case 'h': virshUsage(); exit(EXIT_SUCCESS); break; case 'k': if (virStrToLong_i(optarg, NULL, 0, &keepalive) < 0) { vshError(ctl, _("Invalid value for option %s"), longindex == -1 ? "-k" : "--keepalive-interval"); exit(EXIT_FAILURE); } if (keepalive < 0) { vshError(ctl, _("option %s requires a positive integer argument"), longindex == -1 ? "-k" : "--keepalive-interval"); exit(EXIT_FAILURE); } ctl->keepalive_interval = keepalive; break; case 'K': if (virStrToLong_i(optarg, NULL, 0, &keepalive) < 0) { vshError(ctl, _("Invalid value for option %s"), longindex == -1 ? "-K" : "--keepalive-count"); exit(EXIT_FAILURE); } if (keepalive < 0) { vshError(ctl, _("option %s requires a positive integer argument"), longindex == -1 ? "-K" : "--keepalive-count"); exit(EXIT_FAILURE); } ctl->keepalive_count = keepalive; break; case 'l': vshCloseLogFile(ctl); ctl->logfile = vshStrdup(ctl, optarg); vshOpenLogFile(ctl); break; case 'q': ctl->quiet = true; break; case 't': ctl->timing = true; break; case 'r': priv->readonly = true; break; case 'v': if (STRNEQ_NULLABLE(optarg, "long")) { puts(VERSION); exit(EXIT_SUCCESS); } /* fall through */ case 'V': virshShowVersion(ctl); exit(EXIT_SUCCESS); case ':': for (i = 0; opt[i].name != NULL; i++) { if (opt[i].val == optopt) break; } if (opt[i].name) vshError(ctl, _("option '-%c'/'--%s' requires an argument"), optopt, opt[i].name); else vshError(ctl, _("option '-%c' requires an argument"), optopt); exit(EXIT_FAILURE); case '?': if (optopt) vshError(ctl, _("unsupported option '-%c'. See --help."), optopt); else vshError(ctl, _("unsupported option '%s'. See --help."), argv[optind - 1]); exit(EXIT_FAILURE); default: vshError(ctl, _("unknown option")); exit(EXIT_FAILURE); } longindex = -1; } if (argc == optind) { /*no option , command is onli "virsh"*/ ctl->imode = true; } else { /* parse command */ ctl->imode = false; if (argc - optind == 1) { vshDebug(ctl, VSH_ERR_INFO, "commands: \"%s\"\n", argv[optind]); return vshCommandStringParse(ctl, argv[optind]); } else { return vshCommandArgvParse(ctl, argc - optind, argv + optind); } } return true;}
首先调用getopt_long函数解析命令行传入的参数是否有符合"+:c:d:e:hk:K:l:qrtvV"格式。该格式标示如果有'+','c','d','e','k','l'选项,则该选项后必须跟参数,参数可以紧跟在选项后,也可以用空格隔开。 如果有-c选项时,表明需要更改连接,ctl->connname = vshStrdup(ctl, optarg);表示将传入的连接字符串传给ctl->conname。在参数解析完毕后,会调用virshReconnect函数进行连接,此时需要用到ctl->connname。 如果有-d选项时,表明需要更改日志等级,将传入的等级赋值给ctl->debug。 如果有-h选项时,表明是显示帮助信息,调用virshUsage来显示帮助信息。 如果有-l选项时,表明是更改日志文件,先关闭以前的日志文件,然后打开新的日志文件。 如果有-q选项时,表明是结束virsh进程,当virsh是循环模式时,设置ctl->quiet = true。会让描述符监听线程退出。 如果最后argc == optind,则说明用户是执行virsh的循环模式。此时并没有输入具体的除了配置操作意外的操作,此时将ctl->imode = true,表明是循环模式。 如果最后argc - optind == 1,则说明用户只输入了一个不带参数的命令,比如virsh list,此时调用vshCommandStringParse(ctl, argv[optind])来解析该命令。 如果最后argc - optind > 1,则说明用户输入了带参数的命令,比如virsh start vm,此时调用vshCommandArgvParse(ctl, argc - optind, argv + optind)来解析该命令。 首先先分析vshCommandStringParse函数。
boolvshCommandStringParse(vshControl *ctl, char *cmdstr){ vshCommandParser parser; if (cmdstr == NULL || *cmdstr == '\0') return false; parser.pos = cmdstr; parser.getNextArg = vshCommandStringGetArg; return vshCommandParse(ctl, &parser);}
parser.pos指向第一个命令名字。vshCommandStringGetArg函数用来取出传入的操作名字。进入vshCommandParse函数
static boolvshCommandParse(vshControl *ctl, vshCommandParser *parser){ char *tkdata = NULL; vshCmd *clast = NULL; vshCmdOpt *first = NULL; if (ctl->cmd) { vshCommandFree(ctl->cmd); ctl->cmd = NULL; } while (1) { vshCmdOpt *last = NULL; const vshCmdDef *cmd = NULL; vshCommandToken tk; bool data_only = false; uint32_t opts_need_arg = 0; uint32_t opts_required = 0; uint32_t opts_seen = 0; first = NULL; while (1) { const vshCmdOptDef *opt = NULL; tkdata = NULL; tk = parser->getNextArg(ctl, parser, &tkdata); if (tk == VSH_TK_ERROR) goto syntaxError; if (tk != VSH_TK_ARG) { VIR_FREE(tkdata); break; } if (cmd == NULL) { /* first token must be command name */ if (!(cmd = vshCmddefSearch(tkdata))) { vshError(ctl, _("unknown command: '%s'"), tkdata); goto syntaxError; /* ... or ignore this command only? */ } if (vshCmddefOptParse(cmd, &opts_need_arg, &opts_required) < 0) { vshError(ctl, _("internal error: bad options in command: '%s'"), tkdata); goto syntaxError; } VIR_FREE(tkdata); } else if (data_only) { goto get_data; } else if (tkdata[0] == '-' && tkdata[1] == '-' && c_isalnum(tkdata[2])) { char *optstr = strchr(tkdata + 2, '='); int opt_index = 0; if (optstr) { *optstr = '\0'; /* convert the '=' to '\0' */ optstr = vshStrdup(ctl, optstr + 1); } /* Special case 'help' to ignore all spurious options */ if (!(opt = vshCmddefGetOption(ctl, cmd, tkdata + 2, &opts_seen, &opt_index, &optstr))) { VIR_FREE(optstr); if (STREQ(cmd->name, "help")) continue; goto syntaxError; } VIR_FREE(tkdata); if (opt->type != VSH_OT_BOOL) { /* option data */ if (optstr) tkdata = optstr; else tk = parser->getNextArg(ctl, parser, &tkdata); if (tk == VSH_TK_ERROR) goto syntaxError; if (tk != VSH_TK_ARG) { vshError(ctl, _("expected syntax: --%s <%s>"), opt->name, opt->type == VSH_OT_INT ? _("number") : _("string")); goto syntaxError; } if (opt->type != VSH_OT_ARGV) opts_need_arg &= ~(1 << opt_index); } else { tkdata = NULL; if (optstr) { vshError(ctl, _("invalid '=' after option --%s"), opt->name); VIR_FREE(optstr); goto syntaxError; } } } else if (tkdata[0] == '-' && tkdata[1] == '-' && tkdata[2] == '\0') { data_only = true; continue; } else { get_data: /* Special case 'help' to ignore spurious data */ if (!(opt = vshCmddefGetData(cmd, &opts_need_arg, &opts_seen)) && STRNEQ(cmd->name, "help")) { vshError(ctl, _("unexpected data '%s'"), tkdata); goto syntaxError; } } if (opt) { /* save option */ vshCmdOpt *arg = vshMalloc(ctl, sizeof(vshCmdOpt)); arg->def = opt; arg->data = tkdata; arg->next = NULL; tkdata = NULL; if (!first) first = arg; if (last) last->next = arg; last = arg; vshDebug(ctl, VSH_ERR_INFO, "%s: %s(%s): %s\n", cmd->name, opt->name, opt->type != VSH_OT_BOOL ? _("optdata") : _("bool"), opt->type != VSH_OT_BOOL ? arg->data : _("(none)")); } } /* command parsed -- allocate new struct for the command */ if (cmd) { vshCmd *c = vshMalloc(ctl, sizeof(vshCmd)); vshCmdOpt *tmpopt = first; /* if we encountered --help, replace parsed command with * 'help <cmdname>' */ for (tmpopt = first; tmpopt; tmpopt = tmpopt->next) { if (STRNEQ(tmpopt->def->name, "help")) continue; const vshCmdDef *help = vshCmddefSearch("help"); vshCommandOptFree(first); first = vshMalloc(ctl, sizeof(vshCmdOpt)); first->def = help->opts; first->data = vshStrdup(ctl, cmd->name); first->next = NULL; cmd = help; opts_required = 0; opts_seen = 0; break; } c->opts = first; c->def = cmd; c->next = NULL; if (vshCommandCheckOpts(ctl, c, opts_required, opts_seen) < 0) { VIR_FREE(c); goto syntaxError; } if (!ctl->cmd) ctl->cmd = c; if (clast) clast->next = c; clast = c; } if (tk == VSH_TK_END) break; } return true; syntaxError: if (ctl->cmd) { vshCommandFree(ctl->cmd); ctl->cmd = NULL; } if (first) vshCommandOptFree(first); VIR_FREE(tkdata); return false;}
对于无参数操作,第一次调用getNextArg即vshCommandStringGetArg是取出操作的名字。第二次返回VSH_TK_END,然后退出循环。当第一次取出操作的名字后,会进入到下面代码块
if (cmd == NULL) { /* first token must be command name */ if (!(cmd = vshCmddefSearch(tkdata))) { vshError(ctl, _("unknown command: '%s'"), tkdata); goto syntaxError; /* ... or ignore this command only? */ } if (vshCmddefOptParse(cmd, &opts_need_arg, &opts_required) < 0) { vshError(ctl, _("internal error: bad options in command: '%s'"), tkdata); goto syntaxError; } VIR_FREE(tkdata); }
首先利用操作的名字调用vshCmddefSearch寻找操作函数。该函数内部最终会调用vshCmdDefSearchGrp,该函数代码如下:
static const vshCmdDef *vshCmdDefSearchGrp(const char *cmdname){ const vshCmdGrp *g; const vshCmdDef *c; for (g = cmdGroups; g->name; g++) { for (c = g->commands; c->name; c++) { if (STREQ(c->name, cmdname)) return c; } } return NULL;}
vshCmdDefSearchGrp利用操作名字在cmdGroup数组中寻找对应的操作结构体。最总返回的结构体变量指针格式如下:
const vshCmdDef domManagementCmds[] = { {.name = "attach-device", .handler = cmdAttachDevice, .opts = opts_attach_device, .info = info_attach_device, .flags = 0 } }
寻找到操作对应的结构体变量后,然后调用vshCmddefOptParse(cmd, &opts_need_arg, &opts_required)来确定需要的参数。对于virsh list不需要参数所以opts_need_arg和opts_required都为0。最总执行完vshCommandParse函数后,结构体变量的关系是ctl->cmd->def = cmd;
如果是带参数的操作,则会调用vshCommandArgvParse函数,该函数的定义如下:
boolvshCommandArgvParse(vshControl *ctl, int nargs, char **argv){ vshCommandParser parser; if (nargs <= 0) return false; parser.arg_pos = argv; parser.arg_end = argv + nargs; parser.getNextArg = vshCommandArgvGetArg; return vshCommandParse(ctl, &parser);}
进入vshCommandParse函数,以virsh list –all分析
首先第一个解析的list,也会进入到if(cmd == NULL)代码块,同时opts_need_arg和opts_required都为0,第二次解析到”–all”,然后进入到下面的代码块
else if (tkdata[0] == '-' && tkdata[1] == '-' && c_isalnum(tkdata[2])) { char *optstr = strchr(tkdata + 2, '='); int opt_index = 0; if (optstr) { *optstr = '\0'; /* convert the '=' to '\0' */ optstr = vshStrdup(ctl, optstr + 1); } /* Special case 'help' to ignore all spurious options */ if (!(opt = vshCmddefGetOption(ctl, cmd, tkdata + 2, &opts_seen, &opt_index, &optstr))) { VIR_FREE(optstr); if (STREQ(cmd->name, "help")) continue; goto syntaxError; } VIR_FREE(tkdata); if (opt->type != VSH_OT_BOOL) { /* option data */ if (optstr) tkdata = optstr; else tk = parser->getNextArg(ctl, parser, &tkdata); if (tk == VSH_TK_ERROR) goto syntaxError; if (tk != VSH_TK_ARG) { vshError(ctl, _("expected syntax: --%s <%s>"), opt->name, opt->type == VSH_OT_INT ? _("number") : _("string")); goto syntaxError; } if (opt->type != VSH_OT_ARGV) opts_need_arg &= ~(1 << opt_index); } else { tkdata = NULL; if (optstr) { vshError(ctl, _("invalid '=' after option --%s"), opt->name); VIR_FREE(optstr); goto syntaxError; } } }
vshCmddefGetOption返回all参数的详细信息,该代码块定义后,因为opt已经获得了值,则会进入下面的代码块
if (opt) { /* save option */ vshCmdOpt *arg = vshMalloc(ctl, sizeof(vshCmdOpt)); arg->def = opt; arg->data = tkdata; arg->next = NULL; tkdata = NULL; if (!first) first = arg; if (last) last->next = arg; last = arg; vshDebug(ctl, VSH_ERR_INFO, "%s: %s(%s): %s\n", cmd->name, opt->name, opt->type != VSH_OT_BOOL ? _("optdata") : _("bool"), opt->type != VSH_OT_BOOL ? arg->data : _("(none)")); }
可以看见arg->def = opt。first = last = arg。退出最内层的循环后,就进入了下面的代码块
if (cmd) { vshCmd *c = vshMalloc(ctl, sizeof(vshCmd)); vshCmdOpt *tmpopt = first; /* if we encountered --help, replace parsed command with * 'help <cmdname>' */ for (tmpopt = first; tmpopt; tmpopt = tmpopt->next) { if (STRNEQ(tmpopt->def->name, "help")) continue; const vshCmdDef *help = vshCmddefSearch("help"); vshCommandOptFree(first); first = vshMalloc(ctl, sizeof(vshCmdOpt)); first->def = help->opts; first->data = vshStrdup(ctl, cmd->name); first->next = NULL; cmd = help; opts_required = 0; opts_seen = 0; break; } c->opts = first; c->def = cmd; c->next = NULL; if (vshCommandCheckOpts(ctl, c, opts_required, opts_seen) < 0) { VIR_FREE(c); goto syntaxError; } if (!ctl->cmd) ctl->cmd = c; if (clast) clast->next = c; clast = c; }
因为参数里没有”helo”所以,for循环直接跳过去。最后结构体变量之间的关系是:ctl->cmd->opt=first, ctl->cmd->def = cmd, first就是命令的参数信息链表。
如果是virsh start vm1之类的参数不可省的命令(list后的–all参数可省可不省),还要分析参数个数是否匹配。
一开始分析命令名字的步骤和其他命令一样。然后第二次利用vshCmddefOptParse函数获取参数信息,经查询virsh start 命令的参数的信息如下:
static const vshCmdOptDef opts_start[] = { VIRSH_COMMON_OPT_DOMAIN(N_("name of the inactive domain")),#ifndef WIN32 {.name = "console", .type = VSH_OT_BOOL, .help = N_("attach to console after creation") },#endif {.name = "paused", .type = VSH_OT_BOOL, .help = N_("leave the guest paused after creation") }, {.name = "autodestroy", .type = VSH_OT_BOOL, .help = N_("automatically destroy the guest when virsh disconnects") }, {.name = "bypass-cache", .type = VSH_OT_BOOL, .help = N_("avoid file system cache when loading") }, {.name = "force-boot", .type = VSH_OT_BOOL, .help = N_("force fresh boot by discarding any managed save") }, {.name = "pass-fds", .type = VSH_OT_STRING, .help = N_("pass file descriptors N,M,... to the guest") }, {.name = NULL}};
最后opts_need_arg = 1<<5,opts_required为0。1<<5就表示需要第五个(从0开始)参数。
解析完命令名字后,第二次解析的字符串就是命令所需参数,如果解析到了,就说明操作有参数,在参数解析阶段不会判断参数是否正确,只确定参数是否存在。命令的参数解析到后,利用vshCmddefGetData来获得参数的详细信息。同时opts_seen = 1 << 5表示,表示参数列表中的第五个已经获取到了。在获取到命令的参数的结构体后,会进入到下面的代码块:
if (opt) { /* save option */ vshCmdOpt *arg = vshMalloc(ctl, sizeof(vshCmdOpt)); arg->def = opt; arg->data = tkdata; arg->next = NULL; tkdata = NULL; if (!first) first = arg; if (last) last->next = arg; last = arg; vshDebug(ctl, VSH_ERR_INFO, "%s: %s(%s): %s\n", cmd->name, opt->name, opt->type != VSH_OT_BOOL ? _("optdata") : _("bool"), opt->type != VSH_OT_BOOL ? arg->data : _("(none)")); }
可以看到以first开头的链表存储了参数的名字和具体信息。arg->data = tkdata就是复制参数的名字的地址。
最后vshCommandCheckOpts就是判断命令参数有没有获取正确。
至此,virsh参数解析部分结束。
- libvirt-virsh参数解析代码解读
- libvirt-virsh代码解读
- libvirt之virsh代码分析
- libvirt的virsh命令和qemu参数转换
- Libvirt and virsh
- libvirt(virsh命令介绍)
- libvirt(virsh命令介绍)
- libvirt(virsh命令介绍)
- libvirt(virsh命令介绍)
- libvirt-virsh命令
- libvirt(virsh命令介绍)
- libvirt--->virsh 命令执行流程
- linux xen libvirt- Virsh 命令
- libvirt库安装,virsh使用
- libvirt安装后virsh报错问题解决
- qemu kvm libvirt virsh之间的关系!
- libvirt- Virsh 所有命令详单
- 使用libvirt管理kvm(virsh篇)
- iOS 人脸识别功能
- 展开与收起效果
- Redis 1
- 剑指offer——C++面试需要的基础知识
- 山东省第七届省赛L题
- libvirt-virsh参数解析代码解读
- Android中堆unlink利用学习
- Linux堆内存管理深入分析
- View中的getScrollX、getScrollY与getLeft、getRight 以及 MotionEvent中的getX、getY与getRawX、getRawY
- EasyUI初识
- UVA 1347 Tour
- SQL获取时间的方法总结
- lrzsz的上传和下载
- 一起做面试题--用SQL进行行转列