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>
# 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>
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 "halloc.h"
# include "halloc_util.h"
# include "path.h"
2006-11-17 16:24:38 +00:00
# include "print_help.h"
2005-09-20 13:26:39 +00:00
2005-09-23 13:10:31 +00:00
/**
Maximum length of socket filename
*/
2005-09-20 13:26:39 +00:00
# ifndef UNIX_PATH_MAX
# 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
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
/**
The name of the save file . The hostname is appended to this .
*/
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
*/
2005-09-20 13:26:39 +00:00
static connection_t * conn ;
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
*/
2005-09-30 19:50:21 +00:00
static char * get_socket_filename ( )
2005-09-20 13:26:39 +00:00
{
char * name ;
2005-09-30 19:50:21 +00:00
char * dir = getenv ( " FISHD_SOCKET_DIR " ) ;
char * uname = getenv ( " USER " ) ;
2005-09-20 13:26:39 +00:00
2005-09-30 19:50:21 +00:00
if ( dir = = NULL )
{
2005-09-20 13:26:39 +00:00
dir = " /tmp " ;
2005-09-30 19:50:21 +00:00
}
2005-09-28 01:43:09 +00:00
2005-09-30 19:50:21 +00:00
if ( uname = = NULL )
{
2005-09-20 13:26:39 +00:00
struct passwd * pw ;
2005-09-30 19:50:21 +00:00
pw = getpwuid ( getuid ( ) ) ;
uname = strdup ( pw - > pw_name ) ;
2005-09-20 13:26:39 +00:00
}
2005-09-30 19:50:21 +00:00
name = malloc ( strlen ( dir ) + strlen ( uname ) + strlen ( SOCK_FILENAME ) + 2 ) ;
if ( name = = NULL )
{
wperror ( L " get_socket_filename " ) ;
exit ( EXIT_FAILURE ) ;
2005-09-28 01:43:09 +00:00
}
2005-09-30 19:50:21 +00: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 13:26:39 +00:00
}
2005-09-28 01:43:09 +00:00
return name ;
}
2005-11-27 23:24:09 +00:00
/**
Signal handler for the term signal .
*/
static void handle_term ( int signal )
{
quit = 1 ;
}
2005-09-28 01:43:09 +00:00
/**
2005-09-30 19:50:21 +00:00
Acquire the lock for the socket
Returns the name of the lock file if successful or
NULL if unable to obtain lock .
The returned string must be free ( ) d after unlink ( ) ing the file to release
the lock
2005-09-28 01:43:09 +00:00
*/
2005-09-30 19:50:21 +00:00
static char * acquire_socket_lock ( const char * sock_name )
2005-09-28 01:43:09 +00:00
{
2005-09-30 19:50:21 +00:00
int len = strlen ( sock_name ) ;
char * lockfile = malloc ( len + strlen ( LOCKPOSTFIX ) + 1 ) ;
if ( lockfile = = NULL )
{
wperror ( L " acquire_socket_lock " ) ;
exit ( EXIT_FAILURE ) ;
2005-09-28 01:43:09 +00:00
}
2005-09-30 19:50:21 +00:00
strcpy ( lockfile , sock_name ) ;
strcpy ( lockfile + len , LOCKPOSTFIX ) ;
if ( ! acquire_lock_file ( lockfile , LOCKTIMEOUT , 1 ) )
{
free ( lockfile ) ;
lockfile = NULL ;
2005-09-28 01:43:09 +00:00
}
2005-09-30 19:50:21 +00:00
return lockfile ;
2005-09-28 01:43:09 +00:00
}
/**
Connects to the fish socket and starts listening for connections
*/
2005-09-30 19:50:21 +00:00
static int get_socket ( )
2005-09-28 01:43:09 +00:00
{
int s , len , doexit = 0 ;
int exitcode = EXIT_FAILURE ;
struct sockaddr_un local ;
char * sock_name = get_socket_filename ( ) ;
2005-09-30 19:50:21 +00:00
/*
Start critical section protected by lock
2005-09-28 01:43:09 +00:00
*/
2005-09-30 19:50:21 +00:00
char * lockfile = acquire_socket_lock ( sock_name ) ;
if ( lockfile = = NULL )
{
2005-10-01 09:57:09 +00:00
debug ( 0 , L " Unable to obtain lock on socket, exiting " ) ;
2005-09-30 19:50:21 +00:00
exit ( EXIT_FAILURE ) ;
}
2006-06-13 15:39:40 +00:00
debug ( 4 , L " Acquired lockfile: %s " , lockfile ) ;
2005-09-20 13:26:39 +00:00
2005-09-28 01:43:09 +00:00
local . sun_family = AF_UNIX ;
2005-09-30 19:50:21 +00:00
strcpy ( local . sun_path , sock_name ) ;
2006-01-09 14:44:18 +00:00
len = sizeof ( local ) ;
2005-09-28 01:43:09 +00:00
debug ( 1 , L " Connect to socket at %s " , sock_name ) ;
2005-09-20 13:26:39 +00:00
2005-09-30 19:50:21 +00:00
if ( ( s = socket ( AF_UNIX , SOCK_STREAM , 0 ) ) = = - 1 )
{
wperror ( L " socket " ) ;
2005-09-28 01:43:09 +00:00
doexit = 1 ;
goto unlock ;
2005-09-20 13:26:39 +00:00
}
2005-09-30 19:50:21 +00:00
/*
2005-09-28 01:43:09 +00:00
First check whether the socket has been opened by another fishd ;
if so , exit with success status
*/
2005-09-30 19:50:21 +00:00
if ( connect ( s , ( struct sockaddr * ) & local , len ) = = 0 )
{
debug ( 1 , L " Socket already exists, exiting " ) ;
2005-09-28 01:43:09 +00:00
doexit = 1 ;
exitcode = 0 ;
goto unlock ;
}
2005-09-20 13:26:39 +00:00
2005-09-30 19:50:21 +00:00
unlink ( local . sun_path ) ;
if ( bind ( s , ( struct sockaddr * ) & local , len ) = = - 1 )
{
wperror ( L " bind " ) ;
2005-09-28 01:43:09 +00:00
doexit = 1 ;
goto unlock ;
2005-09-20 13:26:39 +00:00
}
2005-09-28 01:43:09 +00:00
2005-09-30 19:50:21 +00:00
if ( fcntl ( s , F_SETFL , O_NONBLOCK ) ! = 0 )
{
2005-09-20 13:26:39 +00:00
wperror ( L " fcntl " ) ;
2005-09-28 01:43:09 +00:00
close ( s ) ;
doexit = 1 ;
2005-09-30 19:50:21 +00:00
} else if ( listen ( s , 64 ) = = - 1 )
{
wperror ( L " listen " ) ;
2005-09-28 01:43:09 +00:00
doexit = 1 ;
2005-09-20 13:26:39 +00:00
}
2005-09-28 01:43:09 +00:00
unlock :
2005-09-30 19:50:21 +00:00
( void ) unlink ( lockfile ) ;
2006-06-13 15:39:40 +00:00
debug ( 4 , L " Released lockfile: %s " , lockfile ) ;
2005-09-30 19:50:21 +00:00
/*
End critical section protected by lock
2005-09-28 01:43:09 +00:00
*/
2005-09-30 19:50:21 +00:00
free ( lockfile ) ;
2005-09-20 13:26:39 +00:00
2005-09-30 19:50:21 +00:00
free ( sock_name ) ;
2005-09-28 01:43:09 +00:00
2005-09-30 19:50:21 +00:00
if ( doexit )
{
exit ( exitcode ) ;
}
2005-09-28 01:43:09 +00:00
2005-09-20 13:26:39 +00:00
return s ;
}
2005-09-23 13:10:31 +00:00
/**
Event handler . Broadcasts updates to all clients .
*/
static void broadcast ( int type , const wchar_t * key , const wchar_t * val )
2005-09-20 13:26:39 +00:00
{
connection_t * c ;
message_t * msg ;
if ( ! conn )
return ;
2005-10-07 09:17:59 +00:00
2005-09-22 20:16:52 +00:00
msg = create_message ( type , key , val ) ;
2005-09-20 13:26:39 +00:00
/*
2005-10-07 09:17:59 +00:00
Don ' t merge these loops , or try_send_all can free the message
prematurely
2005-09-20 13:26:39 +00:00
*/
2005-09-22 20:16:52 +00:00
2005-09-20 13:26:39 +00:00
for ( c = conn ; c ; c = c - > next )
{
msg - > count + + ;
q_put ( & c - > unsent , msg ) ;
}
2005-09-22 20:16:52 +00:00
2005-09-20 13:26:39 +00:00
for ( c = conn ; c ; c = c - > next )
{
try_send_all ( c ) ;
}
}
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
{
/*
Fork , and let parent exit
*/
switch ( fork ( ) )
{
case - 1 :
debug ( 0 , L " Could not put fishd in background. Quitting " ) ;
wperror ( L " fork " ) ;
exit ( 1 ) ;
case 0 :
{
2005-11-23 18:57:43 +00:00
/*
2005-11-27 23:22:08 +00:00
Make fishd ignore the HUP signal .
2005-11-23 18:57:43 +00:00
*/
2005-09-20 13:26:39 +00:00
struct sigaction act ;
sigemptyset ( & act . sa_mask ) ;
act . sa_flags = 0 ;
act . sa_handler = SIG_IGN ;
sigaction ( SIGHUP , & act , 0 ) ;
2005-11-27 23:24:09 +00: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 13:26:39 +00:00
break ;
}
default :
{
2005-11-23 18:57:43 +00:00
debug ( 0 , L " Parent process exiting (This is normal) " ) ;
2005-09-20 13:26:39 +00:00
exit ( 0 ) ;
}
}
2005-11-27 23:22:08 +00:00
2005-09-20 13:26:39 +00:00
/*
Put ourself in out own processing group
*/
2005-10-07 09:17:16 +00:00
setsid ( ) ;
2005-11-27 23:22:08 +00:00
2005-09-20 13:26:39 +00:00
/*
2005-10-07 09:17:59 +00:00
Close stdin and stdout . We only use stderr , anyway .
2005-09-20 13:26:39 +00:00
*/
close ( 0 ) ;
close ( 1 ) ;
}
2006-10-19 11:50:23 +00:00
/**
Get environment variable value . The resulting string needs to be free ' d .
*/
static wchar_t * fishd_env_get ( wchar_t * key )
{
char * nres , * nkey ;
wchar_t * res ;
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 ) ;
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 ;
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
{
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 ) ;
}
}
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 ;
}
}
2005-09-23 13:10:31 +00:00
/**
Load or save all variables
*/
2006-10-18 16:44:38 +00:00
static void load_or_save ( int save )
2005-09-20 13:26:39 +00:00
{
char * name ;
2006-10-19 11:50:23 +00:00
wchar_t * wdir = fishd_get_config ( ) ;
char * dir ;
2005-09-23 13:10:31 +00:00
char hostname [ HOSTNAME_LEN ] ;
connection_t c ;
2006-10-18 16:44:38 +00:00
int fd ;
2005-09-23 13:10:31 +00:00
2006-10-19 11:50:23 +00:00
if ( ! wdir )
2005-09-20 13:26:39 +00:00
{
2006-10-19 11:50:23 +00:00
return ;
2005-09-20 13:26:39 +00:00
}
2005-09-23 13:10:31 +00:00
2006-10-19 11:50:23 +00:00
dir = wcs2str ( wdir ) ;
free ( wdir ) ;
2005-09-23 13:10:31 +00:00
gethostname ( hostname , HOSTNAME_LEN ) ;
name = malloc ( strlen ( dir ) + strlen ( FILE ) + strlen ( hostname ) + 2 ) ;
2005-09-20 13:26:39 +00:00
strcpy ( name , dir ) ;
strcat ( name , " / " ) ;
strcat ( name , FILE ) ;
2005-09-23 13:10:31 +00:00
strcat ( name , hostname ) ;
2006-10-19 11:50:23 +00:00
free ( dir ) ;
2005-09-20 13:26:39 +00:00
2006-06-13 15:39:40 +00:00
debug ( 4 , L " Open file for %s: '%s' " ,
2005-09-23 13:10:31 +00:00
save ? " saving " : " loading " ,
name ) ;
2005-09-20 13:26:39 +00:00
2006-10-18 16:44:38 +00:00
fd = open ( name , save ? ( O_CREAT | O_TRUNC | O_WRONLY ) : O_RDONLY , 0600 ) ;
2005-09-20 13:26:39 +00:00
free ( name ) ;
2006-10-18 16:44:38 +00:00
if ( fd = = - 1 )
2005-09-20 13:26:39 +00:00
{
2005-09-23 13:10:31 +00:00
debug ( 1 , L " Could not open load/save file. No previous saves? " ) ;
wperror ( L " open " ) ;
2005-10-07 09:17:59 +00:00
return ;
2005-09-20 13:26:39 +00:00
}
2006-06-13 15:39:40 +00:00
debug ( 4 , L " File open on fd %d " , c . fd ) ;
2005-09-23 13:10:31 +00:00
2006-10-18 16:44:38 +00:00
connection_init ( & c , fd ) ;
2005-09-23 13:10:31 +00:00
if ( save )
2006-06-17 14:04:06 +00:00
{
write ( c . fd , SAVE_MSG , strlen ( SAVE_MSG ) ) ;
2005-09-23 13:10:31 +00:00
enqueue_all ( & c ) ;
2006-06-17 14:04:06 +00:00
}
2005-09-23 13:10:31 +00:00
else
read_message ( & c ) ;
2006-10-18 16:44:38 +00:00
connection_destroy ( & c ) ;
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
{
2005-09-23 13:10:31 +00:00
load_or_save ( 0 ) ;
}
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 ( )
{
load_or_save ( 1 ) ;
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
2005-09-20 13:26:39 +00:00
sock = get_socket ( ) ;
daemonize ( ) ;
env_universal_common_init ( & broadcast ) ;
load ( ) ;
}
2005-10-24 15:26:25 +00:00
/**
Main function for fishd
*/
2005-09-20 13:26:39 +00:00
int main ( int argc , char * * argv )
{
2005-11-27 23:24:09 +00:00
int child_socket ;
2005-09-20 13:26:39 +00:00
struct sockaddr_un remote ;
2005-11-27 23:24:09 +00:00
socklen_t t ;
2005-09-20 13:26:39 +00:00
int max_fd ;
int update_count = 0 ;
fd_set read_fd , write_fd ;
2006-10-19 11:50:23 +00:00
halloc_util_init ( ) ;
2005-09-20 13:26:39 +00:00
2006-01-23 16:25:34 +00:00
program_name = L " fishd " ;
wsetlocale ( LC_ALL , L " " ) ;
/*
Parse options
*/
while ( 1 )
{
static struct option
long_options [ ] =
{
{
" help " , no_argument , 0 , ' h '
}
,
{
" version " , no_argument , 0 , ' v '
}
,
{
0 , 0 , 0 , 0
}
}
;
int opt_index = 0 ;
int opt = getopt_long ( argc ,
argv ,
2006-05-26 14:55:51 +00:00
GETOPT_STRING ,
2006-01-23 16:25:34 +00:00
long_options ,
& opt_index ) ;
if ( opt = = - 1 )
break ;
switch ( opt )
{
case 0 :
break ;
case ' h ' :
2006-11-17 16:24:38 +00:00
print_help ( argv [ 0 ] , 1 ) ;
2006-01-23 16:25:34 +00:00
exit ( 0 ) ;
case ' v ' :
debug ( 0 , L " %ls, version %s \n " , program_name , PACKAGE_VERSION ) ;
exit ( 0 ) ;
case ' ? ' :
return 1 ;
}
}
2005-09-20 13:26:39 +00:00
2006-01-23 16:25:34 +00:00
init ( ) ;
2005-09-20 13:26:39 +00:00
while ( 1 )
{
connection_t * c ;
int res ;
2006-10-18 16:44:38 +00:00
t = sizeof ( remote ) ;
2005-09-20 13:26:39 +00: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 ) ;
if ( ! q_empty ( & c - > unsent ) )
{
FD_SET ( c - > fd , & write_fd ) ;
}
}
2005-11-27 23:24:09 +00:00
while ( 1 )
2005-09-20 13:26:39 +00:00
{
2005-11-27 23:24:09 +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 ) ;
}
2005-09-20 13:26:39 +00:00
}
2005-11-27 23:22:08 +00:00
2005-09-20 13:26:39 +00:00
if ( FD_ISSET ( sock , & read_fd ) )
{
if ( ( child_socket =
accept ( sock ,
( struct sockaddr * ) & remote ,
& t ) ) = = - 1 ) {
wperror ( L " accept " ) ;
exit ( 1 ) ;
}
else
{
2006-06-13 15:39:40 +00:00
debug ( 4 , L " Connected with new child on fd %d " , child_socket ) ;
2005-09-20 13:26:39 +00:00
if ( fcntl ( child_socket , F_SETFL , O_NONBLOCK ) ! = 0 )
{
wperror ( L " fcntl " ) ;
close ( child_socket ) ;
}
else
{
2006-10-18 16:44:38 +00:00
connection_t * new = malloc ( sizeof ( connection_t ) ) ;
connection_init ( new , child_socket ) ;
2005-09-20 13:26:39 +00:00
new - > next = conn ;
send ( new - > fd , GREETING , strlen ( GREETING ) , MSG_DONTWAIT ) ;
enqueue_all ( new ) ;
conn = new ;
}
}
}
for ( c = conn ; c ; c = c - > next )
{
if ( FD_ISSET ( c - > fd , & write_fd ) )
{
try_send_all ( c ) ;
}
}
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 13:10:31 +00:00
if ( update_count > = 64 )
2005-09-20 13:26:39 +00:00
{
save ( ) ;
update_count = 0 ;
}
}
}
connection_t * prev = 0 ;
c = conn ;
while ( c )
{
if ( c - > killme )
{
2006-06-13 15:39:40 +00:00
debug ( 4 , L " Close connection %d " , c - > fd ) ;
2005-09-20 13:26:39 +00:00
while ( ! q_empty ( & c - > unsent ) )
{
message_t * msg = ( message_t * ) q_get ( & c - > unsent ) ;
msg - > count - - ;
if ( ! msg - > count )
free ( msg ) ;
}
2006-10-18 16:44:38 +00:00
connection_destroy ( c ) ;
2005-09-20 13:26:39 +00:00
if ( prev )
{
prev - > next = c - > next ;
}
else
{
conn = c - > next ;
}
free ( c ) ;
c = ( prev ? prev - > next : conn ) ;
}
else
{
prev = c ;
c = c - > next ;
}
}
2006-10-17 21:11:29 +00:00
2005-09-20 13:26:39 +00:00
if ( ! conn )
{
debug ( 0 , L " No more clients. Quitting " ) ;
save ( ) ;
env_universal_common_destroy ( ) ;
2006-10-19 11:50:23 +00:00
break ;
2005-10-07 09:17:59 +00:00
}
2006-10-18 16:44:38 +00:00
2005-09-20 13:26:39 +00:00
}
2006-10-19 11:50:23 +00:00
halloc_util_destroy ( ) ;
2005-09-20 13:26:39 +00:00
}
2006-10-18 16:44:38 +00:00