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>
2006-02-07 12:58:41 -08:00
# include <sched.h>
2005-04-16 15:20:36 -07:00
# include <sys/stat.h>
# include <sys/ioctl.h>
# include <sys/socket.h>
# include "kern_util.h"
# include "chan_user.h"
# include "user.h"
# include "os.h"
# include "choose-mode.h"
# include "mode.h"
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
{
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
*
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 ;
int count , err ;
char c = 1 ;
pty_fd = data - > pty_fd ;
pipe_fd = data - > pipe_fd ;
2007-05-06 14:51:43 -07:00
count = os_write_file ( pipe_fd , & c , sizeof ( c ) ) ;
2005-04-16 15:20:36 -07:00
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
2005-09-03 15:57:24 -07:00
* do ! If using SIG_IGN , the sigsuspend ( ) call below would not stop on
2005-04-16 15:20:36 -07:00
* SIGWINCH . */
signal ( SIGWINCH , winch_handler ) ;
sigfillset ( & sigs ) ;
2005-09-03 15:57:24 -07:00
/* Block all signals possible. */
2005-04-16 15:20:36 -07:00
if ( sigprocmask ( SIG_SETMASK , & sigs , NULL ) < 0 ) {
printk ( " winch_thread : sigprocmask failed, errno = %d \n " ,
errno ) ;
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
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 ) {
2007-07-15 23:38:55 -07:00
printk ( " winch_thread : new_tty_pgrp failed on fd %d, "
" err = %d \n " , pty_fd , - err ) ;
2005-04-16 15:20:36 -07:00
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
2006-11-30 05:32:19 +01:00
* persistent . */
2007-05-06 14:51:43 -07:00
count = os_read_file ( pipe_fd , & c , sizeof ( c ) ) ;
2005-04-16 15:20:36 -07:00
if ( count ! = sizeof ( c ) )
printk ( " winch_thread : failed to read synchronization byte, "
" err = %d \n " , - count ) ;
while ( 1 ) {
2007-07-15 23:38:55 -07:00
/* This will be interrupted by SIGWINCH only, since
* other signals are blocked .
*/
2005-09-03 15:57:24 -07:00
sigsuspend ( & sigs ) ;
2005-04-16 15:20:36 -07:00
2007-05-06 14:51:43 -07:00
count = os_write_file ( pipe_fd , & c , sizeof ( c ) ) ;
2005-04-16 15:20:36 -07:00
if ( count ! = sizeof ( c ) )
printk ( " winch_thread : write failed, err = %d \n " ,
- count ) ;
}
}
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 ) ;
if ( err < 0 ) {
printk ( " 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 ] } ) ;
/* 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 ) ;
2005-06-08 15:48:27 -07:00
if ( err < 0 ) {
2007-05-06 14:51:03 -07:00
printk ( " 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-05-06 14:51:43 -07:00
n = os_read_file ( fds [ 0 ] , & c , sizeof ( c ) ) ;
2005-04-16 15:20:36 -07:00
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:27 -07:00
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 ) ) {
printk ( " winch_tramp: failed to set thread_fd non-blocking. \n " ) ;
goto out_close ;
}
return err ;
2005-06-08 15:48:27 -07:00
out_close :
os_close_file ( fds [ 1 ] ) ;
os_close_file ( fds [ 0 ] ) ;
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 ;
if ( ! isatty ( fd ) )
return ;
pid = tcgetpgrp ( fd ) ;
2007-07-15 23:38:55 -07:00
if ( ! CHOOSE_MODE_PROC ( is_tracer_winch , is_skas_winch , pid , fd , tty ) & &
( pid = = - 1 ) ) {
thread = winch_tramp ( fd , tty , & thread_fd , & stack ) ;
if ( thread < 0 )
return ;
register_winch_irq ( thread_fd , fd , thread , tty , stack ) ;
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 ) ;
2005-04-16 15:20:36 -07:00
}
}