diff options
Diffstat (limited to 'env_universal.c')
-rw-r--r-- | env_universal.c | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/env_universal.c b/env_universal.c new file mode 100644 index 00000000..b10d307d --- /dev/null +++ b/env_universal.c @@ -0,0 +1,316 @@ +#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 <fcntl.h> +#include <term.h> +#include <signal.h> + +#include "util.h" +#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; + +wchar_t * path; +wchar_t *user; +void (*start_fishd)(); + +static int barrier_reply = 0; + +static void barrier(); + + +/** + 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 ); + len = strlen(local.sun_path) + sizeof(local.sun_family); + + if( connect( s, (struct sockaddr *)&local, len) == -1 ) + { + close( s ); + if( fork_ok ) + { + debug( 2, L"Could not connect to socket %d, starting fishd", s ); + if( start_fishd ) + start_fishd(); + return get_socket( 0 ); + } + + debug( 3, L"Could not connect to socket %d, already tried forking, giving up", s ); + 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; +} + +static void callback( int type, const wchar_t *name, const wchar_t *val ) +{ + if( type == BARRIER_REPLY ) + { + debug( 3, L"Got barrier reply" ); + + barrier_reply = 1; + } + +} + + +void env_universal_init( wchar_t * p, wchar_t *u, void (*sf)() ) +{ + debug( 2, L"env_universal_init()" ); + path=p; + user=u; + start_fishd=sf; + 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 ) + { + barrier(); + } + debug( 2, L"end env_universal_init()" ); +} + +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 ); + } + close( env_universal_server.fd ); + 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; + + if( env_universal_server.fd == -1 ) + { + + if( get_socket_count >= RECONNECT_COUNT ) + return 0; + + debug( 2, L"Get new fishd connection" ); + + init = 0; + env_universal_server.fd = get_socket(1); + init = 1; + + if( env_universal_server.fd >= 0 ) + barrier(); + } + + if( env_universal_server.fd != -1 ) + { + read_message( &env_universal_server ); + if( env_universal_server.killme ) + { + debug( 2, L"Lost connection to universal variable server." ); + close( env_universal_server.fd ); + env_universal_server.fd = -1; + env_universal_server.killme=0; + sb_clear( &env_universal_server.input ); + + env_universal_read_all(); + } + 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; + + debug( 2, L"env_universal_get( %ls )", name ); + barrier(); + + if( !name ) + return 0; + + return (wchar_t *)hash_get( &env_universal_var, name ); +} + +static void barrier() +{ + message_t *msg; + fd_set fds; + + barrier_reply = 0; + + msg= create_message( BARRIER, 0, 0); + msg->count=1; + q_put( &env_universal_server.unsent, msg ); + + debug( 3, L"Create barrier" ); + while( 1 ) + { + try_send_all( &env_universal_server ); + if( q_empty( &env_universal_server.unsent ) ) + break; + FD_ZERO( &fds ); + FD_SET( env_universal_server.fd, &fds ); + select( env_universal_server.fd+1, 0, &fds, 0, 0 ); + } + + debug( 3, L"Sent barrier request" ); + while( !barrier_reply ) + { + 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" ); + +} + + +void env_universal_set( const wchar_t *name, const wchar_t *value ) +{ + message_t *msg; + + if( !init ) + return; + + debug( 2, L"env_universal_set( %ls, %ls )", name, value ); + + msg= create_message( SET, name, value); + msg->count=1; + q_put( &env_universal_server.unsent, msg ); + barrier(); + + +} + +void env_universal_remove( const wchar_t *name ) +{ + message_t *msg; + if( !init ) + return; + + debug( 2, + L"env_universal_remove( %ls )", + name ); + + msg= create_message( ERASE, name, 0); + msg->count=1; + q_put( &env_universal_server.unsent, msg ); + barrier(); +} |