2005-09-20 23:26:39 +10:00
/** \file proc.c
Utilities for keeping track of jobs , processes and subshells , as
well as signal handling functions for tracking children . These
functions do not themselves launch new processes , the exec library
will call proc to create representations of the running jobs as
needed .
Some of the code in this file is based on code from the Glibc manual .
*/
# include "config.h"
# include <stdlib.h>
# include <stdio.h>
# include <sys/wait.h>
# include <wchar.h>
# include <string.h>
# include <errno.h>
# include <termios.h>
# include <sys/types.h>
# include <sys/stat.h>
2006-08-10 08:53:38 +10:00
# ifdef HAVE_SYS_TERMIOS_H
# include <sys/termios.h>
# endif
# ifdef HAVE_SYS_IOCTL_H
2005-09-20 23:26:39 +10:00
# include <sys/ioctl.h>
2006-08-10 08:53:38 +10:00
# endif
2005-09-20 23:26:39 +10:00
# include <unistd.h>
# include <signal.h>
# include <dirent.h>
# include <sys/time.h>
# if HAVE_NCURSES_H
# include <ncurses.h>
# else
# include <curses.h>
# endif
# if HAVE_TERMIO_H
# include <termio.h>
# endif
2006-01-19 22:22:07 +10:00
# if HAVE_TERM_H
2005-09-20 23:26:39 +10:00
# include <term.h>
2006-01-19 22:22:07 +10:00
# elif HAVE_NCURSES_TERM_H
# include <ncurses/term.h>
# endif
2005-09-20 23:26:39 +10:00
2006-07-31 06:26:59 +10:00
# ifdef HAVE_SIGINFO_H
# include <siginfo.h>
# endif
2006-02-28 23:17:16 +10:00
2006-08-10 08:26:05 +10:00
# ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
# endif
2006-02-28 23:17:16 +10:00
# include "fallback.h"
2005-09-20 23:26:39 +10:00
# include "util.h"
2006-02-28 23:17:16 +10:00
2005-09-20 23:26:39 +10:00
# include "wutil.h"
# include "proc.h"
# include "common.h"
# include "reader.h"
# include "sanity.h"
# include "env.h"
2005-09-23 06:16:52 +10:00
# include "parser.h"
2005-10-06 08:37:08 +10:00
# include "signal.h"
2005-10-12 05:23:43 +10:00
# include "event.h"
2006-07-20 08:55:49 +10:00
2006-02-07 01:18:17 +10:00
# include "halloc.h"
2006-02-16 23:36:32 +10:00
# include "halloc_util.h"
# include "output.h"
2005-09-20 23:26:39 +10:00
/**
Size of message buffer
*/
# define MESS_SIZE 256
/**
Size of buffer for reading buffered output
*/
# define BUFFER_SIZE 4096
2006-01-16 10:15:56 +10:00
/**
Status of last process to exit
*/
2005-09-20 23:26:39 +10:00
static int last_status = 0 ;
2006-01-16 10:15:56 +10:00
/**
Signal flag
*/
2005-09-20 23:26:39 +10:00
static sig_atomic_t got_signal = 0 ;
job_t * first_job = 0 ;
2006-02-16 23:36:32 +10:00
int is_interactive = - 1 ;
2005-09-20 23:26:39 +10:00
int is_interactive_session = 0 ;
int is_subshell = 0 ;
int is_block = 0 ;
int is_login = 0 ;
2005-10-06 08:37:08 +10:00
int is_event = 0 ;
2005-09-24 09:15:38 +10:00
int proc_had_barrier ;
2005-09-20 23:26:39 +10:00
pid_t proc_last_bg_pid = 0 ;
2006-01-31 03:54:26 +10:00
int job_control_mode = JOB_CONTROL_INTERACTIVE ;
2006-03-18 11:04:59 +10:00
int no_exec = 0 ;
2005-09-20 23:26:39 +10:00
2005-10-14 21:40:33 +10:00
/**
2005-12-12 08:21:01 +10:00
The event variable used to send all process event
2005-10-14 21:40:33 +10:00
*/
2005-12-12 08:21:01 +10:00
static event_t event ;
2005-10-14 21:40:33 +10:00
/**
Stringbuffer used to create arguments when firing events
*/
static string_buffer_t event_pid ;
2006-01-16 10:15:56 +10:00
2005-10-14 21:40:33 +10:00
/**
Stringbuffer used to create arguments when firing events
*/
static string_buffer_t event_status ;
2006-06-20 10:50:10 +10:00
/**
A stack containing the values of is_interactive . Used by proc_push_interactive and proc_pop_interactive .
*/
2006-02-16 23:36:32 +10:00
static array_list_t * interactive_stack ;
2005-10-14 21:40:33 +10:00
void proc_init ( )
{
2006-02-16 23:36:32 +10:00
interactive_stack = al_halloc ( global_context ) ;
proc_push_interactive ( 0 ) ;
2005-12-12 08:21:01 +10:00
al_init ( & event . arguments ) ;
2005-10-14 21:40:33 +10:00
sb_init ( & event_pid ) ;
sb_init ( & event_status ) ;
}
2006-02-10 01:50:20 +10:00
2006-01-16 10:15:56 +10:00
/**
Remove job from list of jobs
*/
2005-09-20 23:26:39 +10:00
static int job_remove ( job_t * j )
{
job_t * prev = 0 , * curr = first_job ;
while ( ( curr ! = 0 ) & & ( curr ! = j ) )
{
prev = curr ;
curr = curr - > next ;
}
if ( j ! = curr )
{
2006-01-04 22:51:02 +10:00
debug ( 1 , _ ( L " Job inconsistency " ) ) ;
2005-09-20 23:26:39 +10:00
sanity_lose ( ) ;
return 0 ;
}
if ( prev = = 0 )
first_job = j - > next ;
else
prev - > next = j - > next ;
return 1 ;
}
/*
Remove job from the job list and free all memory associated with
it .
*/
void job_free ( job_t * j )
{
job_remove ( j ) ;
2006-02-07 00:25:02 +10:00
halloc_free ( j ) ;
2005-09-20 23:26:39 +10:00
}
void proc_destroy ( )
{
2005-12-12 08:21:01 +10:00
al_destroy ( & event . arguments ) ;
2005-10-14 21:40:33 +10:00
sb_destroy ( & event_pid ) ;
sb_destroy ( & event_status ) ;
2005-09-20 23:26:39 +10:00
while ( first_job )
{
debug ( 2 , L " freeing leaked job %ls " , first_job - > command ) ;
job_free ( first_job ) ;
}
}
void proc_set_last_status ( int s )
{
2006-06-13 23:43:28 +10:00
2005-09-20 23:26:39 +10:00
last_status = s ;
// fwprintf( stderr, L"Set last status to %d\n", s );
}
int proc_get_last_status ( )
{
return last_status ;
}
job_t * job_create ( )
{
2006-04-28 23:21:37 +10:00
int free_id = 1 ;
2005-09-20 23:26:39 +10:00
job_t * res ;
while ( job_get ( free_id ) ! = 0 )
free_id + + ;
2006-02-07 00:25:02 +10:00
res = halloc ( 0 , sizeof ( job_t ) ) ;
2005-09-20 23:26:39 +10:00
res - > next = first_job ;
res - > job_id = free_id ;
first_job = res ;
2006-01-31 03:54:26 +10:00
res - > job_control = ( job_control_mode = = JOB_CONTROL_ALL ) | |
( ( job_control_mode = = JOB_CONTROL_INTERACTIVE ) & & ( is_interactive ) ) ;
2006-03-10 23:38:09 +10:00
2005-09-20 23:26:39 +10:00
// if( res->job_id > 2 )
// fwprintf( stderr, L"Create job %d\n", res->job_id );
return res ;
}
job_t * job_get ( int id )
{
job_t * res = first_job ;
2006-04-28 23:21:37 +10:00
if ( id < = 0 )
2005-09-20 23:26:39 +10:00
{
return res ;
}
while ( res ! = 0 )
{
if ( res - > job_id = = id )
return res ;
res = res - > next ;
}
return 0 ;
}
job_t * job_get_from_pid ( int pid )
{
job_t * res = first_job ;
while ( res ! = 0 )
{
if ( res - > pgid = = pid )
return res ;
res = res - > next ;
}
return 0 ;
}
/*
Return true if all processes in the job have stopped or completed .
2006-01-16 10:15:56 +10:00
\ param j the job to test
2005-09-20 23:26:39 +10:00
*/
int job_is_stopped ( const job_t * j )
{
process_t * p ;
for ( p = j - > first_process ; p ; p = p - > next )
{
if ( ! p - > completed & & ! p - > stopped )
{
return 0 ;
}
}
return 1 ;
}
/*
2006-07-08 01:35:39 +10:00
Return true if the last processes in the job has completed .
2006-01-16 10:15:56 +10:00
\ param j the job to test
2005-09-20 23:26:39 +10:00
*/
int job_is_completed ( const job_t * j )
{
process_t * p ;
2006-07-08 01:35:39 +10:00
for ( p = j - > first_process ; p - > next ; p = p - > next )
;
return p - > completed ;
2005-09-20 23:26:39 +10:00
}
/**
Store the status of the process pid that was returned by waitpid .
Return 0 if all went well , nonzero otherwise .
*/
static void mark_process_status ( job_t * j ,
process_t * p ,
int status )
{
p - > status = status ;
if ( WIFSTOPPED ( status ) )
{
p - > stopped = 1 ;
}
else
{
p - > completed = 1 ;
if ( ( ! WIFEXITED ( status ) ) & &
( ! WIFSIGNALED ( status ) ) )
{
/* This should never be reached */
2005-09-25 05:52:42 +10:00
char mess [ MESS_SIZE ] ;
snprintf ( mess ,
MESS_SIZE ,
2005-09-20 23:26:39 +10:00
" Process %d exited abnormally \n " ,
( int ) p - > pid ) ;
write ( 2 , mess , strlen ( mess ) ) ;
}
}
}
/**
Handle status update for child \ c pid . This function is called by
2006-01-16 10:15:56 +10:00
the signal handler , so it mustn ' t use malloc or any such hitech
nonsense .
\ param pid the pid of the process whose status changes
\ param status the status as returned by wait
2005-09-20 23:26:39 +10:00
*/
static void handle_child_status ( pid_t pid , int status )
{
int found_proc = 0 ;
2005-09-25 05:52:42 +10:00
job_t * j = 0 ;
process_t * p = 0 ;
// char mess[MESS_SIZE];
2005-09-20 23:26:39 +10:00
found_proc = 0 ;
/*
snprintf ( mess ,
MESS_SIZE ,
" Process %d \n " ,
( int ) pid ) ;
write ( 2 , mess , strlen ( mess ) ) ;
*/
for ( j = first_job ; j & & ! found_proc ; j = j - > next )
{
process_t * prev = 0 ;
for ( p = j - > first_process ; p ; p = p - > next )
{
if ( pid = = p - > pid )
{
/* snprintf( mess,
MESS_SIZE ,
" Process %d is %ls from job %ls \n " ,
( int ) pid , p - > actual_cmd , j - > command ) ;
write ( 2 , mess , strlen ( mess ) ) ;
2006-01-31 02:51:50 +10:00
*/
2005-09-20 23:26:39 +10:00
mark_process_status ( j , p , status ) ;
if ( p - > completed & & prev ! = 0 )
{
if ( ! prev - > completed & & prev - > pid )
{
/* snprintf( mess,
MESS_SIZE ,
" Kill previously uncompleted process %ls (%d) \n " ,
prev - > actual_cmd ,
prev - > pid ) ;
write ( 2 , mess , strlen ( mess ) ) ;
*/
kill ( prev - > pid , SIGPIPE ) ;
}
}
found_proc = 1 ;
break ;
}
prev = p ;
}
}
2005-09-23 06:16:52 +10:00
if ( WIFSIGNALED ( status ) & &
( WTERMSIG ( status ) = = SIGINT | |
WTERMSIG ( status ) = = SIGQUIT ) )
2005-09-20 23:26:39 +10:00
{
2005-09-23 06:16:52 +10:00
if ( ! is_interactive_session )
{
2005-09-20 23:26:39 +10:00
struct sigaction act ;
sigemptyset ( & act . sa_mask ) ;
act . sa_flags = 0 ;
act . sa_handler = SIG_DFL ;
sigaction ( SIGINT , & act , 0 ) ;
sigaction ( SIGQUIT , & act , 0 ) ;
kill ( getpid ( ) , WTERMSIG ( status ) ) ;
}
2005-09-23 06:16:52 +10:00
else
{
block_t * c = current_block ;
2005-09-25 05:52:42 +10:00
if ( p & & found_proc )
2005-09-23 06:16:52 +10:00
{
2005-09-25 05:52:42 +10:00
while ( c )
{
c - > skip = 1 ;
c = c - > outer ;
}
}
2005-09-23 06:16:52 +10:00
}
2005-09-20 23:26:39 +10:00
}
if ( ! found_proc )
{
/*
A child we lost track of ?
There have been bugs in both subshell handling and in
builtin handling that have caused this previously . . .
*/
/* snprintf( mess,
MESS_SIZE ,
" Process %d not found by %d \n " ,
( int ) pid , ( int ) getpid ( ) ) ;
write ( 2 , mess , strlen ( mess ) ) ;
*/
}
return ;
}
void job_handle_signal ( int signal , siginfo_t * info , void * con )
{
int status ;
pid_t pid ;
int errno_old = errno ;
got_signal = 1 ;
// write( 2, "got signal\n", 11 );
while ( 1 )
{
switch ( pid = waitpid ( - 1 , & status , WUNTRACED | WNOHANG ) )
{
case 0 :
case - 1 :
{
errno = errno_old ;
return ;
}
default :
handle_child_status ( pid , status ) ;
break ;
}
2006-08-28 22:02:44 +10:00
}
2005-09-20 23:26:39 +10:00
kill ( 0 , SIGIO ) ;
errno = errno_old ;
}
/**
Format information about job status for the user to look at .
2006-01-16 10:15:56 +10:00
\ param j the job to test
\ param status a string description of the job exit type
2005-09-20 23:26:39 +10:00
*/
static void format_job_info ( const job_t * j , const wchar_t * status )
{
2006-01-05 23:54:11 +10:00
fwprintf ( stdout , L " \r " ) ;
fwprintf ( stdout , _ ( L " Job %d, \' %ls \' has %ls " ) , j - > job_id , j - > command , status ) ;
2005-09-20 23:26:39 +10:00
fflush ( stdout ) ;
tputs ( clr_eol , 1 , & writeb ) ;
fwprintf ( stdout , L " \n " ) ;
}
2005-12-04 05:46:18 +10:00
void proc_fire_event ( const wchar_t * msg , int type , pid_t pid , int status )
2005-10-12 05:23:43 +10:00
{
2005-12-12 08:21:01 +10:00
event . type = type ;
event . param1 . pid = pid ;
2005-10-12 05:23:43 +10:00
2005-12-12 08:21:01 +10:00
al_push ( & event . arguments , msg ) ;
2005-10-12 05:23:43 +10:00
2005-10-14 00:11:29 +10:00
sb_printf ( & event_pid , L " %d " , pid ) ;
2005-12-12 08:21:01 +10:00
al_push ( & event . arguments , event_pid . buff ) ;
2005-10-14 00:11:29 +10:00
2005-10-12 05:23:43 +10:00
sb_printf ( & event_status , L " %d " , status ) ;
2005-12-12 08:21:01 +10:00
al_push ( & event . arguments , event_status . buff ) ;
event_fire ( & event ) ;
2005-10-12 05:23:43 +10:00
2005-12-12 08:21:01 +10:00
al_truncate ( & event . arguments , 0 ) ;
2005-10-12 05:23:43 +10:00
sb_clear ( & event_pid ) ;
sb_clear ( & event_status ) ;
}
int job_reap ( int interactive )
2005-09-20 23:26:39 +10:00
{
job_t * j , * jnext ;
int found = 0 ;
2005-10-12 05:23:43 +10:00
static int locked = 0 ;
locked + + ;
2006-04-04 21:27:22 +10:00
/*
job_read may fire an event handler , we do not want to call
ourselves recursively ( to avoid infinite recursion ) .
*/
2005-10-12 05:23:43 +10:00
if ( locked > 1 )
2005-10-15 10:51:26 +10:00
return 0 ;
2005-10-12 05:23:43 +10:00
2005-09-20 23:26:39 +10:00
for ( j = first_job ; j ; j = jnext )
{
process_t * p ;
jnext = j - > next ;
2005-10-12 05:23:43 +10:00
2006-04-04 21:27:22 +10:00
/*
If we are reaping only jobs who do not need status messages
sent to the console , do not consider reaping jobs that need
status messages
*/
2006-01-16 10:19:12 +10:00
if ( ( ! j - > skip_notification ) & & ( ! interactive ) & & ( ! j - > fg ) )
2005-10-12 05:23:43 +10:00
{
continue ;
}
2005-09-20 23:26:39 +10:00
for ( p = j - > first_process ; p ; p = p - > next )
{
2005-10-12 05:23:43 +10:00
int s ;
2005-09-20 23:26:39 +10:00
if ( ! p - > completed )
continue ;
2005-10-12 05:23:43 +10:00
if ( ! p - > pid )
continue ;
2005-09-20 23:26:39 +10:00
2005-10-12 05:23:43 +10:00
s = p - > status ;
2005-12-04 05:46:18 +10:00
proc_fire_event ( L " PROCESS_EXIT " , EVENT_EXIT , p - > pid , ( WIFSIGNALED ( s ) ? - 1 : WEXITSTATUS ( s ) ) ) ;
2005-10-14 00:11:29 +10:00
2005-10-12 05:23:43 +10:00
if ( WIFSIGNALED ( s ) )
2005-09-20 23:26:39 +10:00
{
/*
Ignore signal SIGPIPE . We issue it ourselves to the pipe
writer when the pipe reader dies .
*/
2005-10-12 05:23:43 +10:00
if ( WTERMSIG ( s ) ! = SIGPIPE )
2005-09-20 23:26:39 +10:00
{
int proc_is_job = ( ( p = = j - > first_process ) & & ( p - > next = = 0 ) ) ;
if ( proc_is_job )
j - > notified = 1 ;
if ( ! j - > skip_notification )
{
2005-10-08 00:08:57 +10:00
if ( proc_is_job )
fwprintf ( stdout ,
2006-01-04 22:51:02 +10:00
_ ( L " %ls: Job %d, \' %ls \' terminated by signal %ls (%ls) " ) ,
program_name ,
2005-10-08 00:08:57 +10:00
j - > job_id ,
j - > command ,
sig2wcs ( WTERMSIG ( p - > status ) ) ,
2006-02-02 01:49:11 +10:00
signal_get_desc ( WTERMSIG ( p - > status ) ) ) ;
2005-10-08 00:08:57 +10:00
else
fwprintf ( stdout ,
2006-01-04 22:51:02 +10:00
_ ( L " %ls: Process %d, \' %ls \' from job %d, \' %ls \' terminated by signal %ls (%ls) " ) ,
program_name ,
2005-10-08 00:08:57 +10:00
p - > pid ,
p - > argv [ 0 ] ,
j - > job_id ,
j - > command ,
sig2wcs ( WTERMSIG ( p - > status ) ) ,
2006-02-02 01:49:11 +10:00
signal_get_desc ( WTERMSIG ( p - > status ) ) ) ;
2005-09-20 23:26:39 +10:00
tputs ( clr_eol , 1 , & writeb ) ;
fwprintf ( stdout , L " \n " ) ;
found = 1 ;
}
/*
Clear status so it is not reported more than once
*/
p - > status = 0 ;
}
2005-10-12 05:23:43 +10:00
}
2005-09-20 23:26:39 +10:00
}
/*
If all processes have completed , tell the user the job has
completed and delete it from the active job list .
*/
2005-10-12 05:23:43 +10:00
if ( job_is_completed ( j ) )
{
2005-09-20 23:26:39 +10:00
if ( ! j - > fg & & ! j - > notified )
{
if ( ! j - > skip_notification )
{
2006-01-04 22:51:02 +10:00
format_job_info ( j , _ ( L " ended " ) ) ;
2005-09-20 23:26:39 +10:00
found = 1 ;
}
}
2005-12-04 05:46:18 +10:00
proc_fire_event ( L " JOB_EXIT " , EVENT_EXIT , - j - > pgid , 0 ) ;
proc_fire_event ( L " JOB_EXIT " , EVENT_JOB_ID , j - > job_id , 0 ) ;
2005-10-14 00:11:29 +10:00
job_free ( j ) ;
2005-09-20 23:26:39 +10:00
}
2005-10-12 05:23:43 +10:00
else if ( job_is_stopped ( j ) & & ! j - > notified )
{
2005-09-20 23:26:39 +10:00
/*
Notify the user about newly stopped jobs .
*/
if ( ! j - > skip_notification )
{
2006-01-04 22:51:02 +10:00
format_job_info ( j , _ ( L " stopped " ) ) ;
2005-09-20 23:26:39 +10:00
found = 1 ;
}
j - > notified = 1 ;
}
}
2005-10-12 05:23:43 +10:00
2005-09-20 23:26:39 +10:00
if ( found )
fflush ( stdout ) ;
2005-10-12 05:23:43 +10:00
locked = 0 ;
2005-09-20 23:26:39 +10:00
2005-10-12 05:23:43 +10:00
return found ;
2005-09-20 23:26:39 +10:00
}
# ifdef HAVE__PROC_SELF_STAT
2006-01-21 00:27:21 +10:00
2006-01-24 06:40:14 +10:00
/**
Maximum length of a / proc / [ PID ] / stat filename
*/
2006-01-21 00:27:21 +10:00
# define FN_SIZE 256
2005-09-20 23:26:39 +10:00
/**
Get the CPU time for the specified process
*/
unsigned long proc_get_jiffies ( process_t * p )
{
2006-01-21 00:27:21 +10:00
wchar_t fn [ FN_SIZE ] ;
2005-09-20 23:26:39 +10:00
char state ;
int pid , ppid , pgrp ,
session , tty_nr , tpgid ,
exit_signal , processor ;
long int cutime , cstime , priority ,
nice , placeholder , itrealvalue ,
rss ;
unsigned long int flags , minflt , cminflt ,
majflt , cmajflt , utime ,
stime , starttime , vsize ,
rlim , startcode , endcode ,
startstack , kstkesp , kstkeip ,
signal , blocked , sigignore ,
sigcatch , wchan , nswap , cnswap ;
char comm [ 1024 ] ;
if ( p - > pid < = 0 )
return 0 ;
2006-01-21 00:27:21 +10:00
swprintf ( fn , FN_SIZE , L " /proc/%d/stat " , p - > pid ) ;
2005-09-20 23:26:39 +10:00
FILE * f = wfopen ( fn , " r " ) ;
if ( ! f )
return 0 ;
int count = fscanf ( f ,
" %d %s %c "
" %d %d %d "
" %d %d %lu "
" %lu %lu %lu "
" %lu %lu %lu "
" %ld %ld %ld "
" %ld %ld %ld "
" %lu %lu %ld "
" %lu %lu %lu "
" %lu %lu %lu "
" %lu %lu %lu "
" %lu %lu %lu "
" %lu %d %d " ,
& pid , comm , & state ,
& ppid , & pgrp , & session ,
& tty_nr , & tpgid , & flags ,
& minflt , & cminflt , & majflt ,
& cmajflt , & utime , & stime ,
& cutime , & cstime , & priority ,
& nice , & placeholder , & itrealvalue ,
& starttime , & vsize , & rss ,
& rlim , & startcode , & endcode ,
& startstack , & kstkesp , & kstkeip ,
& signal , & blocked , & sigignore ,
& sigcatch , & wchan , & nswap ,
& cnswap , & exit_signal , & processor
) ;
if ( count < 17 )
{
return 0 ;
}
2006-06-21 19:54:30 +10:00
/*
Don ' t need to check exit status of fclose on read - only streams
*/
2005-09-20 23:26:39 +10:00
fclose ( f ) ;
return utime + stime + cutime + cstime ;
}
/**
Update the CPU time for all jobs
*/
void proc_update_jiffies ( )
{
job_t * j ;
process_t * p ;
for ( j = first_job ; j ; j = j - > next )
{
for ( p = j - > first_process ; p ; p = p - > next )
{
gettimeofday ( & p - > last_time , 0 ) ;
p - > last_jiffies = proc_get_jiffies ( p ) ;
}
}
}
# endif
/**
Check if there are buffers associated with the job , and select on
them for a while if available .
2006-01-16 10:15:56 +10:00
\ param j the job to test
2005-10-08 19:33:10 +10:00
\ return 1 if buffers were avaialble , zero otherwise
2005-09-20 23:26:39 +10:00
*/
static int select_try ( job_t * j )
{
fd_set fds ;
int maxfd = - 1 ;
io_data_t * d ;
FD_ZERO ( & fds ) ;
for ( d = j - > io ; d ; d = d - > next )
{
if ( d - > io_mode = = IO_BUFFER )
{
2005-10-12 05:31:16 +10:00
int fd = d - > param1 . pipe_fd [ 0 ] ;
2005-09-20 23:26:39 +10:00
// fwprintf( stderr, L"fd %d on job %ls\n", fd, j->command );
FD_SET ( fd , & fds ) ;
2005-10-12 05:31:16 +10:00
maxfd = maxi ( maxfd , d - > param1 . pipe_fd [ 0 ] ) ;
2005-09-20 23:26:39 +10:00
debug ( 3 , L " select_try on %d \n " , fd ) ;
}
}
if ( maxfd > = 0 )
{
int retval ;
struct timeval tv ;
tv . tv_sec = 5 ;
tv . tv_usec = 0 ;
retval = select ( maxfd + 1 , & fds , 0 , 0 , & tv ) ;
return retval > 0 ;
}
return - 1 ;
}
/**
Read from descriptors until they are empty .
2006-01-16 10:15:56 +10:00
\ param j the job to test
2005-09-20 23:26:39 +10:00
*/
static void read_try ( job_t * j )
{
io_data_t * d , * buff = 0 ;
/*
Find the last buffer , which is the one we want to read from
*/
for ( d = j - > io ; d ; d = d - > next )
{
if ( d - > io_mode = = IO_BUFFER )
{
buff = d ;
}
}
if ( buff )
{
2005-09-23 23:10:31 +10:00
debug ( 3 , L " proc::read_try('%ls') \n " , j - > command ) ;
2005-09-20 23:26:39 +10:00
while ( 1 )
{
char b [ BUFFER_SIZE ] ;
int l ;
2005-09-23 23:10:31 +10:00
2005-10-12 05:31:16 +10:00
l = read_blocked ( buff - > param1 . pipe_fd [ 0 ] ,
b , BUFFER_SIZE ) ;
2005-09-20 23:26:39 +10:00
if ( l = = 0 )
{
break ;
}
else if ( l < 0 )
{
if ( errno ! = EAGAIN )
{
debug ( 1 ,
2006-01-04 22:51:02 +10:00
_ ( L " An error occured while reading output from code block " ) ) ;
2005-09-20 23:26:39 +10:00
wperror ( L " read_try " ) ;
2005-09-23 23:10:31 +10:00
}
2005-09-20 23:26:39 +10:00
break ;
}
else
{
2005-10-12 05:31:16 +10:00
b_append ( buff - > param2 . out_buffer , b , l ) ;
2006-03-10 23:38:09 +10:00
}
2005-09-20 23:26:39 +10:00
}
}
}
2006-01-16 10:15:56 +10:00
2005-09-20 23:26:39 +10:00
void job_continue ( job_t * j , int cont )
{
/*
Put job first in the job list
*/
job_remove ( j ) ;
j - > next = first_job ;
first_job = j ;
j - > notified = 0 ;
2006-03-10 23:38:09 +10:00
2006-01-18 22:42:48 +10:00
debug ( 4 ,
2006-03-10 23:38:09 +10:00
L " Continue job %d (%ls), %ls, %ls " ,
2005-09-20 23:26:39 +10:00
j - > job_id ,
j - > command ,
job_is_completed ( j ) ? L " COMPLETED " : L " UNCOMPLETED " ,
is_interactive ? L " INTERACTIVE " : L " NON-INTERACTIVE " ) ;
2006-03-10 23:38:09 +10:00
2005-09-20 23:26:39 +10:00
if ( ! job_is_completed ( j ) )
{
2006-02-01 22:27:15 +10:00
if ( j - > terminal & & j - > fg )
2006-01-31 03:54:26 +10:00
{
2005-09-20 23:26:39 +10:00
/* Put the job into the foreground. */
2006-01-31 03:54:26 +10:00
signal_block ( ) ;
if ( tcsetpgrp ( 0 , j - > pgid ) )
2005-09-20 23:26:39 +10:00
{
2006-01-31 03:54:26 +10:00
debug ( 1 ,
_ ( L " Could not send job %d ('%ls') to foreground " ) ,
j - > job_id ,
j - > command ) ;
wperror ( L " tcsetpgrp " ) ;
return ;
}
if ( cont )
{
if ( tcsetattr ( 0 , TCSADRAIN , & j - > tmodes ) )
2005-09-20 23:26:39 +10:00
{
2006-01-31 03:54:26 +10:00
debug ( 1 ,
_ ( L " Could not send job %d ('%ls') to foreground " ) ,
j - > job_id ,
2005-10-14 21:40:33 +10:00
j - > command ) ;
2006-01-31 03:54:26 +10:00
wperror ( L " tcsetattr " ) ;
2005-10-14 21:40:33 +10:00
return ;
2006-01-31 03:54:26 +10:00
}
2005-09-20 23:26:39 +10:00
}
2006-01-31 03:54:26 +10:00
signal_unblock ( ) ;
2005-09-20 23:26:39 +10:00
}
/*
2005-09-23 23:10:31 +10:00
Send the job a continue signal , if necessary .
2005-09-20 23:26:39 +10:00
*/
2005-09-23 23:10:31 +10:00
if ( cont )
2005-09-20 23:26:39 +10:00
{
2005-09-23 23:10:31 +10:00
process_t * p ;
2006-02-01 22:27:15 +10:00
2005-09-23 23:10:31 +10:00
for ( p = j - > first_process ; p ; p = p - > next )
p - > stopped = 0 ;
2006-02-01 22:27:15 +10:00
if ( j - > job_control )
2005-09-20 23:26:39 +10:00
{
2006-02-01 22:27:15 +10:00
if ( killpg ( j - > pgid , SIGCONT ) )
2005-09-23 23:10:31 +10:00
{
2006-02-01 22:27:15 +10:00
wperror ( L " killpg (SIGCONT) " ) ;
2005-09-23 23:10:31 +10:00
return ;
2006-02-01 22:27:15 +10:00
}
}
else
{
for ( p = j - > first_process ; p ; p = p - > next )
{
if ( kill ( p - > pid , SIGCONT ) < 0 )
{
wperror ( L " kill (SIGCONT) " ) ;
return ;
}
}
2005-09-20 23:26:39 +10:00
}
2005-09-23 23:10:31 +10:00
}
if ( j - > fg )
{
int quit = 0 ;
2006-01-18 22:42:48 +10:00
// debug( 1, L"wait loop" );
2005-09-23 23:10:31 +10:00
/*
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 )
2005-09-20 23:26:39 +10:00
{
2005-09-23 23:10:31 +10:00
do
{
got_signal = 0 ;
2006-04-04 21:27:22 +10:00
quit = job_is_stopped ( j ) | | job_is_completed ( j ) ;
2005-09-23 23:10:31 +10:00
}
while ( got_signal & & ! quit ) ;
if ( ! quit )
2005-09-20 23:26:39 +10:00
{
2006-01-18 22:42:48 +10:00
// debug( 1, L"select_try()" );
2005-09-23 23:10:31 +10:00
switch ( select_try ( j ) )
2005-09-20 23:26:39 +10:00
{
2005-09-23 23:10:31 +10:00
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 ;
2006-01-18 22:42:48 +10:00
// debug( 1, L"waitpid" );
2005-09-23 23:10:31 +10:00
pid_t pid = waitpid ( - 1 , & status , WUNTRACED ) ;
if ( pid > 0 )
handle_child_status ( pid , status ) ;
break ;
}
2005-09-20 23:26:39 +10:00
2005-09-23 23:10:31 +10:00
}
}
}
2006-01-16 10:15:56 +10:00
}
2005-09-20 23:26:39 +10:00
}
2006-02-04 21:34:33 +10:00
2005-09-20 23:26:39 +10:00
if ( j - > fg )
{
if ( job_is_completed ( j ) )
{
process_t * p = j - > first_process ;
while ( p - > next )
p = p - > next ;
if ( WIFEXITED ( p - > status ) )
{
/*
Mark process status only if we are in the foreground
and the last process in a pipe , and it is not a short circuted builtin
*/
if ( p - > pid )
{
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 ) ) ;
}
2005-09-23 23:10:31 +10:00
}
2005-09-20 23:26:39 +10:00
}
/*
Put the shell back in the foreground .
*/
2006-02-01 22:27:15 +10:00
if ( j - > terminal & & j - > fg )
2005-09-20 23:26:39 +10:00
{
2005-10-14 21:40:33 +10:00
signal_block ( ) ;
if ( tcsetpgrp ( 0 , getpid ( ) ) )
2005-09-20 23:26:39 +10:00
{
2006-01-04 22:51:02 +10:00
debug ( 1 , _ ( L " Could not return shell to foreground " ) ) ;
2005-10-14 21:40:33 +10:00
wperror ( L " tcsetpgrp " ) ;
return ;
2005-09-20 23:26:39 +10:00
}
/*
Save jobs terminal modes .
*/
2005-10-14 21:40:33 +10:00
if ( tcgetattr ( 0 , & j - > tmodes ) )
2005-09-20 23:26:39 +10:00
{
2006-01-04 22:51:02 +10:00
debug ( 1 , _ ( L " Could not return shell to foreground " ) ) ;
2005-10-14 21:40:33 +10:00
wperror ( L " tcgetattr " ) ;
return ;
2005-09-20 23:26:39 +10:00
}
/*
Restore the shell ' s terminal modes .
*/
2005-10-14 21:40:33 +10:00
if ( tcsetattr ( 0 , TCSADRAIN , & shell_modes ) )
2005-09-20 23:26:39 +10:00
{
2006-01-04 22:51:02 +10:00
debug ( 1 , _ ( L " Could not return shell to foreground " ) ) ;
2005-10-14 21:40:33 +10:00
wperror ( L " tcsetattr " ) ;
return ;
2005-09-20 23:26:39 +10:00
}
2005-10-14 21:40:33 +10:00
signal_unblock ( ) ;
2005-09-20 23:26:39 +10:00
}
}
2006-07-08 01:35:39 +10:00
2005-09-20 23:26:39 +10:00
}
void proc_sanity_check ( )
{
job_t * j ;
job_t * fg_job = 0 ;
for ( j = first_job ; j ; j = j - > next )
{
process_t * p ;
if ( ! j - > constructed )
continue ;
validate_pointer ( j - > command ,
2006-01-04 22:51:02 +10:00
_ ( L " Job command " ) ,
2005-09-20 23:26:39 +10:00
0 ) ;
validate_pointer ( j - > first_process ,
2006-01-04 22:51:02 +10:00
_ ( L " Process list pointer " ) ,
2005-09-20 23:26:39 +10:00
0 ) ;
validate_pointer ( j - > next ,
2006-01-04 22:51:02 +10:00
_ ( L " Job list pointer " ) ,
2005-09-20 23:26:39 +10:00
1 ) ;
2006-01-04 22:51:02 +10:00
2005-09-20 23:26:39 +10:00
/*
More than one foreground job ?
*/
2006-04-04 21:27:22 +10:00
if ( j - > fg & & ! ( job_is_stopped ( j ) | | job_is_completed ( j ) ) )
2005-09-20 23:26:39 +10:00
{
if ( fg_job ! = 0 )
{
debug ( 0 ,
2006-01-04 22:51:02 +10:00
_ ( L " More than one job in foreground: job 1: '%ls' job 2: '%ls' " ) ,
2005-09-20 23:26:39 +10:00
fg_job - > command ,
j - > command ) ;
sanity_lose ( ) ;
}
fg_job = j ;
}
p = j - > first_process ;
while ( p )
{
2006-01-04 22:51:02 +10:00
validate_pointer ( p - > argv , _ ( L " Process argument list " ) , 0 ) ;
validate_pointer ( p - > argv [ 0 ] , _ ( L " Process name " ) , 0 ) ;
validate_pointer ( p - > next , _ ( L " Process list pointer " ) , 1 ) ;
validate_pointer ( p - > actual_cmd , _ ( L " Process command " ) , 1 ) ;
2005-09-20 23:26:39 +10:00
if ( ( p - > stopped & ( ~ 0x00000001 ) ) ! = 0 )
{
debug ( 0 ,
2006-01-04 22:51:02 +10:00
_ ( L " Job '%ls', process '%ls' has inconsistent state \' stopped \' =%d " ) ,
2005-09-20 23:26:39 +10:00
j - > command ,
p - > argv [ 0 ] ,
p - > stopped ) ;
sanity_lose ( ) ;
}
if ( ( p - > completed & ( ~ 0x00000001 ) ) ! = 0 )
{
debug ( 0 ,
2006-01-04 22:51:02 +10:00
_ ( L " Job '%ls', process '%ls' has inconsistent state \' completed \' =%d " ) ,
2005-09-20 23:26:39 +10:00
j - > command ,
p - > argv [ 0 ] ,
p - > completed ) ;
sanity_lose ( ) ;
}
p = p - > next ;
}
}
}
2006-02-16 23:36:32 +10:00
void proc_push_interactive ( int value )
{
int old = is_interactive ;
2006-08-01 02:55:11 +10:00
al_push_long ( interactive_stack , ( long ) is_interactive ) ;
2006-02-16 23:36:32 +10:00
is_interactive = value ;
if ( old ! = value )
signal_set_handlers ( ) ;
}
void proc_pop_interactive ( )
{
int old = is_interactive ;
2006-08-01 02:55:11 +10:00
is_interactive = ( int ) al_pop_long ( interactive_stack ) ;
2006-02-16 23:36:32 +10:00
if ( is_interactive ! = old )
signal_set_handlers ( ) ;
}