2007-01-26 14:54:29 +03:00
/*
* console . c : A dumb serial console client
*
2011-12-20 13:40:13 +04:00
* Copyright ( C ) 2007 - 2008 , 2010 - 2011 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
* License along with this library ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
* Daniel Berrange < berrange @ redhat . com >
*/
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>
2007-01-26 14:54:29 +03:00
2010-03-09 21:22:22 +03:00
# include "internal.h"
2010-07-27 13:40:30 +04:00
# include "console.h"
2010-03-09 21:22:22 +03:00
# include "logging.h"
# include "util.h"
2011-07-19 22:32:58 +04:00
# include "virfile.h"
2010-07-27 13:40:30 +04:00
# include "memory.h"
2011-10-11 17:05:52 +04:00
# include "threads.h"
2010-07-27 13:40:30 +04:00
# include "virterror_internal.h"
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 ;
} ;
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
} ;
2007-01-26 14:54:29 +03:00
static int got_signal = 0 ;
static void do_signal ( int sig ATTRIBUTE_UNUSED ) {
got_signal = 1 ;
}
2010-03-09 21:22:22 +03:00
# ifndef HAVE_CFMAKERAW
2007-11-26 15:14:32 +03:00
static void
cfmakeraw ( struct termios * attr )
{
attr - > c_iflag & = ~ ( IGNBRK | BRKINT | PARMRK | ISTRIP
| INLCR | IGNCR | ICRNL | IXON ) ;
attr - > c_oflag & = ~ OPOST ;
attr - > c_lflag & = ~ ( ECHO | ECHONL | ICANON | ISIG | IEXTEN ) ;
attr - > c_cflag & = ~ ( CSIZE | PARENB ) ;
attr - > c_cflag | = CS8 ;
}
2010-03-09 21:22:22 +03:00
# endif /* !HAVE_CFMAKERAW */
2007-11-26 15:14:32 +03: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 ) ;
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
}
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 ) {
virReportOOMError ( ) ;
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 ) {
if ( VIR_REALLOC_N ( con - > terminalToStream . data ,
con - > terminalToStream . offset + 1024 ) < 0 )
{ }
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
}
}
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 ) {
virReportOOMError ( ) ;
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
}
}
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 ) {
if ( VIR_REALLOC_N ( con - > streamToTerminal . data ,
con - > streamToTerminal . offset + 1024 ) < 0 )
{ }
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 = = ' ^ ' )
return CONTROL ( s [ 1 ] ) ;
return * s ;
}
int vshRunConsole ( virDomainPtr dom ,
const char * dev_name ,
const char * escape_seq )
2010-07-27 13:40:30 +04:00
{
int ret = - 1 ;
2007-01-26 14:54:29 +03:00
struct termios ttyattr , rawattr ;
void ( * old_sigquit ) ( int ) ;
void ( * old_sigterm ) ( int ) ;
void ( * old_sigint ) ( int ) ;
void ( * old_sighup ) ( int ) ;
void ( * old_sigpipe ) ( int ) ;
2010-07-27 13:40:30 +04:00
virConsolePtr con = NULL ;
2007-01-26 14:54:29 +03:00
2009-08-07 12:56:38 +04:00
/* Put STDIN into raw mode so that stuff typed
2007-01-26 14:54:29 +03:00
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 */
if ( tcgetattr ( STDIN_FILENO , & ttyattr ) < 0 ) {
2010-01-19 16:17:20 +03:00
VIR_ERROR ( _ ( " unable to get tty attributes: %s " ) ,
2009-05-20 17:37:30 +04:00
strerror ( errno ) ) ;
2010-07-27 13:40:30 +04:00
return - 1 ;
2007-01-26 14:54:29 +03:00
}
rawattr = ttyattr ;
cfmakeraw ( & rawattr ) ;
if ( tcsetattr ( STDIN_FILENO , TCSAFLUSH , & rawattr ) < 0 ) {
2010-01-19 16:17:20 +03:00
VIR_ERROR ( _ ( " unable to set tty attributes: %s " ) ,
2009-05-20 17:37:30 +04:00
strerror ( errno ) ) ;
2010-07-27 13:40:30 +04:00
goto resettty ;
2007-01-26 14:54:29 +03: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 ! */
old_sigquit = signal ( SIGQUIT , do_signal ) ;
old_sigterm = signal ( SIGTERM , do_signal ) ;
old_sigint = signal ( SIGINT , do_signal ) ;
old_sighup = signal ( SIGHUP , do_signal ) ;
old_sigpipe = signal ( SIGPIPE , do_signal ) ;
got_signal = 0 ;
2010-07-27 13:40:30 +04:00
if ( VIR_ALLOC ( con ) < 0 ) {
virReportOOMError ( ) ;
goto cleanup ;
}
2007-01-26 14:54:29 +03:00
2011-11-22 20:08:05 +04:00
con - > escapeChar = vshGetEscapeChar ( escape_seq ) ;
2010-07-27 13:40:30 +04:00
con - > st = virStreamNew ( virDomainGetConnect ( dom ) ,
VIR_STREAM_NONBLOCK ) ;
if ( ! con - > st )
goto cleanup ;
2011-09-16 16:05:58 +04:00
if ( virDomainOpenConsole ( dom , dev_name , con - > st , 0 ) < 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 ;
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 ) {
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
ret = 0 ;
cleanup :
2010-07-27 13:40:30 +04:00
if ( con ) {
if ( con - > st )
virStreamFree ( con - > st ) ;
2011-10-11 17:05:52 +04:00
virMutexDestroy ( & con - > lock ) ;
ignore_value ( virCondDestroy ( & con - > cond ) ) ;
2010-07-27 13:40:30 +04:00
VIR_FREE ( con ) ;
}
2007-01-26 14:54:29 +03:00
/* Restore original signal handlers */
2010-12-09 09:41:03 +03:00
signal ( SIGPIPE , old_sigpipe ) ;
signal ( SIGHUP , old_sighup ) ;
signal ( SIGINT , old_sigint ) ;
signal ( SIGTERM , old_sigterm ) ;
2007-01-26 14:54:29 +03:00
signal ( SIGQUIT , old_sigquit ) ;
2010-07-27 13:40:30 +04:00
resettty :
2007-01-26 14:54:29 +03:00
/* Put STDIN back into the (sane?) state we found
it in before starting */
tcsetattr ( STDIN_FILENO , TCSAFLUSH , & ttyattr ) ;
return ret ;
}
2010-05-04 00:44:12 +04:00
# endif /* !WIN32 */