diff --git a/tools/console.c b/tools/console.c index e9e01a4675..e6118a07be 100644 --- a/tools/console.c +++ b/tools/console.c @@ -44,9 +44,11 @@ # include "threads.h" # include "virterror_internal.h" - -/* ie Ctrl-] as per telnet */ -# define CTRL_CLOSE_BRACKET '\35' +/* + * Convert given character to control character. + * Basically, we assume ASCII, and take lower 6 bits. + */ +# define CONTROL(c) ((c) ^ 0x40) # define VIR_FROM_THIS VIR_FROM_NONE @@ -69,6 +71,8 @@ struct virConsole { struct virConsoleBuffer streamToTerminal; struct virConsoleBuffer terminalToStream; + + char escapeChar; }; static int got_signal = 0; @@ -219,7 +223,7 @@ virConsoleEventOnStdin(int watch ATTRIBUTE_UNUSED, virConsoleShutdown(con); return; } - if (con->terminalToStream.data[con->terminalToStream.offset] == CTRL_CLOSE_BRACKET) { + if (con->terminalToStream.data[con->terminalToStream.offset] == con->escapeChar) { virConsoleShutdown(con); return; } @@ -282,7 +286,18 @@ virConsoleEventOnStdout(int watch ATTRIBUTE_UNUSED, } -int vshRunConsole(virDomainPtr dom, const char *dev_name) +static char +vshGetEscapeChar(const char *s) +{ + if (*s == '^') + return CONTROL(s[1]); + + return *s; +} + +int vshRunConsole(virDomainPtr dom, + const char *dev_name, + const char *escape_seq) { int ret = -1; struct termios ttyattr, rawattr; @@ -330,6 +345,7 @@ int vshRunConsole(virDomainPtr dom, const char *dev_name) goto cleanup; } + con->escapeChar = vshGetEscapeChar(escape_seq); con->st = virStreamNew(virDomainGetConnect(dom), VIR_STREAM_NONBLOCK); if (!con->st) diff --git a/tools/console.h b/tools/console.h index 9b05ff1ac5..8cca08fb9f 100644 --- a/tools/console.h +++ b/tools/console.h @@ -25,7 +25,9 @@ # ifndef WIN32 -int vshRunConsole(virDomainPtr dom, const char *dev_name); +int vshRunConsole(virDomainPtr dom, + const char *dev_name, + const char *escape_seq); # endif /* !WIN32 */ diff --git a/tools/virsh.c b/tools/virsh.c index 76deaa91ff..ff9cc42968 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -76,6 +76,9 @@ static char *progname; ((((int) ((T)->tv_sec - (U)->tv_sec)) * 1000000.0 + \ ((int) ((T)->tv_usec - (U)->tv_usec))) / 1000.0) +/* Default escape char Ctrl-] as per telnet */ +#define CTRL_CLOSE_BRACKET "^]" + /** * The log configuration */ @@ -254,6 +257,9 @@ typedef struct __vshControl { virMutex lock; bool eventLoopStarted; bool quit; + + const char *escapeChar; /* String representation of + console escape character */ } __vshControl; typedef struct vshCmdGrp { @@ -801,8 +807,8 @@ cmdRunConsole(vshControl *ctl, virDomainPtr dom, const char *name) } vshPrintExtra(ctl, _("Connected to domain %s\n"), virDomainGetName(dom)); - vshPrintExtra(ctl, "%s", _("Escape character is ^]\n")); - if (vshRunConsole(dom, name) == 0) + vshPrintExtra(ctl, _("Escape character is %s\n"), ctl->escapeChar); + if (vshRunConsole(dom, name, ctl->escapeChar) == 0) ret = true; cleanup: @@ -17544,15 +17550,17 @@ vshUsage(void) fprintf(stdout, _("\n%s [options]... []" "\n%s [options]... [args...]\n\n" " options:\n" - " -c | --connect hypervisor connection URI\n" + " -c | --connect=URI hypervisor connection URI\n" " -r | --readonly connect readonly\n" - " -d | --debug debug level [0-4]\n" + " -d | --debug=NUM debug level [0-4]\n" " -h | --help this help\n" " -q | --quiet quiet mode\n" " -t | --timing print timing information\n" - " -l | --log output logging to file\n" - " -v | --version[=short] program version\n" - " -V | --version=long version and full options\n\n" + " -l | --log=FILE output logging to file\n" + " -v short version\n" + " -V long version\n" + " --version[=TYPE] version, TYPE is short or long (default short)\n" + " -e | --escape set escape sequence for console\n\n" " commands (non interactive mode):\n\n"), progname, progname); for (grp = cmdGroups; grp->name; grp++) { @@ -17703,7 +17711,7 @@ static bool vshParseArgv(vshControl *ctl, int argc, char **argv) { bool help = false; - int arg; + int arg, len; struct option opt[] = { {"debug", required_argument, NULL, 'd'}, {"help", no_argument, NULL, 'h'}, @@ -17713,13 +17721,14 @@ vshParseArgv(vshControl *ctl, int argc, char **argv) {"connect", required_argument, NULL, 'c'}, {"readonly", no_argument, NULL, 'r'}, {"log", required_argument, NULL, 'l'}, + {"escape", required_argument, NULL, 'e'}, {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, "+d:hqtc:vVrl:", opt, NULL)) != -1) { + while ((arg = getopt_long(argc, argv, "+d:hqtc:vVrl:e:", opt, NULL)) != -1) { switch (arg) { case 'd': if (virStrToLong_i(optarg, NULL, 10, &ctl->debug) < 0) { @@ -17754,6 +17763,18 @@ vshParseArgv(vshControl *ctl, int argc, char **argv) case 'l': ctl->logfile = vshStrdup(ctl, optarg); break; + case 'e': + len = strlen(optarg); + + if ((len == 2 && *optarg == '^') || + (len == 1 && *optarg != '^')) { + ctl->escapeChar = optarg; + } else { + vshError(ctl, _("Invalid string '%s' for escape sequence"), + optarg); + exit(EXIT_FAILURE); + } + break; default: vshError(ctl, _("unsupported option '-%c'. See --help."), arg); exit(EXIT_FAILURE); @@ -17795,6 +17816,8 @@ main(int argc, char **argv) ctl->imode = true; /* default is interactive mode */ ctl->log_fd = -1; /* Initialize log file descriptor */ ctl->debug = VSH_DEBUG_DEFAULT; + ctl->escapeChar = CTRL_CLOSE_BRACKET; + if (!setlocale(LC_ALL, "")) { perror("setlocale"); diff --git a/tools/virsh.pod b/tools/virsh.pod index 5131ade4b6..fe927140a8 100644 --- a/tools/virsh.pod +++ b/tools/virsh.pod @@ -92,6 +92,11 @@ option of the B command. Output elapsed time information for each command. +=item B<-e>, B<--escape> I + +Set alternative escape sequence for I command. By default, +telnet's B<^]> is used. + =back =head1 NOTES