/* Fork a Unix child process, and set up to debug it, for GDB.

   Copyright (C) 1990-2026 Free Software Foundation, Inc.

   Contributed by Cygnus Support.

   This file is part of GDB.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include "inferior.h"
#include "cli/cli-cmds.h"
#include "terminal.h"
#include "gdbthread.h"
#include "ui.h"
#include "gdbsupport/job-control.h"
#include "gdbsupport/filestuff.h"
#include "nat/fork-inferior.h"
#include "gdbsupport/common-inferior.h"

/* The exec-wrapper, if any, that will be used when starting the
   inferior.  */

static std::string exec_wrapper;

/* See gdbsupport/common-inferior.h.  */

const char *
get_exec_wrapper ()
{
  return !exec_wrapper.empty () ? exec_wrapper.c_str () : nullptr;
}

/* See nat/fork-inferior.h.  */

void
gdb_flush_out_err ()
{
  gdb_flush (main_ui->m_ui_stdout);
  gdb_flush (main_ui->m_ui_stderr);
}

/* The ui structure that will be saved on 'prefork_hook' and
   restored on 'postfork_hook'.  */
static struct ui *saved_ui = NULL;

/* See nat/fork-inferior.h.  */

void
prefork_hook (const char *args)
{
  gdb_assert (saved_ui == NULL);
  /* Retain a copy of our UI, since the child will replace this value
     and if we're vforked, we have to restore it.  */
  saved_ui = current_ui;

  /* Tell the terminal handling subsystem what tty we plan to run on;
     it will just record the information for later.  */
  new_tty_prefork (current_inferior ()->tty ());
}

/* See nat/fork-inferior.h.  */

void
postfork_hook (pid_t pid)
{
  inferior *inf = current_inferior ();

  inferior_appeared (inf, pid);

  gdb_assert (saved_ui != NULL);
  current_ui = saved_ui;
  saved_ui = NULL;

  new_tty_postfork ();
}

/* See nat/fork-inferior.h.  */

void
postfork_child_hook ()
{
  /* This is set to the result of setpgrp, which if vforked, will be
     visible to you in the parent process.  It's only used by humans
     for debugging.  */
  static int debug_setpgrp = 657473;

  /* Make sure we switch to main_ui here in order to be able to
     use the gdb_printf/warning/error functions.  */
  current_ui = main_ui;

  /* Create a new session for the inferior process, if necessary.
     It will also place the inferior in a separate process group.  */
  if (create_tty_session () <= 0)
    {
      /* No session was created, but we still want to run the inferior
	 in a separate process group.  */
      debug_setpgrp = gdb_setpgid ();
      if (debug_setpgrp == -1)
	perror (_("setpgrp failed in child"));
    }

  /* Ask the tty subsystem to switch to the one we specified
     earlier (or to share the current terminal, if none was
     specified).  */
  new_tty ();
}

/* See inferior.h.  */

ptid_t
gdb_startup_inferior (pid_t pid, int num_traps)
{
  inferior *inf = current_inferior ();
  process_stratum_target *proc_target = inf->process_target ();

  scoped_restore save_starting_up
    = make_scoped_restore (&inf->starting_up, true);

  ptid_t ptid = startup_inferior (proc_target, pid, num_traps, NULL, NULL);

  /* Mark all threads internally stopped.  */
  set_internal_state (proc_target, ptid, THREAD_INT_STOPPED);

  return ptid;
}

/* Implement the "unset exec-wrapper" command.  */

static void
unset_exec_wrapper_command (const char *args, int from_tty)
{
  exec_wrapper.clear ();
}

static void
show_startup_with_shell (struct ui_file *file, int from_tty,
			 struct cmd_list_element *c, const char *value)
{
  gdb_printf (file,
	      _("Use of shell to start subprocesses is %s.\n"),
	      value);
}

INIT_GDB_FILE (fork_child)
{
  add_setshow_filename_cmd ("exec-wrapper", class_run, &exec_wrapper, _("\
Set a wrapper for running programs.\n\
The wrapper prepares the system and environment for the new program."),
			    _("\
Show the wrapper for running programs."), NULL,
			    NULL, NULL,
			    &setlist, &showlist);

  add_cmd ("exec-wrapper", class_run, unset_exec_wrapper_command,
	   _("Disable use of an execution wrapper."),
	   &unsetlist);

  add_setshow_boolean_cmd ("startup-with-shell", class_support,
			   &startup_with_shell, _("\
Set use of shell to start subprocesses.  The default is on."), _("\
Show use of shell to start subprocesses."), NULL,
			   NULL,
			   show_startup_with_shell,
			   &setlist, &showlist);
}
