2005-10-17 23:36:57 +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>
2006-08-10 08:34:52 +10:00
# include <string.h>
2005-09-20 23:26:39 +10:00
# include <unistd.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/un.h>
# include <pwd.h>
# include <errno.h>
# include <fcntl.h>
2006-01-19 22:22:07 +10:00
2005-10-11 02:12:55 +10:00
# if HAVE_NCURSES_H
# include <ncurses.h>
# else
# include <curses.h>
# endif
2006-01-19 22:22:07 +10:00
# if HAVE_TERM_H
2005-09-20 23:26:39 +10:00
# include <term.h>
2006-01-19 22:22:07 +10:00
# elif HAVE_NCURSES_TERM_H
# include <ncurses/term.h>
# endif
2006-08-10 08:26:05 +10:00
# ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
# endif
2005-09-20 23:26:39 +10:00
# include <signal.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"
# include "env_universal.h"
/**
Maximum number of times to try to get a new fishd socket
*/
# define RECONNECT_COUNT 32
connection_t env_universal_server ;
/**
Set to 1 after initialization has been performed
*/
static int init = 0 ;
/**
The number of attempts to start fishd
*/
static int get_socket_count = 0 ;
2005-10-03 23:24:46 +10:00
static wchar_t * path ;
static wchar_t * user ;
static void ( * start_fishd ) ( ) ;
2005-10-12 05:23:43 +10:00
static void ( * external_callback ) ( int type , const wchar_t * name , const wchar_t * val ) ;
2005-09-23 06:16:52 +10:00
2005-10-03 23:24:46 +10:00
/**
Flag set to 1 when a barrier reply is recieved
*/
2005-09-20 23:26:39 +10:00
static int barrier_reply = 0 ;
2005-09-23 06:16:52 +10:00
void env_universal_barrier ( ) ;
2005-09-20 23:26:39 +10:00
/**
Get a socket for reading from the server
*/
static int get_socket ( int fork_ok )
{
int s , len ;
struct sockaddr_un local ;
char * name ;
wchar_t * wdir ;
wchar_t * wuname ;
char * dir = 0 , * uname = 0 ;
get_socket_count + + ;
wdir = path ;
wuname = user ;
if ( ( s = socket ( AF_UNIX , SOCK_STREAM , 0 ) ) = = - 1 )
{
wperror ( L " socket " ) ;
return - 1 ;
}
if ( wdir )
dir = wcs2str ( wdir ) ;
else
dir = strdup ( " /tmp " ) ;
if ( wuname )
uname = wcs2str ( wuname ) ;
else
{
struct passwd * pw ;
pw = getpwuid ( getuid ( ) ) ;
uname = strdup ( pw - > pw_name ) ;
}
name = malloc ( strlen ( dir ) +
strlen ( uname ) +
strlen ( SOCK_FILENAME ) +
2 ) ;
strcpy ( name , dir ) ;
strcat ( name , " / " ) ;
strcat ( name , SOCK_FILENAME ) ;
strcat ( name , uname ) ;
free ( dir ) ;
free ( uname ) ;
debug ( 3 , L " Connect to socket %s at fd %2 " , name , s ) ;
local . sun_family = AF_UNIX ;
strcpy ( local . sun_path , name ) ;
free ( name ) ;
2006-01-10 00:44:18 +10:00
len = sizeof ( local ) ;
2005-09-20 23:26:39 +10:00
if ( connect ( s , ( struct sockaddr * ) & local , len ) = = - 1 )
{
close ( s ) ;
2005-11-28 09:22:08 +10:00
if ( fork_ok & & start_fishd )
2005-09-20 23:26:39 +10:00
{
debug ( 2 , L " Could not connect to socket %d, starting fishd " , s ) ;
2005-09-23 06:16:52 +10:00
2005-11-28 09:22:08 +10:00
start_fishd ( ) ;
2005-09-20 23:26:39 +10:00
return get_socket ( 0 ) ;
}
2005-11-28 09:22:08 +10:00
debug ( 2 , L " Could not connect to socket %d, already tried manual restart (or no command supplied), giving up " , s ) ;
2005-09-20 23:26:39 +10:00
return - 1 ;
}
if ( fcntl ( s , F_SETFL , O_NONBLOCK ) ! = 0 )
{
wperror ( L " fcntl " ) ;
close ( s ) ;
return - 1 ;
}
debug ( 3 , L " Connected to fd %d " , s ) ;
return s ;
}
2005-10-03 23:24:46 +10:00
/**
Callback function used whenever a new fishd message is recieved
*/
2005-09-20 23:26:39 +10:00
static void callback ( int type , const wchar_t * name , const wchar_t * val )
2005-10-12 05:23:43 +10:00
{
2005-09-20 23:26:39 +10:00
if ( type = = BARRIER_REPLY )
{
debug ( 3 , L " Got barrier reply " ) ;
barrier_reply = 1 ;
}
2005-09-23 06:16:52 +10:00
else
{
2005-10-12 05:23:43 +10:00
if ( external_callback )
external_callback ( type , name , val ) ;
2005-09-23 06:16:52 +10:00
}
}
2005-10-03 23:24:46 +10:00
/**
Make sure the connection is healthy . If not , close it , and try to
establish a new connection .
*/
2005-09-23 06:16:52 +10:00
static void check_connection ( )
{
if ( ! init )
return ;
2005-09-20 23:26:39 +10:00
2005-09-23 06:16:52 +10:00
if ( env_universal_server . killme )
{
2005-11-29 20:13:03 +10:00
debug ( 3 , L " Lost connection to universal variable server. " ) ;
2006-06-21 19:54:30 +10:00
if ( close ( env_universal_server . fd ) )
{
wperror ( L " close " ) ;
}
2005-09-23 06:16:52 +10:00
env_universal_server . fd = - 1 ;
env_universal_server . killme = 0 ;
sb_clear ( & env_universal_server . input ) ;
env_universal_read_all ( ) ;
}
2005-09-20 23:26:39 +10:00
}
2005-10-03 23:24:46 +10:00
/**
Try to establish a new connection to fishd . If successfull , end
with call to env_universal_barrier ( ) , to make sure everything is in
sync .
*/
2005-10-03 23:09:37 +10:00
static void reconnect ( )
{
if ( get_socket_count > = RECONNECT_COUNT )
2005-10-03 23:24:46 +10:00
return ;
2005-10-03 23:09:37 +10:00
2005-11-29 20:13:03 +10:00
debug ( 3 , L " Get new fishd connection " ) ;
2005-10-03 23:09:37 +10:00
init = 0 ;
env_universal_server . fd = get_socket ( 1 ) ;
init = 1 ;
if ( env_universal_server . fd > = 0 )
{
env_universal_barrier ( ) ;
}
}
2005-09-20 23:26:39 +10:00
2005-09-23 06:16:52 +10:00
2005-10-12 05:23:43 +10:00
void env_universal_init ( wchar_t * p ,
wchar_t * u ,
void ( * sf ) ( ) ,
void ( * cb ) ( int type , const wchar_t * name , const wchar_t * val ) )
2005-09-20 23:26:39 +10:00
{
2006-02-23 01:41:52 +10:00
debug ( 3 , L " env_universal_init() " ) ;
2005-09-20 23:26:39 +10:00
path = p ;
user = u ;
start_fishd = sf ;
2005-10-12 05:23:43 +10:00
external_callback = cb ;
2005-09-20 23:26:39 +10:00
env_universal_server . fd = - 1 ;
env_universal_server . killme = 0 ;
env_universal_server . fd = get_socket ( 1 ) ;
memset ( & env_universal_server . wstate , ' \0 ' , sizeof ( mbstate_t ) ) ;
q_init ( & env_universal_server . unsent ) ;
env_universal_common_init ( & callback ) ;
sb_init ( & env_universal_server . input ) ;
env_universal_read_all ( ) ;
init = 1 ;
if ( env_universal_server . fd > = 0 )
{
2005-09-23 06:16:52 +10:00
env_universal_barrier ( ) ;
2005-09-20 23:26:39 +10:00
}
2006-02-23 01:41:52 +10:00
debug ( 3 , L " end env_universal_init() " ) ;
2005-09-20 23:26:39 +10:00
}
void env_universal_destroy ( )
{
/*
Go into blocking mode and send all data before exiting
*/
if ( env_universal_server . fd > = 0 )
{
if ( fcntl ( env_universal_server . fd , F_SETFL , 0 ) ! = 0 )
{
wperror ( L " fcntl " ) ;
}
try_send_all ( & env_universal_server ) ;
2006-07-19 03:27:56 +10:00
if ( close ( env_universal_server . fd ) )
{
wperror ( L " close " ) ;
}
2006-06-21 19:54:30 +10:00
}
2005-09-20 23:26:39 +10:00
env_universal_server . fd = - 1 ;
q_destroy ( & env_universal_server . unsent ) ;
sb_destroy ( & env_universal_server . input ) ;
env_universal_common_destroy ( ) ;
init = 0 ;
}
/**
Read all available messages from the server .
*/
int env_universal_read_all ( )
{
if ( ! init )
return 0 ;
2005-11-29 20:13:03 +10:00
debug ( 3 , L " env_universal_read_all() " ) ;
2005-09-20 23:26:39 +10:00
if ( env_universal_server . fd = = - 1 )
{
2005-10-03 23:09:37 +10:00
reconnect ( ) ;
2005-10-03 23:24:46 +10:00
if ( env_universal_server . fd = = - 1 )
return 0 ;
2005-09-20 23:26:39 +10:00
}
if ( env_universal_server . fd ! = - 1 )
{
read_message ( & env_universal_server ) ;
2005-09-23 06:16:52 +10:00
check_connection ( ) ;
2005-09-20 23:26:39 +10:00
return 1 ;
}
else
{
debug ( 2 , L " No connection to universal variable server " ) ;
return 0 ;
}
}
wchar_t * env_universal_get ( const wchar_t * name )
{
if ( ! init )
return 0 ;
2005-09-24 09:15:38 +10:00
2006-06-21 20:07:46 +10:00
CHECK ( name , 0 ) ;
2005-11-29 20:13:03 +10:00
debug ( 3 , L " env_universal_get( \" %ls \" ) " , name ) ;
2005-09-23 06:16:52 +10:00
return env_universal_common_get ( name ) ;
2005-09-20 23:26:39 +10:00
}
2005-09-23 06:16:52 +10:00
int env_universal_get_export ( const wchar_t * name )
{
2006-06-21 20:07:46 +10:00
if ( ! init )
return 0 ;
CHECK ( name , 0 ) ;
2005-11-29 20:13:03 +10:00
debug ( 3 , L " env_universal_get_export() " ) ;
2005-09-23 06:16:52 +10:00
return env_universal_common_get_export ( name ) ;
}
void env_universal_barrier ( )
2005-09-20 23:26:39 +10:00
{
message_t * msg ;
fd_set fds ;
2005-09-23 06:16:52 +10:00
2005-09-27 18:35:07 +10:00
if ( ! init | | ( env_universal_server . fd = = - 1 ) )
2005-09-23 06:16:52 +10:00
return ;
2005-11-28 09:22:08 +10:00
2005-09-20 23:26:39 +10:00
barrier_reply = 0 ;
2005-09-23 06:16:52 +10:00
/*
Create barrier request
*/
2005-09-20 23:26:39 +10:00
msg = create_message ( BARRIER , 0 , 0 ) ;
msg - > count = 1 ;
q_put ( & env_universal_server . unsent , msg ) ;
2005-09-23 06:16:52 +10:00
/*
Wait until barrier request has been sent
*/
2005-09-20 23:26:39 +10:00
debug ( 3 , L " Create barrier " ) ;
while ( 1 )
{
try_send_all ( & env_universal_server ) ;
2005-09-23 06:16:52 +10:00
check_connection ( ) ;
2005-09-20 23:26:39 +10:00
if ( q_empty ( & env_universal_server . unsent ) )
break ;
2005-10-03 23:09:37 +10:00
if ( env_universal_server . fd = = - 1 )
{
reconnect ( ) ;
2005-11-28 09:22:08 +10:00
debug ( 2 , L " barrier interrupted, exiting " ) ;
2005-10-03 23:09:37 +10:00
return ;
}
2005-09-20 23:26:39 +10:00
FD_ZERO ( & fds ) ;
FD_SET ( env_universal_server . fd , & fds ) ;
select ( env_universal_server . fd + 1 , 0 , & fds , 0 , 0 ) ;
}
2005-09-23 06:16:52 +10:00
/*
Wait for barrier reply
*/
2005-09-20 23:26:39 +10:00
debug ( 3 , L " Sent barrier request " ) ;
while ( ! barrier_reply )
{
2005-10-03 23:09:37 +10:00
if ( env_universal_server . fd = = - 1 )
{
reconnect ( ) ;
2005-11-28 09:22:08 +10:00
debug ( 2 , L " barrier interrupted, exiting (2) " ) ;
2005-10-03 23:09:37 +10:00
return ;
}
2005-09-20 23:26:39 +10:00
FD_ZERO ( & fds ) ;
FD_SET ( env_universal_server . fd , & fds ) ;
select ( env_universal_server . fd + 1 , & fds , 0 , 0 , 0 ) ;
env_universal_read_all ( ) ;
}
debug ( 3 , L " End barrier " ) ;
}
2005-09-23 06:16:52 +10:00
void env_universal_set ( const wchar_t * name , const wchar_t * value , int export )
2005-09-20 23:26:39 +10:00
{
message_t * msg ;
if ( ! init )
return ;
2006-06-21 20:07:46 +10:00
CHECK ( name , ) ;
2005-11-28 09:22:08 +10:00
debug ( 3 , L " env_universal_set( \" %ls \" , \" %ls \" ) " , name , value ) ;
2005-09-23 06:16:52 +10:00
msg = create_message ( export ? SET_EXPORT : SET ,
name ,
value ) ;
2005-09-20 23:26:39 +10:00
2005-09-23 06:16:52 +10:00
if ( ! msg )
{
debug ( 1 , L " Could not create universal variable message " ) ;
return ;
}
2005-09-20 23:26:39 +10:00
msg - > count = 1 ;
q_put ( & env_universal_server . unsent , msg ) ;
2005-09-23 06:16:52 +10:00
env_universal_barrier ( ) ;
2005-09-20 23:26:39 +10:00
}
2006-06-05 06:14:51 +10:00
int env_universal_remove ( const wchar_t * name )
2005-09-20 23:26:39 +10:00
{
2006-06-05 06:14:51 +10:00
int res ;
2005-09-20 23:26:39 +10:00
message_t * msg ;
if ( ! init )
2006-06-05 06:14:51 +10:00
return 1 ;
2006-06-21 20:07:46 +10:00
CHECK ( name , 1 ) ;
2006-06-05 06:14:51 +10:00
res = ! env_universal_common_get ( name ) ;
2005-09-20 23:26:39 +10:00
2005-11-28 09:22:08 +10:00
debug ( 3 ,
L " env_universal_remove( \" %ls \" ) " ,
2005-09-20 23:26:39 +10:00
name ) ;
msg = create_message ( ERASE , name , 0 ) ;
msg - > count = 1 ;
q_put ( & env_universal_server . unsent , msg ) ;
2005-09-23 06:16:52 +10:00
env_universal_barrier ( ) ;
2006-06-05 06:14:51 +10:00
return res ;
2005-09-23 06:16:52 +10:00
}
void env_universal_get_names ( array_list_t * l ,
int show_exported ,
int show_unexported )
{
2006-06-21 20:07:46 +10:00
if ( ! init )
return ;
2005-09-23 06:16:52 +10:00
env_universal_common_get_names ( l ,
show_exported ,
show_unexported ) ;
2005-09-20 23:26:39 +10:00
}