2017-06-15 09:25:51 +08:00
|
|
|
// Implementation of the bg builtin.
|
|
|
|
#include "config.h" // IWYU pragma: keep
|
|
|
|
|
2019-10-14 06:50:48 +08:00
|
|
|
#include "builtin_bg.h"
|
|
|
|
|
2019-11-19 09:11:16 +08:00
|
|
|
#include <cerrno>
|
|
|
|
#include <cstdlib>
|
2017-06-15 09:25:51 +08:00
|
|
|
#include <memory>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "builtin.h"
|
|
|
|
#include "common.h"
|
|
|
|
#include "fallback.h" // IWYU pragma: keep
|
|
|
|
#include "io.h"
|
2019-05-05 13:12:31 +08:00
|
|
|
#include "parser.h"
|
2017-06-15 09:25:51 +08:00
|
|
|
#include "proc.h"
|
|
|
|
#include "wutil.h" // IWYU pragma: keep
|
|
|
|
|
|
|
|
/// Helper function for builtin_bg().
|
|
|
|
static int send_to_bg(parser_t &parser, io_streams_t &streams, job_t *j) {
|
2019-11-19 10:34:50 +08:00
|
|
|
assert(j != nullptr);
|
2019-06-24 03:39:29 +08:00
|
|
|
if (!j->wants_job_control()) {
|
2019-10-20 17:38:17 +08:00
|
|
|
wcstring error_message = format_string(
|
2017-06-15 09:25:51 +08:00
|
|
|
_(L"%ls: Can't put job %d, '%ls' to background because it is not under job control\n"),
|
Introduce the internal jobs for functions
This PR is aimed at improving how job ids are assigned. In particular,
previous to this commit, a job id would be consumed by functions (and
thus aliases). Since it's usual to use functions as command wrappers
this results in awkward job id assignments.
For example if the user is like me and just made the jump from vim -> neovim
then the user might create the following alias:
```
alias vim=nvim
```
Previous to this commit if the user ran `vim` after setting up this
alias, backgrounded (^Z) and ran `jobs` then the output might be:
```
Job Group State Command
2 60267 stopped nvim $argv
```
If the user subsequently opened another vim (nvim) session, backgrounded
and ran jobs then they might see what follows:
```
Job Group State Command
4 70542 stopped nvim $argv
2 60267 stopped nvim $argv
```
These job ids feel unnatural, especially when transitioning away from
e.g. bash where job ids are sequentially incremented (and aliases/functions
don't consume a job id).
See #6053 for more details.
As @ridiculousfish pointed out in
https://github.com/fish-shell/fish-shell/issues/6053#issuecomment-559899400,
we want to elide a job's job id if it corresponds to a single function in the
foreground. This translates to the following prerequisites:
- A job must correspond to a single process (i.e. the job continuation
must be empty)
- A job must be in the foreground (i.e. `&` wasn't appended)
- The job's single process must resolve to a function invocation
If all of these conditions are true then we should mark a job as
"internal" and somehow remove it from consideration when any
infrastructure tries to interact with jobs / job ids.
I saw two paths to implement these requirements:
- At the time of job creation calculate whether or not a job is
"internal" and use a separate list of job ids to track their ids.
Additionally introduce a new flag denoting that a job is internal so
that e.g. `jobs` doesn't list internal jobs
- I started implementing this route but quickly realized I was
computing the same information that would be computed later on (e.g.
"is this job a single process" and "is this jobs statement a
function"). Specifically I was computing data that populate_job_process
would end up computing later anyway. Additionally this added some
weird complexities to the job system (after the change there were two
job id lists AND an additional flag that had to be taken into
consideration)
- Once a function is about to be executed we release the current jobs
job id if the prerequisites are satisfied (which at this point have
been fully computed).
- I opted for this solution since it seems cleaner. In this
implementation "releasing a job id" is done by both calling
`release_job_id` and by marking the internal job_id member variable to
-1. The former operation allows subsequent child jobs to reuse that
same job id (so e.g. the situation described in Motivation doesn't
occur), and the latter ensures that no other job / job id
infrastructure will interact with these jobs because valid jobs have
positive job ids. The second operation causes job_id to become
non-const which leads to the list of code changes outside of `exec.c`
(i.e. a codemod from `job_t::job_id` -> `job_t::job_id()` and moving the
old member variable to a non-const private `job_t::job_id_`)
Note: Its very possible I missed something and setting the job id to -1
will break some other infrastructure, please let me know if so!
I tried to run `make/ninja lint`, but a bunch of non-relevant issues
appeared (e.g. `fatal error: 'config.h' file not found`). I did
successfully clang-format (`git clang-format -f`) and run tests, though.
This PR closes #6053.
2019-12-29 23:46:07 +08:00
|
|
|
L"bg", j->job_id(), j->command_wcstr());
|
2019-10-20 17:38:17 +08:00
|
|
|
builtin_print_help(parser, streams, L"bg", &error_message);
|
2017-06-15 09:25:51 +08:00
|
|
|
return STATUS_CMD_ERROR;
|
|
|
|
}
|
|
|
|
|
Introduce the internal jobs for functions
This PR is aimed at improving how job ids are assigned. In particular,
previous to this commit, a job id would be consumed by functions (and
thus aliases). Since it's usual to use functions as command wrappers
this results in awkward job id assignments.
For example if the user is like me and just made the jump from vim -> neovim
then the user might create the following alias:
```
alias vim=nvim
```
Previous to this commit if the user ran `vim` after setting up this
alias, backgrounded (^Z) and ran `jobs` then the output might be:
```
Job Group State Command
2 60267 stopped nvim $argv
```
If the user subsequently opened another vim (nvim) session, backgrounded
and ran jobs then they might see what follows:
```
Job Group State Command
4 70542 stopped nvim $argv
2 60267 stopped nvim $argv
```
These job ids feel unnatural, especially when transitioning away from
e.g. bash where job ids are sequentially incremented (and aliases/functions
don't consume a job id).
See #6053 for more details.
As @ridiculousfish pointed out in
https://github.com/fish-shell/fish-shell/issues/6053#issuecomment-559899400,
we want to elide a job's job id if it corresponds to a single function in the
foreground. This translates to the following prerequisites:
- A job must correspond to a single process (i.e. the job continuation
must be empty)
- A job must be in the foreground (i.e. `&` wasn't appended)
- The job's single process must resolve to a function invocation
If all of these conditions are true then we should mark a job as
"internal" and somehow remove it from consideration when any
infrastructure tries to interact with jobs / job ids.
I saw two paths to implement these requirements:
- At the time of job creation calculate whether or not a job is
"internal" and use a separate list of job ids to track their ids.
Additionally introduce a new flag denoting that a job is internal so
that e.g. `jobs` doesn't list internal jobs
- I started implementing this route but quickly realized I was
computing the same information that would be computed later on (e.g.
"is this job a single process" and "is this jobs statement a
function"). Specifically I was computing data that populate_job_process
would end up computing later anyway. Additionally this added some
weird complexities to the job system (after the change there were two
job id lists AND an additional flag that had to be taken into
consideration)
- Once a function is about to be executed we release the current jobs
job id if the prerequisites are satisfied (which at this point have
been fully computed).
- I opted for this solution since it seems cleaner. In this
implementation "releasing a job id" is done by both calling
`release_job_id` and by marking the internal job_id member variable to
-1. The former operation allows subsequent child jobs to reuse that
same job id (so e.g. the situation described in Motivation doesn't
occur), and the latter ensures that no other job / job id
infrastructure will interact with these jobs because valid jobs have
positive job ids. The second operation causes job_id to become
non-const which leads to the list of code changes outside of `exec.c`
(i.e. a codemod from `job_t::job_id` -> `job_t::job_id()` and moving the
old member variable to a non-const private `job_t::job_id_`)
Note: Its very possible I missed something and setting the job id to -1
will break some other infrastructure, please let me know if so!
I tried to run `make/ninja lint`, but a bunch of non-relevant issues
appeared (e.g. `fatal error: 'config.h' file not found`). I did
successfully clang-format (`git clang-format -f`) and run tests, though.
This PR closes #6053.
2019-12-29 23:46:07 +08:00
|
|
|
streams.err.append_format(_(L"Send job %d '%ls' to background\n"), j->job_id(),
|
2017-06-15 09:25:51 +08:00
|
|
|
j->command_wcstr());
|
2019-05-05 13:12:31 +08:00
|
|
|
parser.job_promote(j);
|
2019-10-16 05:37:10 +08:00
|
|
|
j->mut_flags().foreground = false;
|
2019-05-05 13:12:31 +08:00
|
|
|
j->continue_job(parser, true, j->is_stopped());
|
2017-06-15 09:25:51 +08:00
|
|
|
return STATUS_CMD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Builtin for putting a job in the background.
|
|
|
|
int builtin_bg(parser_t &parser, io_streams_t &streams, wchar_t **argv) {
|
|
|
|
const wchar_t *cmd = argv[0];
|
|
|
|
int argc = builtin_count_args(argv);
|
2017-06-17 11:42:33 +08:00
|
|
|
help_only_cmd_opts_t opts;
|
2017-06-15 09:25:51 +08:00
|
|
|
|
|
|
|
int optind;
|
2017-06-17 11:42:33 +08:00
|
|
|
int retval = parse_help_only_cmd_opts(opts, &optind, argc, argv, parser, streams);
|
2017-06-15 09:25:51 +08:00
|
|
|
if (retval != STATUS_CMD_OK) return retval;
|
|
|
|
|
|
|
|
if (opts.print_help) {
|
2019-10-20 17:38:17 +08:00
|
|
|
builtin_print_help(parser, streams, cmd);
|
2017-06-15 09:25:51 +08:00
|
|
|
return STATUS_CMD_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (optind == argc) {
|
|
|
|
// No jobs were specified so use the most recent (i.e., last) job.
|
2018-12-31 11:25:16 +08:00
|
|
|
job_t *job = nullptr;
|
2019-05-05 13:12:31 +08:00
|
|
|
for (const auto &j : parser.jobs()) {
|
2019-06-24 03:39:29 +08:00
|
|
|
if (j->is_stopped() && j->wants_job_control() && (!j->is_completed())) {
|
2018-12-31 11:25:16 +08:00
|
|
|
job = j.get();
|
2017-06-15 09:25:51 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-31 11:25:16 +08:00
|
|
|
if (!job) {
|
2017-06-15 09:25:51 +08:00
|
|
|
streams.err.append_format(_(L"%ls: There are no suitable jobs\n"), cmd);
|
|
|
|
retval = STATUS_CMD_ERROR;
|
|
|
|
} else {
|
2018-12-31 11:25:16 +08:00
|
|
|
retval = send_to_bg(parser, streams, job);
|
2017-06-15 09:25:51 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The user specified at least one job to be backgrounded.
|
|
|
|
std::vector<int> pids;
|
|
|
|
|
|
|
|
// If one argument is not a valid pid (i.e. integer >= 0), fail without backgrounding anything,
|
|
|
|
// but still print errors for all of them.
|
|
|
|
for (int i = optind; argv[i]; i++) {
|
|
|
|
int pid = fish_wcstoi(argv[i]);
|
|
|
|
if (errno || pid < 0) {
|
|
|
|
streams.err.append_format(_(L"%ls: '%ls' is not a valid job specifier\n"), L"bg",
|
|
|
|
argv[i]);
|
|
|
|
retval = STATUS_INVALID_ARGS;
|
|
|
|
}
|
|
|
|
pids.push_back(pid);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (retval != STATUS_CMD_OK) return retval;
|
|
|
|
|
|
|
|
// Background all existing jobs that match the pids.
|
|
|
|
// Non-existent jobs aren't an error, but information about them is useful.
|
|
|
|
for (auto p : pids) {
|
2020-02-09 04:39:03 +08:00
|
|
|
if (job_t *j = parser.job_get_from_pid(p)) {
|
2017-06-15 09:25:51 +08:00
|
|
|
retval |= send_to_bg(parser, streams, j);
|
|
|
|
} else {
|
|
|
|
streams.err.append_format(_(L"%ls: Could not find job '%d'\n"), cmd, p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|