diff --git a/.gitignore b/.gitignore index 0db993001..e9170f427 100644 --- a/.gitignore +++ b/.gitignore @@ -528,6 +528,7 @@ Makefile.in /tests/mount-local/test-parallel-mount-local /tests/mountable/test-internal-parse-mountable /tests/parallel/test-parallel +/tests/protocol/test-error-messages /tests/regressions/rhbz501893 /tests/regressions/rhbz790721 /tests/regressions/rhbz914931 diff --git a/daemon/debug.c b/daemon/debug.c index b5470d71c..abc2bec3b 100644 --- a/daemon/debug.c +++ b/daemon/debug.c @@ -65,6 +65,7 @@ static char *debug_help (const char *subcmd, size_t argc, char *const *const arg static char *debug_binaries (const char *subcmd, size_t argc, char *const *const argv); static char *debug_core_pattern (const char *subcmd, size_t argc, char *const *const argv); static char *debug_env (const char *subcmd, size_t argc, char *const *const argv); +static char *debug_error (const char *subcmd, size_t argc, char *const *const argv); static char *debug_fds (const char *subcmd, size_t argc, char *const *const argv); static char *debug_ldd (const char *subcmd, size_t argc, char *const *const argv); static char *debug_ls (const char *subcmd, size_t argc, char *const *const argv); @@ -83,6 +84,7 @@ static struct cmd cmds[] = { { "binaries", debug_binaries }, { "core_pattern", debug_core_pattern }, { "env", debug_env }, + { "error", debug_error }, { "fds", debug_fds }, { "ldd", debug_ldd }, { "ls", debug_ls }, @@ -318,6 +320,44 @@ debug_setenv (const char *subcmd, size_t argc, char *const *const argv) return ret; } +/* Send back an error of different lengths. */ +static char * +debug_error (const char *subcmd, size_t argc, char *const *const argv) +{ + unsigned len; + CLEANUP_FREE char *buf = NULL; + + if (argc != 1) { + error: + reply_with_error ("debug error: expecting one arg: length of error message"); + return NULL; + } + + if (sscanf (argv[0], "%u", &len) != 1) + goto error; + + if (len > 1000000) { + reply_with_error ("debug error: length argument too large"); + return NULL; + } + + buf = malloc (len + 1); + if (buf == NULL) { + reply_with_perror ("malloc"); + return NULL; + } + + memset (buf, 'a', len); + buf[len] = '\0'; + + /* So that the regression test can tell this is the true return path + * from the function and not an actual error, we set errno to some + * value that cannot be returned by any other error path. + */ + reply_with_error_errno (EROFS, "%s", buf); + return NULL; +} + /* Return binaries in the appliance. * See tests/regressions/rhbz727178.sh */ diff --git a/tests/protocol/Makefile.am b/tests/protocol/Makefile.am index f336e85d2..8c57733ae 100644 --- a/tests/protocol/Makefile.am +++ b/tests/protocol/Makefile.am @@ -17,7 +17,7 @@ include $(top_srcdir)/subdir-rules.mk -TESTS = \ +EXTRA_DIST = \ test-both-ends-cancel.sh \ test-cancellation-download-librarycancels.sh \ test-cancellation-upload-daemoncancels.sh \ @@ -28,5 +28,29 @@ TESTS = \ TESTS_ENVIRONMENT = $(top_builddir)/run --test -EXTRA_DIST = \ - $(TESTS) +TESTS = \ + test-both-ends-cancel.sh \ + test-cancellation-download-librarycancels.sh \ + test-cancellation-upload-daemoncancels.sh \ + test-error-messages \ + test-launch-race.pl \ + test-qemudie-killsub.sh \ + test-qemudie-midcommand.sh \ + test-qemudie-synch.sh + +check_PROGRAMS = test-error-messages + +test_error_messages_SOURCES = \ + test-error-messages.c +test_error_messages_CPPFLAGS = \ + -DGUESTFS_PRIVATE=1 \ + -I$(top_srcdir)/gnulib/lib -I$(top_builddir)/gnulib/lib \ + -I$(top_srcdir)/src -I$(top_builddir)/src +test_error_messages_CFLAGS = \ + $(WARN_CFLAGS) $(WERROR_CFLAGS) +test_error_messages_LDADD = \ + $(top_builddir)/src/libutils.la \ + $(top_builddir)/src/libguestfs.la \ + $(LIBXML2_LIBS) \ + $(LIBVIRT_LIBS) \ + $(top_builddir)/gnulib/lib/libgnu.la diff --git a/tests/protocol/test-error-messages.c b/tests/protocol/test-error-messages.c new file mode 100644 index 000000000..3d668c043 --- /dev/null +++ b/tests/protocol/test-error-messages.c @@ -0,0 +1,83 @@ +/* libguestfs + * Copyright (C) 2014 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. + */ + +/* Test error messages from the appliance. + * + * Note that we already test errno from the appliance (see + * tests/c-api/test-last-errno.c) so we don't need to test that here. + */ + +#include + +#include +#include +#include + +#include "guestfs.h" +#include "guestfs_protocol.h" /* For GUESTFS_ERROR_LEN. */ + +int +main (int argc, char *argv[]) +{ + guestfs_h *g; + size_t i; + int lengths[] = { 0, 1, 1024, + GUESTFS_ERROR_LEN-2, GUESTFS_ERROR_LEN-1, + GUESTFS_ERROR_LEN, GUESTFS_ERROR_LEN+1, + GUESTFS_ERROR_LEN+2, + GUESTFS_ERROR_LEN*2, -1 }; + char len_s[64]; + char *args[2]; + + g = guestfs_create (); + if (g == NULL) { + perror ("guestfs_create"); + exit (EXIT_FAILURE); + } + + if (guestfs_add_drive (g, "/dev/null") == -1) + exit (EXIT_FAILURE); + + if (guestfs_launch (g) == -1) + exit (EXIT_FAILURE); + + guestfs_push_error_handler (g, NULL, NULL); + + for (i = 0; lengths[i] != -1; ++i) { + snprintf (len_s, sizeof len_s, "%d", lengths[i]); + args[0] = len_s; + args[1] = NULL; + + if (guestfs_debug (g, "error", args) != NULL) { + fprintf (stderr, "%s: unexpected return value from 'debug error'\n", + argv[0]); + exit (EXIT_FAILURE); + } + /* EROFS is a magic value returned by debug_error in the daemon. */ + if (guestfs_last_errno (g) != EROFS) { + fprintf (stderr, "%s: unexpected error from 'debug error': %s\n", + argv[0], guestfs_last_error (g)); + exit (EXIT_FAILURE); + } + /* else OK */ + } + + guestfs_pop_error_handler (g); + guestfs_close (g); + exit (EXIT_SUCCESS); +}