Esempio n. 1
0
/// Print information about the specified job.
static void builtin_jobs_print(const job_t *j, int mode, int header, io_streams_t &streams) {
    process_t *p;
    switch (mode) {
        case JOBS_DEFAULT: {
            if (header) {
                // Print table header before first job.
                streams.out.append(_(L"Job\tGroup\t"));
#ifdef HAVE__PROC_SELF_STAT
                streams.out.append(_(L"CPU\t"));
#endif
                streams.out.append(_(L"State\tCommand\n"));
            }

            streams.out.append_format(L"%d\t%d\t", j->job_id, j->pgid);

#ifdef HAVE__PROC_SELF_STAT
            streams.out.append_format(L"%d%%\t", cpu_use(j));
#endif
            streams.out.append(job_is_stopped(j) ? _(L"stopped") : _(L"running"));
            streams.out.append(L"\t");
            streams.out.append(j->command_wcstr());
            streams.out.append(L"\n");
            break;
        }
        case JOBS_PRINT_GROUP: {
            if (header) {
                // Print table header before first job.
                streams.out.append(_(L"Group\n"));
            }
            streams.out.append_format(L"%d\n", j->pgid);
            break;
        }
        case JOBS_PRINT_PID: {
            if (header) {
                // Print table header before first job.
                streams.out.append(_(L"Process\n"));
            }

            for (p = j->first_process; p; p = p->next) {
                streams.out.append_format(L"%d\n", p->pid);
            }
            break;
        }
        case JOBS_PRINT_COMMAND: {
            if (header) {
                // Print table header before first job.
                streams.out.append(_(L"Command\n"));
            }

            for (p = j->first_process; p; p = p->next) {
                streams.out.append_format(L"%ls\n", p->argv0());
            }
            break;
        }
        default: {
            DIE("unexpected mode");
            break;
        }
    }
}
Esempio n. 2
0
/* Notify the user about stopped or terminated jobs.
   Delete terminated jobs from the active job list.  */
void do_job_notification (void) {
	job *j, *jlast, *jnext;
	process *p;

	/* Update status information for child processes.  */
	update_status ();

	jlast = NULL;
	for (j = first_job; j; j = jnext) {
		jnext = j->next;

		/* If all processes have completed, tell the user the job has
		 completed and delete it from the list of active jobs.  */
		if (job_is_completed (j)) {
			format_job_info (j, "completed");
			
			if (jlast)
				jlast->next = jnext;
			else
				first_job = jnext;
			
			free_job (j);
		}
		/* Notify the user about stopped jobs,
		 marking them so that we won’t do this more than once.  */
		else if (job_is_stopped (j) && !j->notified) {
			format_job_info (j, "stopped");
			j->notified = 1;
			jlast = j;
		}
		/* Don’t say anything about jobs that are still running.  */
		else
			jlast = j;
	}
}
Esempio n. 3
0
void wait_for_job(job_t *j) {
   int status;
   pid_t pid;
   do
     pid = waitpid(WAIT_ANY, &status, WUNTRACED);
   while (!mark_process_status(pid, status)&& !job_is_stopped(j)
          && !job_is_completed(j));
 }
Esempio n. 4
0
// This isn't used so the lint tools were complaining about its presence. I'm keeping it in the
// source because it could be useful for debugging. However, it would probably be better to add a
// verbose or debug option to the builtin `jobs` command.
void print_jobs(void)
{
    job_iterator_t jobs;
    job_t *j;
    while (j = jobs.next()) {
        fwprintf(stdout, L"%p -> %ls -> (foreground %d, complete %d, stopped %d, constructed %d)\n",
                 j, j->command_wcstr(), j->get_flag(JOB_FOREGROUND), job_is_completed(j),
                 job_is_stopped(j), j->get_flag(JOB_CONSTRUCTED));
    }
}
Esempio n. 5
0
void proc_sanity_check() {
    job_t *j;
    job_t *fg_job = 0;

    job_iterator_t jobs;
    while ((j = jobs.next())) {
        process_t *p;

        if (!job_get_flag(j, JOB_CONSTRUCTED)) continue;

        validate_pointer(j->first_process, _(L"Process list pointer"), 0);

        // More than one foreground job?
        if (job_get_flag(j, JOB_FOREGROUND) && !(job_is_stopped(j) || job_is_completed(j))) {
            if (fg_job != 0) {
                debug(0, _(L"More than one job in foreground: job 1: '%ls' job 2: '%ls'"),
                      fg_job->command_wcstr(), j->command_wcstr());
                sanity_lose();
            }
            fg_job = j;
        }

        p = j->first_process;
        while (p) {
            // Internal block nodes do not have argv - see issue #1545.
            bool null_ok = (p->type == INTERNAL_BLOCK_NODE);
            validate_pointer(p->get_argv(), _(L"Process argument list"), null_ok);
            validate_pointer(p->argv0(), _(L"Process name"), null_ok);
            validate_pointer(p->next, _(L"Process list pointer"), true);

            if ((p->stopped & (~0x00000001)) != 0) {
                debug(0, _(L"Job '%ls', process '%ls' has inconsistent state \'stopped\'=%d"),
                      j->command_wcstr(), p->argv0(), p->stopped);
                sanity_lose();
            }

            if ((p->completed & (~0x00000001)) != 0) {
                debug(0, _(L"Job '%ls', process '%ls' has inconsistent state \'completed\'=%d"),
                      j->command_wcstr(), p->argv0(), p->completed);
                sanity_lose();
            }

            p = p->next;
        }
    }
}
Esempio n. 6
0
void job_continue (job_t *j, int cont)
{
	/*
	  Put job first in the job list
	*/
    job_promote(j);
	job_set_flag( j, JOB_NOTIFIED, 0 );

	CHECK_BLOCK();
	
	debug( 4,
		   L"Continue job %d, gid %d (%ls), %ls, %ls",
		   j->job_id, 
		   j->pgid,
		   j->command_wcstr(), 
		   job_is_completed( j )?L"COMPLETED":L"UNCOMPLETED", 
		   is_interactive?L"INTERACTIVE":L"NON-INTERACTIVE" );
	
	if( !job_is_completed( j ) )
	{
		if( job_get_flag( j, JOB_TERMINAL ) && job_get_flag( j, JOB_FOREGROUND ) )
		{							
			/* Put the job into the foreground.  */
			int ok;
			
			signal_block();
			
			ok = terminal_give_to_job( j, cont );
			
			signal_unblock();		

			if( !ok )
				return;
			
		}
		
		/* 
		   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;

			if( job_get_flag( j, JOB_CONTROL ) )
			{
				if( killpg( j->pgid, SIGCONT ) )
				{
					wperror( L"killpg (SIGCONT)" );
					return;
				}
			}
			else
			{
				for( p=j->first_process; p; p=p->next )
				{
					if (kill ( p->pid, SIGCONT) < 0)
					{
						wperror (L"kill (SIGCONT)");
						return;
					}		
				}
			}
		}
	
		if( job_get_flag( j, JOB_FOREGROUND ) )
		{
			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_is_completed( j );
				}
				while (got_signal && !quit);
                
                if (quit) {
                    // It's possible that the job will produce output and exit before we've even read from it.
                    // We'll eventually read the output, but it may be after we've executed subsequent calls
                    // This is why my prompt colors kept getting screwed up - the builtin echo calls
                    // were sometimes having their output combined with the set_color calls in the wrong order!
                    read_try(j);
                }

				if( !quit )
				{
					
//					debug( 1, 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 );
							}
							else
							{
								/*
								  This probably means we got a
								  signal. A signal might mean that the
								  terminal emulator sent us a hup
								  signal to tell is to close. If so,
								  we should exit.
								*/
								if( reader_exit_forced() )
								{
									quit = 1;
								}
								
							}
							break;
						}
								
					}
				}					
			}
		}	
	}
	
	if( job_get_flag( j, JOB_FOREGROUND ) )
	{
		
		if( job_is_completed( j ))
		{
			process_t *p = j->first_process;
			while( p->next )
				p = p->next;

			if( WIFEXITED( p->status ) || WIFSIGNALED(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 )
				{
					int status = proc_format_status(p->status);
					//wprintf(L"setting status %d for %ls\n", job_get_flag( j, JOB_NEGATE )?!status:status, j->command);
					proc_set_last_status( job_get_flag( j, JOB_NEGATE )?!status:status);
				}
			}			
		}
		/* 
		   Put the shell back in the foreground.  
		*/
		if( job_get_flag( j, JOB_TERMINAL ) && job_get_flag( j, JOB_FOREGROUND ) )
		{
			int ok;
			
			signal_block();

			ok = terminal_return_from_job( j );
			
			signal_unblock();
			
			if( !ok )
				return;
			
		}
	}
	
}
Esempio n. 7
0
int job_reap( bool interactive )
{
    ASSERT_IS_MAIN_THREAD();
	job_t *jnext;	
	int found=0;
	
	static int locked = 0;
	
	locked++;	
	
	/*
	  job_read may fire an event handler, we do not want to call
	  ourselves recursively (to avoid infinite recursion).
	*/
	if( locked>1 )
		return 0;
    
    job_iterator_t jobs;
    jnext = jobs.next();
	while (jnext)
    {
        job_t *j = jnext;
        jnext = jobs.next();
		process_t *p;
		
		/*
		  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
		*/
		if( (!job_get_flag( j, JOB_SKIP_NOTIFICATION ) ) && (!interactive) && (!job_get_flag( j, JOB_FOREGROUND )))
		{
			continue;
		}
	
		for( p=j->first_process; p; p=p->next )
		{
			int s;
			if( !p->completed )
				continue;
			
			if( !p->pid )
				continue;			
			
			s = p->status;
			
			proc_fire_event( L"PROCESS_EXIT", EVENT_EXIT, p->pid, ( WIFSIGNALED(s)?-1:WEXITSTATUS( s )) );			
			
			if( WIFSIGNALED(s) )
			{
				/* 
				   Ignore signal SIGPIPE.We issue it ourselves to the pipe
				   writer when the pipe reader dies.
				*/
				if( WTERMSIG(s) != SIGPIPE )
				{	
					int proc_is_job = ((p==j->first_process) && (p->next == 0));
					if( proc_is_job )
						job_set_flag( j, JOB_NOTIFIED, 1 );
					if( !job_get_flag( j, JOB_SKIP_NOTIFICATION ) )
					{
						if( proc_is_job )
							fwprintf( stdout,
									  _( L"%ls: Job %d, \'%ls\' terminated by signal %ls (%ls)" ),
									  program_name,
									  j->job_id, 
									  j->command_wcstr(),
									  sig2wcs(WTERMSIG(p->status)),
									  signal_get_desc( WTERMSIG(p->status) ) );
						else
							fwprintf( stdout,
									  _( L"%ls: Process %d, \'%ls\' from job %d, \'%ls\' terminated by signal %ls (%ls)" ),
									  program_name,
									  p->pid,
									  p->argv0(),
									  j->job_id,
									  j->command_wcstr(),
									  sig2wcs(WTERMSIG(p->status)),
									  signal_get_desc( 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( !job_get_flag( j, JOB_FOREGROUND) && !job_get_flag( j, JOB_NOTIFIED ) && !job_get_flag( j, JOB_SKIP_NOTIFICATION ) )
			{
				format_job_info( j, _( L"ended" ) );
				found=1;
			}
			proc_fire_event( L"JOB_EXIT", EVENT_EXIT, -j->pgid, 0 );			
			proc_fire_event( L"JOB_EXIT", EVENT_JOB_ID, j->job_id, 0 );			

			job_free(j);
		}		
		else if( job_is_stopped( j ) && !job_get_flag( j, JOB_NOTIFIED ) ) 
		{
			/* 
			   Notify the user about newly stopped jobs. 
			*/
			if( !job_get_flag( j, JOB_SKIP_NOTIFICATION ) )
			{
				format_job_info( j, _( L"stopped" ) );
				found=1;
			}			
			job_set_flag( j, JOB_NOTIFIED, 1 );
		}
	}

	if( found )
		fflush( stdout );

	locked = 0;
	
	return found;	
}
Esempio n. 8
0
void proc_sanity_check()
{
	job_t *j;
	job_t *fg_job=0;
	
    job_iterator_t jobs;
    while ((j = jobs.next()))
	{
		process_t *p;

		if( !job_get_flag( j, JOB_CONSTRUCTED ) )
			continue;
		
		
		validate_pointer( j->first_process,
						  _( L"Process list pointer" ),
						  0 );

		/*
		  More than one foreground job?
		*/
		if( job_get_flag( j, JOB_FOREGROUND ) && !(job_is_stopped(j) || job_is_completed(j) ) )
		{
			if( fg_job != 0 )
			{
				debug( 0, 
					   _( L"More than one job in foreground: job 1: '%ls' job 2: '%ls'"),
					   fg_job->command_wcstr(),
					   j->command_wcstr() );
				sanity_lose();
			}
			fg_job = j;
		}
		
   		p = j->first_process;
		while( p )
		{			
			validate_pointer( p->get_argv(), _( L"Process argument list" ), 0 );
			validate_pointer( p->argv0(), _( 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' has inconsistent state \'stopped\'=%d" ),
					   j->command_wcstr(), 
					   p->argv0(),
					   p->stopped );
				sanity_lose();
			}
			
			if ( (p->completed & (~0x00000001)) != 0 )
			{
				debug( 0,
					   _( L"Job '%ls', process '%ls' has inconsistent state \'completed\'=%d" ),
					   j->command_wcstr(), 
					   p->argv0(),
					   p->completed );
				sanity_lose();
			}
			
			p=p->next;
		}
		
	}	
}
Esempio n. 9
0
/**
   Print information about the specified job
*/
static void builtin_jobs_print( job_t *j, int mode, int header )
{
	process_t *p;
	switch( mode )
	{
		case JOBS_DEFAULT:
		{

			if( header )
			{
				/*
				  Print table header before first job
				*/
				sb_append( sb_out, _( L"Job\tGroup\t" ));
#ifdef HAVE__PROC_SELF_STAT
				sb_append( sb_out, _( L"CPU\t" ) );
#endif
				sb_append( sb_out, _( L"State\tCommand\n" ) );
			}

			sb_printf( sb_out, L"%d\t%d\t", j->job_id, j->pgid );

#ifdef HAVE__PROC_SELF_STAT
			sb_printf( sb_out, L"%d%%\t", cpu_use(j) );
#endif
			sb_append( sb_out,
						job_is_stopped(j)?_(L"stopped"):_(L"running"),
						L"\t",
						j->command,
						L"\n",
						(void *)0 );
			break;
		}

		case JOBS_PRINT_GROUP:
		{
			if( header )
			{
				/*
				  Print table header before first job
				*/
				sb_append( sb_out, _( L"Group\n" ));
			}
			sb_printf( sb_out, L"%d\n", j->pgid );
			break;
		}

		case JOBS_PRINT_PID:
		{
			if( header )
			{
				/*
				  Print table header before first job
				*/
				sb_append( sb_out, _( L"Procces\n" ));
			}

			for( p=j->first_process; p; p=p->next )
			{
				sb_printf( sb_out, L"%d\n", p->pid );
			}
			break;
		}

		case JOBS_PRINT_COMMAND:
		{
			if( header )
			{
				/*
				  Print table header before first job
				*/
				sb_append( sb_out, _( L"Command\n" ));
			}

			for( p=j->first_process; p; p=p->next )
			{
				sb_printf( sb_out, L"%ls\n", p->argv[0] );
			}
			break;
		}
	}

}
Esempio n. 10
0
void job_continue(job_t *j, bool cont) {
    // Put job first in the job list.
    job_promote(j);
    j->set_flag(JOB_NOTIFIED, false);

    CHECK_BLOCK();
    debug(4, L"%ls job %d, gid %d (%ls), %ls, %ls", cont ? L"Continue" : L"Start", j->job_id,
          j->pgid, j->command_wcstr(), job_is_completed(j) ? L"COMPLETED" : L"UNCOMPLETED",
          is_interactive ? L"INTERACTIVE" : L"NON-INTERACTIVE");

    if (!job_is_completed(j)) {
        if (j->get_flag(JOB_TERMINAL) && j->get_flag(JOB_FOREGROUND)) {
            // Put the job into the foreground. Hack: ensure that stdin is marked as blocking first
            // (issue #176).
            make_fd_blocking(STDIN_FILENO);
            if (!terminal_give_to_job(j, cont)) return;
        }

        // Send the job a continue signal, if necessary.
        if (cont) {
            for (process_ptr_t &p : j->processes) p->stopped = false;

            if (j->get_flag(JOB_CONTROL)) {
                if (killpg(j->pgid, SIGCONT)) {
                    wperror(L"killpg (SIGCONT)");
                    return;
                }
            } else {
                for (const process_ptr_t &p : j->processes) {
                    if (kill(p->pid, SIGCONT) < 0) {
                        wperror(L"kill (SIGCONT)");
                        return;
                    }
                }
            }
        }

        if (j->get_flag(JOB_FOREGROUND)) {
            // Look for finished processes first, to avoid select() if it's already done.
            process_mark_finished_children(false);

            // Wait for job to report.
            while (!reader_exit_forced() && !job_is_stopped(j) && !job_is_completed(j)) {
                // debug( 1, L"select_try()" );
                switch (select_try(j)) {
                    case 1: {
                        read_try(j);
                        process_mark_finished_children(false);
                        break;
                    }
                    case 0: {
                        // No FDs are ready. Look for finished processes.
                        process_mark_finished_children(false);
                        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.
                        //
                        // This will return early if we get a signal, like SIGHUP.
                        process_mark_finished_children(true);
                        break;
                    }
                    default: {
                        DIE("unexpected return value from select_try()");
                        break;
                    }
                }
            }
        }
    }

    if (j->get_flag(JOB_FOREGROUND)) {
        if (job_is_completed(j)) {
            // It's possible that the job will produce output and exit before we've even read from
            // it.
            //
            // We'll eventually read the output, but it may be after we've executed subsequent calls
            // This is why my prompt colors kept getting screwed up - the builtin echo calls
            // were sometimes having their output combined with the set_color calls in the wrong
            // order!
            read_try(j);

            const std::unique_ptr<process_t> &p = j->processes.back();

            // Mark process status only if we are in the foreground and the last process in a pipe,
            // and it is not a short circuited builtin.
            if ((WIFEXITED(p->status) || WIFSIGNALED(p->status)) && p->pid) {
                int status = proc_format_status(p->status);
                // fwprintf(stdout, L"setting status %d for %ls\n", job_get_flag( j, JOB_NEGATE
                // )?!status:status, j->command);
                proc_set_last_status(j->get_flag(JOB_NEGATE) ? !status : status);
            }
        }

        // Put the shell back in the foreground.
        if (j->get_flag(JOB_TERMINAL) && j->get_flag(JOB_FOREGROUND)) {
            terminal_return_from_job(j);
        }
    }
}
Esempio n. 11
0
static int process_clean_after_marking(bool allow_interactive) {
    ASSERT_IS_MAIN_THREAD();
    job_t *jnext;
    int found = 0;

    // this function may fire an event handler, we do not want to call ourselves recursively (to avoid
    // infinite recursion).
    static bool locked = false;
    if (locked) {
        return 0;
    }
    locked = true;

    // this may be invoked in an exit handler, after the TERM has been torn down
    // don't try to print in that case (#3222)
    const bool interactive = allow_interactive && cur_term != NULL;


    job_iterator_t jobs;
    const size_t job_count = jobs.count();
    jnext = jobs.next();
    while (jnext) {
        job_t *j = jnext;
        jnext = jobs.next();

        // 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.
        if ((!j->get_flag(JOB_SKIP_NOTIFICATION)) && (!interactive) &&
            (!j->get_flag(JOB_FOREGROUND))) {
            continue;
        }

        for (const process_ptr_t &p : j->processes) {
            int s;
            if (!p->completed) continue;

            if (!p->pid) continue;

            s = p->status;

            // TODO: The generic process-exit event is useless and unused.
            // Remove this in future.
            proc_fire_event(L"PROCESS_EXIT", EVENT_EXIT, p->pid,
                            (WIFSIGNALED(s) ? -1 : WEXITSTATUS(s)));

            // Ignore signal SIGPIPE.We issue it ourselves to the pipe writer when the pipe reader
            // dies.
            if (!WIFSIGNALED(s) || WTERMSIG(s) == SIGPIPE) {
                continue;
            }

            // Handle signals other than SIGPIPE.
            int proc_is_job = (p->is_first_in_job && p->is_last_in_job);
            if (proc_is_job) j->set_flag(JOB_NOTIFIED, true);
            if (j->get_flag(JOB_SKIP_NOTIFICATION)) {
                continue;
            }

            // Print nothing if we get SIGINT in the foreground process group, to avoid spamming
            // obvious stuff on the console (#1119). If we get SIGINT for the foreground
            // process, assume the user typed ^C and can see it working. It's possible they
            // didn't, and the signal was delivered via pkill, etc., but the SIGINT/SIGTERM
            // distinction is precisely to allow INT to be from a UI
            // and TERM to be programmatic, so this assumption is keeping with the design of
            // signals. If echoctl is on, then the terminal will have written ^C to the console.
            // If off, it won't have. We don't echo ^C either way, so as to respect the user's
            // preference.
            if (WTERMSIG(p->status) != SIGINT || !j->get_flag(JOB_FOREGROUND)) {
                if (proc_is_job) {
                    // We want to report the job number, unless it's the only job, in which case
                    // we don't need to.
                    const wcstring job_number_desc =
                        (job_count == 1) ? wcstring() : format_string(_(L"Job %d, "), j->job_id);
                    fwprintf(stdout, _(L"%ls: %ls\'%ls\' terminated by signal %ls (%ls)"),
                             program_name, job_number_desc.c_str(),
                             truncate_command(j->command()).c_str(), sig2wcs(WTERMSIG(p->status)),
                             signal_get_desc(WTERMSIG(p->status)));
                } else {
                    const wcstring job_number_desc =
                        (job_count == 1) ? wcstring() : format_string(L"from job %d, ", j->job_id);
                    const wchar_t *fmt =
                        _(L"%ls: Process %d, \'%ls\' %ls\'%ls\' terminated by signal %ls (%ls)");
                    fwprintf(stdout, fmt, program_name, p->pid, p->argv0(), job_number_desc.c_str(),
                             truncate_command(j->command()).c_str(), sig2wcs(WTERMSIG(p->status)),
                             signal_get_desc(WTERMSIG(p->status)));
                }

                if (cur_term != NULL) {
                    tputs(clr_eol, 1, &writeb);
                } else {
                    fwprintf(stdout, L"\e[K");  // no term set up - do clr_eol manually
                }
                fwprintf(stdout, L"\n");
            }
            found = 1;
            p->status = 0;  // clear status so it is not reported more than once
        }

        // 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->get_flag(JOB_FOREGROUND) && !j->get_flag(JOB_NOTIFIED) &&
                !j->get_flag(JOB_SKIP_NOTIFICATION)) {
                format_job_info(j, JOB_ENDED);
                found = 1;
            }
            // TODO: The generic process-exit event is useless and unused.
            // Remove this in future.
            // Don't fire the exit-event for jobs with pgid -2.
            // That's our "sentinel" pgid, for jobs that don't (yet) have a pgid,
            // or jobs that consist entirely of builtins (and hence don't have a process).
            // This causes issues if fish is PID 2, which is quite common on WSL. See #4582.
            if (j->pgid != -2) {
                proc_fire_event(L"JOB_EXIT", EVENT_EXIT, -j->pgid, 0);
            }
            proc_fire_event(L"JOB_EXIT", EVENT_JOB_ID, j->job_id, 0);

            job_remove(j);
        } else if (job_is_stopped(j) && !j->get_flag(JOB_NOTIFIED)) {
            // Notify the user about newly stopped jobs.
            if (!j->get_flag(JOB_SKIP_NOTIFICATION)) {
                format_job_info(j, JOB_STOPPED);
                found = 1;
            }
            j->set_flag(JOB_NOTIFIED, true);
        }
    }

    if (found) fflush(stdout);

    locked = false;

    return found;
}
Esempio n. 12
0
int job_reap(bool interactive)
{
    ASSERT_IS_MAIN_THREAD();
    job_t *jnext;
    int found=0;

    /* job_reap may fire an event handler, we do not want to call ourselves recursively (to avoid infinite recursion). */
    static bool locked = false;
    if (locked)
    {
        return 0;
    }
    locked = true;
    
    process_mark_finished_children(false);

    /* Preserve the exit status */
    const int saved_status = proc_get_last_status();

    job_iterator_t jobs;
    const size_t job_count = jobs.count();
    jnext = jobs.next();
    while (jnext)
    {
        job_t *j = jnext;
        jnext = jobs.next();

        /*
          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
        */
        if ((!job_get_flag(j, JOB_SKIP_NOTIFICATION)) && (!interactive) && (!job_get_flag(j, JOB_FOREGROUND)))
        {
            continue;
        }

        for (process_t *p = j->first_process; p; p=p->next)
        {
            int s;
            if (!p->completed)
                continue;

            if (!p->pid)
                continue;

            s = p->status;

            proc_fire_event(L"PROCESS_EXIT", EVENT_EXIT, p->pid, (WIFSIGNALED(s)?-1:WEXITSTATUS(s)));

            if (WIFSIGNALED(s))
            {
                /*
                   Ignore signal SIGPIPE.We issue it ourselves to the pipe
                   writer when the pipe reader dies.
                */
                if (WTERMSIG(s) != SIGPIPE)
                {
                    int proc_is_job = ((p==j->first_process) && (p->next == 0));
                    if (proc_is_job)
                        job_set_flag(j, JOB_NOTIFIED, 1);
                    if (!job_get_flag(j, JOB_SKIP_NOTIFICATION))
                    {
                        /* Print nothing if we get SIGINT in the foreground process group, to avoid spamming obvious stuff on the console (#1119). If we get SIGINT for the foreground process, assume the user typed ^C and can see it working. It's possible they didn't, and the signal was delivered via pkill, etc., but the SIGINT/SIGTERM distinction is precisely to allow INT to be from a UI and TERM to be programmatic, so this assumption is keeping with the design of signals.
                        If echoctl is on, then the terminal will have written ^C to the console. If off, it won't have. We don't echo ^C either way, so as to respect the user's preference. */
                        if (WTERMSIG(p->status) != SIGINT || ! job_get_flag(j, JOB_FOREGROUND))
                            {
                            if (proc_is_job)
                            {
                                // We want to report the job number, unless it's the only job, in which case we don't need to
                                const wcstring job_number_desc = (job_count == 1) ? wcstring() : format_string(L"Job %d, ", j->job_id);
                                fwprintf(stdout,
                                         _(L"%ls: %ls\'%ls\' terminated by signal %ls (%ls)"),
                                         program_name,
                                         job_number_desc.c_str(),
                                         truncate_command(j->command()).c_str(),
                                         sig2wcs(WTERMSIG(p->status)),
                                         signal_get_desc(WTERMSIG(p->status)));
                            }
                            else
                            {
                                const wcstring job_number_desc = (job_count == 1) ? wcstring() : format_string(L"from job %d, ", j->job_id);
                                fwprintf(stdout,
                                         _(L"%ls: Process %d, \'%ls\' %ls\'%ls\' terminated by signal %ls (%ls)"),
                                         program_name,
                                         p->pid,
                                         p->argv0(),
                                         job_number_desc.c_str(),
                                         truncate_command(j->command()).c_str(),
                                         sig2wcs(WTERMSIG(p->status)),
                                         signal_get_desc(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 (!job_get_flag(j, JOB_FOREGROUND) && !job_get_flag(j, JOB_NOTIFIED) && !job_get_flag(j, JOB_SKIP_NOTIFICATION))
            {
                format_job_info(j, _(L"ended"), job_count);
                found=1;
            }
            proc_fire_event(L"JOB_EXIT", EVENT_EXIT, -j->pgid, 0);
            proc_fire_event(L"JOB_EXIT", EVENT_JOB_ID, j->job_id, 0);

            job_free(j);
        }
        else if (job_is_stopped(j) && !job_get_flag(j, JOB_NOTIFIED))
        {
            /*
               Notify the user about newly stopped jobs.
            */
            if (!job_get_flag(j, JOB_SKIP_NOTIFICATION))
            {
                format_job_info(j, _(L"stopped"), job_count);
                found=1;
            }
            job_set_flag(j, JOB_NOTIFIED, 1);
        }
    }

    if (found)
        fflush(stdout);

    /* Restore the exit status. */
    proc_set_last_status(saved_status);

    locked = false;

    return found;
}
Esempio n. 13
0
void job_continue(job_t *j, bool cont)
{
    /*
      Put job first in the job list
    */
    job_promote(j);
    job_set_flag(j, JOB_NOTIFIED, 0);

    CHECK_BLOCK();

    debug(4,
          L"Continue job %d, gid %d (%ls), %ls, %ls",
          j->job_id,
          j->pgid,
          j->command_wcstr(),
          job_is_completed(j)?L"COMPLETED":L"UNCOMPLETED",
          is_interactive?L"INTERACTIVE":L"NON-INTERACTIVE");

    if (!job_is_completed(j))
    {
        if (job_get_flag(j, JOB_TERMINAL) && job_get_flag(j, JOB_FOREGROUND))
        {
            /* Put the job into the foreground. Hack: ensure that stdin is marked as blocking first (#176). */
            make_fd_blocking(STDIN_FILENO);

            signal_block();

            bool ok = terminal_give_to_job(j, cont);

            signal_unblock();

            if (!ok)
                return;
        }

        /*
           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;

            if (job_get_flag(j, JOB_CONTROL))
            {
                if (killpg(j->pgid, SIGCONT))
                {
                    wperror(L"killpg (SIGCONT)");
                    return;
                }
            }
            else
            {
                for (p=j->first_process; p; p=p->next)
                {
                    if (kill(p->pid, SIGCONT) < 0)
                    {
                        wperror(L"kill (SIGCONT)");
                        return;
                    }
                }
            }
        }

        if (job_get_flag(j, JOB_FOREGROUND))
        {
            /* Look for finished processes first, to avoid select() if it's already done. */
            process_mark_finished_children(false);

            /*
               Wait for job to report.
            */
            while (! reader_exit_forced() && ! job_is_stopped(j) && ! job_is_completed(j))
            {
//					debug( 1, L"select_try()" );
                switch (select_try(j))
                {
                    case 1:
                    {
                        read_try(j);
                        process_mark_finished_children(false);
                        break;
                    }
                    
                    case 0:
                    {
                        /* No FDs are ready. Look for finished processes. */
                        process_mark_finished_children(false);
                        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.
                         
                          This will return early if we get a signal,
                          like SIGHUP.
                        */
                        process_mark_finished_children(true);
                        break;
                    }
                }
            }
        }
    }

    if (job_get_flag(j, JOB_FOREGROUND))
    {

        if (job_is_completed(j))
        {

            // It's possible that the job will produce output and exit before we've even read from it.
            // We'll eventually read the output, but it may be after we've executed subsequent calls
            // This is why my prompt colors kept getting screwed up - the builtin echo calls
            // were sometimes having their output combined with the set_color calls in the wrong order!
            read_try(j);

            process_t *p = j->first_process;
            while (p->next)
                p = p->next;

            if (WIFEXITED(p->status) || WIFSIGNALED(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 circuited builtin
                */
                if (p->pid)
                {
                    int status = proc_format_status(p->status);
                    //wprintf(L"setting status %d for %ls\n", job_get_flag( j, JOB_NEGATE )?!status:status, j->command);
                    proc_set_last_status(job_get_flag(j, JOB_NEGATE)?!status:status);
                }
            }
        }

        /* Put the shell back in the foreground. */
        if (job_get_flag(j, JOB_TERMINAL) && job_get_flag(j, JOB_FOREGROUND))
        {
            int ok;

            signal_block();

            ok = terminal_return_from_job(j);

            signal_unblock();

            if (!ok)
                return;

        }
    }

}
Esempio n. 14
0
/* 
 * builtin_cmd - If the user has typed a built-in command then execute
 * it immediately.  
 */
bool builtin_cmd(job_t *last_job, int argc, char **argv) 
{

	    /* check whether the cmd is a built in command
        */

  if (!strcmp("quit", argv[0])) {
    /* Your code here */
    exit(EXIT_SUCCESS);
  }
  else if (!strcmp("jobs", argv[0])) {
    /* Your code here */
    job_t* currentJob = active_jobs_head;
    job_t* deletedJob = NULL;

    while(currentJob != NULL) {
      // print active jobs and then change notified to 0

      //if((currentJob->first_process)->completed == true && currentJob->notified == false) {

      //need to check if every process has completed for a job to be complete, not just the first process
      if (job_is_completed(currentJob) == true)
      {
        printf("%d (Completed): %s\n", currentJob->pgid, currentJob->commandinfo);
        deletedJob = currentJob;

         //currentJob->notified = true;
      }
      //otherwise it is stopped
      else if (job_is_stopped(currentJob) == true)
      {
        printf("%d (Stopped): %s\n", currentJob->pgid, currentJob->commandinfo);
      }

      else
      {
        printf("%d (Running): %s\n", currentJob->pgid, currentJob->commandinfo);
      }

      currentJob = currentJob->next;

      // delete job after it is completed, don't need to save notified 
     
      if (deletedJob != NULL) 
      {
        if (deletedJob == active_jobs_head)
        {
          active_jobs_head = deletedJob->next;
          free_job(deletedJob); //TBD warning about this?
        }
        else
        {
          delete_job(deletedJob, active_jobs_head);
        }
        deletedJob = NULL;
      }
    }
    return true;
  }

  else if (!strcmp("cd", argv[0])) {
    int chdir_return = chdir(argv[1]);
    if(chdir_return == -1) {
      printf("cd: %s: No such file or directory\n", argv[1]);
    }
    return true;
  }
  else if (!strcmp("bg", argv[0])) {
    /* Your code here */
    job_t* currentJob = active_jobs_head;
    process_t* p2;

    if(argv[1] == NULL) 
    {
      // continue most recent stopped job

      //use find_last_job
      currentJob = find_last_job(active_jobs_head);

      continue_job(currentJob);
      seize_tty(currentJob->pgid);


      p2 = currentJob->first_process;

      while(p2 != NULL)
      {
        p2->stopped = false;
        
        struct sigaction action;
        action.sa_sigaction = sighandler;

        sigfillset(&action.sa_mask);
        action.sa_flags = SA_SIGINFO;
        sigaction(SIGCHLD, &action, NULL);

        p2 = p2->next;
      }
      seize_tty(getpid());
    }

    else 
    {
      pid_t job_number = atoi(argv[1]);

      
      while(currentJob != NULL) {
        // Need to eventually iterate through all processes?
        
        if((job_is_stopped(currentJob)) && currentJob->pgid == job_number) {
          //seize_tty(currentJob->pgid);
          continue_job(currentJob); 
          seize_tty(currentJob->pgid);

          p2 = currentJob->first_process;
          while (p2 != NULL)
          {
            p2->stopped = false; 

            struct sigaction action;
            action.sa_sigaction = sighandler;

            sigfillset(&action.sa_mask);
            action.sa_flags = SA_SIGINFO;
            sigaction(SIGCHLD, &action, NULL);
            p2 = p2->next;
          }

          seize_tty(getpid());
          break;
        }
        else if (currentJob->pgid == job_number) {
          printf("%s\n", "This process wasn't stopped`");
        }
        else {
          printf("%s\n", "This job number is not the requested one");
        }
        currentJob = currentJob->next;
      }
    }
    return true;
  }


  else if (!strcmp("fg", argv[0])) {
    /* Your code here */
    job_t* currentJob = active_jobs_head;
    process_t* p2;

    if(argv[1] == NULL) {
      // continue most recent stopped job

      //use find_last_job
      currentJob = find_last_job(active_jobs_head);
      continue_job(currentJob);
      seize_tty(currentJob->pgid);


      p2 = currentJob->first_process;

      while(p2 != NULL)
      {
        waiting(p2);
        p2 = p2->next;
      }
      seize_tty(getpid());
    }
    else {
      pid_t job_number = atoi(argv[1]);

      
      while(currentJob != NULL) {
        
        
        if((job_is_stopped(currentJob)) && currentJob->pgid == job_number) {
          //seize_tty(currentJob->pgid);
          continue_job(currentJob); 
          seize_tty(currentJob->pgid);

          p2 = currentJob->first_process;
          while (p2 != NULL)
          {
            waiting(p2);
            p2 = p2->next;
          }
          seize_tty(getpid());
          break;
        }
        else if (currentJob->pgid == job_number) {
          printf("%s\n", "This process wasn't stopped`");
        }
        else {
          printf("%s\n", "This job number is not the requested one");
        }
        currentJob = currentJob->next;
      }
    }
    return true;
  }
  /* not a builtin command */
  return false;
}