2005-04-16 15:20:36 -07:00
/*
* Copyright ( C ) 2000 - 2003 Jeff Dike ( jdike @ addtoit . com )
* Licensed under the GPL
*/
# include <unistd.h>
# include <stdlib.h>
# include <errno.h>
# include <termios.h>
# include <string.h>
# include <signal.h>
# include <sys/stat.h>
# include <sys/ioctl.h>
# include <sys/socket.h>
# include "kern_util.h"
# include "user_util.h"
# include "chan_user.h"
# include "user.h"
# include "helper.h"
# include "os.h"
# include "choose-mode.h"
# include "mode.h"
int generic_console_write ( int fd , const char * buf , int n , void * unused )
{
struct termios save , new ;
int err ;
if ( isatty ( fd ) ) {
CATCH_EINTR ( err = tcgetattr ( fd , & save ) ) ;
if ( err )
goto error ;
new = save ;
/* The terminal becomes a bit less raw, to handle \n also as
* " Carriage Return " , not only as " New Line " . Otherwise , the new
* line won ' t start at the first column . */
new . c_oflag | = OPOST ;
CATCH_EINTR ( err = tcsetattr ( fd , TCSAFLUSH , & new ) ) ;
if ( err )
goto error ;
}
err = generic_write ( fd , buf , n , NULL ) ;
/* Restore raw mode, in any case; we *must* ignore any error apart
* EINTR , except for debug . */
if ( isatty ( fd ) )
CATCH_EINTR ( tcsetattr ( fd , TCSAFLUSH , & save ) ) ;
return ( err ) ;
error :
return ( - errno ) ;
}
/*
* UML SIGWINCH handling
*
* The point of this is to handle SIGWINCH on consoles which have host ttys and
* relay them inside UML to whatever might be running on the console and cares
* about the window size ( since SIGWINCH notifies about terminal size changes ) .
*
* So , we have a separate thread for each host tty attached to a UML device
* ( side - issue - I ' m annoyed that one thread can ' t have multiple controlling
* ttys for purposed of handling SIGWINCH , but I imagine there are other reasons
* that doesn ' t make any sense ) .
*
* SIGWINCH can ' t be received synchronously , so you have to set up to receive it
* as a signal . That being the case , if you are going to wait for it , it is
* convenient to sit in a pause ( ) and wait for the signal to bounce you out of
* it ( see below for how we make sure to exit only on SIGWINCH ) .
*/
static void winch_handler ( int sig )
{
}
struct winch_data {
int pty_fd ;
int pipe_fd ;
int close_me ;
} ;
static int winch_thread ( void * arg )
{
struct winch_data * data = arg ;
sigset_t sigs ;
int pty_fd , pipe_fd ;
int count , err ;
char c = 1 ;
os_close_file ( data - > close_me ) ;
pty_fd = data - > pty_fd ;
pipe_fd = data - > pipe_fd ;
count = os_write_file ( pipe_fd , & c , sizeof ( c ) ) ;
if ( count ! = sizeof ( c ) )
printk ( " winch_thread : failed to write synchronization "
" byte, err = %d \n " , - count ) ;
/* We are not using SIG_IGN on purpose, so don't fix it as I thought to
* do ! If using SIG_IGN , the pause ( ) call below would not stop on
* SIGWINCH . */
signal ( SIGWINCH , winch_handler ) ;
sigfillset ( & sigs ) ;
sigdelset ( & sigs , SIGWINCH ) ;
/* Block anything else than SIGWINCH. */
if ( sigprocmask ( SIG_SETMASK , & sigs , NULL ) < 0 ) {
printk ( " winch_thread : sigprocmask failed, errno = %d \n " ,
errno ) ;
exit ( 1 ) ;
}
if ( setsid ( ) < 0 ) {
printk ( " winch_thread : setsid failed, errno = %d \n " , errno ) ;
exit ( 1 ) ;
}
err = os_new_tty_pgrp ( pty_fd , os_getpid ( ) ) ;
if ( err < 0 ) {
printk ( " winch_thread : new_tty_pgrp failed, err = %d \n " , - err ) ;
exit ( 1 ) ;
}
/* These are synchronization calls between various UML threads on the
* host - since they are not different kernel threads , we cannot use
* kernel semaphores . We don ' t use SysV semaphores because they are
* persistant . */
count = os_read_file ( pipe_fd , & c , sizeof ( c ) ) ;
if ( count ! = sizeof ( c ) )
printk ( " winch_thread : failed to read synchronization byte, "
" err = %d \n " , - count ) ;
while ( 1 ) {
/* This will be interrupted by SIGWINCH only, since other signals
* are blocked . */
pause ( ) ;
count = os_write_file ( pipe_fd , & c , sizeof ( c ) ) ;
if ( count ! = sizeof ( c ) )
printk ( " winch_thread : write failed, err = %d \n " ,
- count ) ;
}
}
static int winch_tramp ( int fd , struct tty_struct * tty , int * fd_out )
{
struct winch_data data ;
unsigned long stack ;
int fds [ 2 ] , pid , n , err ;
char c ;
err = os_pipe ( fds , 1 , 1 ) ;
if ( err < 0 ) {
printk ( " winch_tramp : os_pipe failed, err = %d \n " , - err ) ;
return ( err ) ;
}
data = ( ( struct winch_data ) { . pty_fd = fd ,
. pipe_fd = fds [ 1 ] ,
. close_me = fds [ 0 ] } ) ;
pid = run_helper_thread ( winch_thread , & data , 0 , & stack , 0 ) ;
if ( pid < 0 ) {
printk ( " fork of winch_thread failed - errno = %d \n " , errno ) ;
return ( pid ) ;
}
os_close_file ( fds [ 1 ] ) ;
* fd_out = fds [ 0 ] ;
n = os_read_file ( fds [ 0 ] , & c , sizeof ( c ) ) ;
if ( n ! = sizeof ( c ) ) {
printk ( " winch_tramp : failed to read synchronization byte \n " ) ;
printk ( " read failed, err = %d \n " , - n ) ;
printk ( " fd %d will not support SIGWINCH \n " , fd ) ;
2005-06-08 15:48:01 -07:00
pid = - 1 ;
2005-04-16 15:20:36 -07:00
}
return ( pid ) ;
}
void register_winch ( int fd , struct tty_struct * tty )
{
int pid , thread , thread_fd ;
int count ;
char c = 1 ;
if ( ! isatty ( fd ) )
return ;
pid = tcgetpgrp ( fd ) ;
if ( ! CHOOSE_MODE_PROC ( is_tracer_winch , is_skas_winch , pid , fd ,
tty ) & & ( pid = = - 1 ) ) {
thread = winch_tramp ( fd , tty , & thread_fd ) ;
2005-06-08 15:48:01 -07:00
if ( thread > 0 ) {
2005-04-16 15:20:36 -07:00
register_winch_irq ( thread_fd , fd , thread , tty ) ;
count = os_write_file ( thread_fd , & c , sizeof ( c ) ) ;
if ( count ! = sizeof ( c ) )
printk ( " register_winch : failed to write "
" synchronization byte, err = %d \n " ,
- count ) ;
}
}
}
/*
* Overrides for Emacs so that we follow Linus ' s tabbing style .
* Emacs will notice this stuff at the end of the file and automatically
* adjust the settings for this buffer only . This must remain at the end
* of the file .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Local variables :
* c - file - style : " linux "
* End :
*/