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
*/
2011-12-27 11:18:46 +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
{
char * name ;
2012-01-15 14:00:00 +08:00
const char * dir = getenv ( " FISHD_SOCKET_DIR " ) ;
2005-10-01 03:50:21 +08:00
char * uname = getenv ( " USER " ) ;
2005-09-20 21:26:39 +08:00
2005-10-01 03:50:21 +08:00
if ( dir = = NULL )
{
2005-09-20 21:26:39 +08:00
dir = " /tmp " ;
2005-10-01 03:50:21 +08:00
}
2005-09-28 09:43:09 +08:00
2005-10-01 03:50:21 +08:00
if ( uname = = NULL )
{
2005-09-20 21:26:39 +08:00
struct passwd * pw ;
2005-10-01 03:50:21 +08:00
pw = getpwuid ( getuid ( ) ) ;
uname = strdup ( pw - > pw_name ) ;
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
name = ( char * ) malloc ( strlen ( dir ) + strlen ( uname ) + strlen ( SOCK_FILENAME ) + 2 ) ;
2005-10-01 03:50:21 +08:00
if ( name = = NULL )
{
wperror ( L " get_socket_filename " ) ;
exit ( EXIT_FAILURE ) ;
2005-09-28 09:43:09 +08:00
}
2005-10-01 03:50:21 +08:00
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 ) ;
2005-09-20 21:26:39 +08:00
}
2005-09-28 09:43:09 +08:00
return name ;
}
2005-11-28 07:24:09 +08:00
/**
2011-12-27 11:18:46 +08:00
Signal handler for the term signal .
2005-11-28 07:24:09 +08:00
*/
static void handle_term ( int signal )
{
quit = 1 ;
}
2012-03-02 16:27:40 +08:00
/**
Writes a pid_t in decimal representation to str .
str must contain sufficient space .
The conservatively approximate maximum number of characters a pid_t will
represent is given by : ( int ) ( 0.31 * sizeof ( pid_t ) + CHAR_BIT + 1 )
Returns the length of the string
*/
static int sprint_pid_t ( pid_t pid , char * str )
{
int len , i = 0 ;
int dig ;
/* Store digits in reverse order into string */
while ( pid ! = 0 )
{
dig = pid % 10 ;
str [ i ] = ' 0 ' + dig ;
pid = ( pid - dig ) / 10 ;
i + + ;
}
len = i ;
/* Reverse digits */
i / = 2 ;
while ( i )
{
i - - ;
dig = str [ i ] ;
str [ i ] = str [ len - 1 - i ] ;
str [ len - 1 - i ] = dig ;
}
return len ;
}
/**
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 .
*/
static int sprint_rand_digits ( char * str , int maxlen )
{
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 ) ;
srand ( ( unsigned int ) tv . tv_sec + ( unsigned int ) tv . tv_usec * 1000000UL ) ;
max = 1 + ( maxlen - 1 ) * ( rand ( ) / ( RAND_MAX + 1.0 ) ) ;
for ( i = 0 ; i < max ; i + + )
{
str [ i ] = ' 0 ' + 10 * ( rand ( ) / ( RAND_MAX + 1.0 ) ) ;
}
return i ;
}
/**
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
should be strong enough across different machines . The main assumption
though is that gethostname will not fail and this is just a " safe enough "
fallback .
The memory returned should be freed using free ( ) .
*/
static char * gen_unique_nfs_filename ( const char * filename )
{
int pidlen , hnlen , orglen = strlen ( filename ) ;
char hostname [ HOST_NAME_MAX + 1 ] ;
char * newname ;
if ( gethostname ( hostname , HOST_NAME_MAX + 1 ) = = 0 )
{
hnlen = strlen ( hostname ) ;
}
else
{
hnlen = sprint_rand_digits ( hostname , HOST_NAME_MAX ) ;
hostname [ hnlen ] = ' \0 ' ;
}
newname = ( char * ) malloc ( orglen + 1 /* period */ + hnlen + 1 /* period */ +
/* max possible pid size: 0.31 ~= log(10)2 */
( int ) ( 0.31 * sizeof ( pid_t ) * CHAR_BIT + 1 )
+ 1 /* '\0' */ ) ;
if ( newname = = NULL )
{
debug ( 1 , L " gen_unique_nfs_filename: %s " , strerror ( errno ) ) ;
return newname ;
}
memcpy ( newname , filename , orglen ) ;
newname [ orglen ] = ' . ' ;
memcpy ( newname + orglen + 1 , hostname , hnlen ) ;
newname [ orglen + 1 + hnlen ] = ' . ' ;
pidlen = sprint_pid_t ( getpid ( ) , newname + orglen + 1 + hnlen + 1 ) ;
newname [ orglen + 1 + hnlen + 1 + pidlen ] = ' \0 ' ;
/* debug( 1, L"gen_unique_nfs_filename returning with: newname = \"%s\"; "
L " HOST_NAME_MAX = %d; hnlen = %d; orglen = %d; "
L " sizeof(pid_t) = %d; maxpiddigits = %d; malloc'd size: %d " ,
newname , ( int ) HOST_NAME_MAX , hnlen , orglen ,
( int ) sizeof ( pid_t ) ,
( int ) ( 0.31 * sizeof ( pid_t ) * CHAR_BIT + 1 ) ,
( int ) ( orglen + 1 + hnlen + 1 +
( int ) ( 0.31 * sizeof ( pid_t ) * CHAR_BIT + 1 ) + 1 ) ) ; */
return newname ;
}
/**
The number of milliseconds to wait between polls when attempting to acquire
a lockfile
*/
# define LOCKPOLLINTERVAL 10
/**
Attempt to acquire a lock based on a lockfile , waiting LOCKPOLLINTERVAL
milliseconds between polls and timing out after timeout seconds ,
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 .
A unique temporary file named by appending characters to the lockfile name
is used ; any pre - existing file of the same name is subject to deletion .
*/
static int acquire_lock_file ( const char * lockfile , const int timeout , int force )
{
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 .
*/
char * linkfile = gen_unique_nfs_filename ( lockfile ) ;
if ( linkfile = = NULL )
{
goto done ;
}
( void ) unlink ( linkfile ) ;
/* OK to not use CLO_EXEC here because fishd is single threaded */
if ( ( fd = open ( linkfile , O_CREAT | O_RDONLY , 0600 ) ) = = - 1 )
{
debug ( 1 , L " acquire_lock_file: open: %s " , strerror ( errno ) ) ;
goto done ;
}
/*
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
{
/*
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 ) ;
/*
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 "
L " trying to obtain lockfile %s using "
L " linkfile %s " , lockfile , linkfile ) ;
break ;
}
}
nanosleep ( & pollint , NULL ) ;
} while ( gettimeofday ( & end , NULL ) = = 0 ) ;
done :
/* The linkfile is not needed once the lockfile has been created */
( void ) unlink ( linkfile ) ;
free ( linkfile ) ;
return ret ;
}
2005-09-28 09:43:09 +08:00
/**
2005-10-01 03:50:21 +08:00
Acquire the lock for the socket
2011-12-27 11:18:46 +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 .
2011-12-27 11:18:46 +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
*/
2005-10-01 03:50:21 +08:00
static char * acquire_socket_lock ( const char * sock_name )
2005-09-28 09:43:09 +08:00
{
2005-10-01 03:50:21 +08:00
int len = strlen ( sock_name ) ;
2011-12-27 11:18:46 +08:00
char * lockfile = ( char * ) malloc ( len + strlen ( LOCKPOSTFIX ) + 1 ) ;
if ( lockfile = = NULL )
2005-10-01 03:50:21 +08:00
{
wperror ( L " acquire_socket_lock " ) ;
exit ( EXIT_FAILURE ) ;
2005-09-28 09:43:09 +08:00
}
2005-10-01 03:50:21 +08:00
strcpy ( lockfile , sock_name ) ;
strcpy ( lockfile + len , LOCKPOSTFIX ) ;
if ( ! acquire_lock_file ( lockfile , LOCKTIMEOUT , 1 ) )
{
free ( lockfile ) ;
lockfile = NULL ;
2005-09-28 09:43:09 +08:00
}
2005-10-01 03:50:21 +08:00
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
{
int s , len , doexit = 0 ;
int exitcode = EXIT_FAILURE ;
struct sockaddr_un local ;
char * sock_name = get_socket_filename ( ) ;
2005-10-01 03:50:21 +08:00
/*
Start critical section protected by lock
2005-09-28 09:43:09 +08:00
*/
2005-10-01 03:50:21 +08:00
char * lockfile = acquire_socket_lock ( sock_name ) ;
if ( lockfile = = NULL )
{
2005-10-01 17:57:09 +08:00
debug ( 0 , L " Unable to obtain lock on socket, exiting " ) ;
2005-10-01 03:50:21 +08:00
exit ( EXIT_FAILURE ) ;
}
2006-06-13 23:39:40 +08:00
debug ( 4 , L " Acquired lockfile: %s " , lockfile ) ;
2011-12-27 11:18:46 +08:00
2005-09-28 09:43:09 +08:00
local . sun_family = AF_UNIX ;
2005-10-01 03:50:21 +08:00
strcpy ( local . sun_path , sock_name ) ;
2006-01-09 22:44:18 +08:00
len = sizeof ( local ) ;
2011-12-27 11:18:46 +08:00
2005-09-28 09:43:09 +08:00
debug ( 1 , L " Connect to socket at %s " , sock_name ) ;
2011-12-27 11:18:46 +08:00
2005-10-01 03:50:21 +08:00
if ( ( s = socket ( AF_UNIX , SOCK_STREAM , 0 ) ) = = - 1 )
{
wperror ( L " socket " ) ;
2005-09-28 09:43:09 +08:00
doexit = 1 ;
goto unlock ;
2005-09-20 21:26:39 +08:00
}
2005-10-01 03:50:21 +08:00
/*
2005-09-28 09:43:09 +08:00
First check whether the socket has been opened by another fishd ;
if so , exit with success status
*/
2005-10-01 03:50:21 +08:00
if ( connect ( s , ( struct sockaddr * ) & local , len ) = = 0 )
{
debug ( 1 , L " Socket already exists, exiting " ) ;
2005-09-28 09:43:09 +08:00
doexit = 1 ;
exitcode = 0 ;
goto unlock ;
}
2011-12-27 11:18:46 +08:00
2005-10-01 03:50:21 +08:00
unlink ( local . sun_path ) ;
if ( bind ( s , ( struct sockaddr * ) & local , len ) = = - 1 )
{
wperror ( L " bind " ) ;
2005-09-28 09:43:09 +08:00
doexit = 1 ;
goto unlock ;
2005-09-20 21:26:39 +08:00
}
2005-09-28 09:43:09 +08:00
2005-10-01 03:50:21 +08:00
if ( fcntl ( s , F_SETFL , O_NONBLOCK ) ! = 0 )
{
2005-09-20 21:26:39 +08:00
wperror ( L " fcntl " ) ;
2005-09-28 09:43:09 +08:00
close ( s ) ;
doexit = 1 ;
2005-10-01 03:50:21 +08:00
} else if ( listen ( s , 64 ) = = - 1 )
{
wperror ( L " listen " ) ;
2005-09-28 09:43:09 +08:00
doexit = 1 ;
2005-09-20 21:26:39 +08:00
}
2005-09-28 09:43:09 +08:00
unlock :
2005-10-01 03:50:21 +08:00
( void ) unlink ( lockfile ) ;
2006-06-13 23:39:40 +08:00
debug ( 4 , L " Released lockfile: %s " , lockfile ) ;
2005-10-01 03:50:21 +08:00
/*
End critical section protected by lock
2005-09-28 09:43:09 +08:00
*/
2011-12-27 11:18:46 +08:00
2005-10-01 03:50:21 +08:00
free ( lockfile ) ;
2011-12-27 11:18:46 +08:00
2005-10-01 03:50:21 +08:00
free ( sock_name ) ;
2005-09-28 09:43:09 +08:00
2005-10-01 03:50:21 +08:00
if ( doexit )
{
exit ( exitcode ) ;
}
2005-09-28 09:43:09 +08:00
2005-09-20 21:26:39 +08:00
return s ;
}
2005-09-23 21:10:31 +08:00
/**
Event handler . Broadcasts updates to all clients .
*/
static void broadcast ( int type , const wchar_t * key , const wchar_t * val )
2005-09-20 21:26:39 +08:00
{
connection_t * c ;
message_t * msg ;
if ( ! conn )
return ;
2011-12-27 11:18:46 +08:00
2005-09-23 04:16:52 +08:00
msg = create_message ( type , key , val ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
/*
2005-10-07 17:17:59 +08:00
Don ' t merge these loops , or try_send_all can free the message
prematurely
2005-09-20 21:26:39 +08:00
*/
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
for ( c = conn ; c ; c = c - > next )
{
msg - > count + + ;
2011-12-27 13:50:23 +08:00
c - > unsent - > push ( msg ) ;
2011-12-27 11:18:46 +08:00
}
2005-09-20 21:26:39 +08:00
for ( c = conn ; c ; c = c - > next )
{
try_send_all ( c ) ;
2011-12-27 11:18:46 +08:00
}
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
{
/*
Fork , and let parent exit
*/
switch ( fork ( ) )
{
case - 1 :
debug ( 0 , L " Could not put fishd in background. Quitting " ) ;
wperror ( L " fork " ) ;
exit ( 1 ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
case 0 :
{
2005-11-24 02:57:43 +08:00
/*
2005-11-28 07:22:08 +08:00
Make fishd ignore the HUP signal .
2005-11-24 02:57:43 +08:00
*/
2005-09-20 21:26:39 +08:00
struct sigaction act ;
sigemptyset ( & act . sa_mask ) ;
act . sa_flags = 0 ;
act . sa_handler = SIG_IGN ;
sigaction ( SIGHUP , & act , 0 ) ;
2005-11-28 07:24:09 +08:00
/*
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 ) ;
2005-09-20 21:26:39 +08:00
break ;
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
default :
2011-12-27 11:18:46 +08:00
{
2005-11-24 02:57:43 +08:00
debug ( 0 , L " Parent process exiting (This is normal) " ) ;
2005-09-20 21:26:39 +08:00
exit ( 0 ) ;
2011-12-27 11:18:46 +08:00
}
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
/*
Put ourself in out own processing group
*/
2005-10-07 17:17:16 +08:00
setsid ( ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
/*
2005-10-07 17:17:59 +08:00
Close stdin and stdout . We only use stderr , anyway .
2005-09-20 21:26:39 +08:00
*/
close ( 0 ) ;
close ( 1 ) ;
}
2006-10-19 19:50:23 +08:00
/**
Get environment variable value . The resulting string needs to be free ' d .
*/
2012-01-15 14:48:53 +08:00
static wchar_t * fishd_env_get ( const wchar_t * key )
2006-10-19 19:50:23 +08:00
{
char * nres , * nkey ;
wchar_t * res ;
2011-12-27 11:18:46 +08:00
2006-10-19 19:50:23 +08:00
nkey = wcs2str ( key ) ;
nres = getenv ( nkey ) ;
free ( nkey ) ;
if ( nres )
{
return str2wcs ( nres ) ;
}
else
{
res = env_universal_common_get ( key ) ;
if ( res )
res = wcsdup ( res ) ;
2011-12-27 11:18:46 +08:00
2006-10-19 19:50:23 +08:00
return env_universal_common_get ( key ) ;
}
}
/**
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 .
*/
static wchar_t * fishd_get_config ( )
{
wchar_t * xdg_dir , * home ;
int done = 0 ;
wchar_t * res = 0 ;
2011-12-27 11:18:46 +08:00
2006-10-19 19:50:23 +08:00
xdg_dir = fishd_env_get ( L " XDG_CONFIG_HOME " ) ;
if ( xdg_dir )
{
res = wcsdupcat ( xdg_dir , L " /fish " ) ;
if ( ! create_directory ( res ) )
{
done = 1 ;
}
else
{
free ( res ) ;
}
free ( xdg_dir ) ;
}
else
2011-12-27 11:18:46 +08:00
{
2006-10-19 19:50:23 +08:00
home = fishd_env_get ( L " HOME " ) ;
if ( home )
{
res = wcsdupcat ( home , L " /.config/fish " ) ;
if ( ! create_directory ( res ) )
{
done = 1 ;
}
else
{
free ( res ) ;
}
free ( home ) ;
}
}
2011-12-27 11:18:46 +08:00
2006-10-19 19:50:23 +08:00
if ( done )
{
return res ;
}
else
{
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. " ) ) ;
return 0 ;
}
2011-12-27 11:18:46 +08:00
2006-10-19 19:50:23 +08:00
}
2005-09-23 21:10:31 +08:00
/**
Load or save all variables
*/
2006-10-19 00:44:38 +08:00
static void load_or_save ( int save )
2005-09-20 21:26:39 +08:00
{
char * name ;
2006-10-19 19:50:23 +08:00
wchar_t * wdir = fishd_get_config ( ) ;
2011-12-27 11:18:46 +08:00
char * dir ;
2005-09-23 21:10:31 +08:00
char hostname [ HOSTNAME_LEN ] ;
connection_t c ;
2006-10-19 00:44:38 +08:00
int fd ;
2011-12-27 11:18:46 +08:00
2006-10-19 19:50:23 +08:00
if ( ! wdir )
2005-09-20 21:26:39 +08:00
{
2006-10-19 19:50:23 +08:00
return ;
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
2006-10-19 19:50:23 +08:00
dir = wcs2str ( wdir ) ;
free ( wdir ) ;
2011-12-27 11:18:46 +08:00
2005-09-23 21:10:31 +08:00
gethostname ( hostname , HOSTNAME_LEN ) ;
2011-12-27 11:18:46 +08:00
name = ( char * ) malloc ( strlen ( dir ) + strlen ( FILE ) + strlen ( hostname ) + 2 ) ;
2005-09-20 21:26:39 +08:00
strcpy ( name , dir ) ;
strcat ( name , " / " ) ;
strcat ( name , FILE ) ;
2005-09-23 21:10:31 +08:00
strcat ( name , hostname ) ;
2006-10-19 19:50:23 +08:00
free ( dir ) ;
2011-12-27 11:18:46 +08:00
debug ( 4 , L " Open file for %s: '%s' " ,
save ? " saving " : " loading " ,
2005-09-23 21:10:31 +08:00
name ) ;
2011-12-27 11:18:46 +08:00
2012-03-02 16:27:40 +08:00
/* OK to not use CLO_EXEC here because fishd is single threaded */
2006-10-19 00:44:38 +08:00
fd = open ( name , save ? ( O_CREAT | O_TRUNC | O_WRONLY ) : O_RDONLY , 0600 ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
free ( name ) ;
2011-12-27 11:18:46 +08:00
2006-10-19 00:44:38 +08:00
if ( fd = = - 1 )
2005-09-20 21:26:39 +08:00
{
2005-09-23 21:10:31 +08:00
debug ( 1 , L " Could not open load/save file. No previous saves? " ) ;
wperror ( L " open " ) ;
2011-12-27 11:18:46 +08:00
return ;
2005-09-20 21:26:39 +08:00
}
2006-06-13 23:39:40 +08:00
debug ( 4 , L " File open on fd %d " , c . fd ) ;
2005-09-23 21:10:31 +08:00
2006-10-19 00:44:38 +08:00
connection_init ( & c , fd ) ;
2005-09-23 21:10:31 +08:00
if ( save )
2006-06-17 22:04:06 +08:00
{
2011-12-27 11:18:46 +08:00
2009-02-23 04:28:52 +08:00
write_loop ( c . fd , SAVE_MSG , strlen ( SAVE_MSG ) ) ;
2005-09-23 21:10:31 +08:00
enqueue_all ( & c ) ;
2006-06-17 22:04:06 +08:00
}
2005-09-23 21:10:31 +08:00
else
read_message ( & c ) ;
2011-12-27 11:18:46 +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
{
2005-09-23 21:10:31 +08:00
load_or_save ( 0 ) ;
}
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 ( )
{
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
2005-09-20 21:26:39 +08:00
sock = get_socket ( ) ;
2011-12-27 11:18:46 +08:00
daemonize ( ) ;
2005-09-20 21:26:39 +08:00
env_universal_common_init ( & broadcast ) ;
2011-12-27 11:18:46 +08:00
load ( ) ;
2005-09-20 21:26:39 +08:00
}
2005-10-24 23:26:25 +08:00
/**
Main function for fishd
*/
2005-09-20 21:26:39 +08:00
int main ( int argc , char * * argv )
{
2005-11-28 07:24:09 +08:00
int child_socket ;
2005-09-20 21:26:39 +08:00
struct sockaddr_un remote ;
2005-11-28 07:24:09 +08:00
socklen_t t ;
2005-09-20 21:26:39 +08:00
int max_fd ;
int update_count = 0 ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
fd_set read_fd , write_fd ;
2006-10-19 19:50:23 +08:00
2012-01-06 05:58:48 +08:00
set_main_thread ( ) ;
2011-12-27 11:18:46 +08:00
2006-01-24 00:25:34 +08:00
program_name = L " fishd " ;
2011-12-27 11:18:46 +08:00
wsetlocale ( LC_ALL , L " " ) ;
2006-01-24 00:25:34 +08:00
/*
Parse options
*/
while ( 1 )
{
static struct option
long_options [ ] =
{
{
2011-12-27 11:18:46 +08:00
" help " , no_argument , 0 , ' h '
2006-01-24 00:25:34 +08:00
}
,
{
2011-12-27 11:18:46 +08:00
" version " , no_argument , 0 , ' v '
2006-01-24 00:25:34 +08:00
}
,
2011-12-27 11:18:46 +08:00
{
0 , 0 , 0 , 0
2006-01-24 00:25:34 +08:00
}
}
;
2011-12-27 11:18:46 +08:00
2006-01-24 00:25:34 +08:00
int opt_index = 0 ;
2011-12-27 11:18:46 +08:00
2006-01-24 00:25:34 +08:00
int opt = getopt_long ( argc ,
2011-12-27 11:18:46 +08:00
argv ,
2006-05-26 22:55:51 +08:00
GETOPT_STRING ,
2011-12-27 11:18:46 +08:00
long_options ,
2006-01-24 00:25:34 +08:00
& opt_index ) ;
2011-12-27 11:18:46 +08:00
2006-01-24 00:25:34 +08:00
if ( opt = = - 1 )
break ;
2011-12-27 11:18:46 +08:00
2006-01-24 00:25:34 +08:00
switch ( opt )
{
case 0 :
2011-12-27 11:18:46 +08:00
break ;
2006-01-24 00:25:34 +08:00
case ' h ' :
2006-11-18 00:24:38 +08:00
print_help ( argv [ 0 ] , 1 ) ;
2011-12-27 11:18:46 +08:00
exit ( 0 ) ;
2006-01-24 00:25:34 +08:00
case ' v ' :
debug ( 0 , L " %ls, version %s \n " , program_name , PACKAGE_VERSION ) ;
2011-12-27 11:18:46 +08:00
exit ( 0 ) ;
2006-01-24 00:25:34 +08:00
case ' ? ' :
return 1 ;
2011-12-27 11:18:46 +08:00
}
2006-01-24 00:25:34 +08:00
}
2011-12-27 11:18:46 +08:00
2006-01-24 00:25:34 +08:00
init ( ) ;
2011-12-27 11:18:46 +08:00
while ( 1 )
2005-09-20 21:26:39 +08:00
{
connection_t * c ;
int res ;
2011-12-27 11:18:46 +08:00
t = sizeof ( remote ) ;
2005-09-20 21:26:39 +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 ) ;
2011-12-27 11:18:46 +08:00
2011-12-27 13:56:23 +08:00
if ( ! c - > unsent - > empty ( ) )
2005-09-20 21:26:39 +08:00
{
FD_SET ( c - > fd , & write_fd ) ;
}
}
2005-11-28 07:24:09 +08:00
while ( 1 )
2005-09-20 21:26:39 +08:00
{
2005-11-28 07:24:09 +08:00
res = select ( max_fd , & read_fd , & write_fd , 0 , 0 ) ;
if ( quit )
{
save ( ) ;
exit ( 0 ) ;
}
2011-12-27 11:18:46 +08:00
2005-11-28 07:24:09 +08:00
if ( res ! = - 1 )
break ;
2011-12-27 11:18:46 +08:00
2005-11-28 07:24:09 +08:00
if ( errno ! = EINTR )
{
wperror ( L " select " ) ;
exit ( 1 ) ;
}
2005-09-20 21:26:39 +08:00
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
if ( FD_ISSET ( sock , & read_fd ) )
{
2011-12-27 11:18:46 +08:00
if ( ( child_socket =
accept ( sock ,
( struct sockaddr * ) & remote ,
2005-09-20 21:26:39 +08:00
& t ) ) = = - 1 ) {
wperror ( L " accept " ) ;
exit ( 1 ) ;
}
else
{
2006-06-13 23:39:40 +08:00
debug ( 4 , L " Connected with new child on fd %d " , child_socket ) ;
2005-09-20 21:26:39 +08:00
if ( fcntl ( child_socket , F_SETFL , O_NONBLOCK ) ! = 0 )
{
wperror ( L " fcntl " ) ;
2011-12-27 11:18:46 +08:00
close ( child_socket ) ;
2005-09-20 21:26:39 +08:00
}
else
{
2011-12-27 11:18:46 +08:00
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 ;
2005-09-20 21:26:39 +08:00
}
}
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
for ( c = conn ; c ; c = c - > next )
{
if ( FD_ISSET ( c - > fd , & write_fd ) )
{
try_send_all ( c ) ;
}
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +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 + + ;
2005-09-23 21:10:31 +08:00
if ( update_count > = 64 )
2005-09-20 21:26:39 +08:00
{
save ( ) ;
update_count = 0 ;
}
}
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
connection_t * prev = 0 ;
c = conn ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
while ( c )
{
if ( c - > killme )
{
2006-06-13 23:39:40 +08:00
debug ( 4 , L " Close connection %d " , c - > fd ) ;
2005-09-20 21:26:39 +08:00
2011-12-27 13:56:23 +08:00
while ( ! c - > unsent - > empty ( ) )
2005-09-20 21:26:39 +08:00
{
2011-12-27 13:50:23 +08:00
message_t * msg = c - > unsent - > front ( ) ;
c - > unsent - > pop ( ) ;
2005-09-20 21:26:39 +08:00
msg - > count - - ;
if ( ! msg - > count )
free ( msg ) ;
}
2011-12-27 11:18:46 +08:00
2006-10-19 00:44:38 +08:00
connection_destroy ( c ) ;
2005-09-20 21:26:39 +08:00
if ( prev )
{
prev - > next = c - > next ;
}
else
{
conn = c - > next ;
}
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
free ( c ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
c = ( prev ? prev - > next : conn ) ;
2011-12-27 11:18:46 +08:00
2005-09-20 21:26:39 +08:00
}
else
{
prev = c ;
c = c - > next ;
}
}
2006-10-18 05:11:29 +08:00
2005-09-20 21:26:39 +08:00
if ( ! conn )
{
debug ( 0 , L " No more clients. Quitting " ) ;
2011-12-27 11:18:46 +08:00
save ( ) ;
2005-09-20 21:26:39 +08:00
env_universal_common_destroy ( ) ;
2006-10-19 19:50:23 +08:00
break ;
2011-12-27 11:18:46 +08:00
}
2006-10-19 00:44:38 +08:00
2005-09-20 21:26:39 +08:00
}
}
2006-10-19 00:44:38 +08:00