2007-10-16 01:27:34 -07:00
/*
2007-10-16 01:26:41 -07:00
* Copyright ( C ) 2000 - 2007 Jeff Dike ( jdike @ { linux . intel , addtoit } . com )
2005-04-16 15:20:36 -07:00
* Licensed under the GPL
*/
# include <stdlib.h>
2007-10-16 01:26:41 -07:00
# include <unistd.h>
2005-04-16 15:20:36 -07:00
# include <errno.h>
2006-02-07 12:58:41 -08:00
# include <sched.h>
2007-10-16 01:26:41 -07:00
# include <signal.h>
# include <termios.h>
2005-04-16 15:20:36 -07:00
# include <sys/ioctl.h>
# include "chan_user.h"
2008-05-12 14:01:52 -07:00
# include "kern_constants.h"
2005-04-16 15:20:36 -07:00
# include "os.h"
2007-10-16 01:26:39 -07:00
# include "um_malloc.h"
2007-10-16 01:26:41 -07:00
# include "user.h"
2007-10-16 01:26:39 -07:00
void generic_close ( int fd , void * unused )
{
2007-10-16 01:26:40 -07:00
close ( fd ) ;
2007-10-16 01:26:39 -07:00
}
int generic_read ( int fd , char * c_out , void * unused )
{
int n ;
2007-10-16 01:26:40 -07:00
n = read ( fd , c_out , sizeof ( * c_out ) ) ;
if ( n > 0 )
return n ;
else if ( errno = = EAGAIN )
2007-10-16 01:26:39 -07:00
return 0 ;
2007-10-16 01:26:40 -07:00
else if ( n = = 0 )
2007-10-16 01:26:39 -07:00
return - EIO ;
2007-10-16 01:26:40 -07:00
return - errno ;
2007-10-16 01:26:39 -07:00
}
2007-10-16 01:26:40 -07:00
/* XXX Trivial wrapper around write */
2007-10-16 01:26:39 -07:00
int generic_write ( int fd , const char * buf , int n , void * unused )
{
2007-10-16 01:26:42 -07:00
int err ;
err = write ( fd , buf , n ) ;
if ( err > 0 )
return err ;
else if ( errno = = EAGAIN )
return 0 ;
else if ( err = = 0 )
return - EIO ;
return - errno ;
2007-10-16 01:26:39 -07:00
}
int generic_window_size ( int fd , void * unused , unsigned short * rows_out ,
unsigned short * cols_out )
{
2007-10-16 01:26:40 -07:00
struct winsize size ;
2007-10-16 01:26:39 -07:00
int ret ;
2007-10-16 01:26:41 -07:00
if ( ioctl ( fd , TIOCGWINSZ , & size ) < 0 )
2007-10-16 01:26:40 -07:00
return - errno ;
2007-10-16 01:26:39 -07:00
2007-10-16 01:26:40 -07:00
ret = ( ( * rows_out ! = size . ws_row ) | | ( * cols_out ! = size . ws_col ) ) ;
2007-10-16 01:26:39 -07:00
2007-10-16 01:26:40 -07:00
* rows_out = size . ws_row ;
* cols_out = size . ws_col ;
2007-10-16 01:26:39 -07:00
return ret ;
}
void generic_free ( void * data )
{
kfree ( data ) ;
}
2005-04-16 15:20:36 -07:00
2005-11-13 16:07:11 -08:00
int generic_console_write ( int fd , const char * buf , int n )
2005-04-16 15:20:36 -07:00
{
2007-12-01 12:16:30 -08:00
sigset_t old , no_sigio ;
2005-04-16 15:20:36 -07:00
struct termios save , new ;
int err ;
2007-10-16 01:26:41 -07:00
if ( isatty ( fd ) ) {
2007-12-01 12:16:30 -08:00
sigemptyset ( & no_sigio ) ;
sigaddset ( & no_sigio , SIGIO ) ;
if ( sigprocmask ( SIG_BLOCK , & no_sigio , & old ) )
goto error ;
2005-04-16 15:20:36 -07:00
CATCH_EINTR ( err = tcgetattr ( fd , & save ) ) ;
if ( err )
goto error ;
new = save ;
2007-10-16 01:27:34 -07:00
/*
* The terminal becomes a bit less raw , to handle \ n also as
2005-04-16 15:20:36 -07:00
* " Carriage Return " , not only as " New Line " . Otherwise , the new
2007-10-16 01:27:34 -07:00
* line won ' t start at the first column .
*/
2005-04-16 15:20:36 -07:00
new . c_oflag | = OPOST ;
CATCH_EINTR ( err = tcsetattr ( fd , TCSAFLUSH , & new ) ) ;
if ( err )
goto error ;
}
err = generic_write ( fd , buf , n , NULL ) ;
2007-10-16 01:27:34 -07:00
/*
* Restore raw mode , in any case ; we * must * ignore any error apart
* EINTR , except for debug .
*/
2007-12-01 12:16:30 -08:00
if ( isatty ( fd ) ) {
2005-04-16 15:20:36 -07:00
CATCH_EINTR ( tcsetattr ( fd , TCSAFLUSH , & save ) ) ;
2007-12-01 12:16:30 -08:00
sigprocmask ( SIG_SETMASK , & old , NULL ) ;
}
2007-10-16 01:26:41 -07:00
return err ;
2005-04-16 15:20:36 -07:00
error :
2007-10-16 01:26:41 -07:00
return - errno ;
2005-04-16 15:20:36 -07:00
}
/*
* UML SIGWINCH handling
*
2007-07-15 23:38:55 -07:00
* 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 ) .
2005-04-16 15:20:36 -07:00
*
2007-07-15 23:38:55 -07:00
* 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 the purpose of handling SIGWINCH , but
* I imagine there are other reasons that doesn ' t make any sense ) .
2005-04-16 15:20:36 -07:00
*
2007-07-15 23:38:55 -07:00
* 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 sigsuspend ( ) and wait for
* the signal to bounce you out of it ( see below for how we make sure
* to exit only on SIGWINCH ) .
2005-04-16 15:20:36 -07:00
*/
static void winch_handler ( int sig )
{
}
struct winch_data {
int pty_fd ;
int pipe_fd ;
} ;
static int winch_thread ( void * arg )
{
struct winch_data * data = arg ;
sigset_t sigs ;
int pty_fd , pipe_fd ;
2007-10-16 01:27:08 -07:00
int count ;
2005-04-16 15:20:36 -07:00
char c = 1 ;
pty_fd = data - > pty_fd ;
pipe_fd = data - > pipe_fd ;
2007-10-16 01:27:08 -07:00
count = write ( pipe_fd , & c , sizeof ( c ) ) ;
2007-10-16 01:26:41 -07:00
if ( count ! = sizeof ( c ) )
printk ( UM_KERN_ERR " winch_thread : failed to write "
" synchronization byte, err = %d \n " , - count ) ;
2005-04-16 15:20:36 -07:00
2007-10-16 01:26:41 -07:00
/*
* We are not using SIG_IGN on purpose , so don ' t fix it as I thought to
2005-09-03 15:57:24 -07:00
* do ! If using SIG_IGN , the sigsuspend ( ) call below would not stop on
2007-10-16 01:26:41 -07:00
* SIGWINCH .
*/
2005-04-16 15:20:36 -07:00
signal ( SIGWINCH , winch_handler ) ;
sigfillset ( & sigs ) ;
2005-09-03 15:57:24 -07:00
/* Block all signals possible. */
2007-10-16 01:26:41 -07:00
if ( sigprocmask ( SIG_SETMASK , & sigs , NULL ) < 0 ) {
printk ( UM_KERN_ERR " winch_thread : sigprocmask failed, "
" errno = %d \n " , errno ) ;
2005-04-16 15:20:36 -07:00
exit ( 1 ) ;
}
2005-09-03 15:57:24 -07:00
/* In sigsuspend(), block anything else than SIGWINCH. */
sigdelset ( & sigs , SIGWINCH ) ;
2005-04-16 15:20:36 -07:00
2007-10-16 01:26:41 -07:00
if ( setsid ( ) < 0 ) {
printk ( UM_KERN_ERR " winch_thread : setsid failed, errno = %d \n " ,
errno ) ;
2005-04-16 15:20:36 -07:00
exit ( 1 ) ;
}
2007-10-16 01:27:34 -07:00
if ( ioctl ( pty_fd , TIOCSCTTY , 0 ) < 0 ) {
2007-10-16 01:27:08 -07:00
printk ( UM_KERN_ERR " winch_thread : TIOCSCTTY failed on "
" fd %d err = %d \n " , pty_fd , errno ) ;
exit ( 1 ) ;
}
2007-10-16 01:27:34 -07:00
if ( tcsetpgrp ( pty_fd , os_getpid ( ) ) < 0 ) {
2007-10-16 01:27:08 -07:00
printk ( UM_KERN_ERR " winch_thread : tcsetpgrp failed on "
" fd %d err = %d \n " , pty_fd , errno ) ;
2005-04-16 15:20:36 -07:00
exit ( 1 ) ;
}
2007-10-16 01:26:41 -07:00
/*
* These are synchronization calls between various UML threads on the
2005-04-16 15:20:36 -07:00
* host - since they are not different kernel threads , we cannot use
* kernel semaphores . We don ' t use SysV semaphores because they are
2007-10-16 01:26:41 -07:00
* persistent .
*/
2007-10-16 01:27:08 -07:00
count = read ( pipe_fd , & c , sizeof ( c ) ) ;
2007-10-16 01:26:41 -07:00
if ( count ! = sizeof ( c ) )
printk ( UM_KERN_ERR " winch_thread : failed to read "
2007-10-16 01:27:08 -07:00
" synchronization byte, err = %d \n " , errno ) ;
2005-04-16 15:20:36 -07:00
2007-10-16 01:26:41 -07:00
while ( 1 ) {
/*
* This will be interrupted by SIGWINCH only , since
2007-07-15 23:38:55 -07:00
* other signals are blocked .
*/
2005-09-03 15:57:24 -07:00
sigsuspend ( & sigs ) ;
2005-04-16 15:20:36 -07:00
2007-10-16 01:27:08 -07:00
count = write ( pipe_fd , & c , sizeof ( c ) ) ;
2007-10-16 01:26:41 -07:00
if ( count ! = sizeof ( c ) )
printk ( UM_KERN_ERR " winch_thread : write failed, "
2007-10-16 01:27:08 -07:00
" err = %d \n " , errno ) ;
2005-04-16 15:20:36 -07:00
}
}
2007-07-15 23:38:55 -07:00
static int winch_tramp ( int fd , struct tty_struct * tty , int * fd_out ,
unsigned long * stack_out )
2005-04-16 15:20:36 -07:00
{
struct winch_data data ;
2005-06-08 15:48:27 -07:00
int fds [ 2 ] , n , err ;
2005-04-16 15:20:36 -07:00
char c ;
err = os_pipe ( fds , 1 , 1 ) ;
2007-10-16 01:26:41 -07:00
if ( err < 0 ) {
printk ( UM_KERN_ERR " winch_tramp : os_pipe failed, err = %d \n " ,
- err ) ;
2005-06-08 15:48:27 -07:00
goto out ;
2005-04-16 15:20:36 -07:00
}
data = ( ( struct winch_data ) { . pty_fd = fd ,
2006-02-07 12:58:41 -08:00
. pipe_fd = fds [ 1 ] } ) ;
2007-10-16 01:26:41 -07:00
/*
* CLONE_FILES so this thread doesn ' t hold open files which are open
2007-07-15 23:38:55 -07:00
* now , but later closed in a different thread . This is a
* problem with / dev / net / tun , which if held open by this
* thread , prevents the TUN / TAP device from being reused .
2006-02-07 12:58:41 -08:00
*/
2007-07-15 23:38:56 -07:00
err = run_helper_thread ( winch_thread , & data , CLONE_FILES , stack_out ) ;
2007-10-16 01:26:41 -07:00
if ( err < 0 ) {
printk ( UM_KERN_ERR " fork of winch_thread failed - errno = %d \n " ,
- err ) ;
2005-06-08 15:48:27 -07:00
goto out_close ;
2005-04-16 15:20:36 -07:00
}
* fd_out = fds [ 0 ] ;
2007-10-16 01:27:08 -07:00
n = read ( fds [ 0 ] , & c , sizeof ( c ) ) ;
2007-10-16 01:26:41 -07:00
if ( n ! = sizeof ( c ) ) {
printk ( UM_KERN_ERR " winch_tramp : failed to read "
" synchronization byte \n " ) ;
2007-10-16 01:27:08 -07:00
printk ( UM_KERN_ERR " read failed, err = %d \n " , errno ) ;
2007-10-16 01:26:41 -07:00
printk ( UM_KERN_ERR " fd %d will not support SIGWINCH \n " , fd ) ;
err = - EINVAL ;
2006-02-07 12:58:41 -08:00
goto out_close ;
2005-04-16 15:20:36 -07:00
}
2007-07-15 23:38:51 -07:00
if ( os_set_fd_block ( * fd_out , 0 ) ) {
2007-10-16 01:26:41 -07:00
printk ( UM_KERN_ERR " winch_tramp: failed to set thread_fd "
" non-blocking. \n " ) ;
2007-07-15 23:38:51 -07:00
goto out_close ;
}
return err ;
2005-06-08 15:48:27 -07:00
out_close :
2007-10-16 01:27:08 -07:00
close ( fds [ 1 ] ) ;
close ( fds [ 0 ] ) ;
2005-06-08 15:48:27 -07:00
out :
return err ;
2005-04-16 15:20:36 -07:00
}
void register_winch ( int fd , struct tty_struct * tty )
{
2007-07-15 23:38:55 -07:00
unsigned long stack ;
int pid , thread , count , thread_fd = - 1 ;
2005-04-16 15:20:36 -07:00
char c = 1 ;
2007-10-16 01:26:41 -07:00
if ( ! isatty ( fd ) )
2005-04-16 15:20:36 -07:00
return ;
pid = tcgetpgrp ( fd ) ;
2007-10-16 01:26:56 -07:00
if ( ! is_skas_winch ( pid , fd , tty ) & & ( pid = = - 1 ) ) {
2007-07-15 23:38:55 -07:00
thread = winch_tramp ( fd , tty , & thread_fd , & stack ) ;
if ( thread < 0 )
return ;
register_winch_irq ( thread_fd , fd , thread , tty , stack ) ;
2007-10-16 01:27:08 -07:00
count = write ( thread_fd , & c , sizeof ( c ) ) ;
2007-10-16 01:26:41 -07:00
if ( count ! = sizeof ( c ) )
printk ( UM_KERN_ERR " register_winch : failed to write "
2007-10-16 01:27:08 -07:00
" synchronization byte, err = %d \n " , errno ) ;
2005-04-16 15:20:36 -07:00
}
}