2005-09-20 23:26:39 +10:00
/**
\ file env_universal_common . c
The utility library for universal variables . Used both by the
client library and by the daemon .
*/
2006-01-31 02:58:00 +10:00
# include "config.h"
2006-02-28 23:17:16 +10:00
2005-09-20 23:26:39 +10:00
# include <stdlib.h>
# include <stdio.h>
# include <wchar.h>
# include <strings.h>
# include <unistd.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/un.h>
# include <pwd.h>
# include <errno.h>
# include <sys/stat.h>
# include <dirent.h>
# include <wctype.h>
# include <errno.h>
# include <locale.h>
# include <dirent.h>
# include <signal.h>
# include <sys/stat.h>
2006-02-28 23:17:16 +10:00
# include "fallback.h"
2005-09-20 23:26:39 +10:00
# include "util.h"
2006-02-28 23:17:16 +10:00
2005-09-20 23:26:39 +10:00
# include "common.h"
# include "wutil.h"
# include "env_universal_common.h"
/**
Non - wide version of the set command
*/
# define SET_MBS "SET"
2005-09-23 06:16:52 +10:00
/**
Non - wide version of the set_export command
*/
# define SET_EXPORT_MBS "SET_EXPORT"
2005-09-20 23:26:39 +10:00
/**
Non - wide version of the erase command
*/
# define ERASE_MBS "ERASE"
2005-09-23 06:16:52 +10:00
/**
Non - wide version of the barrier command
*/
2005-09-20 23:26:39 +10:00
# define BARRIER_MBS "BARRIER"
2005-09-23 06:16:52 +10:00
/**
Non - wide version of the barrier_reply command
*/
2005-09-20 23:26:39 +10:00
# define BARRIER_REPLY_MBS "BARRIER_REPLY"
/**
Error message
*/
# define PARSE_ERR L"Unable to parse universal variable message: '%ls'"
2005-09-23 06:16:52 +10:00
/**
A variable entry . Stores the value of a variable and whether it
should be exported . Obviously , it needs to be allocated large
enough to fit the value string .
*/
2005-10-25 01:26:25 +10:00
typedef struct var_uni_entry
2005-09-23 06:16:52 +10:00
{
int export ; /**< Whether the variable should be exported */
wchar_t val [ 0 ] ; /**< The value of the variable */
}
2005-10-25 01:26:25 +10:00
var_uni_entry_t ;
2005-09-23 06:16:52 +10:00
2005-09-21 09:42:00 +10:00
static void parse_message ( wchar_t * msg ,
connection_t * src ) ;
2005-09-20 23:26:39 +10:00
/**
The table of all universal variables
*/
hash_table_t env_universal_var ;
2005-10-25 01:26:25 +10:00
/**
Callback function , should be called on all events
*/
2005-09-21 09:42:00 +10:00
void ( * callback ) ( int type ,
const wchar_t * key ,
const wchar_t * val ) ;
2005-09-20 23:26:39 +10:00
2005-09-23 06:16:52 +10:00
/**
Variable used by env_get_names to communicate auxiliary information
to add_key_to_hash
*/
static int get_names_show_exported ;
/**
Variable used by env_get_names to communicate auxiliary information
to add_key_to_hash
*/
static int get_names_show_unexported ;
2005-09-20 23:26:39 +10:00
2005-09-21 09:42:00 +10:00
void env_universal_common_init ( void ( * cb ) ( int type , const wchar_t * key , const wchar_t * val ) )
2005-09-20 23:26:39 +10:00
{
2006-02-23 01:41:52 +10:00
debug ( 3 , L " Init env_universal_common " ) ;
2005-09-20 23:26:39 +10:00
callback = cb ;
hash_init ( & env_universal_var , & hash_wcs_func , & hash_wcs_cmp ) ;
}
2005-10-25 01:26:25 +10:00
/**
Free both key and data
*/
2005-09-20 23:26:39 +10:00
static void erase ( const void * key ,
const void * data )
{
free ( ( void * ) key ) ;
free ( ( void * ) data ) ;
}
void env_universal_common_destroy ( )
{
hash_foreach ( & env_universal_var , & erase ) ;
hash_destroy ( & env_universal_var ) ;
}
void read_message ( connection_t * src )
{
while ( 1 )
{
char b ;
int read_res = read ( src - > fd , & b , 1 ) ;
wchar_t res = 0 ;
if ( read_res < 0 )
{
if ( errno ! = EAGAIN & &
errno ! = EINTR )
{
debug ( 2 , L " Read error on fd %d, set killme flag " , src - > fd ) ;
wperror ( L " read " ) ;
src - > killme = 1 ;
}
return ;
}
if ( read_res = = 0 )
{
src - > killme = 1 ;
debug ( 3 , L " Fd %d has reached eof, set killme flag " , src - > fd ) ;
if ( src - > input . used > 0 )
{
debug ( 1 ,
2005-09-21 09:42:00 +10:00
L " Universal variable connection closed while reading command. Partial command recieved: '%ls' " ,
( wchar_t * ) src - > input . buff ) ;
2005-09-20 23:26:39 +10:00
}
return ;
}
int sz = mbrtowc ( & res , & b , 1 , & src - > wstate ) ;
if ( sz = = - 1 )
{
debug ( 1 , L " Error while reading universal variable after '%ls' " , ( wchar_t * ) src - > input . buff ) ;
wperror ( L " mbrtowc " ) ;
}
else if ( sz > 0 )
{
if ( res = = L ' \n ' )
{
2005-10-12 05:23:43 +10:00
/*
Before calling parse_message , we must empty reset
everything , since the callback function could
potentially call read_message .
*/
wchar_t * msg = wcsdup ( ( wchar_t * ) src - > input . buff ) ;
2005-09-20 23:26:39 +10:00
sb_clear ( & src - > input ) ;
2005-10-12 05:23:43 +10:00
memset ( & src - > wstate , ' \0 ' , sizeof ( mbstate_t ) ) ;
parse_message ( msg , src ) ;
free ( msg ) ;
2005-09-20 23:26:39 +10:00
}
else
{
2006-02-05 23:13:35 +10:00
sb_append_char ( & src - > input , res ) ;
2005-09-20 23:26:39 +10:00
}
}
}
}
2005-10-25 01:26:25 +10:00
/**
Remove variable with specified name
*/
2005-09-20 23:26:39 +10:00
static void remove_entry ( wchar_t * name )
{
void * k , * v ;
hash_remove ( & env_universal_var ,
name ,
( const void * * ) & k ,
( const void * * ) & v ) ;
free ( k ) ;
free ( v ) ;
}
2005-10-25 01:26:25 +10:00
/**
Test if the message msg contains the command cmd
*/
2005-09-20 23:26:39 +10:00
static int match ( const wchar_t * msg , const wchar_t * cmd )
{
size_t len = wcslen ( cmd ) ;
if ( wcsncasecmp ( msg , cmd , len ) ! = 0 )
return 0 ;
2005-09-23 06:16:52 +10:00
2005-09-20 23:26:39 +10:00
if ( msg [ len ] & & msg [ len ] ! = L ' ' & & msg [ len ] ! = L ' \t ' )
return 0 ;
return 1 ;
}
2005-10-25 01:26:25 +10:00
/**
Parse message msg
*/
2005-09-21 09:42:00 +10:00
static void parse_message ( wchar_t * msg ,
connection_t * src )
2005-09-20 23:26:39 +10:00
{
2006-02-23 01:41:52 +10:00
debug ( 3 , L " parse_message( %ls ); " , msg ) ;
2005-10-12 05:23:43 +10:00
2005-09-20 23:26:39 +10:00
if ( msg [ 0 ] = = L ' # ' )
return ;
2005-09-23 06:16:52 +10:00
if ( match ( msg , SET_STR ) | | match ( msg , SET_EXPORT_STR ) )
2005-09-20 23:26:39 +10:00
{
wchar_t * name , * val , * tmp ;
2005-09-23 06:16:52 +10:00
int export = match ( msg , SET_EXPORT_STR ) ;
name = msg + ( export ? wcslen ( SET_EXPORT_STR ) : wcslen ( SET_STR ) ) ;
2005-09-20 23:26:39 +10:00
while ( wcschr ( L " \t " , * name ) )
name + + ;
tmp = wcschr ( name , L ' : ' ) ;
if ( tmp )
{
wchar_t * key = malloc ( sizeof ( wchar_t ) * ( tmp - name + 1 ) ) ;
memcpy ( key , name , sizeof ( wchar_t ) * ( tmp - name ) ) ;
key [ tmp - name ] = 0 ;
val = tmp + 1 ;
2005-10-07 20:36:51 +10:00
val = unescape ( val , 0 ) ;
2005-09-20 23:26:39 +10:00
2005-10-25 01:26:25 +10:00
var_uni_entry_t * entry =
malloc ( sizeof ( var_uni_entry_t ) + sizeof ( wchar_t ) * ( wcslen ( val ) + 1 ) ) ;
2005-09-23 06:16:52 +10:00
if ( ! entry )
die_mem ( ) ;
entry - > export = export ;
wcscpy ( entry - > val , val ) ;
2005-09-20 23:26:39 +10:00
remove_entry ( key ) ;
2005-09-23 06:16:52 +10:00
hash_put ( & env_universal_var , key , entry ) ;
2005-09-20 23:26:39 +10:00
if ( callback )
{
2005-09-23 06:16:52 +10:00
callback ( export ? SET_EXPORT : SET , key , val ) ;
2005-09-20 23:26:39 +10:00
}
2005-09-23 06:16:52 +10:00
free ( val ) ;
2005-09-20 23:26:39 +10:00
}
else
{
debug ( 1 , PARSE_ERR , msg ) ;
}
}
else if ( match ( msg , ERASE_STR ) )
{
2005-09-23 06:16:52 +10:00
wchar_t * name , * tmp ;
2005-09-20 23:26:39 +10:00
name = msg + wcslen ( ERASE_STR ) ;
while ( wcschr ( L " \t " , * name ) )
name + + ;
tmp = name ;
while ( iswalnum ( * tmp ) | | * tmp = = L ' _ ' )
tmp + + ;
* tmp = 0 ;
if ( ! wcslen ( name ) )
{
debug ( 1 , PARSE_ERR , msg ) ;
}
remove_entry ( name ) ;
if ( callback )
{
callback ( ERASE , name , 0 ) ;
}
}
else if ( match ( msg , BARRIER_STR ) )
{
message_t * msg = create_message ( BARRIER_REPLY , 0 , 0 ) ;
msg - > count = 1 ;
q_put ( & src - > unsent , msg ) ;
try_send_all ( src ) ;
}
else if ( match ( msg , BARRIER_REPLY_STR ) )
{
if ( callback )
{
callback ( BARRIER_REPLY , 0 , 0 ) ;
}
}
else
{
debug ( 1 , PARSE_ERR , msg ) ;
}
}
2005-10-25 01:26:25 +10:00
/**
Attempt to send the specified message to the specified file descriptor
\ return 1 on sucess , 0 if the message could not be sent without blocking and - 1 on error
*/
static int try_send ( message_t * msg ,
int fd )
2005-09-20 23:26:39 +10:00
{
2005-09-23 06:16:52 +10:00
debug ( 3 ,
L " before write of %d chars to fd %d " , strlen ( msg - > body ) , fd ) ;
2005-09-20 23:26:39 +10:00
int res = write ( fd , msg - > body , strlen ( msg - > body ) ) ;
2005-09-23 06:16:52 +10:00
2005-09-20 23:26:39 +10:00
if ( res = = - 1 )
{
switch ( errno )
{
case EAGAIN :
return 0 ;
default :
debug ( 1 ,
2005-09-23 23:10:31 +10:00
L " Error while sending universal variable message to fd %d. Closing connection " ,
2005-09-20 23:26:39 +10:00
fd ) ;
wperror ( L " write " ) ;
return - 1 ;
}
}
msg - > count - - ;
if ( ! msg - > count )
{
free ( msg ) ;
}
return 1 ;
}
void try_send_all ( connection_t * c )
{
2005-09-23 06:16:52 +10:00
debug ( 3 ,
2005-09-20 23:26:39 +10:00
L " Send all updates to connection on fd %d " ,
c - > fd ) ;
while ( ! q_empty ( & c - > unsent ) )
{
switch ( try_send ( ( message_t * ) q_peek ( & c - > unsent ) , c - > fd ) )
{
case 1 :
q_get ( & c - > unsent ) ;
break ;
case 0 :
2005-09-23 06:16:52 +10:00
debug ( 1 ,
L " Socket full, send rest later " ) ;
2005-09-20 23:26:39 +10:00
return ;
case - 1 :
c - > killme = 1 ;
return ;
}
}
}
message_t * create_message ( int type ,
const wchar_t * key_in ,
const wchar_t * val_in )
{
message_t * msg = 0 ;
char * key = 0 ;
size_t sz ;
if ( key_in )
{
key = wcs2str ( key_in ) ;
if ( ! key )
{
debug ( 0 ,
L " Could not convert %ls to narrow character string " ,
key_in ) ;
return 0 ;
}
}
switch ( type )
{
case SET :
2005-09-23 06:16:52 +10:00
case SET_EXPORT :
2005-09-20 23:26:39 +10:00
{
if ( ! val_in )
{
val_in = L " " ;
}
2005-10-07 20:36:51 +10:00
wchar_t * esc = escape ( val_in , 1 ) ;
2005-09-20 23:26:39 +10:00
if ( ! esc )
break ;
char * val = wcs2str ( esc ) ;
free ( esc ) ;
2005-09-23 06:16:52 +10:00
sz = strlen ( type = = SET ? SET_MBS : SET_EXPORT_MBS ) + strlen ( key ) + strlen ( val ) + 4 ;
2005-09-20 23:26:39 +10:00
msg = malloc ( sizeof ( message_t ) + sz ) ;
2005-09-23 06:16:52 +10:00
2005-09-20 23:26:39 +10:00
if ( ! msg )
die_mem ( ) ;
2005-09-23 06:16:52 +10:00
strcpy ( msg - > body , ( type = = SET ? SET_MBS : SET_EXPORT_MBS ) ) ;
strcat ( msg - > body , " " ) ;
2005-09-20 23:26:39 +10:00
strcat ( msg - > body , key ) ;
strcat ( msg - > body , " : " ) ;
strcat ( msg - > body , val ) ;
strcat ( msg - > body , " \n " ) ;
2005-09-23 06:16:52 +10:00
2005-09-20 23:26:39 +10:00
free ( val ) ;
break ;
}
case ERASE :
{
sz = strlen ( ERASE_MBS ) + strlen ( key ) + 3 ;
msg = malloc ( sizeof ( message_t ) + sz ) ;
if ( ! msg )
die_mem ( ) ;
strcpy ( msg - > body , ERASE_MBS " " ) ;
strcat ( msg - > body , key ) ;
strcat ( msg - > body , " \n " ) ;
break ;
}
case BARRIER :
{
msg = malloc ( sizeof ( message_t ) +
strlen ( BARRIER_MBS ) + 2 ) ;
if ( ! msg )
die_mem ( ) ;
strcpy ( msg - > body , BARRIER_MBS " \n " ) ;
break ;
}
case BARRIER_REPLY :
{
msg = malloc ( sizeof ( message_t ) +
strlen ( BARRIER_REPLY_MBS ) + 2 ) ;
if ( ! msg )
die_mem ( ) ;
strcpy ( msg - > body , BARRIER_REPLY_MBS " \n " ) ;
break ;
}
default :
{
debug ( 0 , L " create_message: Unknown message type " ) ;
}
}
free ( key ) ;
if ( msg )
msg - > count = 0 ;
return msg ;
}
2005-09-23 06:16:52 +10:00
/**
Function used with hash_foreach to insert keys of one table into
another
*/
static void add_key_to_hash ( const void * key ,
const void * data ,
void * aux )
{
2005-10-25 01:26:25 +10:00
var_uni_entry_t * e = ( var_uni_entry_t * ) data ;
2005-09-23 06:16:52 +10:00
if ( ( e - > export & & get_names_show_exported ) | |
( ! e - > export & & get_names_show_unexported ) )
al_push ( ( array_list_t * ) aux , key ) ;
}
void env_universal_common_get_names ( array_list_t * l ,
int show_exported ,
int show_unexported )
{
get_names_show_exported = show_exported ;
get_names_show_unexported = show_unexported ;
hash_foreach2 ( & env_universal_var ,
add_key_to_hash ,
l ) ;
}
wchar_t * env_universal_common_get ( const wchar_t * name )
{
2005-10-25 01:26:25 +10:00
var_uni_entry_t * e = ( var_uni_entry_t * ) hash_get ( & env_universal_var , name ) ;
2005-09-23 06:16:52 +10:00
if ( e )
return e - > val ;
return 0 ;
}
int env_universal_common_get_export ( const wchar_t * name )
{
2005-10-25 01:26:25 +10:00
var_uni_entry_t * e = ( var_uni_entry_t * ) hash_get ( & env_universal_var , name ) ;
2005-09-23 06:16:52 +10:00
if ( e )
return e - > export ;
return 0 ;
}
2005-10-25 01:26:25 +10:00
/**
Adds a variable creation message about the specified variable to
the specified queue . The function signature is non - obvious since
this function is used together with hash_foreach2 , which requires
the specified function signature .
\ param k the variable name
\ param v the variable value
\ param q the queue to add the message to
*/
2005-09-23 06:16:52 +10:00
static void enqueue ( const void * k ,
const void * v ,
void * q )
{
const wchar_t * key = ( const wchar_t * ) k ;
2005-10-25 01:26:25 +10:00
const var_uni_entry_t * val = ( const var_uni_entry_t * ) v ;
2005-10-11 02:12:55 +10:00
dyn_queue_t * queue = ( dyn_queue_t * ) q ;
2005-09-23 06:16:52 +10:00
message_t * msg = create_message ( val - > export ? SET_EXPORT : SET , key , val - > val ) ;
msg - > count = 1 ;
q_put ( queue , msg ) ;
}
void enqueue_all ( connection_t * c )
{
hash_foreach2 ( & env_universal_var ,
& enqueue ,
( void * ) & c - > unsent ) ;
try_send_all ( c ) ;
}