1This file is exec.def, from which is created exec.c.
2It implements the builtin "exec" in Bash.
3
4Copyright (C) 1987-2019 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 exec.c
22
23$BUILTIN exec
24$FUNCTION exec_builtin
25$SHORT_DOC exec [-cl] [-a name] [command [argument ...]] [redirection ...]
26Replace the shell with the given command.
27
28Execute COMMAND, replacing this shell with the specified program.
29ARGUMENTS become the arguments to COMMAND.  If COMMAND is not specified,
30any redirections take effect in the current shell.
31
32Options:
33  -a name	pass NAME as the zeroth argument to COMMAND
34  -c	execute COMMAND with an empty environment
35  -l	place a dash in the zeroth argument to COMMAND
36
37If the command cannot be executed, a non-interactive shell exits, unless
38the shell option `execfail' is set.
39
40Exit Status:
41Returns success unless COMMAND is not found or a redirection error occurs.
42$END
43
44#include <config.h>
45
46#include "../bashtypes.h"
47#include "posixstat.h"
48#include <signal.h>
49#include <errno.h>
50
51#if defined (HAVE_UNISTD_H)
52#  include <unistd.h>
53#endif
54
55#include <stdio.h>
56
57#include "../bashansi.h"
58#include "../bashintl.h"
59
60#include "../shell.h"
61#include "../execute_cmd.h"
62#include "../findcmd.h"
63#if defined (JOB_CONTROL)
64#  include "../jobs.h"
65#endif
66#include "../flags.h"
67#include "../trap.h"
68#if defined (HISTORY)
69#  include "../bashhist.h"
70#endif
71#include "common.h"
72#include "bashgetopt.h"
73#include "input.h"
74
75/* Not all systems declare ERRNO in errno.h... and some systems #define it! */
76#if !defined (errno)
77extern int errno;
78#endif /* !errno */
79
80extern REDIRECT *redirection_undo_list;
81extern char *exec_argv0;
82
83int no_exit_on_failed_exec;
84
85/* If the user wants this to look like a login shell, then
86   prepend a `-' onto NAME and return the new name. */
87static char *
88mkdashname (name)
89     char *name;
90{
91  char *ret;
92
93  ret = (char *)xmalloc (2 + strlen (name));
94  ret[0] = '-';
95  strcpy (ret + 1, name);
96  return ret;
97}
98
99int
100exec_builtin (list)
101     WORD_LIST *list;
102{
103  int exit_value = EXECUTION_FAILURE;
104  int cleanenv, login, opt, orig_job_control;
105  char *argv0, *command, **args, **env, *newname, *com2;
106
107  cleanenv = login = 0;
108  exec_argv0 = argv0 = (char *)NULL;
109
110  reset_internal_getopt ();
111  while ((opt = internal_getopt (list, "cla:")) != -1)
112    {
113      switch (opt)
114	{
115	case 'c':
116	  cleanenv = 1;
117	  break;
118	case 'l':
119	  login = 1;
120	  break;
121	case 'a':
122	  argv0 = list_optarg;
123	  break;
124	CASE_HELPOPT;
125	default:
126	  builtin_usage ();
127	  return (EX_USAGE);
128	}
129    }
130  list = loptend;
131
132  /* First, let the redirections remain. */
133  dispose_redirects (redirection_undo_list);
134  redirection_undo_list = (REDIRECT *)NULL;
135
136  if (list == 0)
137    return (EXECUTION_SUCCESS);
138
139#if defined (RESTRICTED_SHELL)
140  if (restricted)
141    {
142      sh_restricted ((char *)NULL);
143      return (EXECUTION_FAILURE);
144    }
145#endif /* RESTRICTED_SHELL */
146
147  args = strvec_from_word_list (list, 1, 0, (int *)NULL);
148  env = (char **)0;
149
150  /* A command with a slash anywhere in its name is not looked up in $PATH. */
151  command = absolute_program (args[0]) ? args[0] : search_for_command (args[0], 1);
152
153  if (command == 0)
154    {
155      if (file_isdir (args[0]))
156	{
157#if defined (EISDIR)
158	  builtin_error (_("%s: cannot execute: %s"), args[0], strerror (EISDIR));
159#else
160	  builtin_error (_("%s: cannot execute: %s"), args[0], strerror (errno));
161#endif
162	  exit_value = EX_NOEXEC;
163	}
164      else
165	{
166	  sh_notfound (args[0]);
167	  exit_value = EX_NOTFOUND;	/* As per Posix.2, 3.14.6 */
168	}
169      goto failed_exec;
170    }
171
172  com2 = full_pathname (command);
173  if (com2)
174    {
175      if (command != args[0])
176	free (command);
177      command = com2;
178    }
179
180  if (argv0)
181    {
182      free (args[0]);
183      args[0] = login ? mkdashname (argv0) : savestring (argv0);
184      exec_argv0 = savestring (args[0]);
185    }
186  else if (login)
187    {
188      newname = mkdashname (args[0]);
189      free (args[0]);
190      args[0] = newname;
191    }
192
193  /* Decrement SHLVL by 1 so a new shell started here has the same value,
194     preserving the appearance.  After we do that, we need to change the
195     exported environment to include the new value.  If we've already forked
196     and are in a subshell, we don't want to decrement the shell level,
197     since we are `increasing' the level */
198
199  if (cleanenv == 0 && (subshell_environment & SUBSHELL_PAREN) == 0)
200    adjust_shell_level (-1);
201
202  if (cleanenv)
203    {
204      env = strvec_create (1);
205      env[0] = (char *)0;
206    }
207  else
208    {
209      maybe_make_export_env ();
210      env = export_env;
211    }
212
213#if defined (HISTORY)
214  if (interactive_shell && subshell_environment == 0)
215    maybe_save_shell_history ();
216#endif /* HISTORY */
217
218  restore_original_signals ();
219
220#if defined (JOB_CONTROL)
221  orig_job_control = job_control;	/* XXX - was also interactive_shell */
222  if (subshell_environment == 0)
223    end_job_control ();
224  if (interactive || job_control)
225    default_tty_job_signals ();		/* undo initialize_job_signals */
226#endif /* JOB_CONTROL */
227
228#if defined (BUFFERED_INPUT)
229  if (default_buffered_input >= 0)
230    sync_buffered_stream (default_buffered_input);
231#endif
232
233  exit_value = shell_execve (command, args, env);
234
235  /* We have to set this to NULL because shell_execve has called realloc()
236     to stuff more items at the front of the array, which may have caused
237     the memory to be freed by realloc().  We don't want to free it twice. */
238  args = (char **)NULL;
239  if (cleanenv == 0)
240    adjust_shell_level (1);
241
242  if (exit_value == EX_NOTFOUND)	/* no duplicate error message */
243    goto failed_exec;
244  else if (executable_file (command) == 0)
245    {
246      builtin_error (_("%s: cannot execute: %s"), command, strerror (errno));
247      exit_value = EX_NOEXEC;	/* As per Posix.2, 3.14.6 */
248    }
249  else
250    file_error (command);
251
252failed_exec:
253  FREE (command);
254
255  if (subshell_environment || (interactive == 0 && no_exit_on_failed_exec == 0))
256    exit_shell (exit_value);
257
258  if (args)
259    strvec_dispose (args);
260
261  if (env && env != export_env)
262    strvec_dispose (env);
263
264  initialize_traps ();
265  initialize_signals (1);
266
267#if defined (JOB_CONTROL)
268  if (orig_job_control)
269    restart_job_control ();
270#endif /* JOB_CONTROL */
271
272  return (exit_value);
273}
274