diff options
author | axel <axel@liljencrantz.se> | 2005-09-20 23:26:39 +1000 |
---|---|---|
committer | axel <axel@liljencrantz.se> | 2005-09-20 23:26:39 +1000 |
commit | 149594f974350bb364a76c73b91b1d5ffddaa1fa (patch) | |
tree | 95650e9982d5fabe4bd805d94c5d700cbbc1ca7f /proc.c |
Initial revision
darcs-hash:20050920132639-ac50b-fa3b476891e1f5f67207cf4cc7bf623834cc5edc.gz
Diffstat (limited to 'proc.c')
-rw-r--r-- | proc.c | 1268 |
1 files changed, 1268 insertions, 0 deletions
@@ -0,0 +1,1268 @@ +/** \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> +#include <sys/ioctl.h> +#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 + +#include <term.h> + +#include "util.h" +#include "wutil.h" +#include "proc.h" +#include "common.h" +#include "reader.h" +#include "sanity.h" +#include "env.h" + +/** + Size of message buffer +*/ +#define MESS_SIZE 256 + +/** + Size of buffer for reading buffered output +*/ +#define BUFFER_SIZE 4096 + +/** Status of last process to exit */ +static int last_status=0; + +/** Signal flag */ +static sig_atomic_t got_signal=0; + +job_t *first_job=0; +int is_interactive=0; +int is_interactive_session=0; +int is_subshell=0; +int is_block=0; +int is_login=0; + +pid_t proc_last_bg_pid = 0; + +io_data_t *io_add( io_data_t *list, io_data_t *element ) +{ + io_data_t *curr = list; + if( curr == 0 ) + return element; + while( curr->next != 0 ) + curr = curr->next; + curr->next = element; + return list; +} + +io_data_t *io_remove( io_data_t *list, io_data_t *element ) +{ + io_data_t *curr, *prev=0; + for( curr=list; curr; curr = curr->next ) + { + if( element == curr ) + { + if( prev == 0 ) + { + io_data_t *tmp = element->next; + element->next = 0; + return tmp; + } + else + { + prev->next = element->next; + element->next = 0; + return list; + } + } + prev = curr; + } + return list; +} + +io_data_t *io_duplicate( io_data_t *l ) +{ + io_data_t *res; + + if( l == 0 ) + return 0; + + res = malloc( sizeof( io_data_t) ); + + if( !res ) + { + die_mem(); + + } + + memcpy( res, l, sizeof(io_data_t )); + res->next=io_duplicate( l->next ); + return res; +} + +io_data_t *io_get( io_data_t *io, int fd ) +{ + if( io == 0 ) + return 0; + + io_data_t *res = io_get( io->next, fd ); + if( res ) + return res; + + if( io->fd == fd ) + return io; + + return 0; +} + + +/** + Recursively free a process and those following it +*/ +static void free_process( process_t *p ) +{ + wchar_t **arg; + + if( p==0 ) + return; + + free_process( p->next ); + free( p->actual_cmd ); + if( p->argv != 0 ) + { + for( arg=p->argv; *arg; arg++ ) + free( *arg ); + free(p->argv ); + } + free( p ); +} + +/** Remove job from list of jobs */ + +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 ) + { + debug( 1, L"Job inconsistency" ); + 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 ) +{ + io_data_t *io, *ionext; + +// fwprintf( stderr, L"Remove job %d (%ls)\n", j->job_id, j->command ); + + job_remove( j ); + + /* Then free ourselves */ + free_process( j->first_process); + + if( j->command != 0 ) + free( j->command ); + + for( io=j->io; io; io=ionext ) + { + ionext = io->next; +// fwprintf( stderr, L"Kill redirect %d of type %d\n", ionext, io->io_mode ); + if( io->io_mode == IO_FILE ) + { + free( io->filename ); + } + free( io ); + } + + free( j ); +} + +void proc_destroy() +{ + while( first_job ) + { + debug( 2, L"freeing leaked job %ls", first_job->command ); + job_free( first_job ); + } +} + +void proc_set_last_status( int s ) +{ + last_status = s; + wchar_t stat[16]; + swprintf( stat, 16, L"%d", s ); + env_set( L"status", stat, ENV_GLOBAL ); + // fwprintf( stderr, L"Set last status to %d\n", s ); +} + +int proc_get_last_status() +{ + return last_status; +} + +job_t *job_create() +{ + int free_id=0; + job_t *res; + + while( job_get( free_id ) != 0 ) + free_id++; + res = calloc( 1, sizeof(job_t) ); + res->next = first_job; + res->job_id = free_id; + first_job = res; +// 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; + if( id == -1 ) + { + 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. +*/ +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; +} + + +/* + Return true if all processes in the job have completed. +*/ +int job_is_completed( const job_t *j ) +{ + process_t *p; + + for (p = j->first_process; p; p = p->next) + { + if (!p->completed) + { +// fwprintf( stderr, L"Process %ls not finished\n", p->argv[0] ); + return 0; + } + } + return 1; +} + +/** + Return true if all processes in the job have completed. +*/ +static int job_last_is_completed( const job_t *j ) +{ + process_t *p; + + for (p = j->first_process; p->next; p = p->next) + ; + return p->completed; +} + +/** + Get string representation of a signal +*/ +static wchar_t *sig2wcs( int sig ) +{ + switch( sig ) + { + case SIGHUP: + return L"SIGHUP"; + case SIGINT: + return L"SIGINT"; + case SIGQUIT: + return L"SIGQUIT"; + case SIGILL: + return L"SIGILL"; + case SIGTRAP: + return L"SIGTRAP"; + case SIGABRT: + return L"SIGABRT"; + case SIGBUS: + return L"SIGBUS"; + case SIGFPE: + return L"SIGFPE"; + case SIGKILL: + return L"SIGKILL"; + case SIGUSR1: + return L"SIGUSR1"; + case SIGSEGV: + return L"SIGSEGV"; + case SIGUSR2: + return L"SIGUSR2"; + case SIGPIPE: + return L"SIGPIPE"; + case SIGALRM: + return L"SIGALRM"; + case SIGTERM: + return L"SIGTERM"; + case SIGCHLD: + return L"SIGCHLD"; + case SIGCONT: + return L"SIGCONT"; + case SIGSTOP: + return L"SIGSTOP"; + case SIGTSTP: + return L"SIGTSTP"; + case SIGTTIN: + return L"SIGTTIN"; + case SIGTTOU: + return L"SIGTTOU"; + case SIGURG: + return L"SIGURG"; + case SIGXCPU: + return L"SIGXCPU"; + case SIGXFSZ: + return L"SIGFXSZ"; + case SIGVTALRM: + return L"SIGVTALRM"; + case SIGPROF: + return L"SIGPROF"; + case SIGWINCH: + return L"SIGWINCH"; + case SIGIO: + return L"SIGIO"; +#ifdef SIGPWR + case SIGPWR: + return L"SIGPWR"; +#endif + case SIGSYS: + return L"SIGSYS"; + default: + return L"Unknown"; + } + +} + +/** + Returns a description of the specified signal. +*/ +static wchar_t *sig_description( int sig ) +{ + switch( sig ) + { + case SIGHUP: + return L"Terminal hung up"; + case SIGINT: + return L"Quit request from job control (^C)"; + case SIGQUIT: + return L"Quit request from job control with core dump (^\\)"; + case SIGILL: + return L"Illegal instruction"; + case SIGTRAP: + return L"Trace or breakpoint trap"; + case SIGABRT: + return L"Abort"; + case SIGBUS: + return L"Misaligned address error"; + case SIGFPE: + return L"Floating point exception"; + case SIGKILL: + return L"Forced quit"; + case SIGUSR1: + return L"User defined signal 1"; + case SIGUSR2: + return L"User defined signal 2"; + case SIGSEGV: + return L"Address boundary error"; + case SIGPIPE: + return L"Broken pipe"; + case SIGALRM: + return L"Timer expired"; + case SIGTERM: + return L"Polite quit request"; + case SIGCHLD: + return L"Child process status changed"; + case SIGCONT: + return L"Continue previously stopped process"; + case SIGSTOP: + return L"Forced stop"; + case SIGTSTP: + return L"Stop request from job control (^Z)"; + case SIGTTIN: + return L"Stop from terminal input"; + case SIGTTOU: + return L"Stop from terminal output"; + case SIGURG: + return L"Urgent socket condition"; + case SIGXCPU: + return L"CPU time limit exceeded"; + case SIGXFSZ: + return L"File size limit exceeded"; + case SIGVTALRM: + return L"Virtual timer expired"; + case SIGPROF: + return L"Profiling timer expired"; + case SIGWINCH: + return L"Window size change"; + case SIGIO: + return L"Asynchronous IO event"; +#ifdef SIGPWR + case SIGPWR: + return L"Power failure"; +#endif + case SIGSYS: + return L"Bad system call"; + default: + return L"Unknown"; + } + +} + + +/** + 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; +// fwprintf( stderr, L"Proc %d (%ls) stopped\n", p->pid, p->actual_cmd ); + +/* sprintf( mess, + "%ls (%d): Process stopped\n", + j->command, + (int) p->pid ); + write( 2, mess, strlen(mess) );*/ + } + else + { + p->completed = 1; +// fwprintf( stderr, L"Proc %d (%ls) exited\n", p->pid, p->actual_cmd ); + + if (( !WIFEXITED( status ) ) && + (! WIFSIGNALED( status )) ) + { + /* This should never be reached */ + char mess[128]; + sprintf( mess, + "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 + the signal handler, so it mustn't use malloc or any such nonsense. +*/ +static void handle_child_status( pid_t pid, int status ) +{ + int found_proc = 0; + job_t *j; + process_t *p; +// char mess[MESS_SIZE]; + 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 )); +*/ + + 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; + } + } + + + if( !is_interactive ) + { + + if( WIFSIGNALED( status ) && + ( WTERMSIG(status)==SIGINT || + WTERMSIG(status)==SIGQUIT ) ) + { + 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) ); + } + } + + 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; + } + } + kill( 0, SIGIO ); + errno=errno_old; +} + +/** + Format information about job status for the user to look at. +*/ +static void format_job_info( const job_t *j, const wchar_t *status ) +{ + fwprintf (stdout, L"\rJob %d, \'%ls\' has %ls", j->job_id, j->command, status); + fflush( stdout ); + tputs(clr_eol,1,&writeb); + fwprintf (stdout, L"\n" ); +} + +int job_do_notification() +{ + job_t *j, *jnext; + int found=0; + + for( j=first_job; j; j=jnext) + { + process_t *p; + jnext = j->next; + + + for( p=j->first_process; p; p=p->next ) + { + if( !p->completed ) + continue; + + if( p->type ) + continue; + + + if( WIFSIGNALED(p->status) ) + { + /* + Ignore signal SIGPIPE.We issue it ourselves to the pipe + writer when the pipe reader dies. + */ + if( WTERMSIG(p->status) != SIGPIPE ) + { + int proc_is_job = ((p==j->first_process) && (p->next == 0)); + if( proc_is_job ) + j->notified = 1; + if( !j->skip_notification ) + { + fwprintf( stdout, + L"fish: %ls %d, \'%ls\' terminated by signal %ls (%ls)", + proc_is_job?L"Job":L"Process", + proc_is_job?j->job_id:p->pid, + j->command, + sig2wcs(WTERMSIG(p->status)), + sig_description( WTERMSIG(p->status) ) ); + tputs(clr_eol,1,&writeb); + fwprintf (stdout, L"\n" ); + found=1; + } + + /* + Clear status so it is not reported more than once + */ + p->status = 0; + } + } + } + + /* + If all processes have completed, tell the user the job has + completed and delete it from the active job list. + */ + if( job_is_completed(j) ) { + if( !j->fg && !j->notified ) + { + if( !j->skip_notification ) + { + format_job_info (j, L"ended"); + found=1; + } + } + job_free(j); + } + else if(job_is_stopped (j) && !j->notified) { + /* + Notify the user about newly stopped jobs. + */ + if( !j->skip_notification ) + { + format_job_info(j, L"stopped"); + found=1; + } + j->notified = 1; + } + } + if( found ) + fflush( stdout ); + return found; + +} + + +#ifdef HAVE__PROC_SELF_STAT +/** + Get the CPU time for the specified process +*/ +unsigned long proc_get_jiffies( process_t *p ) +{ + wchar_t fn[256]; + //char stat_line[1024]; + + 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; + + swprintf( fn, 512, L"/proc/%d/stat", p->pid ); + + 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; + } + 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. + + \return 1 if buffers where avaialble, zero otherwise +*/ +static int select_try( job_t *j ) +{ + fd_set fds; + int maxfd=-1; + io_data_t *d; + + + +/* if( j->stop_reading ) + { + sleep(1); + return; + } +*/ + + FD_ZERO(&fds); + + for( d = j->io; d; d=d->next ) + { + if( d->io_mode == IO_BUFFER ) + { + int fd = d->pipe_fd[0]; +// fwprintf( stderr, L"fd %d on job %ls\n", fd, j->command ); + FD_SET( fd, &fds ); + maxfd=maxi( maxfd, d->pipe_fd[0] ); + 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. +*/ +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 ) + { + // fwprintf( stderr, 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 ) + { + break; + } + else if( l<0 ) + { + if( errno != EAGAIN ) + { + debug( 1, + L"An error occured while reading output from code block" ); + wperror( L"read_try" ); + } + + break; + } + else + { + b_append( buff->out_buffer, b, l ); + } + + } + } +} + +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; + +// if( is_interactive ) + debug( 3, + L"Continue on job %d (%ls), %ls, %ls", + j->job_id, + j->command, + job_is_completed( j )?L"COMPLETED":L"UNCOMPLETED", + is_interactive?L"INTERACTIVE":L"NON-INTERACTIVE" ); + + if( !job_is_completed( j ) ) + { + if( !is_subshell && is_interactive && !is_block) + { + + /* Put the job into the foreground. */ + if( j->fg ) + { + while( 1 ) + { + if( tcsetpgrp (0, j->pgid) ) + { + if( errno != EINTR ) + { + debug( 1, + L"Could not send job %d ('%ls') to foreground", + j->job_id, + j->command ); + wperror( L"tcsetpgrp" ); + return; + } + } + else + break; + } + + if( cont ) + { +// fwprintf( stderr, L"tcsetattr\n" ); + while( 1 ) + { + if( tcsetattr (0, TCSADRAIN, &j->tmodes)) + { + if( errno != EINTR ) + { + debug( 1, + L"Could not send job %d ('%ls') to foreground", + j->job_id, + j->command ); + wperror( L"tcsetattr" ); + return; + } + } + else + break; + } + + } + } + } + } + + /* + 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(). + */ + /* + fwprintf( stderr, L"Wait for %ls (%d)\n", j->command, j->pgid ); + */ + 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: + { + 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; + } + + } + } + } + } + + 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) ); + } + } + + } + /* + Put the shell back in the foreground. + */ + if( !is_subshell && is_interactive && !is_block) + { + + while( 1 ) + { + + if( tcsetpgrp (0, getpid()) ) + { + if( errno != EINTR ) + { + debug( 1, L"Could not return shell to foreground" ); + wperror( L"tcsetpgrp" ); + return; + } + } + else break; + } + + /* + Save jobs terminal modes. + */ + while( 1 ) + { + if( tcgetattr (0, &j->tmodes) ) + { + if( errno != EINTR ) + { + debug( 1, L"Could not return shell to foreground" ); + wperror( L"tcgetattr" ); + return; + } + } + else + break; + } + + /* + Restore the shell's terminal modes. + */ + while( 1 ) + { + if( tcsetattr (0, TCSADRAIN, &shell_modes)) + { + if( errno != EINTR ) + { + debug( 1, L"Could not return shell to foreground" ); + wperror( L"tcsetattr" ); + return; + } + } + else + break; + } + } + } +// fwprintf( stderr, L"Job_continue end\n" ); +} + +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, + L"Job command", + 0 ); + validate_pointer( j->first_process, + L"Process list pointer", + 0 ); + validate_pointer( j->next, + L"Job list pointer", + 1 ); + validate_pointer( j->command, + L"Job command", + 0 ); + /* + More than one foreground job? + */ + if( j->fg && !(job_is_stopped(j) || job_last_is_completed(j) ) ) + { + if( fg_job != 0 ) + { + debug( 0, + L"More than one job in foreground:\n" + L"job 1: %ls\njob 2: %ls", + fg_job->command, + j->command ); + sanity_lose(); + } + fg_job = j; + } + + p = j->first_process; + while( p ) + { + 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 ); + + if ( (p->stopped & (~0x00000001)) != 0 ) + { + debug( 0, + L"Job %ls, process %ls " + L"has inconsistent state \'stopped\'=%d", + j->command, + p->argv[0], + p->stopped ); + sanity_lose(); + } + + if ( (p->completed & (~0x00000001)) != 0 ) + { + debug( 0, + L"Job %ls, process %ls " + L"has inconsistent state \'completed\'=%d", + j->command, + p->argv[0], + p->completed ); + sanity_lose(); + } + + p=p->next; + } + + } +} + |