2007-01-26 14:54:29 +03:00
/*
2013-08-26 13:53:43 +04:00
* virsh - console . c : A dumb serial console client
2007-01-26 14:54:29 +03:00
*
2013-08-26 13:53:43 +04:00
* Copyright ( C ) 2007 - 2008 , 2010 - 2013 Red Hat , Inc .
2007-01-26 14:54:29 +03:00
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
* This library is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
2012-09-21 02:30:55 +04:00
* License along with this library . If not , see
2012-07-21 14:06:23 +04:00
* < http : //www.gnu.org/licenses/>.
2007-01-26 14:54:29 +03:00
*
2013-08-26 13:53:43 +04:00
* Authors :
* Daniel Berrange < berrange @ redhat . com >
2007-01-26 14:54:29 +03:00
*/
2008-01-29 21:15:54 +03:00
# include <config.h>
2007-11-26 15:14:32 +03:00
2010-05-04 00:44:12 +04:00
# ifndef WIN32
2007-12-07 17:56:37 +03:00
2010-03-09 21:22:22 +03:00
# include <stdio.h>
# include <sys / types.h>
# include <sys / stat.h>
# include <fcntl.h>
# include <termios.h>
# include <poll.h>
# include <string.h>
# include <errno.h>
# include <unistd.h>
# include <signal.h>
2012-04-03 16:59:06 +04:00
# include <c-ctype.h>
2007-01-26 14:54:29 +03:00
2010-03-09 21:22:22 +03:00
# include "internal.h"
2013-08-29 20:15:07 +04:00
# include "virsh.h"
2013-08-26 13:53:43 +04:00
# include "virsh-console.h"
2012-12-12 21:59:27 +04:00
# include "virlog.h"
2011-07-19 22:32:58 +04:00
# include "virfile.h"
2012-12-12 22:06:53 +04:00
# include "viralloc.h"
2012-12-13 19:49:48 +04:00
# include "virthread.h"
2012-12-13 22:21:53 +04:00
# include "virerror.h"
2010-07-27 13:40:30 +04:00
2014-02-28 16:16:17 +04:00
VIR_LOG_INIT ( " tools.virsh-console " ) ;
2011-11-22 20:08:05 +04:00
/*
* Convert given character to control character .
* Basically , we assume ASCII , and take lower 6 bits .
*/
# define CONTROL(c) ((c) ^ 0x40)
2007-01-26 14:54:29 +03:00
2010-07-27 13:40:30 +04:00
# define VIR_FROM_THIS VIR_FROM_NONE
struct virConsoleBuffer {
size_t length ;
size_t offset ;
char * data ;
} ;
2013-08-26 13:53:43 +04:00
2010-07-27 13:40:30 +04:00
typedef struct virConsole virConsole ;
typedef virConsole * virConsolePtr ;
struct virConsole {
virStreamPtr st ;
bool quit ;
2011-10-11 17:05:52 +04:00
virMutex lock ;
virCond cond ;
2010-07-27 13:40:30 +04:00
int stdinWatch ;
int stdoutWatch ;
struct virConsoleBuffer streamToTerminal ;
struct virConsoleBuffer terminalToStream ;
2011-11-22 20:08:05 +04:00
char escapeChar ;
2010-07-27 13:40:30 +04:00
} ;
2013-08-26 13:53:43 +04:00
2007-01-26 14:54:29 +03:00
static int got_signal = 0 ;
2013-08-26 13:53:43 +04:00
static void
virConsoleHandleSignal ( int sig ATTRIBUTE_UNUSED )
{
2007-01-26 14:54:29 +03:00
got_signal = 1 ;
}
2013-08-26 13:53:43 +04:00
2010-11-11 18:15:46 +03:00
static void
virConsoleShutdown ( virConsolePtr con )
{
2011-03-01 17:59:45 +03:00
if ( con - > st ) {
virStreamEventRemoveCallback ( con - > st ) ;
virStreamAbort ( con - > st ) ;
2011-03-14 10:11:03 +03:00
virStreamFree ( con - > st ) ;
2012-08-03 15:16:24 +04:00
con - > st = NULL ;
2011-03-01 17:59:45 +03:00
}
2011-12-20 13:40:13 +04:00
VIR_FREE ( con - > streamToTerminal . data ) ;
VIR_FREE ( con - > terminalToStream . data ) ;
2010-11-11 18:15:46 +03:00
if ( con - > stdinWatch ! = - 1 )
2011-03-02 18:08:31 +03:00
virEventRemoveHandle ( con - > stdinWatch ) ;
2011-08-10 19:03:23 +04:00
if ( con - > stdoutWatch ! = - 1 )
2011-03-02 18:08:31 +03:00
virEventRemoveHandle ( con - > stdoutWatch ) ;
2010-11-11 18:15:46 +03:00
con - > stdinWatch = - 1 ;
con - > stdoutWatch = - 1 ;
2011-10-11 17:05:52 +04:00
con - > quit = true ;
virCondSignal ( & con - > cond ) ;
2010-11-11 18:15:46 +03:00
}
2013-08-26 13:53:43 +04:00
static void
virConsoleFree ( virConsolePtr con )
{
if ( ! con )
return ;
if ( con - > st )
virStreamFree ( con - > st ) ;
virMutexDestroy ( & con - > lock ) ;
virCondDestroy ( & con - > cond ) ;
VIR_FREE ( con ) ;
}
2010-07-27 13:40:30 +04:00
static void
virConsoleEventOnStream ( virStreamPtr st ,
int events , void * opaque )
{
virConsolePtr con = opaque ;
if ( events & VIR_STREAM_EVENT_READABLE ) {
size_t avail = con - > streamToTerminal . length -
con - > streamToTerminal . offset ;
int got ;
if ( avail < 1024 ) {
if ( VIR_REALLOC_N ( con - > streamToTerminal . data ,
con - > streamToTerminal . length + 1024 ) < 0 ) {
2010-11-11 18:15:46 +03:00
virConsoleShutdown ( con ) ;
2010-07-27 13:40:30 +04:00
return ;
}
con - > streamToTerminal . length + = 1024 ;
avail + = 1024 ;
}
got = virStreamRecv ( st ,
con - > streamToTerminal . data +
con - > streamToTerminal . offset ,
avail ) ;
if ( got = = - 2 )
return ; /* blocking */
if ( got < = 0 ) {
2010-11-11 18:15:46 +03:00
virConsoleShutdown ( con ) ;
2010-07-27 13:40:30 +04:00
return ;
}
con - > streamToTerminal . offset + = got ;
if ( con - > streamToTerminal . offset )
2011-03-02 18:08:31 +03:00
virEventUpdateHandle ( con - > stdoutWatch ,
VIR_EVENT_HANDLE_WRITABLE ) ;
2010-07-27 13:40:30 +04:00
}
if ( events & VIR_STREAM_EVENT_WRITABLE & &
con - > terminalToStream . offset ) {
ssize_t done ;
size_t avail ;
done = virStreamSend ( con - > st ,
con - > terminalToStream . data ,
con - > terminalToStream . offset ) ;
if ( done = = - 2 )
return ; /* blocking */
if ( done < 0 ) {
2010-11-11 18:15:46 +03:00
virConsoleShutdown ( con ) ;
2010-07-27 13:40:30 +04:00
return ;
}
memmove ( con - > terminalToStream . data ,
con - > terminalToStream . data + done ,
con - > terminalToStream . offset - done ) ;
con - > terminalToStream . offset - = done ;
avail = con - > terminalToStream . length - con - > terminalToStream . offset ;
if ( avail > 1024 ) {
2013-08-26 13:53:43 +04:00
ignore_value ( VIR_REALLOC_N ( con - > terminalToStream . data ,
con - > terminalToStream . offset + 1024 ) ) ;
2010-07-27 13:40:30 +04:00
con - > terminalToStream . length = con - > terminalToStream . offset + 1024 ;
}
}
if ( ! con - > terminalToStream . offset )
virStreamEventUpdateCallback ( con - > st ,
VIR_STREAM_EVENT_READABLE ) ;
if ( events & VIR_STREAM_EVENT_ERROR | |
events & VIR_STREAM_EVENT_HANGUP ) {
2010-11-11 18:15:46 +03:00
virConsoleShutdown ( con ) ;
2010-07-27 13:40:30 +04:00
}
}
2013-08-26 13:53:43 +04:00
2010-07-27 13:40:30 +04:00
static void
virConsoleEventOnStdin ( int watch ATTRIBUTE_UNUSED ,
int fd ATTRIBUTE_UNUSED ,
int events ,
void * opaque )
{
virConsolePtr con = opaque ;
if ( events & VIR_EVENT_HANDLE_READABLE ) {
size_t avail = con - > terminalToStream . length -
con - > terminalToStream . offset ;
int got ;
if ( avail < 1024 ) {
if ( VIR_REALLOC_N ( con - > terminalToStream . data ,
con - > terminalToStream . length + 1024 ) < 0 ) {
2010-11-11 18:15:46 +03:00
virConsoleShutdown ( con ) ;
2010-07-27 13:40:30 +04:00
return ;
}
con - > terminalToStream . length + = 1024 ;
avail + = 1024 ;
}
got = read ( fd ,
con - > terminalToStream . data +
con - > terminalToStream . offset ,
avail ) ;
if ( got < 0 ) {
if ( errno ! = EAGAIN ) {
2010-11-11 18:15:46 +03:00
virConsoleShutdown ( con ) ;
2010-07-27 13:40:30 +04:00
}
return ;
}
if ( got = = 0 ) {
2010-11-11 18:15:46 +03:00
virConsoleShutdown ( con ) ;
2010-07-27 13:40:30 +04:00
return ;
}
2011-11-22 20:08:05 +04:00
if ( con - > terminalToStream . data [ con - > terminalToStream . offset ] = = con - > escapeChar ) {
2010-11-11 18:15:46 +03:00
virConsoleShutdown ( con ) ;
2010-07-27 13:40:30 +04:00
return ;
}
con - > terminalToStream . offset + = got ;
if ( con - > terminalToStream . offset )
virStreamEventUpdateCallback ( con - > st ,
VIR_STREAM_EVENT_READABLE |
VIR_STREAM_EVENT_WRITABLE ) ;
}
if ( events & VIR_EVENT_HANDLE_ERROR | |
events & VIR_EVENT_HANDLE_HANGUP ) {
2010-11-11 18:15:46 +03:00
virConsoleShutdown ( con ) ;
2010-07-27 13:40:30 +04:00
}
}
2013-08-26 13:53:43 +04:00
2010-07-27 13:40:30 +04:00
static void
virConsoleEventOnStdout ( int watch ATTRIBUTE_UNUSED ,
int fd ,
int events ,
void * opaque )
{
virConsolePtr con = opaque ;
if ( events & VIR_EVENT_HANDLE_WRITABLE & &
con - > streamToTerminal . offset ) {
ssize_t done ;
size_t avail ;
done = write ( fd ,
con - > streamToTerminal . data ,
con - > streamToTerminal . offset ) ;
if ( done < 0 ) {
if ( errno ! = EAGAIN ) {
2010-11-11 18:15:46 +03:00
virConsoleShutdown ( con ) ;
2010-07-27 13:40:30 +04:00
}
return ;
}
memmove ( con - > streamToTerminal . data ,
con - > streamToTerminal . data + done ,
con - > streamToTerminal . offset - done ) ;
con - > streamToTerminal . offset - = done ;
avail = con - > streamToTerminal . length - con - > streamToTerminal . offset ;
if ( avail > 1024 ) {
2013-08-26 13:53:43 +04:00
ignore_value ( VIR_REALLOC_N ( con - > streamToTerminal . data ,
con - > streamToTerminal . offset + 1024 ) ) ;
2010-07-27 13:40:30 +04:00
con - > streamToTerminal . length = con - > streamToTerminal . offset + 1024 ;
}
}
if ( ! con - > streamToTerminal . offset )
2011-03-02 18:08:31 +03:00
virEventUpdateHandle ( con - > stdoutWatch , 0 ) ;
2010-07-27 13:40:30 +04:00
if ( events & VIR_EVENT_HANDLE_ERROR | |
events & VIR_EVENT_HANDLE_HANGUP ) {
2010-11-11 18:15:46 +03:00
virConsoleShutdown ( con ) ;
2010-07-27 13:40:30 +04:00
}
}
2011-11-22 20:08:05 +04:00
static char
vshGetEscapeChar ( const char * s )
{
if ( * s = = ' ^ ' )
2012-04-03 16:59:06 +04:00
return CONTROL ( c_toupper ( s [ 1 ] ) ) ;
2011-11-22 20:08:05 +04:00
return * s ;
}
2013-08-26 13:53:43 +04:00
2012-09-05 03:35:27 +04:00
int
2013-08-29 20:15:07 +04:00
vshRunConsole ( vshControl * ctl ,
virDomainPtr dom ,
2013-08-26 13:53:43 +04:00
const char * dev_name ,
unsigned int flags )
2010-07-27 13:40:30 +04:00
{
virConsolePtr con = NULL ;
2013-08-29 20:30:06 +04:00
int ret = - 1 ;
struct sigaction old_sigquit ;
struct sigaction old_sigterm ;
struct sigaction old_sigint ;
struct sigaction old_sighup ;
struct sigaction old_sigpipe ;
struct sigaction sighandler = { . sa_handler = virConsoleHandleSignal ,
. sa_flags = SA_SIGINFO } ;
sigemptyset ( & sighandler . sa_mask ) ;
2007-01-26 14:54:29 +03:00
2013-08-29 20:30:06 +04:00
/* Put STDIN into raw mode so that stuff typed does not echo to the screen
* ( the TTY reads will result in it being echoed back already ) , and also
* ensure Ctrl - C , etc is blocked , and misc other bits */
2013-08-29 20:15:07 +04:00
if ( vshTTYMakeRaw ( ctl , true ) < 0 )
2010-07-27 13:40:30 +04:00
goto resettty ;
2007-01-26 14:54:29 +03:00
2013-08-29 20:30:06 +04:00
/* Trap all common signals so that we can safely restore the original
* terminal settings on STDIN before the process exits - people don ' t like
* being left with a messed up terminal ! */
2007-01-26 14:54:29 +03:00
got_signal = 0 ;
2013-08-29 20:30:06 +04:00
sigaction ( SIGQUIT , & sighandler , & old_sigquit ) ;
sigaction ( SIGTERM , & sighandler , & old_sigterm ) ;
sigaction ( SIGINT , & sighandler , & old_sigint ) ;
sigaction ( SIGHUP , & sighandler , & old_sighup ) ;
sigaction ( SIGPIPE , & sighandler , & old_sigpipe ) ;
2007-01-26 14:54:29 +03:00
2013-07-04 14:20:45 +04:00
if ( VIR_ALLOC ( con ) < 0 )
2010-07-27 13:40:30 +04:00
goto cleanup ;
2007-01-26 14:54:29 +03:00
2013-08-29 20:15:07 +04:00
con - > escapeChar = vshGetEscapeChar ( ctl - > escapeChar ) ;
2010-07-27 13:40:30 +04:00
con - > st = virStreamNew ( virDomainGetConnect ( dom ) ,
VIR_STREAM_NONBLOCK ) ;
if ( ! con - > st )
goto cleanup ;
2011-10-12 14:59:15 +04:00
if ( virDomainOpenConsole ( dom , dev_name , con - > st , flags ) < 0 )
2010-07-27 13:40:30 +04:00
goto cleanup ;
2011-10-11 17:05:52 +04:00
if ( virCondInit ( & con - > cond ) < 0 | | virMutexInit ( & con - > lock ) < 0 )
goto cleanup ;
2014-03-10 14:51:32 +04:00
virMutexLock ( & con - > lock ) ;
2011-03-02 18:08:31 +03:00
con - > stdinWatch = virEventAddHandle ( STDIN_FILENO ,
VIR_EVENT_HANDLE_READABLE ,
virConsoleEventOnStdin ,
con ,
NULL ) ;
con - > stdoutWatch = virEventAddHandle ( STDOUT_FILENO ,
0 ,
virConsoleEventOnStdout ,
con ,
NULL ) ;
2010-07-27 13:40:30 +04:00
virStreamEventAddCallback ( con - > st ,
VIR_STREAM_EVENT_READABLE ,
virConsoleEventOnStream ,
con ,
NULL ) ;
while ( ! con - > quit ) {
2011-10-11 17:05:52 +04:00
if ( virCondWait ( & con - > cond , & con - > lock ) < 0 ) {
2014-03-10 14:51:32 +04:00
virMutexUnlock ( & con - > lock ) ;
2011-10-11 17:05:52 +04:00
VIR_ERROR ( _ ( " unable to wait on console condition " ) ) ;
goto cleanup ;
}
2010-07-27 13:40:30 +04:00
}
2007-01-26 14:54:29 +03:00
2014-03-10 14:51:32 +04:00
virMutexUnlock ( & con - > lock ) ;
2007-01-26 14:54:29 +03:00
ret = 0 ;
2014-03-25 10:53:59 +04:00
cleanup :
2013-08-26 13:53:43 +04:00
virConsoleFree ( con ) ;
2010-07-27 13:40:30 +04:00
2007-01-26 14:54:29 +03:00
/* Restore original signal handlers */
2013-08-29 20:30:06 +04:00
sigaction ( SIGQUIT , & old_sigquit , NULL ) ;
sigaction ( SIGTERM , & old_sigterm , NULL ) ;
sigaction ( SIGINT , & old_sigint , NULL ) ;
sigaction ( SIGHUP , & old_sighup , NULL ) ;
sigaction ( SIGPIPE , & old_sigpipe , NULL ) ;
2007-01-26 14:54:29 +03:00
2014-03-25 10:53:59 +04:00
resettty :
2007-01-26 14:54:29 +03:00
/* Put STDIN back into the (sane?) state we found
it in before starting */
2013-08-29 20:15:07 +04:00
vshTTYRestore ( ctl ) ;
2007-01-26 14:54:29 +03:00
return ret ;
}
2010-05-04 00:44:12 +04:00
# endif /* !WIN32 */