2005-09-20 13:26:39 +00: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-24 17:20:31 +00:00
variables from ~ / . fishd , and takes care of communication between fish
2005-09-20 13:26:39 +00:00
instances . When no clients are running , fishd will automatically shut
down and save .
2006-06-20 00:50:10 +00:00
\ section fishd - commands Commands
2006-01-24 17:20:31 +00: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-17 21:11:29 +00: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-24 17:20:31 +00: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 13:26:39 +00:00
*/
2006-01-23 16:25:34 +00:00
# include "config.h"
2006-02-28 13:17:16 +00:00
2005-09-20 13:26:39 +00: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>
2013-01-08 10:39:22 +00:00
# include <sys/ioctl.h>
2005-09-20 13:26:39 +00:00
# include <sys/un.h>
# include <pwd.h>
# include <fcntl.h>
2006-01-23 16:25:34 +00:00
# ifdef HAVE_GETOPT_H
# include <getopt.h>
# endif
2005-09-20 13:26:39 +00:00
# include <errno.h>
# include <locale.h>
# include <signal.h>
2013-02-16 08:32:15 +00:00
# include <list>
2005-09-20 13:26:39 +00:00
2006-02-28 13:17:16 +00:00
# include "fallback.h"
2005-09-20 13:26:39 +00:00
# include "util.h"
2006-02-28 13:17:16 +00:00
2005-09-20 13:26:39 +00:00
# include "common.h"
# include "wutil.h"
# include "env_universal_common.h"
2006-10-19 11:50:23 +00:00
# include "path.h"
2006-11-17 16:24:38 +00:00
# include "print_help.h"
2005-09-20 13:26:39 +00:00
2012-03-02 08:27:40 +00: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 13:10:31 +00:00
/**
Maximum length of socket filename
*/
2012-11-18 10:23:22 +00:00
# ifndef UNIX_PATH_MAX
2005-09-20 13:26:39 +00:00
# define UNIX_PATH_MAX 100
# endif
2006-01-26 09:57:06 +00: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
2013-01-08 10:39:22 +00:00
/* Length of a MAC address */
# define MAC_ADDRESS_MAX_LEN 6
2005-09-23 13:10:31 +00:00
/**
Small greeting to show that fishd is running
*/
2006-06-17 14:04:06 +00:00
# define GREETING "# Fish universal variable daemon\n"
2006-06-20 00:50:10 +00:00
/**
Small not about not editing ~ / . fishd manually . Inserted at the top of all . fishd files .
*/
2006-06-17 14:04:06 +00: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 13:26:39 +00:00
2005-09-23 13:10:31 +00:00
/**
2013-01-08 10:39:22 +00:00
The name of the save file . The machine identifier is appended to this .
2005-09-23 13:10:31 +00:00
*/
2006-10-19 11:50:23 +00:00
# define FILE "fishd."
2005-09-20 13:26:39 +00:00
2005-09-23 13:10:31 +00:00
/**
Maximum length of hostname . Longer hostnames are truncated
*/
# define HOSTNAME_LEN 32
2005-09-28 01:43:09 +00: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 14:55:51 +00:00
/**
Getopt short switches for fishd
*/
# define GETOPT_STRING "hv"
2005-09-23 13:10:31 +00:00
/**
The list of connections to clients
*/
2013-02-16 08:32:15 +00:00
typedef std : : list < connection_t > connection_list_t ;
static connection_list_t connections ;
2005-09-20 13:26:39 +00:00
2005-09-23 13:10:31 +00:00
/**
The socket to accept new clients on
*/
static int sock ;
2005-09-20 13:26:39 +00:00
2005-11-27 23:24:09 +00:00
/**
Set to one when fishd should save and exit
*/
static int quit = 0 ;
2005-09-23 13:10:31 +00:00
/**
2005-09-28 01:43:09 +00:00
Constructs the fish socket filename
2005-09-23 13:10:31 +00:00
*/
2013-02-16 10:38:13 +00:00
static std : : string get_socket_filename ( void )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
const char * dir = getenv ( " FISHD_SOCKET_DIR " ) ;
char * uname = getenv ( " USER " ) ;
if ( dir = = NULL )
{
dir = " /tmp " ;
}
if ( uname = = NULL )
{
2013-02-16 10:38:13 +00:00
const struct passwd * pw = getpwuid ( getuid ( ) ) ;
uname = pw - > pw_name ;
2012-11-19 00:30:30 +00:00
}
2013-02-16 10:38:13 +00:00
std : : string name ;
name . reserve ( strlen ( dir ) + strlen ( uname ) + strlen ( SOCK_FILENAME ) + 1 ) ;
name . append ( dir ) ;
name . push_back ( ' / ' ) ;
name . append ( SOCK_FILENAME ) ;
name . append ( uname ) ;
2012-11-19 00:30:30 +00:00
2013-02-16 10:38:13 +00:00
if ( name . size ( ) > = UNIX_PATH_MAX )
2012-11-19 00:30:30 +00:00
{
2013-02-16 10:38:13 +00:00
debug ( 1 , L " Filename too long: '%s' " , name . c_str ( ) ) ;
2012-11-19 00:30:30 +00:00
exit ( EXIT_FAILURE ) ;
}
return name ;
2005-09-28 01:43:09 +00:00
}
2005-11-27 23:24:09 +00:00
/**
2012-11-18 10:23:22 +00:00
Signal handler for the term signal .
2005-11-27 23:24:09 +00:00
*/
2012-11-19 00:30:30 +00:00
static void handle_term ( int signal )
2005-11-27 23:24:09 +00:00
{
2012-11-19 00:30:30 +00:00
quit = 1 ;
2005-11-27 23:24:09 +00:00
}
2012-03-02 08:27:40 +00: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 00:30:30 +00:00
static void sprint_rand_digits ( char * str , int maxlen )
2012-03-02 08:27:40 +00:00
{
2012-11-19 00:30:30 +00: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 .
*/
2013-01-08 10:39:22 +00:00
static bool seeded = false ;
if ( ! seeded )
{
( void ) gettimeofday ( & tv , NULL ) ;
unsigned long long seed = tv . tv_sec + tv . tv_usec * 1000000ULL ;
srand ( ( unsigned int ) seed ) ;
seeded = true ;
}
2012-11-19 00:30:30 +00:00
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 08:27:40 +00:00
}
2013-01-08 10:39:22 +00:00
2012-03-02 08:27:40 +00: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 10:23:22 +00:00
should be strong enough across different machines . The main assumption
2012-03-02 08:27:40 +00:00
though is that gethostname will not fail and this is just a " safe enough "
fallback .
The memory returned should be freed using free ( ) .
*/
2013-02-16 10:38:13 +00:00
static std : : string gen_unique_nfs_filename ( const std : : string & filename )
2012-03-02 08:27:40 +00:00
{
2012-11-19 00:30:30 +00:00
char hostname [ HOST_NAME_MAX + 1 ] ;
2012-08-04 22:11:43 +00:00
char pid_str [ 256 ] ;
snprintf ( pid_str , sizeof pid_str , " %ld " , ( long ) getpid ( ) ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( gethostname ( hostname , sizeof hostname ) ! = 0 )
2012-08-04 22:11:43 +00:00
{
2012-11-19 00:30:30 +00:00
sprint_rand_digits ( hostname , HOST_NAME_MAX ) ;
}
2012-11-18 10:23:22 +00:00
2012-08-04 22:11:43 +00:00
std : : string newname ( filename ) ;
2012-11-19 00:30:30 +00:00
newname . push_back ( ' . ' ) ;
2012-08-04 22:11:43 +00:00
newname . append ( hostname ) ;
newname . push_back ( ' . ' ) ;
2012-11-18 10:23:22 +00:00
newname . append ( pid_str ) ;
2012-11-19 00:30:30 +00:00
return newname ;
2012-03-02 08:27:40 +00:00
}
2013-01-08 10:39:22 +00:00
/* Thanks to Jan Brittenson
http : //lists.apple.com/archives/xcode-users/2009/May/msg00062.html
*/
# ifdef SIOCGIFHWADDR
/* Linux */
# include <net/if.h>
static bool get_mac_address ( unsigned char macaddr [ MAC_ADDRESS_MAX_LEN ] , const char * interface = " eth0 " )
{
bool result = false ;
const int dummy = socket ( AF_INET , SOCK_STREAM , 0 ) ;
if ( dummy > = 0 )
{
struct ifreq r ;
strncpy ( ( char * ) r . ifr_name , interface , sizeof r . ifr_name - 1 ) ;
r . ifr_name [ sizeof r . ifr_name - 1 ] = 0 ;
if ( ioctl ( dummy , SIOCGIFHWADDR , & r ) > = 0 )
{
memcpy ( macaddr , r . ifr_hwaddr . sa_data , MAC_ADDRESS_MAX_LEN ) ;
result = true ;
}
close ( dummy ) ;
}
return result ;
}
# elif defined(HAVE_GETIFADDRS)
/* OS X and BSD */
# include <ifaddrs.h>
# include <net/if_dl.h>
static bool get_mac_address ( unsigned char macaddr [ MAC_ADDRESS_MAX_LEN ] , const char * interface = " en0 " )
{
// BSD, Mac OS X
struct ifaddrs * ifap ;
bool ok = false ;
if ( getifaddrs ( & ifap ) = = 0 )
{
for ( const ifaddrs * p = ifap ; p ; p = p - > ifa_next )
{
if ( p - > ifa_addr - > sa_family = = AF_LINK )
{
if ( p - > ifa_name & & p - > ifa_name [ 0 ] & &
! strcmp ( ( const char * ) p - > ifa_name , interface ) )
{
const sockaddr_dl & sdl = * ( sockaddr_dl * ) p - > ifa_addr ;
size_t alen = sdl . sdl_alen ;
if ( alen > MAC_ADDRESS_MAX_LEN ) alen = MAC_ADDRESS_MAX_LEN ;
memcpy ( macaddr , sdl . sdl_data + sdl . sdl_nlen , alen ) ;
ok = true ;
break ;
}
}
}
freeifaddrs ( ifap ) ;
}
return ok ;
}
# else
/* Unsupported */
static bool get_mac_address ( unsigned char macaddr [ MAC_ADDRESS_MAX_LEN ] )
{
return false ;
}
# endif
/* Function to get an identifier based on the hostname */
static bool get_hostname_identifier ( std : : string * result )
{
bool success = false ;
char hostname [ HOSTNAME_LEN + 1 ] = { } ;
if ( gethostname ( hostname , HOSTNAME_LEN ) = = 0 )
{
result - > assign ( hostname ) ;
success = true ;
}
return success ;
}
/* Get a sort of unique machine identifier. Prefer the MAC address; if that fails, fall back to the hostname; if that fails, pick something. */
static std : : string get_machine_identifier ( void )
{
std : : string result ;
unsigned char mac_addr [ MAC_ADDRESS_MAX_LEN ] = { } ;
if ( get_mac_address ( mac_addr ) )
{
result . reserve ( 2 * MAC_ADDRESS_MAX_LEN ) ;
for ( size_t i = 0 ; i < MAC_ADDRESS_MAX_LEN ; i + + )
{
char buff [ 3 ] ;
snprintf ( buff , sizeof buff , " %02x " , mac_addr [ i ] ) ;
result . append ( buff ) ;
}
}
else if ( get_hostname_identifier ( & result ) )
{
/* Hooray */
}
else
{
/* Fallback */
result . assign ( " nohost " ) ;
}
return result ;
}
2012-03-02 08:27:40 +00:00
/**
2012-11-18 10:23:22 +00:00
The number of milliseconds to wait between polls when attempting to acquire
2012-03-02 08:27:40 +00:00
a lockfile
*/
# define LOCKPOLLINTERVAL 10
/**
2012-11-18 10:23:22 +00:00
Attempt to acquire a lock based on a lockfile , waiting LOCKPOLLINTERVAL
milliseconds between polls and timing out after timeout seconds ,
2012-03-02 08:27:40 +00: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 10:23:22 +00:00
A unique temporary file named by appending characters to the lockfile name
2012-03-02 08:27:40 +00:00
is used ; any pre - existing file of the same name is subject to deletion .
*/
2013-02-16 10:38:13 +00:00
static int acquire_lock_file ( const std : : string & lockfile_str , const int timeout , int force )
2012-03-02 08:27:40 +00:00
{
2012-11-19 00:30:30 +00: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 ;
2013-02-16 10:38:13 +00:00
const char * const lockfile = lockfile_str . c_str ( ) ;
2012-11-19 00:30:30 +00:00
/*
( 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-04 22:11:43 +00:00
const char * const linkfile = linkfile_str . c_str ( ) ;
2012-11-19 00:30:30 +00:00
( void ) unlink ( linkfile ) ;
2012-03-02 08:27:40 +00:00
/* OK to not use CLO_EXEC here because fishd is single threaded */
2012-11-19 00:30:30 +00: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 10:23:22 +00:00
/*
2012-11-19 00:30:30 +00: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 10:23:22 +00:00
/*
2012-11-19 00:30:30 +00: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 10:23:22 +00:00
/*
2012-11-19 00:30:30 +00: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 08:27:40 +00:00
L " trying to obtain lockfile %s using "
2012-11-19 00:30:30 +00:00
L " linkfile %s " , lockfile , linkfile ) ;
break ;
}
}
nanosleep ( & pollint , NULL ) ;
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
while ( gettimeofday ( & end , NULL ) = = 0 ) ;
2012-03-02 08:27:40 +00:00
done :
2012-11-19 00:30:30 +00:00
/* The linkfile is not needed once the lockfile has been created */
( void ) unlink ( linkfile ) ;
return ret ;
2012-03-02 08:27:40 +00:00
}
2005-09-28 01:43:09 +00:00
/**
2005-09-30 19:50:21 +00:00
Acquire the lock for the socket
2012-11-18 10:23:22 +00:00
Returns the name of the lock file if successful or
2005-09-30 19:50:21 +00:00
NULL if unable to obtain lock .
2012-11-18 10:23:22 +00:00
The returned string must be free ( ) d after unlink ( ) ing the file to release
2005-09-30 19:50:21 +00:00
the lock
2005-09-28 01:43:09 +00:00
*/
2013-02-16 10:38:13 +00:00
static bool acquire_socket_lock ( const std : : string & sock_name , std : : string * out_lockfile_name )
2005-09-28 01:43:09 +00:00
{
2013-02-16 10:38:13 +00:00
bool success = false ;
std : : string lockfile ;
lockfile . reserve ( sock_name . size ( ) + strlen ( LOCKPOSTFIX ) ) ;
lockfile = sock_name ;
lockfile . append ( LOCKPOSTFIX ) ;
if ( acquire_lock_file ( lockfile , LOCKTIMEOUT , 1 ) )
2012-11-19 00:30:30 +00:00
{
2013-02-16 10:38:13 +00:00
out_lockfile_name - > swap ( lockfile ) ;
success = true ;
2012-11-19 00:30:30 +00:00
}
2013-02-16 10:38:13 +00:00
return success ;
2005-09-28 01:43:09 +00:00
}
/**
Connects to the fish socket and starts listening for connections
*/
2013-02-16 10:38:13 +00:00
static int get_socket ( void )
2005-09-28 01:43:09 +00:00
{
2013-08-12 17:19:51 +00:00
// Cygwin has random problems involving sockets. When using Cygwin,
// allow 20 attempts at making socket correctly.
# ifdef __CYGWIN__
int attempts = 0 ;
repeat :
attempts + = 1 ;
# endif
2012-11-19 00:30:30 +00:00
int s , len , doexit = 0 ;
int exitcode = EXIT_FAILURE ;
struct sockaddr_un local ;
2013-02-16 10:38:13 +00:00
const std : : string sock_name = get_socket_filename ( ) ;
2012-11-19 00:30:30 +00:00
/*
Start critical section protected by lock
*/
2013-02-16 10:38:13 +00:00
std : : string lockfile ;
if ( ! acquire_socket_lock ( sock_name , & lockfile ) )
2012-11-19 00:30:30 +00:00
{
debug ( 0 , L " Unable to obtain lock on socket, exiting " ) ;
exit ( EXIT_FAILURE ) ;
}
2013-02-16 10:38:13 +00:00
debug ( 4 , L " Acquired lockfile: %s " , lockfile . c_str ( ) ) ;
2012-11-19 00:30:30 +00:00
local . sun_family = AF_UNIX ;
2013-02-16 10:38:13 +00:00
strcpy ( local . sun_path , sock_name . c_str ( ) ) ;
2012-11-19 00:30:30 +00:00
len = sizeof ( local ) ;
2013-02-16 10:38:13 +00:00
debug ( 1 , L " Connect to socket at %s " , sock_name . c_str ( ) ) ;
2012-11-19 00:30:30 +00:00
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 ;
}
2013-04-07 19:40:08 +00:00
if ( make_fd_nonblocking ( s ) ! = 0 )
2012-11-19 00:30:30 +00:00
{
wperror ( L " fcntl " ) ;
close ( s ) ;
doexit = 1 ;
}
else if ( listen ( s , 64 ) = = - 1 )
{
wperror ( L " listen " ) ;
doexit = 1 ;
}
2005-09-28 01:43:09 +00:00
unlock :
2013-02-16 10:38:13 +00:00
( void ) unlink ( lockfile . c_str ( ) ) ;
debug ( 4 , L " Released lockfile: %s " , lockfile . c_str ( ) ) ;
2012-11-19 00:30:30 +00:00
/*
End critical section protected by lock
*/
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( doexit )
{
2013-08-12 17:19:51 +00:00
// If Cygwin, only allow normal quit when made lots of attempts.
# ifdef __CYGWIN__
if ( exitcode & & attempts < 20 ) goto repeat ;
# endif
2013-02-16 10:38:13 +00:00
exit_without_destructors ( exitcode ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
return s ;
2005-09-20 13:26:39 +00:00
}
2005-09-23 13:10:31 +00:00
/**
Event handler . Broadcasts updates to all clients .
*/
2012-11-19 00:30:30 +00:00
static void broadcast ( fish_message_type_t type , const wchar_t * key , const wchar_t * val )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
message_t * msg ;
2012-11-18 10:23:22 +00:00
2013-02-16 08:32:15 +00:00
if ( connections . empty ( ) )
2012-11-19 00:30:30 +00:00
return ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
msg = create_message ( type , key , val ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Don ' t merge these loops , or try_send_all can free the message
prematurely
*/
2012-11-18 10:23:22 +00:00
2013-02-16 08:32:15 +00:00
for ( connection_list_t : : iterator iter = connections . begin ( ) ; iter ! = connections . end ( ) ; + + iter )
2012-11-19 00:30:30 +00:00
{
msg - > count + + ;
2013-02-16 08:32:15 +00:00
iter - > unsent . push ( msg ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
2013-02-16 08:32:15 +00:00
for ( connection_list_t : : iterator iter = connections . begin ( ) ; iter ! = connections . end ( ) ; + + iter )
2012-11-19 00:30:30 +00:00
{
2013-02-16 08:32:15 +00:00
try_send_all ( & * iter ) ;
2012-11-19 00:30:30 +00:00
}
2005-09-20 13:26:39 +00:00
}
2005-09-23 13:10:31 +00:00
/**
Make program into a creature of the night .
*/
static void daemonize ( )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
/*
Fork , and let parent exit
*/
switch ( fork ( ) )
{
2012-11-19 08:31:03 +00:00
case - 1 :
debug ( 0 , L " Could not put fishd in background. Quitting " ) ;
wperror ( L " fork " ) ;
exit ( 1 ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00: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 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
}
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
default :
{
debug ( 0 , L " Parent process exiting (This is normal) " ) ;
exit ( 0 ) ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
/*
2013-10-26 22:24:49 +00:00
Put ourself in our own process group
2012-11-19 00:30:30 +00:00
*/
setsid ( ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Close stdin and stdout . We only use stderr , anyway .
*/
close ( 0 ) ;
close ( 1 ) ;
2005-09-20 13:26:39 +00:00
}
2006-10-19 11:50:23 +00:00
/**
2013-02-12 07:16:50 +00:00
Get environment variable value .
2006-10-19 11:50:23 +00:00
*/
2013-02-12 07:16:50 +00:00
static env_var_t fishd_env_get ( const char * key )
2006-10-19 11:50:23 +00:00
{
2013-02-12 07:16:50 +00:00
const char * env = getenv ( key ) ;
if ( env ! = NULL )
2012-11-19 00:30:30 +00:00
{
2013-02-12 07:16:50 +00:00
return env_var_t ( str2wcstring ( env ) ) ;
2012-11-19 00:30:30 +00:00
}
else
{
2013-02-12 07:16:50 +00:00
const wcstring wkey = str2wcstring ( key ) ;
const wchar_t * tmp = env_universal_common_get ( wkey ) ;
return tmp ? env_var_t ( tmp ) : env_var_t : : missing_var ( ) ;
2012-11-19 00:30:30 +00:00
}
2006-10-19 11:50:23 +00: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 09:33:42 +00:00
static wcstring fishd_get_config ( )
2006-10-19 11:50:23 +00:00
{
2012-11-19 00:30:30 +00:00
bool done = false ;
wcstring result ;
2012-11-18 10:23:22 +00:00
2013-02-12 07:16:50 +00:00
env_var_t xdg_dir = fishd_env_get ( " XDG_CONFIG_HOME " ) ;
if ( ! xdg_dir . missing_or_empty ( ) )
2012-11-19 00:30:30 +00:00
{
2012-05-09 09:33:42 +00:00
result = xdg_dir ;
append_path_component ( result , L " /fish " ) ;
2012-11-19 00:30:30 +00:00
if ( ! create_directory ( result ) )
{
done = true ;
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
else
2012-11-18 10:23:22 +00:00
{
2013-02-12 07:16:50 +00:00
env_var_t home = fishd_env_get ( " HOME " ) ;
if ( ! home . missing_or_empty ( ) )
2012-11-19 00:30:30 +00:00
{
2012-05-09 09:33:42 +00:00
result = home ;
append_path_component ( result , L " /.config/fish " ) ;
2012-11-19 00:30:30 +00:00
if ( ! create_directory ( result ) )
{
done = 1 ;
}
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
if ( ! done )
{
2012-05-09 09:33:42 +00:00
/* Bad juju */
2012-11-19 00:30:30 +00: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 10:23:22 +00:00
result . clear ( ) ;
2012-05-09 09:33:42 +00:00
}
2012-11-18 10:23:22 +00:00
2012-05-09 09:33:42 +00:00
return result ;
2006-10-19 11:50:23 +00:00
}
2005-09-23 13:10:31 +00:00
/**
Load or save all variables
*/
2013-01-08 10:39:22 +00:00
static bool load_or_save_variables_at_path ( bool save , const std : : string & path )
2005-09-20 13:26:39 +00:00
{
2013-01-08 10:39:22 +00:00
bool result = false ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
debug ( 4 , L " Open file for %s: '%s' " ,
save ? " saving " : " loading " ,
2013-01-08 10:39:22 +00:00
path . c_str ( ) ) ;
2012-11-18 10:23:22 +00:00
2012-03-02 08:27:40 +00:00
/* OK to not use CLO_EXEC here because fishd is single threaded */
2013-01-08 10:39:22 +00:00
int fd = open ( path . c_str ( ) , save ? ( O_CREAT | O_TRUNC | O_WRONLY ) : O_RDONLY , 0600 ) ;
if ( fd > = 0 )
2012-11-19 00:30:30 +00:00
{
2013-01-08 10:39:22 +00:00
/* Success */
result = true ;
2013-02-16 08:02:40 +00:00
connection_t c ( fd ) ;
2013-01-08 10:39:22 +00:00
if ( save )
{
/* Save to the file */
write_loop ( c . fd , SAVE_MSG , strlen ( SAVE_MSG ) ) ;
enqueue_all ( & c ) ;
}
else
{
/* Read from the file */
read_message ( & c ) ;
}
connection_destroy ( & c ) ;
2012-11-19 00:30:30 +00:00
}
2013-01-08 10:39:22 +00:00
return result ;
}
2012-11-18 10:23:22 +00:00
2013-01-08 10:39:22 +00:00
static std : : string get_variables_file_path ( const std : : string & dir , const std : : string & identifier )
{
std : : string name ;
name . append ( dir ) ;
name . append ( " / " ) ;
name . append ( FILE ) ;
name . append ( identifier ) ;
return name ;
}
2012-11-18 10:23:22 +00:00
2013-01-08 10:39:22 +00:00
static bool load_or_save_variables ( bool save )
{
const wcstring wdir = fishd_get_config ( ) ;
const std : : string dir = wcs2string ( wdir ) ;
if ( dir . empty ( ) )
return false ;
const std : : string machine_id = get_machine_identifier ( ) ;
const std : : string machine_id_path = get_variables_file_path ( dir , machine_id ) ;
bool success = load_or_save_variables_at_path ( save , machine_id_path ) ;
if ( ! success & & ! save & & errno = = ENOENT )
{
/* We failed to load, because the file was not found. Older fish used the hostname only. Try *moving* the filename based on the hostname into place; if that succeeds try again. Silently "upgraded." */
std : : string hostname_id ;
if ( get_hostname_identifier ( & hostname_id ) & & hostname_id ! = machine_id )
{
std : : string hostname_path = get_variables_file_path ( dir , hostname_id ) ;
if ( 0 = = rename ( hostname_path . c_str ( ) , machine_id_path . c_str ( ) ) )
{
/* We renamed - try again */
success = load_or_save_variables_at_path ( save , machine_id_path ) ;
}
}
2012-11-19 00:30:30 +00:00
}
2013-01-08 10:39:22 +00:00
return success ;
2005-09-20 13:26:39 +00:00
}
2005-10-24 15:26:25 +00:00
/**
Load variables from disk
*/
2005-09-23 13:10:31 +00:00
static void load ( )
2005-09-20 13:26:39 +00:00
{
2013-01-08 10:39:22 +00:00
load_or_save_variables ( false /* load, not save */ ) ;
2005-09-23 13:10:31 +00:00
}
2005-09-20 13:26:39 +00:00
2005-10-24 15:26:25 +00:00
/**
Save variables to disk
*/
2005-09-23 13:10:31 +00:00
static void save ( )
{
2013-01-08 10:39:22 +00:00
load_or_save_variables ( true /* save, not load */ ) ;
2005-09-20 13:26:39 +00:00
}
2005-09-23 13:10:31 +00:00
/**
Do all sorts of boring initialization .
*/
2005-09-20 13:26:39 +00:00
static void init ( )
{
2005-09-28 01:43:09 +00:00
2012-11-19 00:30:30 +00:00
sock = get_socket ( ) ;
daemonize ( ) ;
env_universal_common_init ( & broadcast ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
load ( ) ;
2005-09-20 13:26:39 +00:00
}
2005-10-24 15:26:25 +00:00
/**
Main function for fishd
*/
2012-11-19 00:30:30 +00:00
int main ( int argc , char * * argv )
2005-09-20 13:26:39 +00:00
{
2012-11-19 00:30:30 +00:00
int child_socket ;
struct sockaddr_un remote ;
socklen_t t ;
int max_fd ;
int update_count = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
fd_set read_fd , write_fd ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
set_main_thread ( ) ;
2012-03-07 19:35:22 +00:00
setup_fork_guards ( ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
program_name = L " fishd " ;
wsetlocale ( LC_ALL , L " " ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
/*
Parse options
*/
while ( 1 )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
static struct option
long_options [ ] =
{
{
" help " , no_argument , 0 , ' h '
}
,
{
" version " , no_argument , 0 , ' v '
}
,
{
0 , 0 , 0 , 0
}
}
;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
int opt_index = 0 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
int opt = getopt_long ( argc ,
argv ,
GETOPT_STRING ,
long_options ,
& opt_index ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
if ( opt = = - 1 )
break ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
switch ( opt )
{
2012-11-19 08:31:03 +00:00
case 0 :
break ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case ' h ' :
print_help ( argv [ 0 ] , 1 ) ;
exit ( 0 ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case ' v ' :
2013-06-24 10:12:09 +00:00
debug ( 0 , L " %ls, version %s \n " , program_name , FISH_BUILD_VERSION ) ;
2012-11-19 08:31:03 +00:00
exit ( 0 ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 08:31:03 +00:00
case ' ? ' :
return 1 ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
init ( ) ;
while ( 1 )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
int res ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
t = sizeof ( remote ) ;
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
FD_ZERO ( & read_fd ) ;
FD_ZERO ( & write_fd ) ;
FD_SET ( sock , & read_fd ) ;
max_fd = sock + 1 ;
2013-02-16 08:32:15 +00:00
for ( connection_list_t : : const_iterator iter = connections . begin ( ) ; iter ! = connections . end ( ) ; + + iter )
2012-11-19 00:30:30 +00:00
{
2013-02-16 08:32:15 +00:00
const connection_t & c = * iter ;
FD_SET ( c . fd , & read_fd ) ;
max_fd = maxi ( max_fd , c . fd + 1 ) ;
2012-11-18 10:23:22 +00:00
2013-02-16 08:32:15 +00:00
if ( ! c . unsent . empty ( ) )
2012-11-19 00:30:30 +00:00
{
2013-02-16 08:32:15 +00:00
FD_SET ( c . fd , & write_fd ) ;
2012-11-19 00:30:30 +00:00
}
}
2012-11-18 10:23:22 +00:00
2012-11-19 00:30:30 +00:00
while ( 1 )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00: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 10:23:22 +00:00
}
2012-11-19 00:30:30 +00:00
if ( FD_ISSET ( sock , & read_fd ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00: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 ) ;
2013-04-07 19:40:08 +00:00
if ( make_fd_nonblocking ( child_socket ) ! = 0 )
2012-11-19 00:30:30 +00:00
{
wperror ( L " fcntl " ) ;
close ( child_socket ) ;
}
else
{
2013-02-16 08:32:15 +00:00
connections . push_front ( connection_t ( child_socket ) ) ;
connection_t & newc = connections . front ( ) ;
send ( newc . fd , GREETING , strlen ( GREETING ) , MSG_DONTWAIT ) ;
enqueue_all ( & newc ) ;
2012-11-19 00:30:30 +00:00
}
}
2012-11-18 10:23:22 +00:00
}
2013-02-16 08:32:15 +00:00
for ( connection_list_t : : iterator iter = connections . begin ( ) ; iter ! = connections . end ( ) ; + + iter )
2012-11-18 10:23:22 +00:00
{
2013-02-16 08:32:15 +00:00
if ( FD_ISSET ( iter - > fd , & write_fd ) )
2012-11-19 00:30:30 +00:00
{
2013-02-16 08:32:15 +00:00
try_send_all ( & * iter ) ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2013-02-16 08:32:15 +00:00
for ( connection_list_t : : iterator iter = connections . begin ( ) ; iter ! = connections . end ( ) ; + + iter )
2012-11-19 00:30:30 +00:00
{
2013-02-16 08:32:15 +00:00
if ( FD_ISSET ( iter - > fd , & read_fd ) )
2012-11-19 00:30:30 +00:00
{
2013-02-16 08:32:15 +00:00
read_message ( & * iter ) ;
2012-11-19 00:30:30 +00:00
/*
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 10:23:22 +00:00
2013-02-20 01:48:51 +00:00
for ( connection_list_t : : iterator iter = connections . begin ( ) ; iter ! = connections . end ( ) ; )
2012-11-18 10:23:22 +00:00
{
2013-02-16 08:32:15 +00:00
if ( iter - > killme )
2012-11-19 00:30:30 +00:00
{
2013-02-16 08:32:15 +00:00
debug ( 4 , L " Close connection %d " , iter - > fd ) ;
2012-11-19 00:30:30 +00:00
2013-02-16 08:32:15 +00:00
while ( ! iter - > unsent . empty ( ) )
2012-11-19 00:30:30 +00:00
{
2013-02-16 08:32:15 +00:00
message_t * msg = iter - > unsent . front ( ) ;
iter - > unsent . pop ( ) ;
2012-11-19 00:30:30 +00:00
msg - > count - - ;
2013-02-16 08:32:15 +00:00
if ( ! msg - > count )
2012-11-19 00:30:30 +00:00
free ( msg ) ;
}
2013-02-16 08:32:15 +00:00
connection_destroy ( & * iter ) ;
iter = connections . erase ( iter ) ;
2012-11-19 00:30:30 +00:00
}
else
{
2013-02-16 08:32:15 +00:00
+ + iter ;
2012-11-19 00:30:30 +00:00
}
2012-11-18 10:23:22 +00:00
}
2013-02-16 08:32:15 +00:00
if ( connections . empty ( ) )
2012-11-18 10:23:22 +00:00
{
2012-11-19 00:30:30 +00:00
debug ( 0 , L " No more clients. Quitting " ) ;
save ( ) ;
break ;
2012-11-18 10:23:22 +00:00
}
}
2005-09-20 13:26:39 +00:00
}
2006-10-18 16:44:38 +00:00