1This file is jobs.def, from which is created jobs.c.
2It implements the builtins "jobs" and "disown" in Bash.
3
4Copyright (C) 1987-2020 Free Software Foundation, Inc.
5
6This file is part of GNU Bash, the Bourne Again SHell.
7
8Bash is free software: you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation, either version 3 of the License, or
11(at your option) any later version.
12
13Bash is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with Bash.  If not, see <http://www.gnu.org/licenses/>.
20
21$PRODUCES jobs.c
22
23$BUILTIN jobs
24$FUNCTION jobs_builtin
25$DEPENDS_ON JOB_CONTROL
26$SHORT_DOC jobs [-lnprs] [jobspec ...] or jobs -x command [args]
27Display status of jobs.
28
29Lists the active jobs.  JOBSPEC restricts output to that job.
30Without options, the status of all active jobs is displayed.
31
32Options:
33  -l	lists process IDs in addition to the normal information
34  -n	lists only processes that have changed status since the last
35		notification
36  -p	lists process IDs only
37  -r	restrict output to running jobs
38  -s	restrict output to stopped jobs
39
40If -x is supplied, COMMAND is run after all job specifications that
41appear in ARGS have been replaced with the process ID of that job's
42process group leader.
43
44Exit Status:
45Returns success unless an invalid option is given or an error occurs.
46If -x is used, returns the exit status of COMMAND.
47$END
48
49#include <config.h>
50
51#if defined (JOB_CONTROL)
52#include "../bashtypes.h"
53#include <signal.h>
54#if defined (HAVE_UNISTD_H)
55#  include <unistd.h>
56#endif
57
58#include "../bashansi.h"
59#include "../bashintl.h"
60
61#include "../shell.h"
62#include "../jobs.h"
63#include "../execute_cmd.h"
64#include "bashgetopt.h"
65#include "common.h"
66
67#define JSTATE_ANY	0x0
68#define JSTATE_RUNNING	0x1
69#define JSTATE_STOPPED	0x2
70
71static int execute_list_with_replacements PARAMS((WORD_LIST *));
72
73/* The `jobs' command.  Prints outs a list of active jobs.  If the
74   argument `-l' is given, then the process id's are printed also.
75   If the argument `-p' is given, print the process group leader's
76   pid only.  If `-n' is given, only processes that have changed
77   status since the last notification are printed.  If -x is given,
78   replace all job specs with the pid of the appropriate process
79   group leader and execute the command.  The -r and -s options mean
80   to print info about running and stopped jobs only, respectively. */
81int
82jobs_builtin (list)
83     WORD_LIST *list;
84{
85  int form, execute, state, opt, any_failed, job;
86  sigset_t set, oset;
87
88  execute = any_failed = 0;
89  form = JLIST_STANDARD;
90  state = JSTATE_ANY;
91
92  reset_internal_getopt ();
93  while ((opt = internal_getopt (list, "lpnxrs")) != -1)
94    {
95      switch (opt)
96	{
97	case 'l':
98	  form = JLIST_LONG;
99	  break;
100	case 'p':
101	  form = JLIST_PID_ONLY;
102	  break;
103	case 'n':
104	  form = JLIST_CHANGED_ONLY;
105	  break;
106	case 'x':
107	  if (form != JLIST_STANDARD)
108	    {
109	      builtin_error (_("no other options allowed with `-x'"));
110	      return (EXECUTION_FAILURE);
111	    }
112	  execute++;
113	  break;
114	case 'r':
115	  state = JSTATE_RUNNING;
116	  break;
117	case 's':
118	  state = JSTATE_STOPPED;
119	  break;
120
121	CASE_HELPOPT;
122	default:
123	  builtin_usage ();
124	  return (EX_USAGE);
125	}
126    }
127
128  list = loptend;
129
130  if (execute)
131    return (execute_list_with_replacements (list));
132
133  if (!list)
134    {
135      switch (state)
136	{
137	case JSTATE_ANY:
138	  list_all_jobs (form);
139	  break;
140	case JSTATE_RUNNING:
141	  list_running_jobs (form);
142	  break;
143	case JSTATE_STOPPED:
144	  list_stopped_jobs (form);
145	  break;
146	}
147      return (EXECUTION_SUCCESS);
148    }
149
150  while (list)
151    {
152      BLOCK_CHILD (set, oset);
153      job = get_job_spec (list);
154
155      if ((job == NO_JOB) || jobs == 0 || get_job_by_jid (job) == 0)
156	{
157	  sh_badjob (list->word->word);
158	  any_failed++;
159	}
160      else if (job != DUP_JOB)
161	list_one_job ((JOB *)NULL, form, 0, job);
162
163      UNBLOCK_CHILD (oset);
164      list = list->next;
165    }
166  return (any_failed ? EXECUTION_FAILURE : EXECUTION_SUCCESS);
167}
168
169static int
170execute_list_with_replacements (list)
171     WORD_LIST *list;
172{
173  register WORD_LIST *l;
174  int job, result;
175  COMMAND *command;
176  JOB *j;
177
178  /* First do the replacement of job specifications with pids. */
179  for (l = list; l; l = l->next)
180    {
181      if (l->word->word[0] == '%')	/* we have a winner */
182	{
183	  job = get_job_spec (l);
184
185	  /* A bad job spec is not really a job spec! Pass it through. */
186	  if (INVALID_JOB (job))
187	    continue;
188
189	  j = get_job_by_jid (job);
190	  free (l->word->word);
191	  l->word->word = itos (j->pgrp);
192	}
193    }
194
195  /* Next make a new simple command and execute it. */
196  begin_unwind_frame ("jobs_builtin");
197
198  command = make_bare_simple_command ();
199  command->value.Simple->words = copy_word_list (list);
200  command->value.Simple->redirects = (REDIRECT *)NULL;
201  command->flags |= CMD_INHIBIT_EXPANSION;
202  command->value.Simple->flags |= CMD_INHIBIT_EXPANSION;
203
204  add_unwind_protect (dispose_command, command);
205  result = execute_command (command);
206  dispose_command (command);
207
208  discard_unwind_frame ("jobs_builtin");
209  return (result);
210}
211#endif /* JOB_CONTROL */
212
213$BUILTIN disown
214$FUNCTION disown_builtin
215$DEPENDS_ON JOB_CONTROL
216$SHORT_DOC disown [-h] [-ar] [jobspec ... | pid ...]
217Remove jobs from current shell.
218
219Removes each JOBSPEC argument from the table of active jobs.  Without
220any JOBSPECs, the shell uses its notion of the current job.
221
222Options:
223  -a	remove all jobs if JOBSPEC is not supplied
224  -h	mark each JOBSPEC so that SIGHUP is not sent to the job if the
225		shell receives a SIGHUP
226  -r	remove only running jobs
227
228Exit Status:
229Returns success unless an invalid option or JOBSPEC is given.
230$END
231
232#if defined (JOB_CONTROL)
233int
234disown_builtin (list)
235     WORD_LIST *list;
236{
237  int opt, job, retval, nohup_only, running_jobs, all_jobs;
238  sigset_t set, oset;
239  intmax_t pid_value;
240
241  nohup_only = running_jobs = all_jobs = 0;
242  reset_internal_getopt ();
243  while ((opt = internal_getopt (list, "ahr")) != -1)
244    {
245      switch (opt)
246	{
247	case 'a':
248	  all_jobs = 1;
249	  break;
250	case 'h':
251	  nohup_only = 1;
252	  break;
253	case 'r':
254	  running_jobs = 1;
255	  break;
256	CASE_HELPOPT;
257	default:
258	  builtin_usage ();
259	  return (EX_USAGE);
260	}
261    }
262  list = loptend;
263  retval = EXECUTION_SUCCESS;
264
265  /* `disown -a' or `disown -r' */
266  if (list == 0 && (all_jobs || running_jobs))
267    {
268      if (nohup_only)
269	nohup_all_jobs (running_jobs);
270      else
271	delete_all_jobs (running_jobs);
272      return (EXECUTION_SUCCESS);
273    }
274
275  do
276    {
277      BLOCK_CHILD (set, oset);
278      job = (list && legal_number (list->word->word, &pid_value) && pid_value == (pid_t) pid_value)
279		? get_job_by_pid ((pid_t) pid_value, 0, 0)
280		: get_job_spec (list);
281
282      if (job == NO_JOB || jobs == 0 || INVALID_JOB (job))
283	{
284	  sh_badjob (list ? list->word->word : _("current"));
285	  retval = EXECUTION_FAILURE;
286	}
287      else if (nohup_only)
288	nohup_job (job);
289      else
290	delete_job (job, 1);
291      UNBLOCK_CHILD (oset);
292
293      if (list)
294	list = list->next;
295    }
296  while (list);
297
298  return (retval);
299}
300#endif /* JOB_CONTROL */
301