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); } } }
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)) { 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) { // 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)) { // 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; } } }
/// Executes an external command. /// \return true on success, false if there is an exec error. static bool exec_external_command(job_t *j, process_t *p, const io_chain_t &proc_io_chain) { assert(p->type == EXTERNAL && "Process is not external"); // Get argv and envv before we fork. null_terminated_array_t<char> argv_array; convert_wide_array_to_narrow(p->get_argv_array(), &argv_array); // Ensure that stdin is blocking before we hand it off (see issue #176). It's a // little strange that we only do this with stdin and not with stdout or stderr. // However in practice, setting or clearing O_NONBLOCK on stdin also sets it for the // other two fds, presumably because they refer to the same underlying file // (/dev/tty?). make_fd_blocking(STDIN_FILENO); const char *const *argv = argv_array.get(); const char *const *envv = env_export_arr(); std::string actual_cmd_str = wcs2string(p->actual_cmd); const char *actual_cmd = actual_cmd_str.c_str(); const wchar_t *file = reader_current_filename(); #if FISH_USE_POSIX_SPAWN // Prefer to use posix_spawn, since it's faster on some systems like OS X. bool use_posix_spawn = g_use_posix_spawn && can_use_posix_spawn_for_job(j, p); if (use_posix_spawn) { g_fork_count++; // spawn counts as a fork+exec // Create posix spawn attributes and actions. pid_t pid = 0; posix_spawnattr_t attr = posix_spawnattr_t(); posix_spawn_file_actions_t actions = posix_spawn_file_actions_t(); bool made_it = fork_actions_make_spawn_properties(&attr, &actions, j, p, proc_io_chain); if (made_it) { // We successfully made the attributes and actions; actually call // posix_spawn. int spawn_ret = posix_spawn(&pid, actual_cmd, &actions, &attr, const_cast<char *const *>(argv), const_cast<char *const *>(envv)); // This usleep can be used to test for various race conditions // (https://github.com/fish-shell/fish-shell/issues/360). // usleep(10000); if (spawn_ret != 0) { safe_report_exec_error(spawn_ret, actual_cmd, argv, envv); // Make sure our pid isn't set. pid = 0; } // Clean up our actions. posix_spawn_file_actions_destroy(&actions); posix_spawnattr_destroy(&attr); } // A 0 pid means we failed to posix_spawn. Since we have no pid, we'll never get // told when it's exited, so we have to mark the process as failed. debug(4, L"Fork #%d, pid %d: spawn external command '%s' from '%ls'", g_fork_count, pid, actual_cmd, file ? file : L"<no file>"); if (pid == 0) { job_mark_process_as_failed(j, p); return false; } // these are all things do_fork() takes care of normally (for forked processes): p->pid = pid; on_process_created(j, p->pid); // We explicitly don't call set_child_group() for spawned processes because that // a) isn't necessary, and b) causes issues like fish-shell/fish-shell#4715 #if defined(__GLIBC__) // Unfortunately, using posix_spawn() is not the panacea it would appear to be, // glibc has a penchant for using fork() instead of vfork() when posix_spawn() is // called, meaning that atomicity is not guaranteed and we can get here before the // child group has been set. See discussion here: // https://github.com/Microsoft/WSL/issues/2997 And confirmation that this persists // past glibc 2.24+ here: https://github.com/fish-shell/fish-shell/issues/4715 if (j->get_flag(job_flag_t::JOB_CONTROL) && getpgid(p->pid) != j->pgid) { set_child_group(j, p->pid); } #else // In do_fork, the pid of the child process is used as the group leader if j->pgid // invalid, posix_spawn assigned the new group a pgid equal to its own id if // j->pgid was invalid, so this is what we do instead of calling set_child_group if (j->pgid == INVALID_PID) { j->pgid = pid; } #endif maybe_assign_terminal(j); } else #endif { if (!fork_child_for_process(j, p, proc_io_chain, false, "external command", [&] { safe_launch_process(p, actual_cmd, argv, envv); })) { return false; } } return true; }