#include "defs.h"
#include "gdb_string.h"
#include "frame.h"
#include "inferior.h"
#include "target.h"
#include "gdb_wait.h"
#include "gdb_vfork.h"
#include "gdbcore.h"
#include "terminal.h"
#include "gdbthread.h"
#include "gdbcmd.h"
#include "command.h"
#include "solib.h"
#include "osabi.h"
#include <signal.h>
#ifdef USE_POSIX_SPAWN
#include <spawn.h>
#endif
char *exec_argv0 = NULL;
char *exec_pathname = NULL;
#ifndef SHELL_FILE
#define SHELL_FILE "/bin/sh"
#endif
extern char **environ;
static int
escape_bang_in_quoted_argument (const char *shell_file)
{
const int shell_file_len = strlen (shell_file);
if (shell_file_len < 3)
return 0;
if (shell_file[shell_file_len - 3] == 'c'
&& shell_file[shell_file_len - 2] == 's'
&& shell_file[shell_file_len - 1] == 'h')
return 1;
return 0;
}
void
fork_inferior (char *exec_file_arg, char *allargs, char **env,
void (*traceme_fun) (void), void (*init_trace_fun) (int),
void (*pre_trace_fun) (void), char *shell_file_arg)
{
int pid;
char *shell_command;
static char default_shell_file[] = SHELL_FILE;
int len;
static int debug_fork = 1;
static int debug_setpgrp = 657473;
static char *shell_file;
static char *exec_file;
char **save_our_env;
int shell = 0;
static char **argv;
const char *inferior_io_terminal = get_inferior_io_terminal ();
exec_file = exec_file_arg;
if (exec_file == 0)
exec_file = get_exec_file (1);
shell_file = shell_file_arg;
if (start_with_shell_flag)
{
if (shell_file == NULL)
shell_file = getenv ("SHELL");
if (shell_file == NULL)
shell_file = default_shell_file;
shell = 1;
}
#ifdef USE_ARCH_FOR_EXEC
len = 24;
#else
len = 5;
#endif
len += 4 * strlen (exec_file) + 1 + strlen (allargs) + 1 + 12;
#ifdef SHELL_COMMAND_CONCAT
shell_command = (char *) alloca (strlen (SHELL_COMMAND_CONCAT) + len);
strcpy (shell_command, SHELL_COMMAND_CONCAT);
#else
shell_command = (char *) alloca (len);
shell_command[0] = '\0';
#endif
if (!shell)
{
unsigned int i;
char **argt = NULL;
argt = buildargv (allargs);
if (argt == NULL)
{
error ("unable to build argument vector for inferior process (out of memory)");
}
if ((allargs == NULL) || (allargs[0] == '\0'))
argt[0] = NULL;
for (i = 0; argt[i] != NULL; i++);
argv = (char **) xmalloc ((i + 1 + 1) * (sizeof *argt));
argv[0] = exec_file;
if (exec_argv0[0] != '\0')
argv[0] = exec_argv0;
memcpy (&argv[1], argt, (i + 1) * sizeof (*argt));
}
else
{
char *p;
int need_to_quote;
const int escape_bang = escape_bang_in_quoted_argument (shell_file);
#ifdef USE_ARCH_FOR_EXEC
{
char *arch_string = NULL;
const char *osabi_name = gdbarch_osabi_name (gdbarch_osabi (current_gdbarch));
#if defined (TARGET_POWERPC)
if (strcmp (osabi_name, "Darwin") == 0)
arch_string = "ppc";
else if (strcmp (osabi_name, "Darwin64") == 0)
arch_string = "ppc64";
#elif defined (TARGET_I386)
if (strcmp (osabi_name, "Darwin") == 0)
arch_string = "i386";
else if (strcmp (osabi_name, "Darwin64") == 0)
arch_string = "x86_64";
#elif defined (TARGET_ARM)
if (strcmp (osabi_name, "Darwin") == 0)
arch_string = "arm";
else if (strcmp (osabi_name, "DarwinV6") == 0)
arch_string = "armv6";
#endif
if (arch_string != NULL)
sprintf (shell_command, "%s exec arch -arch %s ", shell_command, arch_string);
else
strcat (shell_command, "exec ");
}
#else
strcat (shell_command, "exec ");
#endif
p = exec_file;
while (1)
{
switch (*p)
{
case '\'':
case '!':
case '"':
case '(':
case ')':
case '$':
case '&':
case ';':
case '<':
case '>':
case ' ':
case '\n':
case '\t':
need_to_quote = 1;
goto end_scan;
case '\0':
need_to_quote = 0;
goto end_scan;
default:
break;
}
++p;
}
end_scan:
if (need_to_quote)
{
strcat (shell_command, "'");
for (p = exec_file; *p != '\0'; ++p)
{
if (*p == '\'')
strcat (shell_command, "'\\''");
else if (*p == '!' && escape_bang)
strcat (shell_command, "\\!");
else
strncat (shell_command, p, 1);
}
strcat (shell_command, "'");
}
else
strcat (shell_command, exec_file);
strcat (shell_command, " ");
strcat (shell_command, allargs);
argv = (char **) xmalloc (4 * sizeof (char *));
argv[0] = shell_file;
argv[1] = "-c";
argv[2] = shell_command;
argv[3] = NULL;
}
close_exec_file ();
save_our_env = environ;
new_tty_prefork (inferior_io_terminal);
gdb_flush (gdb_stdout);
gdb_flush (gdb_stderr);
if (pre_trace_fun != NULL)
(*pre_trace_fun) ();
if (pre_trace_fun || debug_fork)
pid = fork ();
else
pid = vfork ();
if (pid < 0)
perror_with_name (("vfork"));
if (pid == 0)
{
if (debug_fork)
sleep (debug_fork);
debug_setpgrp = gdb_setpgid ();
if (debug_setpgrp == -1)
perror ("setpgrp failed in child");
new_tty ();
(*traceme_fun) ();
environ = env;
setgid (getgid ());
{
int i;
char *errstring;
char *fileptr;
if (shell)
fileptr = shell_file;
else
{
if (exec_pathname[0] != '\0')
fileptr = exec_pathname;
else
fileptr = exec_file;
}
#ifdef USE_POSIX_SPAWN
{
posix_spawnattr_t attr;
int retval;
size_t copied;
cpu_type_t cpu = 0;
int count = 0;
pid_t pid;
const char *osabi_name = gdbarch_osabi_name (gdbarch_osabi (current_gdbarch));
#if defined (TARGET_POWERPC)
if (strcmp (osabi_name, "Darwin") == 0)
{
cpu = CPU_TYPE_POWERPC;
count = 1;
}
else if (strcmp (osabi_name, "Darwin64") == 0)
{
cpu = CPU_TYPE_POWERPC64;
count = 1;
}
#elif defined (TARGET_I386)
if (strcmp (osabi_name, "Darwin") == 0)
{
cpu = CPU_TYPE_I386;
count = 1;
}
else if (strcmp (osabi_name, "Darwin64") == 0)
{
cpu = CPU_TYPE_X86_64;
count = 1;
}
#elif define (TARGET_ARM)
if (strcmp (osabi_name, "Darwin") == 0)
{
cpu = CPU_TYPE_ARM;
count = 1;
}
else if (strcmp (osabi_name, "DarwinV6") == 0)
{
cpu = CPU_TYPE_ARM;
count = 1;
}
#endif
retval = posix_spawnattr_init (&attr);
if (retval != 0)
{
warning ("Couldn't initialize attributes for posix_spawn, error: %d", retval);
goto try_execvp;
}
retval = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETEXEC);
if (retval != 0)
{
warning ("Couldn't add POSIX_SPAWN_SETEXEC to attributes, error: %d", retval);
goto try_execvp;
}
if (count == 1)
{
retval = posix_spawnattr_setbinpref_np(&attr, 1, &cpu, &copied);
if (retval != 0 || copied != 1)
{
warning ("Couldn't set the binary preferences, error: %d", retval);
goto try_execvp;
}
}
retval = posix_spawnattr_setpgroup (&attr, debug_setpgrp);
if (retval != 0 || copied != 1)
{
warning ("Couldn't set the process group, error: %d", retval);
goto try_execvp;
}
retval = posix_spawnp (&pid, fileptr, NULL, &attr, argv, env);
warning ("posix_spawn failed, trying execvp, error: %d", retval);
}
#endif
try_execvp:
execvp (fileptr, argv);
errstring = safe_strerror (errno);
fprintf_unfiltered (gdb_stderr, "Cannot exec %s ", fileptr);
i = 1;
while (argv[i] != NULL)
{
if (i != 1)
fprintf_unfiltered (gdb_stderr, " ");
fprintf_unfiltered (gdb_stderr, "%s", argv[i]);
i++;
}
fprintf_unfiltered (gdb_stderr, ".\n");
#if 0
fprintf_unfiltered (gdb_stderr, "Got error %s.\n", errstring);
#endif
gdb_flush (gdb_stderr);
_exit (0177);
}
}
environ = save_our_env;
init_thread_list ();
inferior_ptid = pid_to_ptid (pid);
(*init_trace_fun) (pid);
TARGET_CREATE_INFERIOR_HOOK (pid);
#ifdef SOLIB_CREATE_INFERIOR_HOOK
SOLIB_CREATE_INFERIOR_HOOK (pid);
#else
solib_create_inferior_hook ();
#endif
}
void
startup_inferior (int ntraps)
{
int pending_execs = ntraps;
int terminal_initted = 0;
clear_proceed_status ();
init_wait_for_inferior ();
inferior_ignoring_startup_exec_events = pending_execs;
inferior_ignoring_leading_exec_events =
target_reported_exec_events_per_exec_call () - 1;
while (1)
{
stop_soon = STOP_QUIETLY;
wait_for_inferior ();
if (stop_signal != TARGET_SIGNAL_TRAP)
{
#ifdef NM_NEXTSTEP
if (stop_signal == TARGET_EXC_BAD_ACCESS
|| stop_signal == TARGET_EXC_BAD_INSTRUCTION)
{
warning ("The target crashed on startup, maybe the shell is crashing.\n"
"\"Try set start-with-shell 0\" to workaround.");
abort ();
}
#endif
if (PIDGET (inferior_ptid) == 0)
break;
resume (0, stop_signal);
}
else
{
if (!terminal_initted)
{
target_terminal_init ();
target_terminal_inferior ();
terminal_initted = 1;
}
if (--pending_execs == 0)
break;
if (PIDGET (inferior_ptid) == 0)
break;
resume (0, TARGET_SIGNAL_0);
}
}
stop_soon = NO_STOP_QUIETLY;
}
void
_initialize_fork_child (void)
{
exec_pathname = savestring ("", 1);
exec_argv0 = savestring ("", 1);
add_setshow_string_cmd ("exec-pathname", no_class,
&exec_pathname, _("\
Set the pathname to be used to start the target executable."), _("\
Show the pathname to be used to start the target executable."), NULL,
NULL, NULL,
&setlist, &showlist);
add_setshow_string_cmd ("exec-argv0", no_class,
&exec_argv0, _("\
Set the value of argv[0] to be passed to the target executable.\n\
This will be used only if 'start-with-shell' is set to 'off'."), _("\
x"), NULL,
NULL, NULL,
&setlist, &showlist);
add_setshow_boolean_cmd ("start-with-shell", class_obscure,
&start_with_shell_flag, _("\
Set if GDB should use shell to invoke inferior (performs argument expansion in shell)."), _("\
Show if GDB should use shell to invoke inferior (performs argument expansion in shell)."), NULL,
NULL, NULL,
&setlist, &showlist);
}