2007-02-10 12:44:02 +03:00
/*
2007-10-16 12:26:41 +04:00
* Copyright ( C ) 2001 - 2007 Jeff Dike ( jdike @ { linux . intel , addtoit } . com )
2005-04-17 02:20:36 +04:00
* Licensed under the GPL
*/
# include <stdio.h>
# include <stdlib.h>
# include <errno.h>
# include <termios.h>
2007-10-16 12:26:41 +04:00
# include <unistd.h>
2005-04-17 02:20:36 +04:00
# include <netinet/in.h>
# include "chan_user.h"
2007-10-16 12:26:41 +04:00
# include "kern_constants.h"
2005-04-17 02:20:36 +04:00
# include "os.h"
2007-10-16 12:26:41 +04:00
# include "port.h"
2006-10-20 10:28:20 +04:00
# include "um_malloc.h"
2007-10-16 12:26:41 +04:00
# include "user.h"
2005-04-17 02:20:36 +04:00
struct port_chan {
int raw ;
struct termios tt ;
void * kernel_data ;
char dev [ sizeof ( " 32768 \0 " ) ] ;
} ;
2006-09-27 12:50:33 +04:00
static void * port_init ( char * str , int device , const struct chan_opts * opts )
2005-04-17 02:20:36 +04:00
{
struct port_chan * data ;
void * kern_data ;
char * end ;
int port ;
2007-10-16 12:26:41 +04:00
if ( * str ! = ' : ' ) {
printk ( UM_KERN_ERR " port_init : channel type 'port' must "
" specify a port number \n " ) ;
2007-02-10 12:44:02 +03:00
return NULL ;
2005-04-17 02:20:36 +04:00
}
str + + ;
port = strtoul ( str , & end , 0 ) ;
2007-10-16 12:26:41 +04:00
if ( ( * end ! = ' \0 ' ) | | ( end = = str ) ) {
printk ( UM_KERN_ERR " port_init : couldn't parse port '%s' \n " ,
str ) ;
2007-02-10 12:44:02 +03:00
return NULL ;
2005-04-17 02:20:36 +04:00
}
kern_data = port_data ( port ) ;
2007-10-16 12:26:41 +04:00
if ( kern_data = = NULL )
2007-02-10 12:44:02 +03:00
return NULL ;
2005-04-17 02:20:36 +04:00
2008-05-13 01:01:52 +04:00
data = uml_kmalloc ( sizeof ( * data ) , UM_GFP_KERNEL ) ;
2007-10-16 12:26:41 +04:00
if ( data = = NULL )
2005-04-17 02:20:36 +04:00
goto err ;
* data = ( ( struct port_chan ) { . raw = opts - > raw ,
. kernel_data = kern_data } ) ;
sprintf ( data - > dev , " %d " , port ) ;
2007-02-10 12:44:02 +03:00
return data ;
2005-04-17 02:20:36 +04:00
err :
port_kern_free ( kern_data ) ;
2007-02-10 12:44:02 +03:00
return NULL ;
2005-04-17 02:20:36 +04:00
}
static void port_free ( void * d )
{
struct port_chan * data = d ;
port_kern_free ( data - > kernel_data ) ;
kfree ( data ) ;
}
static int port_open ( int input , int output , int primary , void * d ,
char * * dev_out )
{
struct port_chan * data = d ;
int fd , err ;
fd = port_wait ( data - > kernel_data ) ;
2007-10-16 12:26:41 +04:00
if ( ( fd > = 0 ) & & data - > raw ) {
2005-04-17 02:20:36 +04:00
CATCH_EINTR ( err = tcgetattr ( fd , & data - > tt ) ) ;
2007-10-16 12:26:41 +04:00
if ( err )
2007-02-10 12:44:02 +03:00
return err ;
2005-04-17 02:20:36 +04:00
err = raw ( fd ) ;
2007-10-16 12:26:41 +04:00
if ( err )
2007-02-10 12:44:02 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}
* dev_out = data - > dev ;
2007-02-10 12:44:02 +03:00
return fd ;
2005-04-17 02:20:36 +04:00
}
static void port_close ( int fd , void * d )
{
struct port_chan * data = d ;
port_remove_dev ( data - > kernel_data ) ;
os_close_file ( fd ) ;
}
2006-09-27 12:50:33 +04:00
const struct chan_ops port_ops = {
2005-04-17 02:20:36 +04:00
. type = " port " ,
. init = port_init ,
. open = port_open ,
. close = port_close ,
. read = generic_read ,
. write = generic_write ,
2005-11-14 03:07:10 +03:00
. console_write = generic_console_write ,
2005-04-17 02:20:36 +04:00
. window_size = generic_window_size ,
. free = port_free ,
. winch = 1 ,
} ;
int port_listen_fd ( int port )
{
struct sockaddr_in addr ;
int fd , err , arg ;
fd = socket ( PF_INET , SOCK_STREAM , 0 ) ;
2007-10-16 12:26:41 +04:00
if ( fd = = - 1 )
2007-02-10 12:44:02 +03:00
return - errno ;
2005-04-17 02:20:36 +04:00
arg = 1 ;
2007-10-16 12:26:41 +04:00
if ( setsockopt ( fd , SOL_SOCKET , SO_REUSEADDR , & arg , sizeof ( arg ) ) < 0 ) {
2005-04-17 02:20:36 +04:00
err = - errno ;
goto out ;
}
addr . sin_family = AF_INET ;
addr . sin_port = htons ( port ) ;
addr . sin_addr . s_addr = htonl ( INADDR_ANY ) ;
2007-10-16 12:26:41 +04:00
if ( bind ( fd , ( struct sockaddr * ) & addr , sizeof ( addr ) ) < 0 ) {
2005-04-17 02:20:36 +04:00
err = - errno ;
goto out ;
}
2007-02-10 12:44:02 +03:00
2007-10-16 12:26:41 +04:00
if ( listen ( fd , 1 ) < 0 ) {
2005-04-17 02:20:36 +04:00
err = - errno ;
goto out ;
}
err = os_set_fd_block ( fd , 0 ) ;
2007-10-16 12:26:41 +04:00
if ( err < 0 )
2005-04-17 02:20:36 +04:00
goto out ;
2007-02-10 12:44:02 +03:00
return fd ;
2005-04-17 02:20:36 +04:00
out :
2007-10-16 12:26:41 +04:00
close ( fd ) ;
2007-02-10 12:44:02 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}
struct port_pre_exec_data {
int sock_fd ;
int pipe_fd ;
} ;
2008-04-28 13:13:56 +04:00
static void port_pre_exec ( void * arg )
2005-04-17 02:20:36 +04:00
{
struct port_pre_exec_data * data = arg ;
dup2 ( data - > sock_fd , 0 ) ;
dup2 ( data - > sock_fd , 1 ) ;
dup2 ( data - > sock_fd , 2 ) ;
2007-10-16 12:26:41 +04:00
close ( data - > sock_fd ) ;
2005-04-17 02:20:36 +04:00
dup2 ( data - > pipe_fd , 3 ) ;
2007-10-16 12:26:41 +04:00
shutdown ( 3 , SHUT_RD ) ;
close ( data - > pipe_fd ) ;
2005-04-17 02:20:36 +04:00
}
int port_connection ( int fd , int * socket , int * pid_out )
{
int new , err ;
2007-02-10 12:44:02 +03:00
char * argv [ ] = { " /usr/sbin/in.telnetd " , " -L " ,
2005-04-17 02:20:36 +04:00
" /usr/lib/uml/port-helper " , NULL } ;
struct port_pre_exec_data data ;
2007-10-16 12:26:41 +04:00
new = accept ( fd , NULL , 0 ) ;
if ( new < 0 )
return - errno ;
2005-04-17 02:20:36 +04:00
err = os_pipe ( socket , 0 , 0 ) ;
2007-10-16 12:26:41 +04:00
if ( err < 0 )
2005-04-17 02:20:36 +04:00
goto out_close ;
data = ( ( struct port_pre_exec_data )
{ . sock_fd = new ,
. pipe_fd = socket [ 1 ] } ) ;
2007-07-16 10:38:56 +04:00
err = run_helper ( port_pre_exec , & data , argv ) ;
2007-10-16 12:26:41 +04:00
if ( err < 0 )
2005-04-17 02:20:36 +04:00
goto out_shutdown ;
* pid_out = err ;
2007-02-10 12:44:02 +03:00
return new ;
2005-04-17 02:20:36 +04:00
out_shutdown :
2007-10-16 12:26:41 +04:00
shutdown ( socket [ 0 ] , SHUT_RDWR ) ;
close ( socket [ 0 ] ) ;
shutdown ( socket [ 1 ] , SHUT_RDWR ) ;
close ( socket [ 1 ] ) ;
2005-04-17 02:20:36 +04:00
out_close :
2007-10-16 12:26:41 +04:00
close ( new ) ;
2007-02-10 12:44:02 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}