예제 #1
0
/**
	Format information about job status for the user to look at.

	\param j the job to test
	\param status a string description of the job exit type
*/
static void format_job_info(const job_t *j, const wchar_t *status, size_t job_count)
{
    fwprintf(stdout, L"\r");
    if (job_count == 1)
    {
        fwprintf(stdout, _(L"\'%ls\' has %ls"), truncate_command(j->command()).c_str(), status);
    }
    else
    {
        fwprintf(stdout, _(L"Job %d, \'%ls\' has %ls"), j->job_id, truncate_command(j->command()).c_str(), status);
    }
    fflush(stdout);
    tputs(clr_eol,1,&writeb);
    fwprintf(stdout, L"\n");
}
예제 #2
0
파일: proc.cpp 프로젝트: Sam0523/fish-shell
static void format_job_info(const job_t *j, job_status_t status) {
    const wchar_t *msg = L"Job %d, '%ls' has ended";  // this is the most common status msg
    if (status == JOB_STOPPED) msg = L"Job %d, '%ls' has stopped";

    fwprintf(stdout, L"\r");
    fwprintf(stdout, _(msg), j->job_id, truncate_command(j->command()).c_str());
    fflush(stdout);
    if (cur_term) {
        tputs(clr_eol, 1, &writeb);
    } else {
        fwprintf(stdout, L"\e[K");
    }
    fwprintf(stdout, L"\n");
}
예제 #3
0
파일: proc.cpp 프로젝트: Sam0523/fish-shell
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;
}
예제 #4
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;
}