From 77c7a026ee2ca46a114d6886d8b02ad0a96b1e3b Mon Sep 17 00:00:00 2001 From: axel Date: Fri, 23 Sep 2005 23:10:31 +1000 Subject: [PATCH] Fix for various redirection problems darcs-hash:20050923131031-ac50b-b9e2897e7f20a087260f97d1342deaed65ad7d70.gz --- ChangeLog | 5 ++ env.c | 2 +- env_universal.c | 5 +- env_universal_common.c | 4 +- exec.c | 137 +++++++++++++++-------------------- fishd.c | 143 ++++++++++++++++++++++--------------- parser.c | 2 +- proc.c | 157 +++++++++++++++++++---------------------- reader.c | 7 ++ 9 files changed, 232 insertions(+), 230 deletions(-) diff --git a/ChangeLog b/ChangeLog index 73010c957..3f55d4f07 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2005-09-22 Axel Liljencrantz + + * env_universal.c, env_universal_common.c, env.c, fishd.c, builtin_set.c, exec.c, init/fish_interactive.fish: Exportable universal variables + + 2005-09-20 Axel Liljencrantz * exec.c, reader.c (exec_read_io_buffer, run_pager): Don't leak file descriptors when showing completion pager diff --git a/env.c b/env.c index ea146196f..cf96515e3 100644 --- a/env.c +++ b/env.c @@ -42,7 +42,7 @@ /** Command used to start fishd */ -#define FISHD_CMD L"if which fishd >/dev/null ^/dev/null; fishd ^/tmp/fish.%s.log; end" +#define FISHD_CMD L"fishd ^/tmp/fish.%s.log" /** At init, we read all the environment variables from this array diff --git a/env_universal.c b/env_universal.c index 3f73d7b4c..e9b4b492b 100644 --- a/env_universal.c +++ b/env_universal.c @@ -228,13 +228,15 @@ int env_universal_read_all() return 0; debug( 2, L"Get new fishd connection" ); - + init = 0; env_universal_server.fd = get_socket(1); init = 1; if( env_universal_server.fd >= 0 ) + { env_universal_barrier(); + } } if( env_universal_server.fd != -1 ) @@ -252,7 +254,6 @@ int env_universal_read_all() wchar_t *env_universal_get( const wchar_t *name ) { - debug( 3, L"env_universal_get( %ls )", name ); if( !init) return 0; diff --git a/env_universal_common.c b/env_universal_common.c index 6aa42810d..175fb28e7 100644 --- a/env_universal_common.c +++ b/env_universal_common.c @@ -313,7 +313,7 @@ int try_send( message_t *msg, default: debug( 1, - L"Error while sending message to fd %d. Closing connection", + L"Error while sending universal variable message to fd %d. Closing connection", fd ); wperror( L"write" ); @@ -348,8 +348,6 @@ void try_send_all( connection_t *c ) return; case -1: - debug( 1, - L"Socket dead!!!" ); c->killme = 1; return; } diff --git a/exec.c b/exec.c index c280aba08..953afd87a 100644 --- a/exec.c +++ b/exec.c @@ -61,6 +61,20 @@ pid_t getpgid( pid_t pid ); */ #define FORK_ERROR L"Could not create child process - exiting" + +static void close_loop( int fd ) +{ + while( close(fd) == -1 ) + { + if( errno != EINTR ) + { + debug( 1, FD_ERROR, fd ); + wperror( L"close" ); + } + } +} + + /** Make sure the fd used by this redirection is not used by i.e. a pipe. */ @@ -102,7 +116,7 @@ static void handle_child_io( io_data_t *io ) { if( env_universal_server.fd >= 0 ) - close( env_universal_server.fd ); + close_loop( env_universal_server.fd ); for( ; io; io=io->next ) { @@ -122,8 +136,11 @@ static void handle_child_io( io_data_t *io ) } - if( close(io->fd) == -1 ) + while( close(io->fd) == -1 ) { + if( errno != EINTR ) + break; + /* debug( 1, FD_ERROR, io->fd ); @@ -155,14 +172,7 @@ static void handle_child_io( io_data_t *io ) wperror( L"dup2" ); exit(1); } - if( close( tmp ) == -1 ) - { - debug( 1, - FD_ERROR, - io->fd ); - wperror( L"close" ); - exit(1); - } + close_loop( tmp ); } break; case IO_FD: @@ -203,11 +213,11 @@ static void handle_child_io( io_data_t *io ) if( fd_to_dup ) { - close( io->pipe_fd[0]); - close( io->pipe_fd[1]); + close_loop( io->pipe_fd[0]); + close_loop( io->pipe_fd[1]); } else - close( io->pipe_fd[fd_to_dup] ); + close_loop( io->pipe_fd[fd_to_dup] ); /* if( close( io[i].pipe_fd[ io->fd ] ) == -1 ) @@ -314,37 +324,44 @@ static int has_fd( io_data_t *d, int fd ) void exec_read_io_buffer( io_data_t *d ) { - if( close( d->pipe_fd[1] ) == -1 ) - { - debug( 1, PIPE_ERROR ); - wperror( L"close" ); - } + close_loop(d->pipe_fd[1] ); if( d->io_mode == IO_BUFFER ) - { - - if( fcntl( d->pipe_fd[0], F_SETFL, 0 ) ) + { +/* if( fcntl( d->pipe_fd[0], F_SETFL, 0 ) ) { wperror( L"fcntl" ); return; } - +*/ debug( 3, L"exec_read_io_buffer: blocking read on fd %d", d->pipe_fd[0] ); while(1) { char b[4096]; int l; - l=read_blocked( d->pipe_fd[0], b, 4096 ); + l=read_blocked( d->pipe_fd[0], b, 4096 ); if( l==0 ) { break; } else if( l<0 ) { - debug( 1, - L"An error occured while reading output from code block on fd %d", d->pipe_fd[0] ); - wperror( L"exec_read_io_buffer" ); + /* + exec_read_io_buffer is only called on jobs that have + exited, and will therefore never block. But a broken + pipe seems to cause some flags to reset, causing the + EOF flag to not be set. Therefore, EAGAIN is ignored + and we exit anyway. + */ + if( errno != EAGAIN ) + { + debug( 1, + L"An error occured while reading output from code block on fd %d", + d->pipe_fd[0] ); + wperror( L"exec_read_io_buffer" ); + } + break; } else @@ -375,7 +392,9 @@ io_data_t *exec_make_io_buffer() free( buffer_redirect ); return 0; } - else if( fcntl( buffer_redirect->pipe_fd[0], F_SETFL, O_NONBLOCK ) ) + else if( fcntl( buffer_redirect->pipe_fd[0], + F_SETFL, + O_NONBLOCK ) ) { debug( 1, PIPE_ERROR ); wperror( L"fcntl" ); @@ -388,12 +407,8 @@ io_data_t *exec_make_io_buffer() void exec_free_io_buffer( io_data_t *io_buffer ) { - if( close( io_buffer->pipe_fd[0] ) == -1) - { - debug( 1, PIPE_ERROR ); - wperror( L"close" ); - - } + + close_loop( io_buffer->pipe_fd[0] ); /* Dont free fd for writing. This should already be free'd before calling exec_read_io_buffer on the buffer @@ -457,13 +472,6 @@ static io_data_t *io_transmogrify( io_data_t * in ) } out->old_fd = fd; -/* - fwprintf( stderr, - L"Replacing call to redirect %d to file '%ls' with call to redirect to fd %d\n", - in->fd, - in->filename, - fd ); -*/ break; } } @@ -484,13 +492,7 @@ static void io_untransmogrify( io_data_t * in, io_data_t *out ) switch( in->io_mode ) { case IO_FILE: - if( close( out->old_fd ) == -1 ) - { - debug( 1, - FILE_ERROR, - in->filename ); - wperror( L"close" ); - } + close_loop( out->old_fd ); break; } free(out); @@ -567,11 +569,6 @@ static void io_print( io_data_t *io ) static int handle_new_child( job_t *j, process_t *p ) { - /* - If we do not output to stdout, builtin IO will not appear. - I have no idea why... - */ - //fwprintf( stdout, L"" ); if(is_interactive && !is_subshell && !is_block) { @@ -664,10 +661,8 @@ void exec( job_t *j ) io_data_t *tmp; io_data_t *io_buffer =0; - -// if( j->job_id > 3 ) - - debug( 2, L"Exec job %ls with id %d", j->command, j->job_id ); + + debug( 3, L"Exec job %ls with id %d", j->command, j->job_id ); if( j->first_process->type==INTERNAL_EXEC ) { @@ -694,25 +689,12 @@ void exec( job_t *j ) if( block_io ) { -// fwprintf( stderr, L"Before\n" ); -// io_print( j->io ); - if( j->io ) j->io = io_add( io_duplicate(block_io), j->io ); else j->io=io_duplicate(block_io); - -// fwprintf( stderr, L"After\n" ); -// io_print( j->io ); - } -/* - if true; - read foo; read bar; read baz; - end io = io_add( j->io, &pipe_write ); for (p = j->first_process; p; p = p->next) @@ -722,12 +704,10 @@ void exec( job_t *j ) if( p->type == EXTERNAL ) env_export_arr( 1 ); - /* Set up fd:s that will be used in the pipe */ -// fwprintf( stderr, L"Create process %ls\n", p->actual_cmd ); if( p == j->first_process->next ) { @@ -924,7 +904,7 @@ void exec( job_t *j ) if( close_stdin ) { // fwprintf( stderr, L"Close builtin_stdin\n" ); - close( builtin_stdin ); + close_loop( builtin_stdin ); } break; } @@ -1146,7 +1126,7 @@ void exec( job_t *j ) Close the pipe the current process uses to read from the previous process_t */ if( pipe_read.pipe_fd[0] >= 0 ) - close( pipe_read.pipe_fd[0] ); + close_loop( pipe_read.pipe_fd[0] ); /* Set up the pipe the next process uses to read from the current process_t */ @@ -1159,15 +1139,11 @@ void exec( job_t *j ) */ if( p->next ) { - if( close(mypipe[1]) != 0 ) - { - debug( 1, PIPE_ERROR ); - wperror( L"close" ); - } + close_loop(mypipe[1]); } } -// fwprintf( stderr, L"Job is constructedk\n" ); + debug( 3, L"Job is constructed" ); j->io = io_remove( j->io, &pipe_read ); j->io = io_remove( j->io, &pipe_write ); @@ -1175,10 +1151,7 @@ void exec( job_t *j ) for( tmp = block_io; tmp; tmp=tmp->next ) j->io = io_remove( j->io, tmp ); - -// assert( !job_is_stopped(j)); j->constructed = 1; -// ggg( j->command, j->io ); if( !j->fg ) { diff --git a/fishd.c b/fishd.c index 2cf4e5995..bd18b6d02 100644 --- a/fishd.c +++ b/fishd.c @@ -28,20 +28,42 @@ down and save. #include "wutil.h" #include "env_universal_common.h" - +/** + Maximum length of socket filename +*/ #ifndef UNIX_PATH_MAX #define UNIX_PATH_MAX 100 #endif -#define GREETING "#Fish universal variable daemon\n#Lines beginning with '#' are ignored\n#Syntax:\n#SET VARNAME:VALUE\n#or\n#ERASE VARNAME\n#Where VALUE is the escaped value of the variable\n#Backslash escapes and \\xxx hexadecimal style escapes are supported\n" -#define FILE ".fishd" +/** + Small greeting to show that fishd is running +*/ +#define GREETING "#Fish universal variable daemon\n" +/** + The name of the save file. The hostname is appended to this. +*/ +#define FILE ".fishd." +/** + Maximum length of hostname. Longer hostnames are truncated +*/ +#define HOSTNAME_LEN 32 + +/** + The list of connections to clients +*/ static connection_t *conn; + +/** + The socket to accept new clients on +*/ static int sock; - -int get_socket() +/** + Connects to the fish socket +*/ +static int get_socket() { int s, len; struct sockaddr_un local; @@ -107,7 +129,10 @@ int get_socket() return s; } -void broadcast( int type, const wchar_t *key, const wchar_t *val ) +/** + Event handler. Broadcasts updates to all clients. +*/ +static void broadcast( int type, const wchar_t *key, const wchar_t *val ) { connection_t *c; message_t *msg; @@ -136,7 +161,10 @@ void broadcast( int type, const wchar_t *key, const wchar_t *val ) } } -void daemonize() +/** + Make program into a creature of the night. +*/ +static void daemonize() { /* Fork, and let parent exit @@ -178,77 +206,75 @@ void daemonize() } - -void load() +/** + Load or save all variables +*/ +void load_or_save( int save) { struct passwd *pw; char *name; char *dir = getenv( "HOME" ); + char hostname[HOSTNAME_LEN]; + connection_t c; + if( !dir ) { pw = getpwuid( getuid() ); dir = pw->pw_dir; } - - name = malloc( strlen(dir)+ strlen(FILE)+ 2 ); + + gethostname( hostname, HOSTNAME_LEN ); + + name = malloc( strlen(dir)+ strlen(FILE)+ strlen(hostname) + 2 ); strcpy( name, dir ); strcat( name, "/" ); strcat( name, FILE ); + strcat( name, hostname ); - debug( 1, L"Open file for loading: '%s'", name ); - - connection_t load; - load.fd = open( name, O_RDONLY); + debug( 1, L"Open file for %s: '%s'", + save?"saving":"loading", + name ); + c.fd = open( name, save?(O_CREAT | O_TRUNC | O_WRONLY):O_RDONLY, 0600); free( name ); - if( load.fd == -1 ) + if( c.fd == -1 ) { - debug( 0, L"Could not open save file. No previous saves?" ); - } - debug( 1, L"Load input file on fd %d", load.fd ); - sb_init( &load.input ); - memset (&load.wstate, '\0', sizeof (mbstate_t)); - read_message( &load ); - sb_destroy( &load.input ); - close( load.fd ); -} - -void save() -{ - struct passwd *pw; - char *name; - char *dir = getenv( "HOME" ); - if( !dir ) - { - pw = getpwuid( getuid() ); - dir = pw->pw_dir; - } - - name = malloc( strlen(dir)+ strlen(FILE)+ 2 ); - strcpy( name, dir ); - strcat( name, "/" ); - strcat( name, FILE ); - - debug( 1, L"Open file for saving: '%s'", name ); - - connection_t save; - save.fd = open( name, O_CREAT | O_TRUNC | O_WRONLY); - free( name ); - - if( save.fd == -1 ) - { - debug( 0, L"Could not open save file" ); + debug( 1, L"Could not open load/save file. No previous saves?" ); wperror( L"open" ); - exit(1); + } - debug( 1, L"File open on fd %d'", save.fd ); - q_init( &save.unsent ); - enqueue_all( &save ); - close( save.fd ); - q_destroy( &save.unsent ); + debug( 1, L"File open on fd %d", c.fd ); + + sb_init( &c.input ); + memset (&c.wstate, '\0', sizeof (mbstate_t)); + q_init( &c.unsent ); + + if( save ) + enqueue_all( &c ); + else + read_message( &c ); + + q_destroy( &c.unsent ); + sb_destroy( &c.input ); + close( c.fd ); + } +static void load() +{ + load_or_save(0); +} + + +static void save() +{ + load_or_save(1); +} + +/** + Do all sorts of boring initialization. +*/ static void init() { program_name=L"fishd"; @@ -268,6 +294,7 @@ static void init() load(); } + int main( int argc, char ** argv ) { int child_socket, t; @@ -362,7 +389,7 @@ int main( int argc, char ** argv ) won't lose everything on a system crash */ update_count++; - if( update_count >= 8 ) + if( update_count >= 64 ) { save(); update_count = 0; diff --git a/parser.c b/parser.c index 6a8fa9322..7aa04787d 100644 --- a/parser.c +++ b/parser.c @@ -1762,7 +1762,7 @@ static void eval_job( tokenizer *tok ) } } - if( is_subshell ) + if( is_subshell || is_block ) job_do_notification(); // debug( 2, L"end eval_job()\n" ); } diff --git a/proc.c b/proc.c index 86b23a0bb..911edae37 100644 --- a/proc.c +++ b/proc.c @@ -606,12 +606,12 @@ static void handle_child_status( pid_t pid, int status ) { block_t *c = current_block; - snprintf( mess, - MESS_SIZE, - "Process %ls from job %ls exited through signal, breaking loops\n", - p->actual_cmd, - j->command ); - write( 2, mess, strlen(mess )); + snprintf( mess, + MESS_SIZE, + "Process %ls from job %ls exited through signal, breaking loops\n", + p->actual_cmd, + j->command ); + write( 2, mess, strlen(mess )); while( c ) { @@ -946,12 +946,12 @@ static void read_try( job_t *j ) if( buff ) { - // fwprintf( stderr, L"proc::read_try('%ls')\n", j->command ); + debug( 3, L"proc::read_try('%ls')\n", j->command ); while(1) { char b[BUFFER_SIZE]; int l; - //fwprintf( stderr, L"read...\n"); + l=read_blocked( buff->pipe_fd[0], b, BUFFER_SIZE ); if( l==0 ) { @@ -964,8 +964,7 @@ static void read_try( job_t *j ) debug( 1, L"An error occured while reading output from code block" ); wperror( L"read_try" ); - } - + } break; } else @@ -987,7 +986,6 @@ void job_continue (job_t *j, int cont) first_job = j; j->notified = 0; -// if( is_interactive ) debug( 3, L"Continue on job %d (%ls), %ls, %ls", j->job_id, @@ -1023,7 +1021,6 @@ void job_continue (job_t *j, int cont) if( cont ) { -// fwprintf( stderr, L"tcsetattr\n" ); while( 1 ) { if( tcsetattr (0, TCSADRAIN, &j->tmodes)) @@ -1045,82 +1042,78 @@ void job_continue (job_t *j, int cont) } } } - } - /* - Send the job a continue signal, if necessary. - */ - if( cont ) - { - process_t *p; - for( p=j->first_process; p; p=p->next ) - p->stopped=0; - for( p=j->first_process; p; p=p->next ) - { - if (kill ( p->pid, SIGCONT) < 0) - { - wperror (L"kill (SIGCONT)"); - return; - } - } - } - - if( j->fg ) - { - int quit = 0; - /* - Wait for job to report. Looks a bit ugly because it has to - handle the possibility that a signal is dispatched while - running job_is_stopped(). + Send the job a continue signal, if necessary. */ - /* - fwprintf( stderr, L"Wait for %ls (%d)\n", j->command, j->pgid ); - */ - while( !quit ) + if( cont ) { - do + process_t *p; + for( p=j->first_process; p; p=p->next ) + p->stopped=0; + for( p=j->first_process; p; p=p->next ) { - got_signal = 0; - quit = job_is_stopped( j ) || job_last_is_completed( j ); - } - while( got_signal && !quit ); - if( !quit ) - { - - debug( 3, L"select_try()" ); - switch( select_try(j) ) + if (kill ( p->pid, SIGCONT) < 0) { - case 1: - { - debug( 3, L"1" ); - read_try( j ); - break; - } - - case -1: - { - /* - If there is no funky IO magic, we can use - waitpid instead of handling child deaths - through signals. This gives a rather large - speed boost (A factor 3 startup time - improvement on my 300 MHz machine) on - short-lived jobs. - */ - debug( 3, L"-1" ); - int status; - pid_t pid = waitpid(-1, &status, WUNTRACED ); - if( pid > 0 ) - handle_child_status( pid, status ); - break; - } - - } - } + wperror (L"kill (SIGCONT)"); + return; + } + } } - } + + if( j->fg ) + { + int quit = 0; + /* + Wait for job to report. Looks a bit ugly because it has to + handle the possibility that a signal is dispatched while + running job_is_stopped(). + */ + while( !quit ) + { + do + { + got_signal = 0; + quit = job_is_stopped( j ) || job_last_is_completed( j ); + } + while( got_signal && !quit ); + if( !quit ) + { + + debug( 3, L"select_try()" ); + switch( select_try(j) ) + { + case 1: + { + read_try( j ); + break; + } + + case -1: + { + /* + If there is no funky IO magic, we can use + waitpid instead of handling child deaths + through signals. This gives a rather large + speed boost (A factor 3 startup time + improvement on my 300 MHz machine) on + short-lived jobs. + */ + int status; + pid_t pid = waitpid(-1, &status, WUNTRACED ); + if( pid > 0 ) + handle_child_status( pid, status ); + break; + } + + } + } + } + } + + } + if( j->fg ) { @@ -1141,8 +1134,7 @@ void job_continue (job_t *j, int cont) debug( 3, L"Set status of %ls to %d", j->command, WEXITSTATUS(p->status) ); proc_set_last_status( j->negate?(WEXITSTATUS(p->status)?0:1):WEXITSTATUS(p->status) ); } - } - + } } /* Put the shell back in the foreground. @@ -1202,7 +1194,6 @@ void job_continue (job_t *j, int cont) } } } -// fwprintf( stderr, L"Job_continue end\n" ); } void proc_sanity_check() diff --git a/reader.c b/reader.c index 7a853cf8b..ac547c763 100644 --- a/reader.c +++ b/reader.c @@ -1483,6 +1483,13 @@ static void set_signal_handlers() sigaction( SIGTTOU, &act, 0); sigaction( SIGCHLD, &act, 0); + /* + Ignore sigpipe, it is generated if fishd dies, but we can + recover. + */ + act.sa_handler=SIG_IGN; + sigaction( SIGPIPE, &act, 0); + if( is_interactive ) {