mirror of
https://git.proxmox.com/git/grub2
synced 2025-07-26 08:38:20 +00:00

The functions grub_util_exec_pipe() and grub_util_exec_pipe_stderr()
currently call execvp(). If the call fails for any reason, the child
currently calls exit(127). This in turn executes the parents
atexit() handlers from the forked child, and then the same handlers
are called again from parent. This is usually not desired, and can
lead to deadlocks, and undesired behavior. So, change the exit() calls
to _exit() calls to avoid calling atexit() handlers from child.
Fixes: e75cf4a58
(unix exec: avoid atexit handlers when child exits)
Signed-off-by: Dimitri John Ledkov <xnox@ubuntu.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
246 lines
5.3 KiB
C
246 lines
5.3 KiB
C
/*
|
|
* GRUB -- GRand Unified Bootloader
|
|
* Copyright (C) 1999,2000,2001,2002,2003,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc.
|
|
*
|
|
* GRUB 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.
|
|
*
|
|
* GRUB 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 GRUB. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config-util.h>
|
|
#include <config.h>
|
|
|
|
#include <grub/misc.h>
|
|
#include <unistd.h>
|
|
#include <grub/emu/exec.h>
|
|
#include <grub/emu/hostdisk.h>
|
|
#include <grub/emu/getroot.h>
|
|
#include <grub/util/misc.h>
|
|
#include <grub/disk.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <sys/wait.h>
|
|
|
|
int
|
|
grub_util_exec_redirect_all (const char *const *argv, const char *stdin_file,
|
|
const char *stdout_file, const char *stderr_file)
|
|
{
|
|
pid_t pid;
|
|
int status = -1;
|
|
char *str, *pstr;
|
|
const char *const *ptr;
|
|
grub_size_t strl = 0;
|
|
for (ptr = argv; *ptr; ptr++)
|
|
strl += grub_strlen (*ptr) + 1;
|
|
if (stdin_file)
|
|
strl += grub_strlen (stdin_file) + 2;
|
|
if (stdout_file)
|
|
strl += grub_strlen (stdout_file) + 2;
|
|
if (stderr_file)
|
|
strl += grub_strlen (stderr_file) + 3;
|
|
|
|
pstr = str = xmalloc (strl);
|
|
for (ptr = argv; *ptr; ptr++)
|
|
{
|
|
pstr = grub_stpcpy (pstr, *ptr);
|
|
*pstr++ = ' ';
|
|
}
|
|
if (stdin_file)
|
|
{
|
|
*pstr++ = '<';
|
|
pstr = grub_stpcpy (pstr, stdin_file);
|
|
*pstr++ = ' ';
|
|
}
|
|
if (stdout_file)
|
|
{
|
|
*pstr++ = '>';
|
|
pstr = grub_stpcpy (pstr, stdout_file);
|
|
*pstr++ = ' ';
|
|
}
|
|
if (stderr_file)
|
|
{
|
|
*pstr++ = '2';
|
|
*pstr++ = '>';
|
|
pstr = grub_stpcpy (pstr, stderr_file);
|
|
pstr++;
|
|
}
|
|
*--pstr = '\0';
|
|
|
|
grub_util_info ("executing %s", str);
|
|
grub_free (str);
|
|
|
|
pid = fork ();
|
|
if (pid < 0)
|
|
grub_util_error (_("Unable to fork: %s"), strerror (errno));
|
|
else if (pid == 0)
|
|
{
|
|
int fd;
|
|
/* Child. */
|
|
|
|
/* Close fd's. */
|
|
#ifdef GRUB_UTIL
|
|
grub_util_devmapper_cleanup ();
|
|
grub_diskfilter_fini ();
|
|
#endif
|
|
|
|
if (stdin_file)
|
|
{
|
|
fd = open (stdin_file, O_RDONLY);
|
|
if (fd < 0)
|
|
_exit (127);
|
|
dup2 (fd, STDIN_FILENO);
|
|
close (fd);
|
|
}
|
|
|
|
if (stdout_file)
|
|
{
|
|
fd = open (stdout_file, O_WRONLY | O_CREAT, 0700);
|
|
if (fd < 0)
|
|
_exit (127);
|
|
dup2 (fd, STDOUT_FILENO);
|
|
close (fd);
|
|
}
|
|
|
|
if (stderr_file)
|
|
{
|
|
fd = open (stderr_file, O_WRONLY | O_CREAT, 0700);
|
|
if (fd < 0)
|
|
_exit (127);
|
|
dup2 (fd, STDERR_FILENO);
|
|
close (fd);
|
|
}
|
|
|
|
/* Ensure child is not localised. */
|
|
setenv ("LC_ALL", "C", 1);
|
|
|
|
execvp ((char *) argv[0], (char **) argv);
|
|
_exit (127);
|
|
}
|
|
waitpid (pid, &status, 0);
|
|
if (!WIFEXITED (status))
|
|
return -1;
|
|
return WEXITSTATUS (status);
|
|
}
|
|
|
|
int
|
|
grub_util_exec (const char *const *argv)
|
|
{
|
|
return grub_util_exec_redirect_all (argv, NULL, NULL, NULL);
|
|
}
|
|
|
|
int
|
|
grub_util_exec_redirect (const char *const *argv, const char *stdin_file,
|
|
const char *stdout_file)
|
|
{
|
|
return grub_util_exec_redirect_all (argv, stdin_file, stdout_file, NULL);
|
|
}
|
|
|
|
int
|
|
grub_util_exec_redirect_null (const char *const *argv)
|
|
{
|
|
return grub_util_exec_redirect_all (argv, "/dev/null", "/dev/null", NULL);
|
|
}
|
|
|
|
pid_t
|
|
grub_util_exec_pipe (const char *const *argv, int *fd)
|
|
{
|
|
int pipe_fd[2];
|
|
pid_t pid;
|
|
|
|
*fd = 0;
|
|
|
|
if (pipe (pipe_fd) < 0)
|
|
{
|
|
grub_util_warn (_("Unable to create pipe: %s"),
|
|
strerror (errno));
|
|
return 0;
|
|
}
|
|
pid = fork ();
|
|
if (pid < 0)
|
|
grub_util_error (_("Unable to fork: %s"), strerror (errno));
|
|
else if (pid == 0)
|
|
{
|
|
/* Child. */
|
|
|
|
/* Close fd's. */
|
|
#ifdef GRUB_UTIL
|
|
grub_util_devmapper_cleanup ();
|
|
grub_diskfilter_fini ();
|
|
#endif
|
|
|
|
/* Ensure child is not localised. */
|
|
setenv ("LC_ALL", "C", 1);
|
|
|
|
close (pipe_fd[0]);
|
|
dup2 (pipe_fd[1], STDOUT_FILENO);
|
|
close (pipe_fd[1]);
|
|
|
|
execvp ((char *) argv[0], (char **) argv);
|
|
_exit (127);
|
|
}
|
|
else
|
|
{
|
|
close (pipe_fd[1]);
|
|
*fd = pipe_fd[0];
|
|
return pid;
|
|
}
|
|
}
|
|
|
|
pid_t
|
|
grub_util_exec_pipe_stderr (const char *const *argv, int *fd)
|
|
{
|
|
int pipe_fd[2];
|
|
pid_t pid;
|
|
|
|
*fd = 0;
|
|
|
|
if (pipe (pipe_fd) < 0)
|
|
{
|
|
grub_util_warn (_("Unable to create pipe: %s"),
|
|
strerror (errno));
|
|
return 0;
|
|
}
|
|
pid = fork ();
|
|
if (pid < 0)
|
|
grub_util_error (_("Unable to fork: %s"), strerror (errno));
|
|
else if (pid == 0)
|
|
{
|
|
/* Child. */
|
|
|
|
/* Close fd's. */
|
|
#ifdef GRUB_UTIL
|
|
grub_util_devmapper_cleanup ();
|
|
grub_diskfilter_fini ();
|
|
#endif
|
|
|
|
/* Ensure child is not localised. */
|
|
setenv ("LC_ALL", "C", 1);
|
|
|
|
close (pipe_fd[0]);
|
|
dup2 (pipe_fd[1], STDOUT_FILENO);
|
|
dup2 (pipe_fd[1], STDERR_FILENO);
|
|
close (pipe_fd[1]);
|
|
|
|
execvp ((char *) argv[0], (char **) argv);
|
|
_exit (127);
|
|
}
|
|
else
|
|
{
|
|
close (pipe_fd[1]);
|
|
*fd = pipe_fd[0];
|
|
return pid;
|
|
}
|
|
}
|