mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
daemon: Split out command* functions and CLEANUP_* macros.
This allows the command* functions and CLEANUP_* macros to be used independently from the daemon.
This commit is contained in:
@@ -96,6 +96,8 @@ guestfsd_SOURCES = \
|
||||
cap.c \
|
||||
checksum.c \
|
||||
cmp.c \
|
||||
command.c \
|
||||
command.h \
|
||||
compress.c \
|
||||
copy.c \
|
||||
cpio.c \
|
||||
|
||||
80
daemon/cleanups.c
Normal file
80
daemon/cleanups.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/* libguestfs - the guestfsd daemon
|
||||
* Copyright (C) 2009-2015 Red Hat Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <augeas.h>
|
||||
|
||||
#include "cleanups.h"
|
||||
|
||||
/* Use by the CLEANUP_* macros. Do not call these directly. */
|
||||
void
|
||||
cleanup_free (void *ptr)
|
||||
{
|
||||
free (* (void **) ptr);
|
||||
}
|
||||
|
||||
extern void free_strings (char **argv);
|
||||
|
||||
void
|
||||
cleanup_free_string_list (void *ptr)
|
||||
{
|
||||
free_strings (* (char ***) ptr);
|
||||
}
|
||||
|
||||
void
|
||||
cleanup_unlink_free (void *ptr)
|
||||
{
|
||||
char *filename = * (char **) ptr;
|
||||
|
||||
if (filename) {
|
||||
unlink (filename);
|
||||
free (filename);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
cleanup_close (void *ptr)
|
||||
{
|
||||
int fd = * (int *) ptr;
|
||||
|
||||
if (fd >= 0)
|
||||
close (fd);
|
||||
}
|
||||
|
||||
void
|
||||
cleanup_aug_close (void *ptr)
|
||||
{
|
||||
augeas *aug = * (augeas **) ptr;
|
||||
|
||||
if (aug != NULL)
|
||||
aug_close (aug);
|
||||
}
|
||||
|
||||
struct stringsbuf;
|
||||
extern void free_stringsbuf (struct stringsbuf *sb);
|
||||
|
||||
void
|
||||
cleanup_free_stringsbuf (void *ptr)
|
||||
{
|
||||
free_stringsbuf ((struct stringsbuf *) ptr);
|
||||
}
|
||||
47
daemon/cleanups.h
Normal file
47
daemon/cleanups.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* libguestfs - the guestfsd daemon
|
||||
* Copyright (C) 2009-2015 Red Hat Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef GUESTFSD_CLEANUPS_H
|
||||
#define GUESTFSD_CLEANUPS_H
|
||||
|
||||
/* Used by the CLEANUP_* macros. */
|
||||
extern void cleanup_free (void *ptr);
|
||||
extern void cleanup_free_string_list (void *ptr);
|
||||
extern void cleanup_unlink_free (void *ptr);
|
||||
extern void cleanup_close (void *ptr);
|
||||
extern void cleanup_aug_close (void *ptr);
|
||||
extern void cleanup_free_stringsbuf (void *ptr);
|
||||
|
||||
#ifdef HAVE_ATTRIBUTE_CLEANUP
|
||||
#define CLEANUP_FREE __attribute__((cleanup(cleanup_free)))
|
||||
#define CLEANUP_FREE_STRING_LIST \
|
||||
__attribute__((cleanup(cleanup_free_string_list)))
|
||||
#define CLEANUP_UNLINK_FREE __attribute__((cleanup(cleanup_unlink_free)))
|
||||
#define CLEANUP_CLOSE __attribute__((cleanup(cleanup_close)))
|
||||
#define CLEANUP_AUG_CLOSE __attribute__((cleanup(cleanup_aug_close)))
|
||||
#define CLEANUP_FREE_STRINGSBUF __attribute__((cleanup(cleanup_free_stringsbuf)))
|
||||
#else
|
||||
#define CLEANUP_FREE
|
||||
#define CLEANUP_FREE_STRING_LIST
|
||||
#define CLEANUP_UNLINK_FREE
|
||||
#define CLEANUP_CLOSE
|
||||
#define CLEANUP_AUG_CLOSE
|
||||
#define CLEANUP_FREE_STRINGSBUF
|
||||
#endif
|
||||
|
||||
#endif /* GUESTFSD_CLEANUPS_H */
|
||||
436
daemon/command.c
Normal file
436
daemon/command.c
Normal file
@@ -0,0 +1,436 @@
|
||||
/* libguestfs - the guestfsd daemon
|
||||
* Copyright (C) 2009-2015 Red Hat Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <error.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "ignore-value.h"
|
||||
|
||||
#include "command.h"
|
||||
#include "cleanups.h"
|
||||
|
||||
extern int verbose;
|
||||
|
||||
extern const char *sysroot;
|
||||
extern size_t sysroot_len;
|
||||
|
||||
#ifndef MAX
|
||||
# define MAX(a,b) ((a)>(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
/* For improved readability dealing with pipe arrays */
|
||||
#define PIPE_READ 0
|
||||
#define PIPE_WRITE 1
|
||||
|
||||
/* Easy ways to run external commands. For full documentation, see
|
||||
* 'commandrvf' below.
|
||||
*/
|
||||
int
|
||||
commandf (char **stdoutput, char **stderror, unsigned flags,
|
||||
const char *name, ...)
|
||||
{
|
||||
va_list args;
|
||||
/* NB: Mustn't free the strings which are on the stack. */
|
||||
CLEANUP_FREE const char **argv = NULL;
|
||||
char *s;
|
||||
size_t i;
|
||||
int r;
|
||||
|
||||
/* Collect the command line arguments into an array. */
|
||||
i = 2;
|
||||
argv = malloc (sizeof (char *) * i);
|
||||
if (argv == NULL) {
|
||||
perror ("malloc");
|
||||
return -1;
|
||||
}
|
||||
argv[0] = (char *) name;
|
||||
argv[1] = NULL;
|
||||
|
||||
va_start (args, name);
|
||||
|
||||
while ((s = va_arg (args, char *)) != NULL) {
|
||||
const char **p = realloc (argv, sizeof (char *) * (++i));
|
||||
if (p == NULL) {
|
||||
perror ("realloc");
|
||||
va_end (args);
|
||||
return -1;
|
||||
}
|
||||
argv = p;
|
||||
argv[i-2] = s;
|
||||
argv[i-1] = NULL;
|
||||
}
|
||||
|
||||
va_end (args);
|
||||
|
||||
r = commandvf (stdoutput, stderror, flags, (const char * const*) argv);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Same as 'command', but we allow the status code from the
|
||||
* subcommand to be non-zero, and return that status code.
|
||||
* We still return -1 if there was some other error.
|
||||
*/
|
||||
int
|
||||
commandrf (char **stdoutput, char **stderror, unsigned flags,
|
||||
const char *name, ...)
|
||||
{
|
||||
va_list args;
|
||||
CLEANUP_FREE const char **argv = NULL;
|
||||
char *s;
|
||||
int i, r;
|
||||
|
||||
/* Collect the command line arguments into an array. */
|
||||
i = 2;
|
||||
argv = malloc (sizeof (char *) * i);
|
||||
if (argv == NULL) {
|
||||
perror ("malloc");
|
||||
return -1;
|
||||
}
|
||||
argv[0] = (char *) name;
|
||||
argv[1] = NULL;
|
||||
|
||||
va_start (args, name);
|
||||
|
||||
while ((s = va_arg (args, char *)) != NULL) {
|
||||
const char **p = realloc (argv, sizeof (char *) * (++i));
|
||||
if (p == NULL) {
|
||||
perror ("realloc");
|
||||
va_end (args);
|
||||
return -1;
|
||||
}
|
||||
argv = p;
|
||||
argv[i-2] = s;
|
||||
argv[i-1] = NULL;
|
||||
}
|
||||
|
||||
va_end (args);
|
||||
|
||||
r = commandrvf (stdoutput, stderror, flags, argv);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Same as 'command', but passing an argv. */
|
||||
int
|
||||
commandvf (char **stdoutput, char **stderror, unsigned flags,
|
||||
char const *const *argv)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = commandrvf (stdoutput, stderror, flags, (void *) argv);
|
||||
if (r == 0)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* This is a more sane version of 'system(3)' for running external
|
||||
* commands. It uses fork/execvp, so we don't need to worry about
|
||||
* quoting of parameters, and it allows us to capture any error
|
||||
* messages in a buffer.
|
||||
*
|
||||
* If stdoutput is not NULL, then *stdoutput will return the stdout
|
||||
* of the command.
|
||||
*
|
||||
* If stderror is not NULL, then *stderror will return the stderr
|
||||
* of the command. If there is a final \n character, it is removed
|
||||
* so you can use the error string directly in a call to
|
||||
* reply_with_error.
|
||||
*
|
||||
* Flags:
|
||||
*
|
||||
* COMMAND_FLAG_FOLD_STDOUT_ON_STDERR: For broken external commands
|
||||
* that send error messages to stdout (hello, parted) but that don't
|
||||
* have any useful stdout information, use this flag to capture the
|
||||
* error messages in the *stderror buffer. If using this flag,
|
||||
* you should pass stdoutput as NULL because nothing could ever be
|
||||
* captured in that buffer.
|
||||
*
|
||||
* COMMAND_FLAG_CHROOT_COPY_FILE_TO_STDIN: For running external
|
||||
* commands on chrooted files correctly (see RHBZ#579608) specifying
|
||||
* this flag causes another process to be forked which chroots into
|
||||
* sysroot and just copies the input file to stdin of the specified
|
||||
* command. The file descriptor is ORed with the flags, and that file
|
||||
* descriptor is always closed by this function. See hexdump.c for an
|
||||
* example of usage.
|
||||
*/
|
||||
int
|
||||
commandrvf (char **stdoutput, char **stderror, unsigned flags,
|
||||
char const* const *argv)
|
||||
{
|
||||
size_t so_size = 0, se_size = 0;
|
||||
int so_fd[2], se_fd[2];
|
||||
unsigned flag_copy_stdin = flags & COMMAND_FLAG_CHROOT_COPY_FILE_TO_STDIN;
|
||||
int flag_copy_fd = (int) (flags & COMMAND_FLAG_FD_MASK);
|
||||
pid_t pid;
|
||||
int r, quit, i;
|
||||
fd_set rset, rset2;
|
||||
char buf[256];
|
||||
char *p;
|
||||
|
||||
if (stdoutput) *stdoutput = NULL;
|
||||
if (stderror) *stderror = NULL;
|
||||
|
||||
if (verbose) {
|
||||
printf ("commandrvf: stdout=%s stderr=%s flags=0x%x\n",
|
||||
stdoutput ? "y" : "n", stderror ? "y" : "n", flags);
|
||||
fputs ("commandrvf: ", stdout);
|
||||
fputs (argv[0], stdout);
|
||||
for (i = 1; argv[i] != NULL; ++i) {
|
||||
char quote;
|
||||
|
||||
/* Do simple (and incorrect) quoting of the debug output. Real
|
||||
* quoting is not necessary because we use execvp to run the
|
||||
* command below.
|
||||
*/
|
||||
if (strchr (argv[i], '\''))
|
||||
quote = '"';
|
||||
else if (strchr (argv[i], '"'))
|
||||
quote = '\'';
|
||||
else if (strchr (argv[i], ' '))
|
||||
quote = '"';
|
||||
else
|
||||
quote = 0;
|
||||
|
||||
putchar (' ');
|
||||
if (quote) putchar (quote);
|
||||
fputs (argv[i], stdout);
|
||||
if (quote) putchar (quote);
|
||||
}
|
||||
putchar ('\n');
|
||||
}
|
||||
|
||||
/* Note: abort is used in a few places along the error paths early
|
||||
* in this function. This is because (a) cleaning up correctly is
|
||||
* very complex at these places and (b) abort is used when a
|
||||
* resource problems is indicated which would be due to much more
|
||||
* serious issues - eg. memory or file descriptor leaks. We
|
||||
* wouldn't expect fork(2) or pipe(2) to fail in normal
|
||||
* circumstances.
|
||||
*/
|
||||
|
||||
if (pipe (so_fd) == -1 || pipe (se_fd) == -1) {
|
||||
error (0, errno, "pipe");
|
||||
abort ();
|
||||
}
|
||||
|
||||
pid = fork ();
|
||||
if (pid == -1) {
|
||||
error (0, errno, "fork");
|
||||
abort ();
|
||||
}
|
||||
|
||||
if (pid == 0) { /* Child process running the command. */
|
||||
signal (SIGALRM, SIG_DFL);
|
||||
signal (SIGPIPE, SIG_DFL);
|
||||
close (0);
|
||||
if (flag_copy_stdin) {
|
||||
if (dup2 (flag_copy_fd, STDIN_FILENO) == -1) {
|
||||
perror ("dup2/flag_copy_fd");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
/* Set stdin to /dev/null. */
|
||||
if (open ("/dev/null", O_RDONLY) == -1) {
|
||||
perror ("open: /dev/null");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
close (so_fd[PIPE_READ]);
|
||||
close (se_fd[PIPE_READ]);
|
||||
if (!(flags & COMMAND_FLAG_FOLD_STDOUT_ON_STDERR)) {
|
||||
if (dup2 (so_fd[PIPE_WRITE], STDOUT_FILENO) == -1) {
|
||||
perror ("dup2/so_fd[PIPE_WRITE]");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
if (dup2 (se_fd[PIPE_WRITE], STDOUT_FILENO) == -1) {
|
||||
perror ("dup2/se_fd[PIPE_WRITE]");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
if (dup2 (se_fd[PIPE_WRITE], STDERR_FILENO) == -1) {
|
||||
perror ("dup2/se_fd[PIPE_WRITE]");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
close (so_fd[PIPE_WRITE]);
|
||||
close (se_fd[PIPE_WRITE]);
|
||||
|
||||
if (flags & COMMAND_FLAG_DO_CHROOT && sysroot_len > 0) {
|
||||
if (chroot (sysroot) == -1) {
|
||||
perror ("chroot in sysroot");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (chdir ("/") == -1) {
|
||||
perror ("chdir");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
execvp (argv[0], (void *) argv);
|
||||
perror (argv[0]);
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Parent process. */
|
||||
close (so_fd[PIPE_WRITE]);
|
||||
close (se_fd[PIPE_WRITE]);
|
||||
|
||||
FD_ZERO (&rset);
|
||||
FD_SET (so_fd[PIPE_READ], &rset);
|
||||
FD_SET (se_fd[PIPE_READ], &rset);
|
||||
|
||||
quit = 0;
|
||||
while (quit < 2) {
|
||||
again:
|
||||
rset2 = rset;
|
||||
r = select (MAX (so_fd[PIPE_READ], se_fd[PIPE_READ]) + 1, &rset2,
|
||||
NULL, NULL, NULL);
|
||||
if (r == -1) {
|
||||
if (errno == EINTR)
|
||||
goto again;
|
||||
|
||||
perror ("select");
|
||||
quit:
|
||||
if (stdoutput) {
|
||||
free (*stdoutput);
|
||||
*stdoutput = NULL;
|
||||
}
|
||||
if (stderror) {
|
||||
free (*stderror);
|
||||
/* Need to return non-NULL *stderror here since most callers
|
||||
* will try to print and then free the err string.
|
||||
* Unfortunately recovery from strdup failure here is not
|
||||
* possible.
|
||||
*/
|
||||
*stderror = strdup ("error running external command, "
|
||||
"see debug output for details");
|
||||
}
|
||||
close (so_fd[PIPE_READ]);
|
||||
close (se_fd[PIPE_READ]);
|
||||
if (flag_copy_stdin) close (flag_copy_fd);
|
||||
waitpid (pid, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (FD_ISSET (so_fd[PIPE_READ], &rset2)) { /* something on stdout */
|
||||
r = read (so_fd[PIPE_READ], buf, sizeof buf);
|
||||
if (r == -1) {
|
||||
perror ("read");
|
||||
goto quit;
|
||||
}
|
||||
if (r == 0) { FD_CLR (so_fd[PIPE_READ], &rset); quit++; }
|
||||
|
||||
if (r > 0 && stdoutput) {
|
||||
so_size += r;
|
||||
p = realloc (*stdoutput, so_size);
|
||||
if (p == NULL) {
|
||||
perror ("realloc");
|
||||
goto quit;
|
||||
}
|
||||
*stdoutput = p;
|
||||
memcpy (*stdoutput + so_size - r, buf, r);
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET (se_fd[PIPE_READ], &rset2)) { /* something on stderr */
|
||||
r = read (se_fd[PIPE_READ], buf, sizeof buf);
|
||||
if (r == -1) {
|
||||
perror ("read");
|
||||
goto quit;
|
||||
}
|
||||
if (r == 0) { FD_CLR (se_fd[PIPE_READ], &rset); quit++; }
|
||||
|
||||
if (r > 0) {
|
||||
if (verbose)
|
||||
ignore_value (write (STDERR_FILENO, buf, r));
|
||||
|
||||
if (stderror) {
|
||||
se_size += r;
|
||||
p = realloc (*stderror, se_size);
|
||||
if (p == NULL) {
|
||||
perror ("realloc");
|
||||
goto quit;
|
||||
}
|
||||
*stderror = p;
|
||||
memcpy (*stderror + se_size - r, buf, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close (so_fd[PIPE_READ]);
|
||||
close (se_fd[PIPE_READ]);
|
||||
|
||||
/* Make sure the output buffers are \0-terminated. Also remove any
|
||||
* trailing \n characters from the error buffer (not from stdout).
|
||||
*/
|
||||
if (stdoutput) {
|
||||
void *q = realloc (*stdoutput, so_size+1);
|
||||
if (q == NULL) {
|
||||
perror ("realloc");
|
||||
free (*stdoutput);
|
||||
}
|
||||
*stdoutput = q;
|
||||
if (*stdoutput)
|
||||
(*stdoutput)[so_size] = '\0';
|
||||
}
|
||||
if (stderror) {
|
||||
void *q = realloc (*stderror, se_size+1);
|
||||
if (q == NULL) {
|
||||
perror ("realloc");
|
||||
free (*stderror);
|
||||
}
|
||||
*stderror = q;
|
||||
if (*stderror) {
|
||||
(*stderror)[se_size] = '\0';
|
||||
while (se_size > 0 && (*stderror)[se_size-1] == '\n') {
|
||||
se_size--;
|
||||
(*stderror)[se_size] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flag_copy_stdin && close (flag_copy_fd) == -1) {
|
||||
perror ("close");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get the exit status of the command. */
|
||||
if (waitpid (pid, &r, 0) != pid) {
|
||||
perror ("waitpid");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (WIFEXITED (r)) {
|
||||
return WEXITSTATUS (r);
|
||||
} else
|
||||
return -1;
|
||||
}
|
||||
41
daemon/command.h
Normal file
41
daemon/command.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/* libguestfs - the guestfsd daemon
|
||||
* Copyright (C) 2009-2015 Red Hat Inc.
|
||||
*
|
||||
* 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 2 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, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef GUESTFSD_COMMAND_H
|
||||
#define GUESTFSD_COMMAND_H
|
||||
|
||||
#define command(out,err,name,...) commandf((out),(err),0,(name),__VA_ARGS__)
|
||||
#define commandr(out,err,name,...) commandrf((out),(err),0,(name),__VA_ARGS__)
|
||||
#define commandv(out,err,argv) commandvf((out),(err),0,(argv))
|
||||
#define commandrv(out,err,argv) commandrvf((out),(err),0,(argv))
|
||||
|
||||
#define COMMAND_FLAG_FD_MASK 0x0000ffff
|
||||
#define COMMAND_FLAG_FOLD_STDOUT_ON_STDERR 0x00010000
|
||||
#define COMMAND_FLAG_CHROOT_COPY_FILE_TO_STDIN 0x00020000
|
||||
#define COMMAND_FLAG_DO_CHROOT 0x00040000
|
||||
|
||||
extern int commandf (char **stdoutput, char **stderror, unsigned flags,
|
||||
const char *name, ...) __attribute__((sentinel));
|
||||
extern int commandrf (char **stdoutput, char **stderror, unsigned flags,
|
||||
const char *name, ...) __attribute__((sentinel));
|
||||
extern int commandvf (char **stdoutput, char **stderror, unsigned flags,
|
||||
char const *const *argv);
|
||||
extern int commandrvf (char **stdoutput, char **stderror, unsigned flags,
|
||||
char const* const *argv);
|
||||
|
||||
#endif /* GUESTFSD_COMMAND_H */
|
||||
@@ -32,6 +32,9 @@
|
||||
|
||||
#include "guestfs-internal-all.h"
|
||||
|
||||
#include "cleanups.h"
|
||||
#include "command.h"
|
||||
|
||||
/* Mountables */
|
||||
|
||||
typedef struct {
|
||||
@@ -117,28 +120,9 @@ extern char **split_lines (char *str);
|
||||
|
||||
extern char **empty_list (void);
|
||||
|
||||
#define command(out,err,name,...) commandf((out),(err),0,(name),__VA_ARGS__)
|
||||
#define commandr(out,err,name,...) commandrf((out),(err),0,(name),__VA_ARGS__)
|
||||
#define commandv(out,err,argv) commandvf((out),(err),0,(argv))
|
||||
#define commandrv(out,err,argv) commandrvf((out),(err),0,(argv))
|
||||
|
||||
#define __external_command __attribute__((__section__(".guestfsd_ext_cmds")))
|
||||
#define GUESTFSD_EXT_CMD(___ext_cmd_var, ___ext_cmd_str) static const char ___ext_cmd_var[] __external_command = #___ext_cmd_str
|
||||
|
||||
#define COMMAND_FLAG_FD_MASK 0x0000ffff
|
||||
#define COMMAND_FLAG_FOLD_STDOUT_ON_STDERR 0x00010000
|
||||
#define COMMAND_FLAG_CHROOT_COPY_FILE_TO_STDIN 0x00020000
|
||||
#define COMMAND_FLAG_DO_CHROOT 0x00040000
|
||||
|
||||
extern int commandf (char **stdoutput, char **stderror, unsigned flags,
|
||||
const char *name, ...) __attribute__((sentinel));
|
||||
extern int commandrf (char **stdoutput, char **stderror, unsigned flags,
|
||||
const char *name, ...) __attribute__((sentinel));
|
||||
extern int commandvf (char **stdoutput, char **stderror, unsigned flags,
|
||||
char const *const *argv);
|
||||
extern int commandrvf (char **stdoutput, char **stderror, unsigned flags,
|
||||
char const* const *argv);
|
||||
|
||||
extern int is_power_of_2 (unsigned long v);
|
||||
|
||||
extern void trim (char *str);
|
||||
@@ -157,14 +141,6 @@ extern char *get_random_uuid (void);
|
||||
|
||||
extern int asprintf_nowarn (char **strp, const char *fmt, ...);
|
||||
|
||||
/* Use by the CLEANUP_* macros. */
|
||||
extern void cleanup_free (void *ptr);
|
||||
extern void cleanup_free_string_list (void *ptr);
|
||||
extern void cleanup_unlink_free (void *ptr);
|
||||
extern void cleanup_close (void *ptr);
|
||||
extern void cleanup_aug_close (void *ptr);
|
||||
extern void cleanup_free_stringsbuf (void *ptr);
|
||||
|
||||
/*-- in names.c (auto-generated) --*/
|
||||
extern const char *function_names[];
|
||||
|
||||
@@ -452,21 +428,4 @@ is_zero (const char *buffer, size_t size)
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#ifdef HAVE_ATTRIBUTE_CLEANUP
|
||||
#define CLEANUP_FREE __attribute__((cleanup(cleanup_free)))
|
||||
#define CLEANUP_FREE_STRING_LIST \
|
||||
__attribute__((cleanup(cleanup_free_string_list)))
|
||||
#define CLEANUP_UNLINK_FREE __attribute__((cleanup(cleanup_unlink_free)))
|
||||
#define CLEANUP_CLOSE __attribute__((cleanup(cleanup_close)))
|
||||
#define CLEANUP_AUG_CLOSE __attribute__((cleanup(cleanup_aug_close)))
|
||||
#define CLEANUP_FREE_STRINGSBUF __attribute__((cleanup(cleanup_free_stringsbuf)))
|
||||
#else
|
||||
#define CLEANUP_FREE
|
||||
#define CLEANUP_FREE_STRING_LIST
|
||||
#define CLEANUP_UNLINK_FREE
|
||||
#define CLEANUP_CLOSE
|
||||
#define CLEANUP_AUG_CLOSE
|
||||
#define CLEANUP_FREE_STRINGSBUF
|
||||
#endif
|
||||
|
||||
#endif /* GUESTFSD_DAEMON_H */
|
||||
|
||||
@@ -68,10 +68,6 @@ GUESTFSD_EXT_CMD(str_uuidgen, uuidgen);
|
||||
# define O_CLOEXEC 0
|
||||
#endif
|
||||
|
||||
/* For improved readability dealing with pipe arrays */
|
||||
#define PIPE_READ 0
|
||||
#define PIPE_WRITE 1
|
||||
|
||||
/* If root device is an ext2 filesystem, this is the major and minor.
|
||||
* This is so we can ignore this device from the point of view of the
|
||||
* user, eg. in guestfs_list_devices and many other places.
|
||||
@@ -752,394 +748,6 @@ join_strings (const char *separator, char *const *argv)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Easy ways to run external commands. For full documentation, see
|
||||
* 'commandrvf' below.
|
||||
*/
|
||||
int
|
||||
commandf (char **stdoutput, char **stderror, unsigned flags,
|
||||
const char *name, ...)
|
||||
{
|
||||
va_list args;
|
||||
/* NB: Mustn't free the strings which are on the stack. */
|
||||
CLEANUP_FREE const char **argv = NULL;
|
||||
char *s;
|
||||
size_t i;
|
||||
int r;
|
||||
|
||||
/* Collect the command line arguments into an array. */
|
||||
i = 2;
|
||||
argv = malloc (sizeof (char *) * i);
|
||||
if (argv == NULL) {
|
||||
perror ("malloc");
|
||||
return -1;
|
||||
}
|
||||
argv[0] = (char *) name;
|
||||
argv[1] = NULL;
|
||||
|
||||
va_start (args, name);
|
||||
|
||||
while ((s = va_arg (args, char *)) != NULL) {
|
||||
const char **p = realloc (argv, sizeof (char *) * (++i));
|
||||
if (p == NULL) {
|
||||
perror ("realloc");
|
||||
va_end (args);
|
||||
return -1;
|
||||
}
|
||||
argv = p;
|
||||
argv[i-2] = s;
|
||||
argv[i-1] = NULL;
|
||||
}
|
||||
|
||||
va_end (args);
|
||||
|
||||
r = commandvf (stdoutput, stderror, flags, (const char * const*) argv);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Same as 'command', but we allow the status code from the
|
||||
* subcommand to be non-zero, and return that status code.
|
||||
* We still return -1 if there was some other error.
|
||||
*/
|
||||
int
|
||||
commandrf (char **stdoutput, char **stderror, unsigned flags,
|
||||
const char *name, ...)
|
||||
{
|
||||
va_list args;
|
||||
CLEANUP_FREE const char **argv = NULL;
|
||||
char *s;
|
||||
int i, r;
|
||||
|
||||
/* Collect the command line arguments into an array. */
|
||||
i = 2;
|
||||
argv = malloc (sizeof (char *) * i);
|
||||
if (argv == NULL) {
|
||||
perror ("malloc");
|
||||
return -1;
|
||||
}
|
||||
argv[0] = (char *) name;
|
||||
argv[1] = NULL;
|
||||
|
||||
va_start (args, name);
|
||||
|
||||
while ((s = va_arg (args, char *)) != NULL) {
|
||||
const char **p = realloc (argv, sizeof (char *) * (++i));
|
||||
if (p == NULL) {
|
||||
perror ("realloc");
|
||||
va_end (args);
|
||||
return -1;
|
||||
}
|
||||
argv = p;
|
||||
argv[i-2] = s;
|
||||
argv[i-1] = NULL;
|
||||
}
|
||||
|
||||
va_end (args);
|
||||
|
||||
r = commandrvf (stdoutput, stderror, flags, argv);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Same as 'command', but passing an argv. */
|
||||
int
|
||||
commandvf (char **stdoutput, char **stderror, unsigned flags,
|
||||
char const *const *argv)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = commandrvf (stdoutput, stderror, flags, (void *) argv);
|
||||
if (r == 0)
|
||||
return 0;
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* This is a more sane version of 'system(3)' for running external
|
||||
* commands. It uses fork/execvp, so we don't need to worry about
|
||||
* quoting of parameters, and it allows us to capture any error
|
||||
* messages in a buffer.
|
||||
*
|
||||
* If stdoutput is not NULL, then *stdoutput will return the stdout
|
||||
* of the command.
|
||||
*
|
||||
* If stderror is not NULL, then *stderror will return the stderr
|
||||
* of the command. If there is a final \n character, it is removed
|
||||
* so you can use the error string directly in a call to
|
||||
* reply_with_error.
|
||||
*
|
||||
* Flags:
|
||||
*
|
||||
* COMMAND_FLAG_FOLD_STDOUT_ON_STDERR: For broken external commands
|
||||
* that send error messages to stdout (hello, parted) but that don't
|
||||
* have any useful stdout information, use this flag to capture the
|
||||
* error messages in the *stderror buffer. If using this flag,
|
||||
* you should pass stdoutput as NULL because nothing could ever be
|
||||
* captured in that buffer.
|
||||
*
|
||||
* COMMAND_FLAG_CHROOT_COPY_FILE_TO_STDIN: For running external
|
||||
* commands on chrooted files correctly (see RHBZ#579608) specifying
|
||||
* this flag causes another process to be forked which chroots into
|
||||
* sysroot and just copies the input file to stdin of the specified
|
||||
* command. The file descriptor is ORed with the flags, and that file
|
||||
* descriptor is always closed by this function. See hexdump.c for an
|
||||
* example of usage.
|
||||
*/
|
||||
int
|
||||
commandrvf (char **stdoutput, char **stderror, unsigned flags,
|
||||
char const* const *argv)
|
||||
{
|
||||
size_t so_size = 0, se_size = 0;
|
||||
int so_fd[2], se_fd[2];
|
||||
unsigned flag_copy_stdin = flags & COMMAND_FLAG_CHROOT_COPY_FILE_TO_STDIN;
|
||||
int flag_copy_fd = (int) (flags & COMMAND_FLAG_FD_MASK);
|
||||
pid_t pid;
|
||||
int r, quit, i;
|
||||
fd_set rset, rset2;
|
||||
char buf[256];
|
||||
char *p;
|
||||
|
||||
if (stdoutput) *stdoutput = NULL;
|
||||
if (stderror) *stderror = NULL;
|
||||
|
||||
if (verbose) {
|
||||
printf ("commandrvf: stdout=%s stderr=%s flags=0x%x\n",
|
||||
stdoutput ? "y" : "n", stderror ? "y" : "n", flags);
|
||||
fputs ("commandrvf: ", stdout);
|
||||
fputs (argv[0], stdout);
|
||||
for (i = 1; argv[i] != NULL; ++i) {
|
||||
char quote;
|
||||
|
||||
/* Do simple (and incorrect) quoting of the debug output. Real
|
||||
* quoting is not necessary because we use execvp to run the
|
||||
* command below.
|
||||
*/
|
||||
if (strchr (argv[i], '\''))
|
||||
quote = '"';
|
||||
else if (strchr (argv[i], '"'))
|
||||
quote = '\'';
|
||||
else if (strchr (argv[i], ' '))
|
||||
quote = '"';
|
||||
else
|
||||
quote = 0;
|
||||
|
||||
putchar (' ');
|
||||
if (quote) putchar (quote);
|
||||
fputs (argv[i], stdout);
|
||||
if (quote) putchar (quote);
|
||||
}
|
||||
putchar ('\n');
|
||||
}
|
||||
|
||||
/* Note: abort is used in a few places along the error paths early
|
||||
* in this function. This is because (a) cleaning up correctly is
|
||||
* very complex at these places and (b) abort is used when a
|
||||
* resource problems is indicated which would be due to much more
|
||||
* serious issues - eg. memory or file descriptor leaks. We
|
||||
* wouldn't expect fork(2) or pipe(2) to fail in normal
|
||||
* circumstances.
|
||||
*/
|
||||
|
||||
if (pipe (so_fd) == -1 || pipe (se_fd) == -1) {
|
||||
error (0, errno, "pipe");
|
||||
abort ();
|
||||
}
|
||||
|
||||
pid = fork ();
|
||||
if (pid == -1) {
|
||||
error (0, errno, "fork");
|
||||
abort ();
|
||||
}
|
||||
|
||||
if (pid == 0) { /* Child process running the command. */
|
||||
signal (SIGALRM, SIG_DFL);
|
||||
signal (SIGPIPE, SIG_DFL);
|
||||
close (0);
|
||||
if (flag_copy_stdin) {
|
||||
if (dup2 (flag_copy_fd, STDIN_FILENO) == -1) {
|
||||
perror ("dup2/flag_copy_fd");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
/* Set stdin to /dev/null. */
|
||||
if (open ("/dev/null", O_RDONLY) == -1) {
|
||||
perror ("open: /dev/null");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
close (so_fd[PIPE_READ]);
|
||||
close (se_fd[PIPE_READ]);
|
||||
if (!(flags & COMMAND_FLAG_FOLD_STDOUT_ON_STDERR)) {
|
||||
if (dup2 (so_fd[PIPE_WRITE], STDOUT_FILENO) == -1) {
|
||||
perror ("dup2/so_fd[PIPE_WRITE]");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
if (dup2 (se_fd[PIPE_WRITE], STDOUT_FILENO) == -1) {
|
||||
perror ("dup2/se_fd[PIPE_WRITE]");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
if (dup2 (se_fd[PIPE_WRITE], STDERR_FILENO) == -1) {
|
||||
perror ("dup2/se_fd[PIPE_WRITE]");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
close (so_fd[PIPE_WRITE]);
|
||||
close (se_fd[PIPE_WRITE]);
|
||||
|
||||
if (flags & COMMAND_FLAG_DO_CHROOT && sysroot_len > 0) {
|
||||
if (chroot (sysroot) == -1) {
|
||||
perror ("chroot in sysroot");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (chdir ("/") == -1) {
|
||||
perror ("chdir");
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
execvp (argv[0], (void *) argv);
|
||||
perror (argv[0]);
|
||||
_exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Parent process. */
|
||||
close (so_fd[PIPE_WRITE]);
|
||||
close (se_fd[PIPE_WRITE]);
|
||||
|
||||
FD_ZERO (&rset);
|
||||
FD_SET (so_fd[PIPE_READ], &rset);
|
||||
FD_SET (se_fd[PIPE_READ], &rset);
|
||||
|
||||
quit = 0;
|
||||
while (quit < 2) {
|
||||
again:
|
||||
rset2 = rset;
|
||||
r = select (MAX (so_fd[PIPE_READ], se_fd[PIPE_READ]) + 1, &rset2,
|
||||
NULL, NULL, NULL);
|
||||
if (r == -1) {
|
||||
if (errno == EINTR)
|
||||
goto again;
|
||||
|
||||
perror ("select");
|
||||
quit:
|
||||
if (stdoutput) {
|
||||
free (*stdoutput);
|
||||
*stdoutput = NULL;
|
||||
}
|
||||
if (stderror) {
|
||||
free (*stderror);
|
||||
/* Need to return non-NULL *stderror here since most callers
|
||||
* will try to print and then free the err string.
|
||||
* Unfortunately recovery from strdup failure here is not
|
||||
* possible.
|
||||
*/
|
||||
*stderror = strdup ("error running external command, "
|
||||
"see debug output for details");
|
||||
}
|
||||
close (so_fd[PIPE_READ]);
|
||||
close (se_fd[PIPE_READ]);
|
||||
if (flag_copy_stdin) close (flag_copy_fd);
|
||||
waitpid (pid, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (FD_ISSET (so_fd[PIPE_READ], &rset2)) { /* something on stdout */
|
||||
r = read (so_fd[PIPE_READ], buf, sizeof buf);
|
||||
if (r == -1) {
|
||||
perror ("read");
|
||||
goto quit;
|
||||
}
|
||||
if (r == 0) { FD_CLR (so_fd[PIPE_READ], &rset); quit++; }
|
||||
|
||||
if (r > 0 && stdoutput) {
|
||||
so_size += r;
|
||||
p = realloc (*stdoutput, so_size);
|
||||
if (p == NULL) {
|
||||
perror ("realloc");
|
||||
goto quit;
|
||||
}
|
||||
*stdoutput = p;
|
||||
memcpy (*stdoutput + so_size - r, buf, r);
|
||||
}
|
||||
}
|
||||
|
||||
if (FD_ISSET (se_fd[PIPE_READ], &rset2)) { /* something on stderr */
|
||||
r = read (se_fd[PIPE_READ], buf, sizeof buf);
|
||||
if (r == -1) {
|
||||
perror ("read");
|
||||
goto quit;
|
||||
}
|
||||
if (r == 0) { FD_CLR (se_fd[PIPE_READ], &rset); quit++; }
|
||||
|
||||
if (r > 0) {
|
||||
if (verbose)
|
||||
ignore_value (write (STDERR_FILENO, buf, r));
|
||||
|
||||
if (stderror) {
|
||||
se_size += r;
|
||||
p = realloc (*stderror, se_size);
|
||||
if (p == NULL) {
|
||||
perror ("realloc");
|
||||
goto quit;
|
||||
}
|
||||
*stderror = p;
|
||||
memcpy (*stderror + se_size - r, buf, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
close (so_fd[PIPE_READ]);
|
||||
close (se_fd[PIPE_READ]);
|
||||
|
||||
/* Make sure the output buffers are \0-terminated. Also remove any
|
||||
* trailing \n characters from the error buffer (not from stdout).
|
||||
*/
|
||||
if (stdoutput) {
|
||||
void *q = realloc (*stdoutput, so_size+1);
|
||||
if (q == NULL) {
|
||||
perror ("realloc");
|
||||
free (*stdoutput);
|
||||
}
|
||||
*stdoutput = q;
|
||||
if (*stdoutput)
|
||||
(*stdoutput)[so_size] = '\0';
|
||||
}
|
||||
if (stderror) {
|
||||
void *q = realloc (*stderror, se_size+1);
|
||||
if (q == NULL) {
|
||||
perror ("realloc");
|
||||
free (*stderror);
|
||||
}
|
||||
*stderror = q;
|
||||
if (*stderror) {
|
||||
(*stderror)[se_size] = '\0';
|
||||
while (se_size > 0 && (*stderror)[se_size-1] == '\n') {
|
||||
se_size--;
|
||||
(*stderror)[se_size] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flag_copy_stdin && close (flag_copy_fd) == -1) {
|
||||
perror ("close");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get the exit status of the command. */
|
||||
if (waitpid (pid, &r, 0) != pid) {
|
||||
perror ("waitpid");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (WIFEXITED (r)) {
|
||||
return WEXITSTATUS (r);
|
||||
} else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Split an output string into a NULL-terminated list of lines,
|
||||
* wrapped into a stringsbuf.
|
||||
* Typically this is used where we have run an external command
|
||||
|
||||
@@ -25,7 +25,9 @@ daemon/blockdev.c
|
||||
daemon/btrfs.c
|
||||
daemon/cap.c
|
||||
daemon/checksum.c
|
||||
daemon/cleanups.c
|
||||
daemon/cmp.c
|
||||
daemon/command.c
|
||||
daemon/compress.c
|
||||
daemon/copy.c
|
||||
daemon/cpio.c
|
||||
|
||||
Reference in New Issue
Block a user