📄 jobs.c
字号:
while (last->next != first) last = last->next; for (;;) { if (p != first) fprintf (stream, format ? " " : " |"); if (format != JLIST_STANDARD) fprintf (stream, "%5ld", (long)p->pid); fprintf (stream, " "); if (format > -1 && job_index >= 0) { show = format ? p : last; temp = printable_job_status (job_index, show, format); if (p != first) { if (format) { if (show->running == first->running && WSTATUS (show->status) == WSTATUS (first->status)) temp = ""; } else temp = (char *)NULL; } if (temp) { fprintf (stream, "%s", temp); es = STRLEN (temp); if (es == 0) es = 2; /* strlen ("| ") */ name_padding = LONGEST_SIGNAL_DESC - es; fprintf (stream, "%*s", name_padding, ""); if ((WIFSTOPPED (show->status) == 0) && (WIFCONTINUED (show->status) == 0) && WIFCORED (show->status)) fprintf (stream, _("(core dumped) ")); } } if (p != first && format) fprintf (stream, "| "); if (p->command) fprintf (stream, "%s", p->command); if (p == last && job_index >= 0) { temp = current_working_directory (); if (RUNNING (job_index) && (IS_FOREGROUND (job_index) == 0)) fprintf (stream, " &"); if (strcmp (temp, jobs[job_index]->wd) != 0) fprintf (stream, _(" (wd: %s)"), polite_directory_format (jobs[job_index]->wd)); } if (format || (p == last)) { /* We need to add a CR only if this is an interactive shell, and we're reporting the status of a completed job asynchronously. We can't really check whether this particular job is being reported asynchronously, so just add the CR if the shell is currently interactive and asynchronous notification is enabled. */ if (asynchronous_notification && interactive) fprintf (stream, "\r\n"); else fprintf (stream, "\n"); } if (p == last) break; p = p->next; } fflush (stream);}/* Print information to STREAM about jobs[JOB_INDEX] according to FORMAT. Must be called with SIGCHLD blocked or queued with queue_sigchld */static voidpretty_print_job (job_index, format, stream) int job_index, format; FILE *stream;{ register PROCESS *p; /* Format only pid information about the process group leader? */ if (format == JLIST_PID_ONLY) { fprintf (stream, "%ld\n", (long)jobs[job_index]->pipe->pid); return; } if (format == JLIST_CHANGED_ONLY) { if (IS_NOTIFIED (job_index)) return; format = JLIST_STANDARD; } if (format != JLIST_NONINTERACTIVE) fprintf (stream, "[%d]%c ", job_index + 1, (job_index == js.j_current) ? '+': (job_index == js.j_previous) ? '-' : ' '); if (format == JLIST_NONINTERACTIVE) format = JLIST_LONG; p = jobs[job_index]->pipe; print_pipeline (p, job_index, format, stream); /* We have printed information about this job. When the job's status changes, waitchld () sets the notification flag to 0. */ jobs[job_index]->flags |= J_NOTIFIED;}static intprint_job (job, format, state, job_index) JOB *job; int format, state, job_index;{ if (state == -1 || (JOB_STATE)state == job->state) pretty_print_job (job_index, format, stdout); return (0);}voidlist_one_job (job, format, ignore, job_index) JOB *job; int format, ignore, job_index;{ pretty_print_job (job_index, format, stdout);}voidlist_stopped_jobs (format) int format;{ cleanup_dead_jobs (); map_over_jobs (print_job, format, (int)JSTOPPED);}voidlist_running_jobs (format) int format;{ cleanup_dead_jobs (); map_over_jobs (print_job, format, (int)JRUNNING);}/* List jobs. If FORMAT is non-zero, then the long form of the information is printed, else just a short version. */voidlist_all_jobs (format) int format;{ cleanup_dead_jobs (); map_over_jobs (print_job, format, -1);}/* Fork, handling errors. Returns the pid of the newly made child, or 0. COMMAND is just for remembering the name of the command; we don't do anything else with it. ASYNC_P says what to do with the tty. If non-zero, then don't give it away. */pid_tmake_child (command, async_p) char *command; int async_p;{ int forksleep; sigset_t set, oset; pid_t pid; sigemptyset (&set); sigaddset (&set, SIGCHLD); sigaddset (&set, SIGINT); sigemptyset (&oset); sigprocmask (SIG_BLOCK, &set, &oset); making_children (); forksleep = 1;#if defined (BUFFERED_INPUT) /* If default_buffered_input is active, we are reading a script. If the command is asynchronous, we have already duplicated /dev/null as fd 0, but have not changed the buffered stream corresponding to the old fd 0. We don't want to sync the stream in this case. */ if (default_buffered_input != -1 && (!async_p || default_buffered_input > 0)) sync_buffered_stream (default_buffered_input);#endif /* BUFFERED_INPUT */ /* Create the child, handle severe errors. Retry on EAGAIN. */ while ((pid = fork ()) < 0 && errno == EAGAIN && forksleep < FORKSLEEP_MAX) { /* bash-4.2 */ /* If we can't create any children, try to reap some dead ones. */ waitchld (-1, 0); sys_error ("fork: retry"); if (sleep (forksleep) != 0) break; forksleep <<= 1; } if (pid < 0) { sys_error ("fork"); /* Kill all of the processes in the current pipeline. */ terminate_current_pipeline (); /* Discard the current pipeline, if any. */ if (the_pipeline) kill_current_pipeline (); last_command_exit_value = EX_NOEXEC; throw_to_top_level (); /* Reset signals, etc. */ } if (pid == 0) { /* In the child. Give this child the right process group, set the signals to the default state for a new process. */ pid_t mypid; mypid = getpid ();#if defined (BUFFERED_INPUT) /* Close default_buffered_input if it's > 0. We don't close it if it's 0 because that's the file descriptor used when redirecting input, and it's wrong to close the file in that case. */ unset_bash_input (0);#endif /* BUFFERED_INPUT */ /* Restore top-level signal mask. */ sigprocmask (SIG_SETMASK, &top_level_mask, (sigset_t *)NULL); if (job_control) { /* All processes in this pipeline belong in the same process group. */ if (pipeline_pgrp == 0) /* This is the first child. */ pipeline_pgrp = mypid; /* Check for running command in backquotes. */ if (pipeline_pgrp == shell_pgrp) ignore_tty_job_signals (); else default_tty_job_signals (); /* Set the process group before trying to mess with the terminal's process group. This is mandated by POSIX. */ /* This is in accordance with the Posix 1003.1 standard, section B.7.2.4, which says that trying to set the terminal process group with tcsetpgrp() to an unused pgrp value (like this would have for the first child) is an error. Section B.4.3.3, p. 237 also covers this, in the context of job control shells. */ if (setpgid (mypid, pipeline_pgrp) < 0) sys_error (_("child setpgid (%ld to %ld)"), (long)mypid, (long)pipeline_pgrp); /* By convention (and assumption above), if pipeline_pgrp == shell_pgrp, we are making a child for command substitution. In this case, we don't want to give the terminal to the shell's process group (we could be in the middle of a pipeline, for example). */ if (async_p == 0 && pipeline_pgrp != shell_pgrp && ((subshell_environment&SUBSHELL_ASYNC) == 0)) give_terminal_to (pipeline_pgrp, 0);#if defined (PGRP_PIPE) if (pipeline_pgrp == mypid) pipe_read (pgrp_pipe);#endif } else /* Without job control... */ { if (pipeline_pgrp == 0) pipeline_pgrp = shell_pgrp; /* If these signals are set to SIG_DFL, we encounter the curious situation of an interactive ^Z to a running process *working* and stopping the process, but being unable to do anything with that process to change its state. On the other hand, if they are set to SIG_IGN, jobs started from scripts do not stop when the shell running the script gets a SIGTSTP and stops. */ default_tty_job_signals (); }#if defined (PGRP_PIPE) /* Release the process group pipe, since our call to setpgid () is done. The last call to sh_closepipe is done in stop_pipeline. */ sh_closepipe (pgrp_pipe);#endif /* PGRP_PIPE */#if 0 /* Don't set last_asynchronous_pid in the child */ if (async_p) last_asynchronous_pid = mypid; /* XXX */ else#endif#if defined (RECYCLES_PIDS) if (last_asynchronous_pid == mypid) /* Avoid pid aliasing. 1 seems like a safe, unusual pid value. */ last_asynchronous_pid = 1;#endif } else { /* In the parent. Remember the pid of the child just created as the proper pgrp if this is the first child. */ if (first_pid == NO_PID) first_pid = pid; else if (pid_wrap == -1 && pid < first_pid) pid_wrap = 0; else if (pid_wrap == 0 && pid >= first_pid) pid_wrap = 1; if (job_control) { if (pipeline_pgrp == 0) { pipeline_pgrp = pid; /* Don't twiddle terminal pgrps in the parent! This is the bug, not the good thing of twiddling them in the child! */ /* give_terminal_to (pipeline_pgrp, 0); */ } /* This is done on the recommendation of the Rationale section of the POSIX 1003.1 standard, where it discusses job control and shells. It is done to avoid possible race conditions. (Ref. 1003.1 Rationale, section B.4.3.3, page 236). */ setpgid (pid, pipeline_pgrp); } else { if (pipeline_pgrp == 0) pipeline_pgrp = shell_pgrp; } /* Place all processes into the jobs array regardless of the state of job_control. */ add_process (command, pid); if (async_p) last_asynchronous_pid = pid;#if defined (RECYCLES_PIDS) else if (last_asynchronous_pid == pid) /* Avoid pid aliasing. 1 seems like a safe, unusual pid value. */ last_asynchronous_pid = 1;#endif if (pid_wrap > 0) delete_old_job (pid);#if !defined (RECYCLES_PIDS) /* Only check for saved status if we've saved more than CHILD_MAX statuses, unless the system recycles pids. */ if ((js.c_reaped + bgpids.npid) >= js.c_childmax)#endif bgp_delete (pid); /* new process, discard any saved status */ last_made_pid = pid; /* keep stats */ js.c_totforked++; js.c_living++; /* Unblock SIGINT and SIGCHLD unless creating a pipeline, in which case SIGCHLD remains blocked until all commands in the pipeline have been created. */ sigprocmask (SIG_SETMASK, &oset, (sigset_t *)NULL); } return (pid);}/* These two functions are called only in child processes. */voidignore_tty_job_signals (){ set_signal_handler (SIGTSTP, SIG_IGN); set_signal_handler (SIGTTIN, SIG_IGN); set_signal_handler (SIGTTOU, SIG_IGN);}voiddefault_tty_job_signals (){ set_signal_handler (SIGTSTP, SIG_DFL); set_signal_handler (SIGTTIN, SIG_DFL); set_signal_handler (SIGTTOU, SIG_DFL);}/* When we end a job abnormally, or if we stop a job, we set the tty to the state kept in here. When a job ends normally, we set the state in here to the state of the tty. */static TTYSTRUCT shell_tty_info;#if defined (NEW_TTY_DRIVER)static struct tchars shell_tchars;static struct ltchars shell_ltchars;#endif /* NEW_TTY_DRIVER */#if defined (NEW_TTY_DRIVER) && defined (DRAIN_OUTPUT)/* Since the BSD tty driver does not allow us to change the tty modes while simultaneously waiting for output to drain and preserving typeahead, we have to drain the output ourselves before calling ioctl. We cheat by finding the length of the output queue, and using select to wait for an appropriate length of time. This is a hack, and should be labeled as such (it's a hastily-adapted mutation of a `usleep' implementation). It's only reason for existing is the flaw in the BSD tty driver. */static int ttspeeds[] ={ 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400};static voiddraino (fd, ospeed) int fd, ospeed;{ register int delay = ttspeeds[ospeed]; int n; if (!delay) return; while ((ioctl (fd, TIOCOUTQ, &n) == 0) && n) { if (n > (delay / 100)) { struct timeval tv; n *= 10; /* 2 bits more for conservativeness. */ tv.tv_sec = n / delay; tv.tv_usec = ((n % delay) * 1000000) / delay; select (fd, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tv); } else break; }}#endif /* NEW_TTY_DRIVER && DRAIN_OUTPUT *//* Return the fd from which we are actually getting input. */#define input_tty() (shell_tty != -1) ? shell_tty : fileno (stderr)/* Fill the contents of shell_tty_info with the current tty info. */intget_tty_state (){ int tty; tty = input_tty (); if (tty != -1) {#if defined (NEW_TTY_DRIVER) ioctl (tty, TIOCGETP, &shell_tty_info); ioctl (tty, TIOCGETC, &shell_tchars); ioctl (tty, TIOCGLTC, &shell_ltchars);#endif /* NEW_TTY_DRIVER */#if defined (TERMIO_TTY_DRIVER) ioctl (tty, TCGETA, &shell_tty_info);#endif /* TERMIO_TTY_DRIVER */#if defined (TERMIOS_TTY_DRIVER) if (tcgetattr (tty, &shell_tty_info) < 0) {#if 0 /* Only print an error message if we're really interactive at this time. */ if (interactive) sys_error ("[%ld: %d (%d)] tcgetattr", (long)getpid (), shell_level, tty);#endif return -1; }#endif /* TERMIOS_TTY_DRIVER */ if (check_window_size) get_new_window_size (0, (int *)0, (int *)0); } return 0;}/* Make the current tty use the state in shell_tty_info. */intset_tty_state (){ int tty; tty = input_tty (); if (tty != -1) {#if defined (NEW_TTY_DRIVER)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -