The __linux__ version of qemu_chr_open_pp_fd() tries to claim the
parport device with a PPCLAIM ioctl(). On success, it stores the file
descriptor in the chardev object, and returns success. On failure, it
closes the file descriptor, and returns failure.
chardev_new() then passes the Chardev to object_unref(). This duly
calls char_parallel_finalize(), which closes the file descriptor
stored in the chardev object. Since qemu_chr_open_pp_fd() didn't
store it, it's still zero, so this closes standard input. Ooopsie.
To demonstate, add a unit test. With the bug above unfixed, running
this test closes standard input. char_hotswap_test() happens to run
next. It opens a socket, duly gets file descriptor 0, and since it
tests for success with > 0 instead of >= 0, it fails.
The test needs to be conditional exactly like the chardev it tests.
Since the condition is rather complicated, steal the solution from the
serial chardev: define HAVE_CHARDEV_PARALLEL in qemu/osdep.h. This
also permits simplifying chardev/meson.build a bit.
The bug fix is easy enough: store the file descriptor, and leave
closing it to char_parallel_finalize().
Signed-off-by: Markus Armbruster <armbru@redhat.com>
---
include/qemu/osdep.h | 9 ++++++++-
chardev/char-parallel.c | 7 +++++--
tests/unit/test-char.c | 21 +++++++++++++++++++++
chardev/meson.build | 4 +---
4 files changed, 35 insertions(+), 6 deletions(-)
diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h
index c9692cc314..3b0f3389d3 100644
--- a/include/qemu/osdep.h
+++ b/include/qemu/osdep.h
@@ -508,11 +508,18 @@ void qemu_anon_ram_free(void *ptr, size_t size);
#ifdef _WIN32
#define HAVE_CHARDEV_SERIAL 1
-#elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
+#define HAVE_CHARDEV_PARALLEL 1
+#else
+#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
|| defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
|| defined(__GLIBC__) || defined(__APPLE__)
#define HAVE_CHARDEV_SERIAL 1
#endif
+#if defined(__linux__) || defined(__FreeBSD__) \
+ || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
+#define HAVE_CHARDEV_PARALLEL 1
+#endif
+#endif
#if defined(__HAIKU__)
#define SIGIO SIGPOLL
diff --git a/chardev/char-parallel.c b/chardev/char-parallel.c
index a5164f975a..78697d7522 100644
--- a/chardev/char-parallel.c
+++ b/chardev/char-parallel.c
@@ -164,13 +164,13 @@ static void qemu_chr_open_pp_fd(Chardev *chr,
{
ParallelChardev *drv = PARALLEL_CHARDEV(chr);
+ drv->fd = fd;
+
if (ioctl(fd, PPCLAIM) < 0) {
error_setg_errno(errp, errno, "not a parallel port");
- close(fd);
return;
}
- drv->fd = fd;
drv->mode = IEEE1284_MODE_COMPAT;
}
#endif /* __linux__ */
@@ -238,6 +238,7 @@ static void qemu_chr_open_pp_fd(Chardev *chr,
}
#endif
+#ifdef HAVE_CHARDEV_PARALLEL
static void qmp_chardev_open_parallel(Chardev *chr,
ChardevBackend *backend,
bool *be_opened,
@@ -306,3 +307,5 @@ static void register_types(void)
}
type_init(register_types);
+
+#endif /* HAVE_CHARDEV_PARALLEL */
diff --git a/tests/unit/test-char.c b/tests/unit/test-char.c
index 649fdf64e1..76946e6f90 100644
--- a/tests/unit/test-char.c
+++ b/tests/unit/test-char.c
@@ -1203,6 +1203,24 @@ static void char_serial_test(void)
}
#endif
+#if defined(HAVE_CHARDEV_PARALLEL) && !defined(WIN32)
+static void char_parallel_test(void)
+{
+ QemuOpts *opts;
+ Chardev *chr;
+
+ opts = qemu_opts_create(qemu_find_opts("chardev"), "parallel-id",
+ 1, &error_abort);
+ qemu_opt_set(opts, "backend", "parallel", &error_abort);
+ qemu_opt_set(opts, "path", "/dev/null", &error_abort);
+
+ chr = qemu_chr_new_from_opts(opts, NULL, NULL);
+ g_assert_null(chr);
+
+ qemu_opts_del(opts);
+}
+#endif
+
#ifndef _WIN32
static void char_file_fifo_test(void)
{
@@ -1544,6 +1562,9 @@ int main(int argc, char **argv)
g_test_add_func("/char/udp", char_udp_test);
#if defined(HAVE_CHARDEV_SERIAL) && !defined(WIN32)
g_test_add_func("/char/serial", char_serial_test);
+#endif
+#if defined(HAVE_CHARDEV_PARALLEL) && !defined(WIN32)
+ g_test_add_func("/char/parallel", char_parallel_test);
#endif
g_test_add_func("/char/hotswap", char_hotswap_test);
g_test_add_func("/char/websocket", char_websock_test);
diff --git a/chardev/meson.build b/chardev/meson.build
index c80337d15f..b39028b7b0 100644
--- a/chardev/meson.build
+++ b/chardev/meson.build
@@ -21,11 +21,9 @@ if host_os == 'windows'
else
chardev_ss.add(files(
'char-fd.c',
+ 'char-parallel.c',
'char-pty.c',
), util)
- if host_os in ['linux', 'gnu/kfreebsd', 'freebsd', 'dragonfly']
- chardev_ss.add(files('char-parallel.c'))
- endif
endif
chardev_ss = chardev_ss.apply({})
--
2.43.0
Markus Armbruster <armbru@redhat.com> writes:
> The __linux__ version of qemu_chr_open_pp_fd() tries to claim the
> parport device with a PPCLAIM ioctl(). On success, it stores the file
> descriptor in the chardev object, and returns success. On failure, it
> closes the file descriptor, and returns failure.
>
> chardev_new() then passes the Chardev to object_unref(). This duly
> calls char_parallel_finalize(), which closes the file descriptor
> stored in the chardev object. Since qemu_chr_open_pp_fd() didn't
> store it, it's still zero, so this closes standard input. Ooopsie.
>
> To demonstate, add a unit test. With the bug above unfixed, running
> this test closes standard input. char_hotswap_test() happens to run
> next. It opens a socket, duly gets file descriptor 0, and since it
> tests for success with > 0 instead of >= 0, it fails.
>
> The test needs to be conditional exactly like the chardev it tests.
> Since the condition is rather complicated, steal the solution from the
> serial chardev: define HAVE_CHARDEV_PARALLEL in qemu/osdep.h. This
> also permits simplifying chardev/meson.build a bit.
>
> The bug fix is easy enough: store the file descriptor, and leave
> closing it to char_parallel_finalize().
>
> Signed-off-by: Markus Armbruster <armbru@redhat.com>
[...]
> diff --git a/chardev/char-parallel.c b/chardev/char-parallel.c
> index a5164f975a..78697d7522 100644
> --- a/chardev/char-parallel.c
> +++ b/chardev/char-parallel.c
> @@ -164,13 +164,13 @@ static void qemu_chr_open_pp_fd(Chardev *chr,
> {
> ParallelChardev *drv = PARALLEL_CHARDEV(chr);
>
> + drv->fd = fd;
> +
> if (ioctl(fd, PPCLAIM) < 0) {
> error_setg_errno(errp, errno, "not a parallel port");
> - close(fd);
> return;
> }
>
> - drv->fd = fd;
> drv->mode = IEEE1284_MODE_COMPAT;
> }
> #endif /* __linux__ */
> @@ -238,6 +238,7 @@ static void qemu_chr_open_pp_fd(Chardev *chr,
> }
> #endif
>
> +#ifdef HAVE_CHARDEV_PARALLEL
> static void qmp_chardev_open_parallel(Chardev *chr,
> ChardevBackend *backend,
> bool *be_opened,
> @@ -306,3 +307,5 @@ static void register_types(void)
> }
>
> type_init(register_types);
> +
> +#endif /* HAVE_CHARDEV_PARALLEL */
> diff --git a/tests/unit/test-char.c b/tests/unit/test-char.c
> index 649fdf64e1..76946e6f90 100644
> --- a/tests/unit/test-char.c
> +++ b/tests/unit/test-char.c
> @@ -1203,6 +1203,24 @@ static void char_serial_test(void)
> }
> #endif
>
> +#if defined(HAVE_CHARDEV_PARALLEL) && !defined(WIN32)
> +static void char_parallel_test(void)
> +{
> + QemuOpts *opts;
> + Chardev *chr;
> +
> + opts = qemu_opts_create(qemu_find_opts("chardev"), "parallel-id",
> + 1, &error_abort);
> + qemu_opt_set(opts, "backend", "parallel", &error_abort);
> + qemu_opt_set(opts, "path", "/dev/null", &error_abort);
> +
> + chr = qemu_chr_new_from_opts(opts, NULL, NULL);
> + g_assert_null(chr);
This is wrong.
On a Linux host, qemu_chr_new_from_opts() fails, because
qemu_chr_open_pp_fd()'s attempt to PPCLAIM fails.
On a BSD host, it succeeds.
Proposed fixup appended. Marc-André, is respinning the PR with the
fixup okay, or would you prefer a v2?
> +
> + qemu_opts_del(opts);
> +}
> +#endif
> +
> #ifndef _WIN32
> static void char_file_fifo_test(void)
> {
> @@ -1544,6 +1562,9 @@ int main(int argc, char **argv)
> g_test_add_func("/char/udp", char_udp_test);
> #if defined(HAVE_CHARDEV_SERIAL) && !defined(WIN32)
> g_test_add_func("/char/serial", char_serial_test);
> +#endif
> +#if defined(HAVE_CHARDEV_PARALLEL) && !defined(WIN32)
> + g_test_add_func("/char/parallel", char_parallel_test);
> #endif
> g_test_add_func("/char/hotswap", char_hotswap_test);
> g_test_add_func("/char/websocket", char_websock_test);
[...]
diff --git a/tests/unit/test-char.c b/tests/unit/test-char.c
index e3b783c06b..f273ce5226 100644
--- a/tests/unit/test-char.c
+++ b/tests/unit/test-char.c
@@ -1215,7 +1215,13 @@ static void char_parallel_test(void)
qemu_opt_set(opts, "path", "/dev/null", &error_abort);
chr = qemu_chr_new_from_opts(opts, NULL, NULL);
+#ifdef __linux__
+ /* fails to PPCLAIM, see qemu_chr_open_pp_fd() */
g_assert_null(chr);
+#else
+ g_assert_nonnull(chr);
+ object_unparent(OBJECT(chr));
+#endif
qemu_opts_del(opts);
}
Hi
On Tue, Feb 13, 2024 at 5:58 PM Markus Armbruster <armbru@redhat.com> wrote:
>
> Markus Armbruster <armbru@redhat.com> writes:
>
> > The __linux__ version of qemu_chr_open_pp_fd() tries to claim the
> > parport device with a PPCLAIM ioctl(). On success, it stores the file
> > descriptor in the chardev object, and returns success. On failure, it
> > closes the file descriptor, and returns failure.
> >
> > chardev_new() then passes the Chardev to object_unref(). This duly
> > calls char_parallel_finalize(), which closes the file descriptor
> > stored in the chardev object. Since qemu_chr_open_pp_fd() didn't
> > store it, it's still zero, so this closes standard input. Ooopsie.
> >
> > To demonstate, add a unit test. With the bug above unfixed, running
> > this test closes standard input. char_hotswap_test() happens to run
> > next. It opens a socket, duly gets file descriptor 0, and since it
> > tests for success with > 0 instead of >= 0, it fails.
> >
> > The test needs to be conditional exactly like the chardev it tests.
> > Since the condition is rather complicated, steal the solution from the
> > serial chardev: define HAVE_CHARDEV_PARALLEL in qemu/osdep.h. This
> > also permits simplifying chardev/meson.build a bit.
> >
> > The bug fix is easy enough: store the file descriptor, and leave
> > closing it to char_parallel_finalize().
> >
> > Signed-off-by: Markus Armbruster <armbru@redhat.com>
>
> [...]
>
> > diff --git a/chardev/char-parallel.c b/chardev/char-parallel.c
> > index a5164f975a..78697d7522 100644
> > --- a/chardev/char-parallel.c
> > +++ b/chardev/char-parallel.c
> > @@ -164,13 +164,13 @@ static void qemu_chr_open_pp_fd(Chardev *chr,
> > {
> > ParallelChardev *drv = PARALLEL_CHARDEV(chr);
> >
> > + drv->fd = fd;
> > +
> > if (ioctl(fd, PPCLAIM) < 0) {
> > error_setg_errno(errp, errno, "not a parallel port");
> > - close(fd);
> > return;
> > }
> >
> > - drv->fd = fd;
> > drv->mode = IEEE1284_MODE_COMPAT;
> > }
> > #endif /* __linux__ */
> > @@ -238,6 +238,7 @@ static void qemu_chr_open_pp_fd(Chardev *chr,
> > }
> > #endif
> >
> > +#ifdef HAVE_CHARDEV_PARALLEL
> > static void qmp_chardev_open_parallel(Chardev *chr,
> > ChardevBackend *backend,
> > bool *be_opened,
> > @@ -306,3 +307,5 @@ static void register_types(void)
> > }
> >
> > type_init(register_types);
> > +
> > +#endif /* HAVE_CHARDEV_PARALLEL */
> > diff --git a/tests/unit/test-char.c b/tests/unit/test-char.c
> > index 649fdf64e1..76946e6f90 100644
> > --- a/tests/unit/test-char.c
> > +++ b/tests/unit/test-char.c
> > @@ -1203,6 +1203,24 @@ static void char_serial_test(void)
> > }
> > #endif
> >
> > +#if defined(HAVE_CHARDEV_PARALLEL) && !defined(WIN32)
> > +static void char_parallel_test(void)
> > +{
> > + QemuOpts *opts;
> > + Chardev *chr;
> > +
> > + opts = qemu_opts_create(qemu_find_opts("chardev"), "parallel-id",
> > + 1, &error_abort);
> > + qemu_opt_set(opts, "backend", "parallel", &error_abort);
> > + qemu_opt_set(opts, "path", "/dev/null", &error_abort);
> > +
> > + chr = qemu_chr_new_from_opts(opts, NULL, NULL);
> > + g_assert_null(chr);
>
> This is wrong.
>
> On a Linux host, qemu_chr_new_from_opts() fails, because
> qemu_chr_open_pp_fd()'s attempt to PPCLAIM fails.
>
> On a BSD host, it succeeds.
>
> Proposed fixup appended. Marc-André, is respinning the PR with the
> fixup okay, or would you prefer a v2?
I am okay with a new PR.
thanks
>
> > +
> > + qemu_opts_del(opts);
> > +}
> > +#endif
> > +
> > #ifndef _WIN32
> > static void char_file_fifo_test(void)
> > {
> > @@ -1544,6 +1562,9 @@ int main(int argc, char **argv)
> > g_test_add_func("/char/udp", char_udp_test);
> > #if defined(HAVE_CHARDEV_SERIAL) && !defined(WIN32)
> > g_test_add_func("/char/serial", char_serial_test);
> > +#endif
> > +#if defined(HAVE_CHARDEV_PARALLEL) && !defined(WIN32)
> > + g_test_add_func("/char/parallel", char_parallel_test);
> > #endif
> > g_test_add_func("/char/hotswap", char_hotswap_test);
> > g_test_add_func("/char/websocket", char_websock_test);
>
> [...]
>
> diff --git a/tests/unit/test-char.c b/tests/unit/test-char.c
> index e3b783c06b..f273ce5226 100644
> --- a/tests/unit/test-char.c
> +++ b/tests/unit/test-char.c
> @@ -1215,7 +1215,13 @@ static void char_parallel_test(void)
> qemu_opt_set(opts, "path", "/dev/null", &error_abort);
>
> chr = qemu_chr_new_from_opts(opts, NULL, NULL);
> +#ifdef __linux__
> + /* fails to PPCLAIM, see qemu_chr_open_pp_fd() */
> g_assert_null(chr);
> +#else
> + g_assert_nonnull(chr);
> + object_unparent(OBJECT(chr));
> +#endif
>
> qemu_opts_del(opts);
> }
>
On Sat, Feb 03, 2024 at 09:02:25AM +0100, Markus Armbruster wrote: > The __linux__ version of qemu_chr_open_pp_fd() tries to claim the > parport device with a PPCLAIM ioctl(). On success, it stores the file > descriptor in the chardev object, and returns success. On failure, it > closes the file descriptor, and returns failure. > > chardev_new() then passes the Chardev to object_unref(). This duly > calls char_parallel_finalize(), which closes the file descriptor > stored in the chardev object. Since qemu_chr_open_pp_fd() didn't > store it, it's still zero, so this closes standard input. Ooopsie. > > To demonstate, add a unit test. With the bug above unfixed, running > this test closes standard input. char_hotswap_test() happens to run > next. It opens a socket, duly gets file descriptor 0, and since it > tests for success with > 0 instead of >= 0, it fails. Two bugs for the price of one! > > The test needs to be conditional exactly like the chardev it tests. > Since the condition is rather complicated, steal the solution from the > serial chardev: define HAVE_CHARDEV_PARALLEL in qemu/osdep.h. This > also permits simplifying chardev/meson.build a bit. > > The bug fix is easy enough: store the file descriptor, and leave > closing it to char_parallel_finalize(). > > Signed-off-by: Markus Armbruster <armbru@redhat.com> > --- > +++ b/include/qemu/osdep.h > @@ -508,11 +508,18 @@ void qemu_anon_ram_free(void *ptr, size_t size); > > #ifdef _WIN32 > #define HAVE_CHARDEV_SERIAL 1 > -#elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ > +#define HAVE_CHARDEV_PARALLEL 1 > +#else > +#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ > || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \ > || defined(__GLIBC__) || defined(__APPLE__) > #define HAVE_CHARDEV_SERIAL 1 > #endif > +#if defined(__linux__) || defined(__FreeBSD__) \ > + || defined(__FreeBSD_kernel__) || defined(__DragonFly__) > +#define HAVE_CHARDEV_PARALLEL 1 > +#endif > +#endif Not for this patch, but I've grown to like a preprocessor style I've seen in other projects to make it easier to read nested #if: #ifdef _WIN32 # define HAVE_CHARDEV_SERIAL 1 # define HAVE_CHARDEV_PARALLEL 1 #else # if defined(__linux__) ... defined(__APPLE__) # define HAVE_CHARDEV_SERIAL 1 # endif # if defined(__linux__) ... defined(__DragonFly__) # define HAVE_CHARDEV_PARALLEL 1 # endif #endif > +++ b/chardev/meson.build > @@ -21,11 +21,9 @@ if host_os == 'windows' > else > chardev_ss.add(files( > 'char-fd.c', > + 'char-parallel.c', > 'char-pty.c', Indentation looks off. Otherwise, Reviewed-by: Eric Blake <eblake@redhat.com> -- Eric Blake, Principal Software Engineer Red Hat, Inc. Virtualization: qemu.org | libguestfs.org _______________________________________________ Devel mailing list -- devel@lists.libvirt.org To unsubscribe send an email to devel-leave@lists.libvirt.org
Eric Blake <eblake@redhat.com> writes: > On Sat, Feb 03, 2024 at 09:02:25AM +0100, Markus Armbruster wrote: >> The __linux__ version of qemu_chr_open_pp_fd() tries to claim the >> parport device with a PPCLAIM ioctl(). On success, it stores the file >> descriptor in the chardev object, and returns success. On failure, it >> closes the file descriptor, and returns failure. >> >> chardev_new() then passes the Chardev to object_unref(). This duly >> calls char_parallel_finalize(), which closes the file descriptor >> stored in the chardev object. Since qemu_chr_open_pp_fd() didn't >> store it, it's still zero, so this closes standard input. Ooopsie. >> >> To demonstate, add a unit test. With the bug above unfixed, running >> this test closes standard input. char_hotswap_test() happens to run >> next. It opens a socket, duly gets file descriptor 0, and since it >> tests for success with > 0 instead of >= 0, it fails. > > Two bugs for the price of one! > >> >> The test needs to be conditional exactly like the chardev it tests. >> Since the condition is rather complicated, steal the solution from the >> serial chardev: define HAVE_CHARDEV_PARALLEL in qemu/osdep.h. This >> also permits simplifying chardev/meson.build a bit. >> >> The bug fix is easy enough: store the file descriptor, and leave >> closing it to char_parallel_finalize(). >> >> Signed-off-by: Markus Armbruster <armbru@redhat.com> >> --- > >> +++ b/include/qemu/osdep.h >> @@ -508,11 +508,18 @@ void qemu_anon_ram_free(void *ptr, size_t size); >> >> #ifdef _WIN32 >> #define HAVE_CHARDEV_SERIAL 1 >> -#elif defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ >> +#define HAVE_CHARDEV_PARALLEL 1 >> +#else >> +#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ >> || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \ >> || defined(__GLIBC__) || defined(__APPLE__) >> #define HAVE_CHARDEV_SERIAL 1 >> #endif >> +#if defined(__linux__) || defined(__FreeBSD__) \ >> + || defined(__FreeBSD_kernel__) || defined(__DragonFly__) >> +#define HAVE_CHARDEV_PARALLEL 1 >> +#endif >> +#endif > > Not for this patch, but I've grown to like a preprocessor style I've > seen in other projects to make it easier to read nested #if: > > #ifdef _WIN32 > # define HAVE_CHARDEV_SERIAL 1 > # define HAVE_CHARDEV_PARALLEL 1 > #else > # if defined(__linux__) ... defined(__APPLE__) > # define HAVE_CHARDEV_SERIAL 1 > # endif > # if defined(__linux__) ... defined(__DragonFly__) > # define HAVE_CHARDEV_PARALLEL 1 > # endif > #endif I like this style, too. >> +++ b/chardev/meson.build >> @@ -21,11 +21,9 @@ if host_os == 'windows' >> else >> chardev_ss.add(files( >> 'char-fd.c', >> + 'char-parallel.c', >> 'char-pty.c', > > Indentation looks off. Otherwise, Will fix. > Reviewed-by: Eric Blake <eblake@redhat.com> Thanks!
© 2016 - 2026 Red Hat, Inc.