2005-09-20 21:26:39 +08:00
/** \file fishd.c
The universal variable server . fishd is automatically started by fish
if a fishd server isn ' t already running . fishd reads any saved
2006-01-25 01:20:31 +08:00
variables from ~ / . fishd , and takes care of communication between fish
2005-09-20 21:26:39 +08:00
instances . When no clients are running , fishd will automatically shut
down and save .
2006-06-20 08:50:10 +08:00
\ section fishd - commands Commands
2006-01-25 01:20:31 +08:00
Fishd works by sending and receiving commands . Each command is ended
with a newline . These are the commands supported by fishd :
< pre > set KEY : VALUE
set_export KEY : VALUE
< / pre >
These commands update the value of a variable . The only difference
between the two is that < tt > set_export < / tt > - variables should be
2006-10-18 05:11:29 +08:00
exported to children of the process using them . When sending messages ,
all values below 32 or above 127 must be escaped using C - style
backslash escapes . This means that the over the wire protocol is
ASCII . However , any conforming reader must also accept non - ascii
characters and interpret them as UTF - 8. Lines containing invalid UTF - 8
escape sequences must be ignored entirely .
2006-01-25 01:20:31 +08:00
< pre > erase KEY
< / pre >
Erase the variable with the specified name .
< pre > barrier
barrier_reply
< / pre >
A \ c barrier command will result in a barrier_reply being added to
the end of the senders queue of unsent messages . These commands are
used to synchronize clients , since once the reply for a barrier
message returns , the sender can know that any updates available at the
time the original barrier request was sent have been received .
2005-09-20 21:26:39 +08:00
*/
2006-01-24 00:25:34 +08:00
# include "config.h"
2006-02-28 21:17:16 +08:00
2005-09-20 21:26:39 +08:00
# include <stdio.h>
# include <stdlib.h>
# include <wchar.h>
# include <unistd.h>
# include <errno.h>
# include <string.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/un.h>
# include <pwd.h>
# include <fcntl.h>
2006-01-24 00:25:34 +08:00
# ifdef HAVE_GETOPT_H
# include <getopt.h>
# endif
2005-09-20 21:26:39 +08:00
# include <errno.h>
# include <locale.h>
# include <signal.h>
2006-02-28 21:17:16 +08:00
# include "fallback.h"
2005-09-20 21:26:39 +08:00
# include "util.h"
2006-02-28 21:17:16 +08:00
2005-09-20 21:26:39 +08:00
# include "common.h"
# include "wutil.h"
# include "env_universal_common.h"
2006-10-19 19:50:23 +08:00
# include "path.h"
2006-11-18 00:24:38 +08:00
# include "print_help.h"
2005-09-20 21:26:39 +08:00
2012-03-02 16:27:40 +08:00
# ifndef HOST_NAME_MAX
/**
Maximum length of hostname return . It is ok if this is too short ,
getting the actual hostname is not critical , so long as the string
is unique in the filesystem namespace .
*/
# define HOST_NAME_MAX 255
# endif
2005-09-23 21:10:31 +08:00
/**
Maximum length of socket filename
*/
2012-11-18 18:23:22 +08:00
# ifndef UNIX_PATH_MAX
2005-09-20 21:26:39 +08:00
# define UNIX_PATH_MAX 100
# endif
2006-01-26 17:57:06 +08:00
/**
Fallback if MSG_DONTWAIT isn ' t defined . That ' s actually prerry bad ,
and may lead to strange fishd behaviour , but at least it should
work most of the time .
*/
# ifndef MSG_DONTWAIT
# define MSG_DONTWAIT 0
# endif
2005-09-23 21:10:31 +08:00
/**
Small greeting to show that fishd is running
*/
2006-06-17 22:04:06 +08:00
# define GREETING "# Fish universal variable daemon\n"
2006-06-20 08:50:10 +08:00
/**
Small not about not editing ~ / . fishd manually . Inserted at the top of all . fishd files .
*/
2006-06-17 22:04:06 +08:00
# define SAVE_MSG "# This file is automatically generated by the fishd universal variable daemon.\n# Do NOT edit it directly, your changes will be overwritten.\n"
2005-09-20 21:26:39 +08:00
2005-09-23 21:10:31 +08:00
/**
The name of the save file . The hostname is appended to this .
*/
2006-10-19 19:50:23 +08:00
# define FILE "fishd."
2005-09-20 21:26:39 +08:00
2005-09-23 21:10:31 +08:00
/**
Maximum length of hostname . Longer hostnames are truncated
*/
# define HOSTNAME_LEN 32
2005-09-28 09:43:09 +08:00
/**
The string to append to the socket name to name the lockfile
*/
# define LOCKPOSTFIX ".lock"
/**
The timeout in seconds on the lockfile for critical section
*/
# define LOCKTIMEOUT 1
2006-05-26 22:55:51 +08:00
/**
Getopt short switches for fishd
*/
# define GETOPT_STRING "hv"
2005-09-23 21:10:31 +08:00
/**
The list of connections to clients
*/
2005-09-20 21:26:39 +08:00
static connection_t * conn ;
2005-09-23 21:10:31 +08:00
/**
The socket to accept new clients on
*/
static int sock ;
2005-09-20 21:26:39 +08:00
2005-11-28 07:24:09 +08:00
/**
Set to one when fishd should save and exit
*/
static int quit = 0 ;
2005-09-23 21:10:31 +08:00
/**
2005-09-28 09:43:09 +08:00
Constructs the fish socket filename
2005-09-23 21:10:31 +08:00
*/
2005-10-01 03:50:21 +08:00
static char * get_socket_filename ( )
2005-09-20 21:26:39 +08:00
{
2012-11-19 08:30:30 +08:00
char * name ;
const char * dir = getenv ( " FISHD_SOCKET_DIR " ) ;
char * uname = getenv ( " USER " ) ;
if ( dir = = NULL )
{
dir = " /tmp " ;
}
if ( uname = = NULL )
{
struct passwd * pw ;
pw = getpwuid ( getuid ( ) ) ;
uname = strdup ( pw - > pw_name ) ;
}
name = ( char * ) malloc ( strlen ( dir ) + strlen ( uname ) + strlen ( SOCK_FILENAME ) + 2 ) ;
if ( name = = NULL )
{
wperror ( L " get_socket_filename " ) ;
exit ( EXIT_FAILURE ) ;
}
strcpy ( name , dir ) ;
strcat ( name , " / " ) ;
strcat ( name , SOCK_FILENAME ) ;
strcat ( name , uname ) ;
if ( strlen ( name ) > = UNIX_PATH_MAX )
{
debug ( 1 , L " Filename too long: '%s' " , name ) ;
exit ( EXIT_FAILURE ) ;
}
return name ;
2005-09-28 09:43:09 +08:00
}
2005-11-28 07:24:09 +08:00
/**
2012-11-18 18:23:22 +08:00
Signal handler for the term signal .
2005-11-28 07:24:09 +08:00
*/
2012-11-19 08:30:30 +08:00
static void handle_term ( int signal )
2005-11-28 07:24:09 +08:00
{
2012-11-19 08:30:30 +08:00
quit = 1 ;
2005-11-28 07:24:09 +08:00
}
2012-03-02 16:27:40 +08:00
/**
Writes a pseudo - random number ( between one and maxlen ) of pseudo - random
digits into str .
str must point to an allocated buffer of size of at least maxlen chars .
Returns the number of digits written .
Since the randomness in part depends on machine time it has _some_ extra
strength but still not enough for use in concurrent locking schemes on a
single machine because gettimeofday may not return a different value on
consecutive calls when :
a ) the OS does not support fine enough resolution
b ) the OS is running on an SMP machine .
Additionally , gettimeofday errors are ignored .
Excludes chars other than digits since ANSI C only guarantees that digits
are consecutive .
*/
2012-11-19 08:30:30 +08:00
static void sprint_rand_digits ( char * str , int maxlen )
2012-03-02 16:27:40 +08:00
{
2012-11-19 08:30:30 +08:00
int i , max ;
struct timeval tv ;
/*
Seed the pseudo - random generator based on time - this assumes
that consecutive calls to gettimeofday will return different values
and ignores errors returned by gettimeofday .
Cast to unsigned so that wrapping occurs on overflow as per ANSI C .
*/
( void ) gettimeofday ( & tv , NULL ) ;
2012-08-06 04:24:33 +08:00
unsigned long long seed = tv . tv_sec + tv . tv_usec * 1000000ULL ;
2012-11-19 08:30:30 +08:00
srand ( ( unsigned int ) seed ) ;
max = ( int ) ( 1 + ( maxlen - 1 ) * ( rand ( ) / ( RAND_MAX + 1.0 ) ) ) ;
for ( i = 0 ; i < max ; i + + )
{
str [ i ] = ' 0 ' + 10 * ( rand ( ) / ( RAND_MAX + 1.0 ) ) ;
}
str [ i ] = 0 ;
2012-03-02 16:27:40 +08:00
}
/**
Generate a filename unique in an NFS namespace by creating a copy of str and
appending . { hostname } . { pid } to it . If gethostname ( ) fails then a pseudo -
random string is substituted for { hostname } - the randomness of the string
2012-11-18 18:23:22 +08:00
should be strong enough across different machines . The main assumption
2012-03-02 16:27:40 +08:00
though is that gethostname will not fail and this is just a " safe enough "
fallback .
The memory returned should be freed using free ( ) .
*/
2012-11-19 08:30:30 +08:00
static std : : string gen_unique_nfs_filename ( const char * filename )
2012-03-02 16:27:40 +08:00
{
2012-11-19 08:30:30 +08:00
char hostname [ HOST_NAME_MAX + 1 ] ;
2012-08-05 06:11:43 +08:00
char pid_str [ 256 ] ;
snprintf ( pid_str , sizeof pid_str , " %ld " , ( long ) getpid ( ) ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( gethostname ( hostname , sizeof hostname ) ! = 0 )
2012-08-05 06:11:43 +08:00
{
2012-11-19 08:30:30 +08:00
sprint_rand_digits ( hostname , HOST_NAME_MAX ) ;
}
2012-11-18 18:23:22 +08:00
2012-08-05 06:11:43 +08:00
std : : string newname ( filename ) ;
2012-11-19 08:30:30 +08:00
newname . push_back ( ' . ' ) ;
2012-08-05 06:11:43 +08:00
newname . append ( hostname ) ;
newname . push_back ( ' . ' ) ;
2012-11-18 18:23:22 +08:00
newname . append ( pid_str ) ;
2012-11-19 08:30:30 +08:00
return newname ;
2012-03-02 16:27:40 +08:00
}
/**
2012-11-18 18:23:22 +08:00
The number of milliseconds to wait between polls when attempting to acquire
2012-03-02 16:27:40 +08:00
a lockfile
*/
# define LOCKPOLLINTERVAL 10
/**
2012-11-18 18:23:22 +08:00
Attempt to acquire a lock based on a lockfile , waiting LOCKPOLLINTERVAL
milliseconds between polls and timing out after timeout seconds ,
2012-03-02 16:27:40 +08:00
thereafter forcibly attempting to obtain the lock if force is non - zero .
Returns 1 on success , 0 on failure .
To release the lock the lockfile must be unlinked .
2012-11-18 18:23:22 +08:00
A unique temporary file named by appending characters to the lockfile name
2012-03-02 16:27:40 +08:00
is used ; any pre - existing file of the same name is subject to deletion .
*/
2012-11-19 08:30:30 +08:00
static int acquire_lock_file ( const char * lockfile , const int timeout , int force )
2012-03-02 16:27:40 +08:00
{
2012-11-19 08:30:30 +08:00
int fd , timed_out = 0 ;
int ret = 0 ; /* early exit returns failure */
struct timespec pollint ;
struct timeval start , end ;
double elapsed ;
struct stat statbuf ;
/*
( Re ) create a unique file and check that it has one only link .
*/
const std : : string linkfile_str = gen_unique_nfs_filename ( lockfile ) ;
2012-08-05 06:11:43 +08:00
const char * const linkfile = linkfile_str . c_str ( ) ;
2012-11-19 08:30:30 +08:00
( void ) unlink ( linkfile ) ;
2012-03-02 16:27:40 +08:00
/* OK to not use CLO_EXEC here because fishd is single threaded */
2012-11-19 08:30:30 +08:00
if ( ( fd = open ( linkfile , O_CREAT | O_RDONLY , 0600 ) ) = = - 1 )
{
debug ( 1 , L " acquire_lock_file: open: %s " , strerror ( errno ) ) ;
goto done ;
}
2012-11-18 18:23:22 +08:00
/*
2012-11-19 08:30:30 +08:00
Don ' t need to check exit status of close on read - only file descriptors
*/
close ( fd ) ;
if ( stat ( linkfile , & statbuf ) ! = 0 )
{
debug ( 1 , L " acquire_lock_file: stat: %s " , strerror ( errno ) ) ;
goto done ;
}
if ( statbuf . st_nlink ! = 1 )
{
debug ( 1 , L " acquire_lock_file: number of hardlinks on unique "
L " tmpfile is %d instead of 1. " , ( int ) statbuf . st_nlink ) ;
goto done ;
}
if ( gettimeofday ( & start , NULL ) ! = 0 )
{
debug ( 1 , L " acquire_lock_file: gettimeofday: %s " , strerror ( errno ) ) ;
goto done ;
}
end = start ;
pollint . tv_sec = 0 ;
pollint . tv_nsec = LOCKPOLLINTERVAL * 1000000 ;
do
{
2012-11-18 18:23:22 +08:00
/*
2012-11-19 08:30:30 +08:00
Try to create a hard link to the unique file from the
lockfile . This will only succeed if the lockfile does not
already exist . It is guaranteed to provide race - free
semantics over NFS which the alternative of calling
open ( O_EXCL | O_CREAT ) on the lockfile is not . The lock
succeeds if the call to link returns 0 or the link count on
the unique file increases to 2.
*/
if ( link ( linkfile , lockfile ) = = 0 | |
( stat ( linkfile , & statbuf ) = = 0 & &
statbuf . st_nlink = = 2 ) )
{
/* Successful lock */
ret = 1 ;
break ;
}
elapsed = end . tv_sec + end . tv_usec / 1000000.0 -
( start . tv_sec + start . tv_usec / 1000000.0 ) ;
2012-11-18 18:23:22 +08:00
/*
2012-11-19 08:30:30 +08:00
The check for elapsed < 0 is to deal with the unlikely event
that after the loop is entered the system time is set forward
past the loop ' s end time . This would otherwise result in a
( practically ) infinite loop .
*/
if ( timed_out | | elapsed > = timeout | | elapsed < 0 )
{
if ( timed_out = = 0 & & force )
{
/*
Timed out and force was specified - attempt to
remove stale lock and try a final time
*/
( void ) unlink ( lockfile ) ;
timed_out = 1 ;
continue ;
}
else
{
/*
Timed out and final try was unsuccessful or
force was not specified
*/
debug ( 1 , L " acquire_lock_file: timed out "
2012-03-02 16:27:40 +08:00
L " trying to obtain lockfile %s using "
2012-11-19 08:30:30 +08:00
L " linkfile %s " , lockfile , linkfile ) ;
break ;
}
}
nanosleep ( & pollint , NULL ) ;
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
while ( gettimeofday ( & end , NULL ) = = 0 ) ;
2012-03-02 16:27:40 +08:00
done :
2012-11-19 08:30:30 +08:00
/* The linkfile is not needed once the lockfile has been created */
( void ) unlink ( linkfile ) ;
return ret ;
2012-03-02 16:27:40 +08:00
}
2005-09-28 09:43:09 +08:00
/**
2005-10-01 03:50:21 +08:00
Acquire the lock for the socket
2012-11-18 18:23:22 +08:00
Returns the name of the lock file if successful or
2005-10-01 03:50:21 +08:00
NULL if unable to obtain lock .
2012-11-18 18:23:22 +08:00
The returned string must be free ( ) d after unlink ( ) ing the file to release
2005-10-01 03:50:21 +08:00
the lock
2005-09-28 09:43:09 +08:00
*/
2012-11-19 08:30:30 +08:00
static char * acquire_socket_lock ( const char * sock_name )
2005-09-28 09:43:09 +08:00
{
2012-11-19 08:30:30 +08:00
size_t len = strlen ( sock_name ) ;
char * lockfile = ( char * ) malloc ( len + strlen ( LOCKPOSTFIX ) + 1 ) ;
if ( lockfile = = NULL )
{
wperror ( L " acquire_socket_lock " ) ;
exit ( EXIT_FAILURE ) ;
}
strcpy ( lockfile , sock_name ) ;
strcpy ( lockfile + len , LOCKPOSTFIX ) ;
if ( ! acquire_lock_file ( lockfile , LOCKTIMEOUT , 1 ) )
{
free ( lockfile ) ;
lockfile = NULL ;
}
return lockfile ;
2005-09-28 09:43:09 +08:00
}
/**
Connects to the fish socket and starts listening for connections
*/
2005-10-01 03:50:21 +08:00
static int get_socket ( )
2005-09-28 09:43:09 +08:00
{
2012-11-19 08:30:30 +08:00
int s , len , doexit = 0 ;
int exitcode = EXIT_FAILURE ;
struct sockaddr_un local ;
char * sock_name = get_socket_filename ( ) ;
/*
Start critical section protected by lock
*/
char * lockfile = acquire_socket_lock ( sock_name ) ;
if ( lockfile = = NULL )
{
debug ( 0 , L " Unable to obtain lock on socket, exiting " ) ;
exit ( EXIT_FAILURE ) ;
}
debug ( 4 , L " Acquired lockfile: %s " , lockfile ) ;
local . sun_family = AF_UNIX ;
strcpy ( local . sun_path , sock_name ) ;
len = sizeof ( local ) ;
debug ( 1 , L " Connect to socket at %s " , sock_name ) ;
if ( ( s = socket ( AF_UNIX , SOCK_STREAM , 0 ) ) = = - 1 )
{
wperror ( L " socket " ) ;
doexit = 1 ;
goto unlock ;
}
/*
First check whether the socket has been opened by another fishd ;
if so , exit with success status
*/
if ( connect ( s , ( struct sockaddr * ) & local , len ) = = 0 )
{
debug ( 1 , L " Socket already exists, exiting " ) ;
doexit = 1 ;
exitcode = 0 ;
goto unlock ;
}
unlink ( local . sun_path ) ;
if ( bind ( s , ( struct sockaddr * ) & local , len ) = = - 1 )
{
wperror ( L " bind " ) ;
doexit = 1 ;
goto unlock ;
}
if ( fcntl ( s , F_SETFL , O_NONBLOCK ) ! = 0 )
{
wperror ( L " fcntl " ) ;
close ( s ) ;
doexit = 1 ;
}
else if ( listen ( s , 64 ) = = - 1 )
{
wperror ( L " listen " ) ;
doexit = 1 ;
}
2005-09-28 09:43:09 +08:00
unlock :
2012-11-19 08:30:30 +08:00
( void ) unlink ( lockfile ) ;
debug ( 4 , L " Released lockfile: %s " , lockfile ) ;
/*
End critical section protected by lock
*/
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
free ( lockfile ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
free ( sock_name ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( doexit )
{
exit ( exitcode ) ;
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
return s ;
2005-09-20 21:26:39 +08:00
}
2005-09-23 21:10:31 +08:00
/**
Event handler . Broadcasts updates to all clients .
*/
2012-11-19 08:30:30 +08:00
static void broadcast ( fish_message_type_t type , const wchar_t * key , const wchar_t * val )
2005-09-20 21:26:39 +08:00
{
2012-11-19 08:30:30 +08:00
connection_t * c ;
message_t * msg ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( ! conn )
return ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
msg = create_message ( type , key , val ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
/*
Don ' t merge these loops , or try_send_all can free the message
prematurely
*/
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
for ( c = conn ; c ; c = c - > next )
{
msg - > count + + ;
2011-12-27 13:50:23 +08:00
c - > unsent - > push ( msg ) ;
2012-11-19 08:30:30 +08:00
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
for ( c = conn ; c ; c = c - > next )
{
try_send_all ( c ) ;
}
2005-09-20 21:26:39 +08:00
}
2005-09-23 21:10:31 +08:00
/**
Make program into a creature of the night .
*/
static void daemonize ( )
2005-09-20 21:26:39 +08:00
{
2012-11-19 08:30:30 +08:00
/*
Fork , and let parent exit
*/
switch ( fork ( ) )
{
2012-11-19 16:31:03 +08:00
case - 1 :
debug ( 0 , L " Could not put fishd in background. Quitting " ) ;
wperror ( L " fork " ) ;
exit ( 1 ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 16:31:03 +08:00
case 0 :
{
/* Ordinarily there's very limited things we will do after fork, due to multithreading. But fishd is safe because it's single threaded. So don't die in is_forked_child. */
setup_fork_guards ( ) ;
/*
Make fishd ignore the HUP signal .
*/
struct sigaction act ;
sigemptyset ( & act . sa_mask ) ;
act . sa_flags = 0 ;
act . sa_handler = SIG_IGN ;
sigaction ( SIGHUP , & act , 0 ) ;
/*
Make fishd save and exit on the TERM signal .
*/
sigfillset ( & act . sa_mask ) ;
act . sa_flags = 0 ;
act . sa_handler = & handle_term ;
sigaction ( SIGTERM , & act , 0 ) ;
break ;
2012-11-18 18:23:22 +08:00
2012-11-19 16:31:03 +08:00
}
2012-11-18 18:23:22 +08:00
2012-11-19 16:31:03 +08:00
default :
{
debug ( 0 , L " Parent process exiting (This is normal) " ) ;
exit ( 0 ) ;
}
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
/*
Put ourself in out own processing group
*/
setsid ( ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
/*
Close stdin and stdout . We only use stderr , anyway .
*/
close ( 0 ) ;
close ( 1 ) ;
2005-09-20 21:26:39 +08:00
}
2006-10-19 19:50:23 +08:00
/**
Get environment variable value . The resulting string needs to be free ' d .
*/
2012-11-19 08:30:30 +08:00
static wchar_t * fishd_env_get ( const wchar_t * key )
2006-10-19 19:50:23 +08:00
{
2012-11-19 08:30:30 +08:00
char * nres , * nkey ;
wchar_t * res ;
nkey = wcs2str ( key ) ;
nres = getenv ( nkey ) ;
free ( nkey ) ;
if ( nres )
{
2012-12-20 05:31:06 +08:00
wcstring tmp = str2wcstring ( nres ) ;
return wcsdup ( tmp . c_str ( ) ) ;
2012-11-19 08:30:30 +08:00
}
else
{
res = env_universal_common_get ( key ) ;
if ( res )
res = wcsdup ( res ) ;
return res ;
}
2006-10-19 19:50:23 +08:00
}
/**
Get the configuration directory . The resulting string needs to be
free ' d . This is mostly the same code as path_get_config ( ) , but had
to be rewritten to avoid dragging in additional library
dependencies .
*/
2012-05-09 17:33:42 +08:00
static wcstring fishd_get_config ( )
2006-10-19 19:50:23 +08:00
{
2012-11-19 08:30:30 +08:00
wchar_t * xdg_dir , * home ;
bool done = false ;
wcstring result ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
xdg_dir = fishd_env_get ( L " XDG_CONFIG_HOME " ) ;
if ( xdg_dir )
{
2012-05-09 17:33:42 +08:00
result = xdg_dir ;
append_path_component ( result , L " /fish " ) ;
2012-11-19 08:30:30 +08:00
if ( ! create_directory ( result ) )
{
done = true ;
}
free ( xdg_dir ) ;
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
else
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
home = fishd_env_get ( L " HOME " ) ;
if ( home )
{
2012-05-09 17:33:42 +08:00
result = home ;
append_path_component ( result , L " /.config/fish " ) ;
2012-11-19 08:30:30 +08:00
if ( ! create_directory ( result ) )
{
done = 1 ;
}
free ( home ) ;
}
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
if ( ! done )
{
2012-05-09 17:33:42 +08:00
/* Bad juju */
2012-11-19 08:30:30 +08:00
debug ( 0 , _ ( L " Unable to create a configuration directory for fish. Your personal settings will not be saved. Please set the $XDG_CONFIG_HOME variable to a directory where the current user has write access. " ) ) ;
2012-11-18 18:23:22 +08:00
result . clear ( ) ;
2012-05-09 17:33:42 +08:00
}
2012-11-18 18:23:22 +08:00
2012-05-09 17:33:42 +08:00
return result ;
2006-10-19 19:50:23 +08:00
}
2005-09-23 21:10:31 +08:00
/**
Load or save all variables
*/
2012-11-19 08:30:30 +08:00
static void load_or_save ( int save )
2005-09-20 21:26:39 +08:00
{
2012-11-19 08:30:30 +08:00
const wcstring wdir = fishd_get_config ( ) ;
char hostname [ HOSTNAME_LEN ] ;
connection_t c ;
int fd ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( wdir . empty ( ) )
return ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
std : : string dir = wcs2string ( wdir ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
gethostname ( hostname , HOSTNAME_LEN ) ;
2012-11-18 18:23:22 +08:00
2012-05-09 17:33:42 +08:00
std : : string name ;
name . append ( dir ) ;
name . append ( " / " ) ;
name . append ( FILE ) ;
name . append ( hostname ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
debug ( 4 , L " Open file for %s: '%s' " ,
save ? " saving " : " loading " ,
name . c_str ( ) ) ;
2012-11-18 18:23:22 +08:00
2012-03-02 16:27:40 +08:00
/* OK to not use CLO_EXEC here because fishd is single threaded */
2012-11-19 08:30:30 +08:00
fd = open ( name . c_str ( ) , save ? ( O_CREAT | O_TRUNC | O_WRONLY ) : O_RDONLY , 0600 ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( fd = = - 1 )
{
debug ( 1 , L " Could not open load/save file. No previous saves? " ) ;
wperror ( L " open " ) ;
return ;
}
debug ( 4 , L " File open on fd %d " , c . fd ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
connection_init ( & c , fd ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( save )
{
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
write_loop ( c . fd , SAVE_MSG , strlen ( SAVE_MSG ) ) ;
enqueue_all ( & c ) ;
}
else
read_message ( & c ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
connection_destroy ( & c ) ;
2005-09-20 21:26:39 +08:00
}
2005-10-24 23:26:25 +08:00
/**
Load variables from disk
*/
2005-09-23 21:10:31 +08:00
static void load ( )
2005-09-20 21:26:39 +08:00
{
2012-11-19 08:30:30 +08:00
load_or_save ( 0 ) ;
2005-09-23 21:10:31 +08:00
}
2005-09-20 21:26:39 +08:00
2005-10-24 23:26:25 +08:00
/**
Save variables to disk
*/
2005-09-23 21:10:31 +08:00
static void save ( )
{
2012-11-19 08:30:30 +08:00
load_or_save ( 1 ) ;
2005-09-20 21:26:39 +08:00
}
2005-09-23 21:10:31 +08:00
/**
Do all sorts of boring initialization .
*/
2005-09-20 21:26:39 +08:00
static void init ( )
{
2005-09-28 09:43:09 +08:00
2012-11-19 08:30:30 +08:00
sock = get_socket ( ) ;
daemonize ( ) ;
env_universal_common_init ( & broadcast ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
load ( ) ;
2005-09-20 21:26:39 +08:00
}
2005-10-24 23:26:25 +08:00
/**
Main function for fishd
*/
2012-11-19 08:30:30 +08:00
int main ( int argc , char * * argv )
2005-09-20 21:26:39 +08:00
{
2012-11-19 08:30:30 +08:00
int child_socket ;
struct sockaddr_un remote ;
socklen_t t ;
int max_fd ;
int update_count = 0 ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
fd_set read_fd , write_fd ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
set_main_thread ( ) ;
2012-03-08 03:35:22 +08:00
setup_fork_guards ( ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
program_name = L " fishd " ;
wsetlocale ( LC_ALL , L " " ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
/*
Parse options
*/
while ( 1 )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
static struct option
long_options [ ] =
{
{
" help " , no_argument , 0 , ' h '
}
,
{
" version " , no_argument , 0 , ' v '
}
,
{
0 , 0 , 0 , 0
}
}
;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
int opt_index = 0 ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
int opt = getopt_long ( argc ,
argv ,
GETOPT_STRING ,
long_options ,
& opt_index ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( opt = = - 1 )
break ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
switch ( opt )
{
2012-11-19 16:31:03 +08:00
case 0 :
break ;
2012-11-18 18:23:22 +08:00
2012-11-19 16:31:03 +08:00
case ' h ' :
print_help ( argv [ 0 ] , 1 ) ;
exit ( 0 ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 16:31:03 +08:00
case ' v ' :
debug ( 0 , L " %ls, version %s \n " , program_name , PACKAGE_VERSION ) ;
exit ( 0 ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 16:31:03 +08:00
case ' ? ' :
return 1 ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
}
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
init ( ) ;
while ( 1 )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
connection_t * c ;
int res ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
t = sizeof ( remote ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
FD_ZERO ( & read_fd ) ;
FD_ZERO ( & write_fd ) ;
FD_SET ( sock , & read_fd ) ;
max_fd = sock + 1 ;
for ( c = conn ; c ; c = c - > next )
{
FD_SET ( c - > fd , & read_fd ) ;
max_fd = maxi ( max_fd , c - > fd + 1 ) ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
if ( ! c - > unsent - > empty ( ) )
{
FD_SET ( c - > fd , & write_fd ) ;
}
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
while ( 1 )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
res = select ( max_fd , & read_fd , & write_fd , 0 , 0 ) ;
if ( quit )
{
save ( ) ;
exit ( 0 ) ;
}
if ( res ! = - 1 )
break ;
if ( errno ! = EINTR )
{
wperror ( L " select " ) ;
exit ( 1 ) ;
}
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
if ( FD_ISSET ( sock , & read_fd ) )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
if ( ( child_socket =
accept ( sock ,
( struct sockaddr * ) & remote ,
& t ) ) = = - 1 )
{
wperror ( L " accept " ) ;
exit ( 1 ) ;
}
else
{
debug ( 4 , L " Connected with new child on fd %d " , child_socket ) ;
if ( fcntl ( child_socket , F_SETFL , O_NONBLOCK ) ! = 0 )
{
wperror ( L " fcntl " ) ;
close ( child_socket ) ;
}
else
{
connection_t * newc = ( connection_t * ) malloc ( sizeof ( connection_t ) ) ;
connection_init ( newc , child_socket ) ;
newc - > next = conn ;
send ( newc - > fd , GREETING , strlen ( GREETING ) , MSG_DONTWAIT ) ;
enqueue_all ( newc ) ;
conn = newc ;
}
}
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
for ( c = conn ; c ; c = c - > next )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
if ( FD_ISSET ( c - > fd , & write_fd ) )
{
try_send_all ( c ) ;
}
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
for ( c = conn ; c ; c = c - > next )
{
if ( FD_ISSET ( c - > fd , & read_fd ) )
{
read_message ( c ) ;
/*
Occasionally we save during normal use , so that we
won ' t lose everything on a system crash
*/
update_count + + ;
if ( update_count > = 64 )
{
save ( ) ;
update_count = 0 ;
}
}
}
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
connection_t * prev = 0 ;
c = conn ;
2012-11-18 18:23:22 +08:00
2012-11-19 08:30:30 +08:00
while ( c )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
if ( c - > killme )
{
debug ( 4 , L " Close connection %d " , c - > fd ) ;
while ( ! c - > unsent - > empty ( ) )
{
message_t * msg = c - > unsent - > front ( ) ;
2011-12-27 13:50:23 +08:00
c - > unsent - > pop ( ) ;
2012-11-19 08:30:30 +08:00
msg - > count - - ;
if ( ! msg - > count )
free ( msg ) ;
}
connection_destroy ( c ) ;
if ( prev )
{
prev - > next = c - > next ;
}
else
{
conn = c - > next ;
}
free ( c ) ;
c = ( prev ? prev - > next : conn ) ;
}
else
{
prev = c ;
c = c - > next ;
}
2012-11-18 18:23:22 +08:00
}
2012-11-19 08:30:30 +08:00
if ( ! conn )
2012-11-18 18:23:22 +08:00
{
2012-11-19 08:30:30 +08:00
debug ( 0 , L " No more clients. Quitting " ) ;
save ( ) ;
env_universal_common_destroy ( ) ;
break ;
2012-11-18 18:23:22 +08:00
}
}
2005-09-20 21:26:39 +08:00
}
2006-10-19 00:44:38 +08:00