[spice-streaming-agent,v2,3/5] Starts/stops the agent based on VT status

Submitted by Frediano Ziglio on May 21, 2018, 10:45 a.m.

Details

Message ID 20180521104531.7241-3-fziglio@redhat.com
State New
Headers show
Series "Series without cover letter" ( rev: 5 4 3 2 1 ) in Spice

Not browsing as part of any series.

Commit Message

Frediano Ziglio May 21, 2018, 10:45 a.m.
Check if the current display server is active or not and stream only if
active.
This will allow to support multiple display servers running.
When multiple display servers are running only one have the GPU
associated and is writing to the screen.
Stop capturing and release resources when the display server is not
active and vice versa.

Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
---
 src/Makefile.am               |  2 ++
 src/eventfd.cpp               | 38 ++++++++++++++++++++
 src/eventfd.hpp               | 34 ++++++++++++++++++
 src/spice-streaming-agent.cpp | 65 +++++++++++++++++++++++++++++++----
 4 files changed, 132 insertions(+), 7 deletions(-)
 create mode 100644 src/eventfd.cpp
 create mode 100644 src/eventfd.hpp

Patch hide | download patch | download mbox

diff --git a/src/Makefile.am b/src/Makefile.am
index 276478f..ffc52b2 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -62,4 +62,6 @@  spice_streaming_agent_SOURCES = \
 	jpeg.hpp \
 	stream-port.cpp \
 	stream-port.hpp \
+	eventfd.cpp \
+	eventfd.hpp \
 	$(NULL)
diff --git a/src/eventfd.cpp b/src/eventfd.cpp
new file mode 100644
index 0000000..e3c1e72
--- /dev/null
+++ b/src/eventfd.cpp
@@ -0,0 +1,38 @@ 
+/* Linux event file descriptors
+ *
+ * \copyright
+ * Copyright 2018 Red Hat Inc. All rights reserved.
+ */
+#include <config.h>
+#include "eventfd.hpp"
+
+#include <stdexcept>
+#include <sys/eventfd.h>
+
+template<typename T>
+inline void ignore_result(T /* unused result */) {}
+
+namespace spice {
+namespace streaming_agent {
+
+EventFD::EventFD():
+    fd(eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK))
+{
+    if (fd < 0) {
+        throw std::runtime_error("Failed to create eventfd");
+    }
+}
+
+void EventFD::signal()
+{
+    uint64_t n = 1;
+    ignore_result(write(fd, &n, sizeof(n)));
+}
+
+void EventFD::ack()
+{
+    uint64_t n;
+    ignore_result(read(fd, &n, sizeof(n)));
+}
+
+}} // namespace spice::streaming_agent
diff --git a/src/eventfd.hpp b/src/eventfd.hpp
new file mode 100644
index 0000000..48a175c
--- /dev/null
+++ b/src/eventfd.hpp
@@ -0,0 +1,34 @@ 
+/* Linux event file descriptors
+ *
+ * \copyright
+ * Copyright 2018 Red Hat Inc. All rights reserved.
+ */
+#ifndef SPICE_STREAMING_AGENT_EVENTFD_HPP
+#define SPICE_STREAMING_AGENT_EVENTFD_HPP
+
+#include <unistd.h>
+
+namespace spice {
+namespace streaming_agent {
+
+class EventFD {
+public:
+    EventFD();
+    ~EventFD() { close(fd); }
+    void signal();
+    void ack();
+    /**
+     * Returns underlying file descriptor.
+     * Do not close, should just be used for polling.
+     */
+    int raw_fd() const { return fd; }
+private:
+    EventFD(const EventFD&) = delete;
+    void operator=(const EventFD&) = delete;
+
+    int fd;
+};
+
+}} // namespace spice::streaming_agent
+
+#endif // SPICE_STREAMING_AGENT_EVENTFD_HPP
diff --git a/src/spice-streaming-agent.cpp b/src/spice-streaming-agent.cpp
index 6388bb2..240b9c7 100644
--- a/src/spice-streaming-agent.cpp
+++ b/src/spice-streaming-agent.cpp
@@ -10,6 +10,7 @@ 
 #include "stream-port.hpp"
 #include "error.hpp"
 #include "xorg-utils.hpp"
+#include "eventfd.hpp"
 
 #include <spice/stream-device.h>
 #include <spice/enums.h>
@@ -36,6 +37,7 @@ 
 #include <thread>
 #include <vector>
 #include <string>
+#include <atomic>
 #include <functional>
 #include <X11/Xlib.h>
 #include <X11/extensions/Xfixes.h>
@@ -61,12 +63,21 @@  static bool quit_requested = false;
 static bool log_binary = false;
 static bool log_frames = false;
 static std::set<SpiceVideoCodecType> client_codecs;
+static EventFD update_fd;
+static const char vt_property_name[] = "XFree86_has_VT";
+static const char *stream_port_name = "/dev/virtio-ports/org.spice-space.stream.0";
+static Atom vt_property = None;
+static std::atomic_bool vt_active;
 
-static bool have_something_to_read(StreamPort &stream_port, bool blocking)
+static bool have_something_to_read(StreamPort &stream_port, bool blocking, int &fd)
 {
-    struct pollfd pollfd = {stream_port.fd, POLLIN, 0};
+    struct pollfd pollfds[2] = {
+        { stream_port.fd, POLLIN, 0 },
+        { update_fd.raw_fd(), POLLIN, 0 },
+    };
 
-    if (poll(&pollfd, 1, blocking ? -1 : 0) < 0) {
+    fd = -1;
+    if (poll(pollfds, 2, blocking ? -1 : 0) < 0) {
         if (errno == EINTR) {
             // report nothing to read, next iteration of the enclosing loop will retry
             return false;
@@ -75,7 +86,13 @@  static bool have_something_to_read(StreamPort &stream_port, bool blocking)
         throw IOError("poll failed on the device", errno);
     }
 
-    if (pollfd.revents & POLLIN) {
+    if (pollfds[1].revents & POLLIN) {
+        fd = update_fd.raw_fd();
+        return true;
+    }
+
+    if (pollfds[0].revents & POLLIN) {
+        fd = stream_port.fd;
         return true;
     }
 
@@ -173,8 +190,18 @@  static void read_command_from_device(StreamPort &stream_port)
 static void read_command(StreamPort &stream_port, bool blocking)
 {
     while (!quit_requested) {
-        if (have_something_to_read(stream_port, blocking)) {
-            read_command_from_device(stream_port);
+        int fd;
+        if (have_something_to_read(stream_port, blocking, fd)) {
+            if (fd == stream_port.fd) {
+                read_command_from_device(stream_port);
+            } else if (fd == update_fd.raw_fd()) {
+                update_fd.ack();
+                bool vt_active = ::vt_active.load(std::memory_order_relaxed);
+                if (!vt_active) {
+                    throw std::runtime_error("VT disabled");
+                }
+                continue;
+            }
             break;
         }
 
@@ -305,10 +332,22 @@  send_cursor(StreamPort &stream_port, unsigned width, unsigned height, int hotspo
 static void cursor_changes(StreamPort *stream_port, Display *display, int event_base)
 {
     unsigned long last_serial = 0;
+    Window rootwindow = DefaultRootWindow(display);
 
     while (1) {
         XEvent event;
         XNextEvent(display, &event);
+
+        if (event.type == PropertyNotify) {
+            if (vt_property == None || event.xproperty.atom != vt_property)
+                continue;
+            // update vt property, activate screen read if needed
+            vt_active.store(get_win_prop_int(display, rootwindow, vt_property) != 0, std::memory_order_relaxed);
+            std::atomic_thread_fence(std::memory_order_acquire);
+            update_fd.signal();
+            continue;
+        }
+
         if (event.type != event_base + 1) {
             continue;
         }
@@ -416,7 +455,6 @@  do_capture(StreamPort &stream_port, FILE *f_log)
 
 int main(int argc, char* argv[])
 {
-    const char *stream_port_name = "/dev/virtio-ports/org.spice-space.stream.0";
     int opt;
     const char *log_filename = NULL;
     int logmask = LOG_UPTO(LOG_WARNING);
@@ -522,6 +560,19 @@  int main(int argc, char* argv[])
     Window rootwindow = DefaultRootWindow(display);
     XFixesSelectCursorInput(display, rootwindow, XFixesDisplayCursorNotifyMask);
 
+    vt_property = XInternAtom(display, vt_property_name, True);
+    if (vt_property == None) {
+        syslog(LOG_ERR, "VT property not found\n");
+        return EXIT_FAILURE;
+    }
+    XSelectInput(display, rootwindow, PropertyChangeMask);
+
+    vt_active.store(get_win_prop_int(display, rootwindow, vt_property) != 0, std::memory_order_relaxed);
+    if (!::vt_active.load(std::memory_order_relaxed)) {
+        syslog(LOG_ERR, "VT is disabled");
+        return EXIT_FAILURE;
+    }
+
     int ret = EXIT_SUCCESS;
 
     try {

Comments

On Mon, May 21, 2018 at 11:45:29AM +0100, Frediano Ziglio wrote:
> Check if the current display server is active or not and stream only if
> active.
> This will allow to support multiple display servers running.
> When multiple display servers are running only one have the GPU
> associated and is writing to the screen.
> Stop capturing and release resources when the display server is not
> active and vice versa.

Is your goal to achieve something similar to
https://cgit.freedesktop.org/spice/linux/vd_agent/commit/?id=93003ec27d98cb47b8ea0435179a66c14ff610ce
?

Christophe

> 
> Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
> ---
>  src/Makefile.am               |  2 ++
>  src/eventfd.cpp               | 38 ++++++++++++++++++++
>  src/eventfd.hpp               | 34 ++++++++++++++++++
>  src/spice-streaming-agent.cpp | 65 +++++++++++++++++++++++++++++++----
>  4 files changed, 132 insertions(+), 7 deletions(-)
>  create mode 100644 src/eventfd.cpp
>  create mode 100644 src/eventfd.hpp
> 
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 276478f..ffc52b2 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -62,4 +62,6 @@ spice_streaming_agent_SOURCES = \
>  	jpeg.hpp \
>  	stream-port.cpp \
>  	stream-port.hpp \
> +	eventfd.cpp \
> +	eventfd.hpp \
>  	$(NULL)
> diff --git a/src/eventfd.cpp b/src/eventfd.cpp
> new file mode 100644
> index 0000000..e3c1e72
> --- /dev/null
> +++ b/src/eventfd.cpp
> @@ -0,0 +1,38 @@
> +/* Linux event file descriptors
> + *
> + * \copyright
> + * Copyright 2018 Red Hat Inc. All rights reserved.
> + */
> +#include <config.h>
> +#include "eventfd.hpp"
> +
> +#include <stdexcept>
> +#include <sys/eventfd.h>
> +
> +template<typename T>
> +inline void ignore_result(T /* unused result */) {}
> +
> +namespace spice {
> +namespace streaming_agent {
> +
> +EventFD::EventFD():
> +    fd(eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK))
> +{
> +    if (fd < 0) {
> +        throw std::runtime_error("Failed to create eventfd");
> +    }
> +}
> +
> +void EventFD::signal()
> +{
> +    uint64_t n = 1;
> +    ignore_result(write(fd, &n, sizeof(n)));
> +}
> +
> +void EventFD::ack()
> +{
> +    uint64_t n;
> +    ignore_result(read(fd, &n, sizeof(n)));
> +}
> +
> +}} // namespace spice::streaming_agent
> diff --git a/src/eventfd.hpp b/src/eventfd.hpp
> new file mode 100644
> index 0000000..48a175c
> --- /dev/null
> +++ b/src/eventfd.hpp
> @@ -0,0 +1,34 @@
> +/* Linux event file descriptors
> + *
> + * \copyright
> + * Copyright 2018 Red Hat Inc. All rights reserved.
> + */
> +#ifndef SPICE_STREAMING_AGENT_EVENTFD_HPP
> +#define SPICE_STREAMING_AGENT_EVENTFD_HPP
> +
> +#include <unistd.h>
> +
> +namespace spice {
> +namespace streaming_agent {
> +
> +class EventFD {
> +public:
> +    EventFD();
> +    ~EventFD() { close(fd); }
> +    void signal();
> +    void ack();
> +    /**
> +     * Returns underlying file descriptor.
> +     * Do not close, should just be used for polling.
> +     */
> +    int raw_fd() const { return fd; }
> +private:
> +    EventFD(const EventFD&) = delete;
> +    void operator=(const EventFD&) = delete;
> +
> +    int fd;
> +};
> +
> +}} // namespace spice::streaming_agent
> +
> +#endif // SPICE_STREAMING_AGENT_EVENTFD_HPP
> diff --git a/src/spice-streaming-agent.cpp b/src/spice-streaming-agent.cpp
> index 6388bb2..240b9c7 100644
> --- a/src/spice-streaming-agent.cpp
> +++ b/src/spice-streaming-agent.cpp
> @@ -10,6 +10,7 @@
>  #include "stream-port.hpp"
>  #include "error.hpp"
>  #include "xorg-utils.hpp"
> +#include "eventfd.hpp"
>  
>  #include <spice/stream-device.h>
>  #include <spice/enums.h>
> @@ -36,6 +37,7 @@
>  #include <thread>
>  #include <vector>
>  #include <string>
> +#include <atomic>
>  #include <functional>
>  #include <X11/Xlib.h>
>  #include <X11/extensions/Xfixes.h>
> @@ -61,12 +63,21 @@ static bool quit_requested = false;
>  static bool log_binary = false;
>  static bool log_frames = false;
>  static std::set<SpiceVideoCodecType> client_codecs;
> +static EventFD update_fd;
> +static const char vt_property_name[] = "XFree86_has_VT";
> +static const char *stream_port_name = "/dev/virtio-ports/org.spice-space.stream.0";
> +static Atom vt_property = None;
> +static std::atomic_bool vt_active;
>  
> -static bool have_something_to_read(StreamPort &stream_port, bool blocking)
> +static bool have_something_to_read(StreamPort &stream_port, bool blocking, int &fd)
>  {
> -    struct pollfd pollfd = {stream_port.fd, POLLIN, 0};
> +    struct pollfd pollfds[2] = {
> +        { stream_port.fd, POLLIN, 0 },
> +        { update_fd.raw_fd(), POLLIN, 0 },
> +    };
>  
> -    if (poll(&pollfd, 1, blocking ? -1 : 0) < 0) {
> +    fd = -1;
> +    if (poll(pollfds, 2, blocking ? -1 : 0) < 0) {
>          if (errno == EINTR) {
>              // report nothing to read, next iteration of the enclosing loop will retry
>              return false;
> @@ -75,7 +86,13 @@ static bool have_something_to_read(StreamPort &stream_port, bool blocking)
>          throw IOError("poll failed on the device", errno);
>      }
>  
> -    if (pollfd.revents & POLLIN) {
> +    if (pollfds[1].revents & POLLIN) {
> +        fd = update_fd.raw_fd();
> +        return true;
> +    }
> +
> +    if (pollfds[0].revents & POLLIN) {
> +        fd = stream_port.fd;
>          return true;
>      }
>  
> @@ -173,8 +190,18 @@ static void read_command_from_device(StreamPort &stream_port)
>  static void read_command(StreamPort &stream_port, bool blocking)
>  {
>      while (!quit_requested) {
> -        if (have_something_to_read(stream_port, blocking)) {
> -            read_command_from_device(stream_port);
> +        int fd;
> +        if (have_something_to_read(stream_port, blocking, fd)) {
> +            if (fd == stream_port.fd) {
> +                read_command_from_device(stream_port);
> +            } else if (fd == update_fd.raw_fd()) {
> +                update_fd.ack();
> +                bool vt_active = ::vt_active.load(std::memory_order_relaxed);
> +                if (!vt_active) {
> +                    throw std::runtime_error("VT disabled");
> +                }
> +                continue;
> +            }
>              break;
>          }
>  
> @@ -305,10 +332,22 @@ send_cursor(StreamPort &stream_port, unsigned width, unsigned height, int hotspo
>  static void cursor_changes(StreamPort *stream_port, Display *display, int event_base)
>  {
>      unsigned long last_serial = 0;
> +    Window rootwindow = DefaultRootWindow(display);
>  
>      while (1) {
>          XEvent event;
>          XNextEvent(display, &event);
> +
> +        if (event.type == PropertyNotify) {
> +            if (vt_property == None || event.xproperty.atom != vt_property)
> +                continue;
> +            // update vt property, activate screen read if needed
> +            vt_active.store(get_win_prop_int(display, rootwindow, vt_property) != 0, std::memory_order_relaxed);
> +            std::atomic_thread_fence(std::memory_order_acquire);
> +            update_fd.signal();
> +            continue;
> +        }
> +
>          if (event.type != event_base + 1) {
>              continue;
>          }
> @@ -416,7 +455,6 @@ do_capture(StreamPort &stream_port, FILE *f_log)
>  
>  int main(int argc, char* argv[])
>  {
> -    const char *stream_port_name = "/dev/virtio-ports/org.spice-space.stream.0";
>      int opt;
>      const char *log_filename = NULL;
>      int logmask = LOG_UPTO(LOG_WARNING);
> @@ -522,6 +560,19 @@ int main(int argc, char* argv[])
>      Window rootwindow = DefaultRootWindow(display);
>      XFixesSelectCursorInput(display, rootwindow, XFixesDisplayCursorNotifyMask);
>  
> +    vt_property = XInternAtom(display, vt_property_name, True);
> +    if (vt_property == None) {
> +        syslog(LOG_ERR, "VT property not found\n");
> +        return EXIT_FAILURE;
> +    }
> +    XSelectInput(display, rootwindow, PropertyChangeMask);
> +
> +    vt_active.store(get_win_prop_int(display, rootwindow, vt_property) != 0, std::memory_order_relaxed);
> +    if (!::vt_active.load(std::memory_order_relaxed)) {
> +        syslog(LOG_ERR, "VT is disabled");
> +        return EXIT_FAILURE;
> +    }
> +
>      int ret = EXIT_SUCCESS;
>  
>      try {
> -- 
> 2.17.0
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/spice-devel
> 
> On Mon, May 21, 2018 at 11:45:29AM +0100, Frediano Ziglio wrote:
> > Check if the current display server is active or not and stream only if
> > active.
> > This will allow to support multiple display servers running.
> > When multiple display servers are running only one have the GPU
> > associated and is writing to the screen.
> > Stop capturing and release resources when the display server is not
> > active and vice versa.
> 
> Is your goal to achieve something similar to
> https://cgit.freedesktop.org/spice/linux/vd_agent/commit/?id=93003ec27d98cb47b8ea0435179a66c14ff610ce
> ?
> 
> Christophe
> 

I basically need to follow the active session on seat0 (the physical one,
like the physical display).
Usually GPU is attached to a single session and by the way we don't want
to stream all possible sessions active but just the one that is active.

> > 
> > Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
> > ---
> >  src/Makefile.am               |  2 ++
> >  src/eventfd.cpp               | 38 ++++++++++++++++++++
> >  src/eventfd.hpp               | 34 ++++++++++++++++++
> >  src/spice-streaming-agent.cpp | 65 +++++++++++++++++++++++++++++++----
> >  4 files changed, 132 insertions(+), 7 deletions(-)
> >  create mode 100644 src/eventfd.cpp
> >  create mode 100644 src/eventfd.hpp
> > 
> > diff --git a/src/Makefile.am b/src/Makefile.am
> > index 276478f..ffc52b2 100644
> > --- a/src/Makefile.am
> > +++ b/src/Makefile.am
> > @@ -62,4 +62,6 @@ spice_streaming_agent_SOURCES = \
> >  	jpeg.hpp \
> >  	stream-port.cpp \
> >  	stream-port.hpp \
> > +	eventfd.cpp \
> > +	eventfd.hpp \
> >  	$(NULL)
> > diff --git a/src/eventfd.cpp b/src/eventfd.cpp
> > new file mode 100644
> > index 0000000..e3c1e72
> > --- /dev/null
> > +++ b/src/eventfd.cpp
> > @@ -0,0 +1,38 @@
> > +/* Linux event file descriptors
> > + *
> > + * \copyright
> > + * Copyright 2018 Red Hat Inc. All rights reserved.
> > + */
> > +#include <config.h>
> > +#include "eventfd.hpp"
> > +
> > +#include <stdexcept>
> > +#include <sys/eventfd.h>
> > +
> > +template<typename T>
> > +inline void ignore_result(T /* unused result */) {}
> > +
> > +namespace spice {
> > +namespace streaming_agent {
> > +
> > +EventFD::EventFD():
> > +    fd(eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK))
> > +{
> > +    if (fd < 0) {
> > +        throw std::runtime_error("Failed to create eventfd");
> > +    }
> > +}
> > +
> > +void EventFD::signal()
> > +{
> > +    uint64_t n = 1;
> > +    ignore_result(write(fd, &n, sizeof(n)));
> > +}
> > +
> > +void EventFD::ack()
> > +{
> > +    uint64_t n;
> > +    ignore_result(read(fd, &n, sizeof(n)));
> > +}
> > +
> > +}} // namespace spice::streaming_agent
> > diff --git a/src/eventfd.hpp b/src/eventfd.hpp
> > new file mode 100644
> > index 0000000..48a175c
> > --- /dev/null
> > +++ b/src/eventfd.hpp
> > @@ -0,0 +1,34 @@
> > +/* Linux event file descriptors
> > + *
> > + * \copyright
> > + * Copyright 2018 Red Hat Inc. All rights reserved.
> > + */
> > +#ifndef SPICE_STREAMING_AGENT_EVENTFD_HPP
> > +#define SPICE_STREAMING_AGENT_EVENTFD_HPP
> > +
> > +#include <unistd.h>
> > +
> > +namespace spice {
> > +namespace streaming_agent {
> > +
> > +class EventFD {
> > +public:
> > +    EventFD();
> > +    ~EventFD() { close(fd); }
> > +    void signal();
> > +    void ack();
> > +    /**
> > +     * Returns underlying file descriptor.
> > +     * Do not close, should just be used for polling.
> > +     */
> > +    int raw_fd() const { return fd; }
> > +private:
> > +    EventFD(const EventFD&) = delete;
> > +    void operator=(const EventFD&) = delete;
> > +
> > +    int fd;
> > +};
> > +
> > +}} // namespace spice::streaming_agent
> > +
> > +#endif // SPICE_STREAMING_AGENT_EVENTFD_HPP
> > diff --git a/src/spice-streaming-agent.cpp b/src/spice-streaming-agent.cpp
> > index 6388bb2..240b9c7 100644
> > --- a/src/spice-streaming-agent.cpp
> > +++ b/src/spice-streaming-agent.cpp
> > @@ -10,6 +10,7 @@
> >  #include "stream-port.hpp"
> >  #include "error.hpp"
> >  #include "xorg-utils.hpp"
> > +#include "eventfd.hpp"
> >  
> >  #include <spice/stream-device.h>
> >  #include <spice/enums.h>
> > @@ -36,6 +37,7 @@
> >  #include <thread>
> >  #include <vector>
> >  #include <string>
> > +#include <atomic>
> >  #include <functional>
> >  #include <X11/Xlib.h>
> >  #include <X11/extensions/Xfixes.h>
> > @@ -61,12 +63,21 @@ static bool quit_requested = false;
> >  static bool log_binary = false;
> >  static bool log_frames = false;
> >  static std::set<SpiceVideoCodecType> client_codecs;
> > +static EventFD update_fd;
> > +static const char vt_property_name[] = "XFree86_has_VT";
> > +static const char *stream_port_name =
> > "/dev/virtio-ports/org.spice-space.stream.0";
> > +static Atom vt_property = None;
> > +static std::atomic_bool vt_active;
> >  
> > -static bool have_something_to_read(StreamPort &stream_port, bool blocking)
> > +static bool have_something_to_read(StreamPort &stream_port, bool blocking,
> > int &fd)
> >  {
> > -    struct pollfd pollfd = {stream_port.fd, POLLIN, 0};
> > +    struct pollfd pollfds[2] = {
> > +        { stream_port.fd, POLLIN, 0 },
> > +        { update_fd.raw_fd(), POLLIN, 0 },
> > +    };
> >  
> > -    if (poll(&pollfd, 1, blocking ? -1 : 0) < 0) {
> > +    fd = -1;
> > +    if (poll(pollfds, 2, blocking ? -1 : 0) < 0) {
> >          if (errno == EINTR) {
> >              // report nothing to read, next iteration of the enclosing
> >              loop will retry
> >              return false;
> > @@ -75,7 +86,13 @@ static bool have_something_to_read(StreamPort
> > &stream_port, bool blocking)
> >          throw IOError("poll failed on the device", errno);
> >      }
> >  
> > -    if (pollfd.revents & POLLIN) {
> > +    if (pollfds[1].revents & POLLIN) {
> > +        fd = update_fd.raw_fd();
> > +        return true;
> > +    }
> > +
> > +    if (pollfds[0].revents & POLLIN) {
> > +        fd = stream_port.fd;
> >          return true;
> >      }
> >  
> > @@ -173,8 +190,18 @@ static void read_command_from_device(StreamPort
> > &stream_port)
> >  static void read_command(StreamPort &stream_port, bool blocking)
> >  {
> >      while (!quit_requested) {
> > -        if (have_something_to_read(stream_port, blocking)) {
> > -            read_command_from_device(stream_port);
> > +        int fd;
> > +        if (have_something_to_read(stream_port, blocking, fd)) {
> > +            if (fd == stream_port.fd) {
> > +                read_command_from_device(stream_port);
> > +            } else if (fd == update_fd.raw_fd()) {
> > +                update_fd.ack();
> > +                bool vt_active =
> > ::vt_active.load(std::memory_order_relaxed);
> > +                if (!vt_active) {
> > +                    throw std::runtime_error("VT disabled");
> > +                }
> > +                continue;
> > +            }
> >              break;
> >          }
> >  
> > @@ -305,10 +332,22 @@ send_cursor(StreamPort &stream_port, unsigned width,
> > unsigned height, int hotspo
> >  static void cursor_changes(StreamPort *stream_port, Display *display, int
> >  event_base)
> >  {
> >      unsigned long last_serial = 0;
> > +    Window rootwindow = DefaultRootWindow(display);
> >  
> >      while (1) {
> >          XEvent event;
> >          XNextEvent(display, &event);
> > +
> > +        if (event.type == PropertyNotify) {
> > +            if (vt_property == None || event.xproperty.atom !=
> > vt_property)
> > +                continue;
> > +            // update vt property, activate screen read if needed
> > +            vt_active.store(get_win_prop_int(display, rootwindow,
> > vt_property) != 0, std::memory_order_relaxed);
> > +            std::atomic_thread_fence(std::memory_order_acquire);
> > +            update_fd.signal();
> > +            continue;
> > +        }
> > +
> >          if (event.type != event_base + 1) {
> >              continue;
> >          }
> > @@ -416,7 +455,6 @@ do_capture(StreamPort &stream_port, FILE *f_log)
> >  
> >  int main(int argc, char* argv[])
> >  {
> > -    const char *stream_port_name =
> > "/dev/virtio-ports/org.spice-space.stream.0";
> >      int opt;
> >      const char *log_filename = NULL;
> >      int logmask = LOG_UPTO(LOG_WARNING);
> > @@ -522,6 +560,19 @@ int main(int argc, char* argv[])
> >      Window rootwindow = DefaultRootWindow(display);
> >      XFixesSelectCursorInput(display, rootwindow,
> >      XFixesDisplayCursorNotifyMask);
> >  
> > +    vt_property = XInternAtom(display, vt_property_name, True);
> > +    if (vt_property == None) {
> > +        syslog(LOG_ERR, "VT property not found\n");
> > +        return EXIT_FAILURE;
> > +    }
> > +    XSelectInput(display, rootwindow, PropertyChangeMask);
> > +
> > +    vt_active.store(get_win_prop_int(display, rootwindow, vt_property) !=
> > 0, std::memory_order_relaxed);
> > +    if (!::vt_active.load(std::memory_order_relaxed)) {
> > +        syslog(LOG_ERR, "VT is disabled");
> > +        return EXIT_FAILURE;
> > +    }
> > +
> >      int ret = EXIT_SUCCESS;
> >  
> >      try {

Frediano
On Tue, May 22, 2018 at 07:46:48AM -0400, Frediano Ziglio wrote:
> > 
> > On Mon, May 21, 2018 at 11:45:29AM +0100, Frediano Ziglio wrote:
> > > Check if the current display server is active or not and stream only if
> > > active.
> > > This will allow to support multiple display servers running.
> > > When multiple display servers are running only one have the GPU
> > > associated and is writing to the screen.
> > > Stop capturing and release resources when the display server is not
> > > active and vice versa.
> > 
> > Is your goal to achieve something similar to
> > https://cgit.freedesktop.org/spice/linux/vd_agent/commit/?id=93003ec27d98cb47b8ea0435179a66c14ff610ce
> > ?
> > 
> > Christophe
> > 
> 
> I basically need to follow the active session on seat0 (the physical one,
> like the physical display).
> Usually GPU is attached to a single session and by the way we don't want
> to stream all possible sessions active but just the one that is active.

Isn't this something that logind provides rather than the apparently X
specific code you are using in this patch? (see loginctl
list-sessions/show-session).

Christophe

> 
> > > 
> > > Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
> > > ---
> > >  src/Makefile.am               |  2 ++
> > >  src/eventfd.cpp               | 38 ++++++++++++++++++++
> > >  src/eventfd.hpp               | 34 ++++++++++++++++++
> > >  src/spice-streaming-agent.cpp | 65 +++++++++++++++++++++++++++++++----
> > >  4 files changed, 132 insertions(+), 7 deletions(-)
> > >  create mode 100644 src/eventfd.cpp
> > >  create mode 100644 src/eventfd.hpp
> > > 
> > > diff --git a/src/Makefile.am b/src/Makefile.am
> > > index 276478f..ffc52b2 100644
> > > --- a/src/Makefile.am
> > > +++ b/src/Makefile.am
> > > @@ -62,4 +62,6 @@ spice_streaming_agent_SOURCES = \
> > >  	jpeg.hpp \
> > >  	stream-port.cpp \
> > >  	stream-port.hpp \
> > > +	eventfd.cpp \
> > > +	eventfd.hpp \
> > >  	$(NULL)
> > > diff --git a/src/eventfd.cpp b/src/eventfd.cpp
> > > new file mode 100644
> > > index 0000000..e3c1e72
> > > --- /dev/null
> > > +++ b/src/eventfd.cpp
> > > @@ -0,0 +1,38 @@
> > > +/* Linux event file descriptors
> > > + *
> > > + * \copyright
> > > + * Copyright 2018 Red Hat Inc. All rights reserved.
> > > + */
> > > +#include <config.h>
> > > +#include "eventfd.hpp"
> > > +
> > > +#include <stdexcept>
> > > +#include <sys/eventfd.h>
> > > +
> > > +template<typename T>
> > > +inline void ignore_result(T /* unused result */) {}
> > > +
> > > +namespace spice {
> > > +namespace streaming_agent {
> > > +
> > > +EventFD::EventFD():
> > > +    fd(eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK))
> > > +{
> > > +    if (fd < 0) {
> > > +        throw std::runtime_error("Failed to create eventfd");
> > > +    }
> > > +}
> > > +
> > > +void EventFD::signal()
> > > +{
> > > +    uint64_t n = 1;
> > > +    ignore_result(write(fd, &n, sizeof(n)));
> > > +}
> > > +
> > > +void EventFD::ack()
> > > +{
> > > +    uint64_t n;
> > > +    ignore_result(read(fd, &n, sizeof(n)));
> > > +}
> > > +
> > > +}} // namespace spice::streaming_agent
> > > diff --git a/src/eventfd.hpp b/src/eventfd.hpp
> > > new file mode 100644
> > > index 0000000..48a175c
> > > --- /dev/null
> > > +++ b/src/eventfd.hpp
> > > @@ -0,0 +1,34 @@
> > > +/* Linux event file descriptors
> > > + *
> > > + * \copyright
> > > + * Copyright 2018 Red Hat Inc. All rights reserved.
> > > + */
> > > +#ifndef SPICE_STREAMING_AGENT_EVENTFD_HPP
> > > +#define SPICE_STREAMING_AGENT_EVENTFD_HPP
> > > +
> > > +#include <unistd.h>
> > > +
> > > +namespace spice {
> > > +namespace streaming_agent {
> > > +
> > > +class EventFD {
> > > +public:
> > > +    EventFD();
> > > +    ~EventFD() { close(fd); }
> > > +    void signal();
> > > +    void ack();
> > > +    /**
> > > +     * Returns underlying file descriptor.
> > > +     * Do not close, should just be used for polling.
> > > +     */
> > > +    int raw_fd() const { return fd; }
> > > +private:
> > > +    EventFD(const EventFD&) = delete;
> > > +    void operator=(const EventFD&) = delete;
> > > +
> > > +    int fd;
> > > +};
> > > +
> > > +}} // namespace spice::streaming_agent
> > > +
> > > +#endif // SPICE_STREAMING_AGENT_EVENTFD_HPP
> > > diff --git a/src/spice-streaming-agent.cpp b/src/spice-streaming-agent.cpp
> > > index 6388bb2..240b9c7 100644
> > > --- a/src/spice-streaming-agent.cpp
> > > +++ b/src/spice-streaming-agent.cpp
> > > @@ -10,6 +10,7 @@
> > >  #include "stream-port.hpp"
> > >  #include "error.hpp"
> > >  #include "xorg-utils.hpp"
> > > +#include "eventfd.hpp"
> > >  
> > >  #include <spice/stream-device.h>
> > >  #include <spice/enums.h>
> > > @@ -36,6 +37,7 @@
> > >  #include <thread>
> > >  #include <vector>
> > >  #include <string>
> > > +#include <atomic>
> > >  #include <functional>
> > >  #include <X11/Xlib.h>
> > >  #include <X11/extensions/Xfixes.h>
> > > @@ -61,12 +63,21 @@ static bool quit_requested = false;
> > >  static bool log_binary = false;
> > >  static bool log_frames = false;
> > >  static std::set<SpiceVideoCodecType> client_codecs;
> > > +static EventFD update_fd;
> > > +static const char vt_property_name[] = "XFree86_has_VT";
> > > +static const char *stream_port_name =
> > > "/dev/virtio-ports/org.spice-space.stream.0";
> > > +static Atom vt_property = None;
> > > +static std::atomic_bool vt_active;
> > >  
> > > -static bool have_something_to_read(StreamPort &stream_port, bool blocking)
> > > +static bool have_something_to_read(StreamPort &stream_port, bool blocking,
> > > int &fd)
> > >  {
> > > -    struct pollfd pollfd = {stream_port.fd, POLLIN, 0};
> > > +    struct pollfd pollfds[2] = {
> > > +        { stream_port.fd, POLLIN, 0 },
> > > +        { update_fd.raw_fd(), POLLIN, 0 },
> > > +    };
> > >  
> > > -    if (poll(&pollfd, 1, blocking ? -1 : 0) < 0) {
> > > +    fd = -1;
> > > +    if (poll(pollfds, 2, blocking ? -1 : 0) < 0) {
> > >          if (errno == EINTR) {
> > >              // report nothing to read, next iteration of the enclosing
> > >              loop will retry
> > >              return false;
> > > @@ -75,7 +86,13 @@ static bool have_something_to_read(StreamPort
> > > &stream_port, bool blocking)
> > >          throw IOError("poll failed on the device", errno);
> > >      }
> > >  
> > > -    if (pollfd.revents & POLLIN) {
> > > +    if (pollfds[1].revents & POLLIN) {
> > > +        fd = update_fd.raw_fd();
> > > +        return true;
> > > +    }
> > > +
> > > +    if (pollfds[0].revents & POLLIN) {
> > > +        fd = stream_port.fd;
> > >          return true;
> > >      }
> > >  
> > > @@ -173,8 +190,18 @@ static void read_command_from_device(StreamPort
> > > &stream_port)
> > >  static void read_command(StreamPort &stream_port, bool blocking)
> > >  {
> > >      while (!quit_requested) {
> > > -        if (have_something_to_read(stream_port, blocking)) {
> > > -            read_command_from_device(stream_port);
> > > +        int fd;
> > > +        if (have_something_to_read(stream_port, blocking, fd)) {
> > > +            if (fd == stream_port.fd) {
> > > +                read_command_from_device(stream_port);
> > > +            } else if (fd == update_fd.raw_fd()) {
> > > +                update_fd.ack();
> > > +                bool vt_active =
> > > ::vt_active.load(std::memory_order_relaxed);
> > > +                if (!vt_active) {
> > > +                    throw std::runtime_error("VT disabled");
> > > +                }
> > > +                continue;
> > > +            }
> > >              break;
> > >          }
> > >  
> > > @@ -305,10 +332,22 @@ send_cursor(StreamPort &stream_port, unsigned width,
> > > unsigned height, int hotspo
> > >  static void cursor_changes(StreamPort *stream_port, Display *display, int
> > >  event_base)
> > >  {
> > >      unsigned long last_serial = 0;
> > > +    Window rootwindow = DefaultRootWindow(display);
> > >  
> > >      while (1) {
> > >          XEvent event;
> > >          XNextEvent(display, &event);
> > > +
> > > +        if (event.type == PropertyNotify) {
> > > +            if (vt_property == None || event.xproperty.atom !=
> > > vt_property)
> > > +                continue;
> > > +            // update vt property, activate screen read if needed
> > > +            vt_active.store(get_win_prop_int(display, rootwindow,
> > > vt_property) != 0, std::memory_order_relaxed);
> > > +            std::atomic_thread_fence(std::memory_order_acquire);
> > > +            update_fd.signal();
> > > +            continue;
> > > +        }
> > > +
> > >          if (event.type != event_base + 1) {
> > >              continue;
> > >          }
> > > @@ -416,7 +455,6 @@ do_capture(StreamPort &stream_port, FILE *f_log)
> > >  
> > >  int main(int argc, char* argv[])
> > >  {
> > > -    const char *stream_port_name =
> > > "/dev/virtio-ports/org.spice-space.stream.0";
> > >      int opt;
> > >      const char *log_filename = NULL;
> > >      int logmask = LOG_UPTO(LOG_WARNING);
> > > @@ -522,6 +560,19 @@ int main(int argc, char* argv[])
> > >      Window rootwindow = DefaultRootWindow(display);
> > >      XFixesSelectCursorInput(display, rootwindow,
> > >      XFixesDisplayCursorNotifyMask);
> > >  
> > > +    vt_property = XInternAtom(display, vt_property_name, True);
> > > +    if (vt_property == None) {
> > > +        syslog(LOG_ERR, "VT property not found\n");
> > > +        return EXIT_FAILURE;
> > > +    }
> > > +    XSelectInput(display, rootwindow, PropertyChangeMask);
> > > +
> > > +    vt_active.store(get_win_prop_int(display, rootwindow, vt_property) !=
> > > 0, std::memory_order_relaxed);
> > > +    if (!::vt_active.load(std::memory_order_relaxed)) {
> > > +        syslog(LOG_ERR, "VT is disabled");
> > > +        return EXIT_FAILURE;
> > > +    }
> > > +
> > >      int ret = EXIT_SUCCESS;
> > >  
> > >      try {
> 
> Frediano
> _______________________________________________
> Spice-devel mailing list
> Spice-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/spice-devel
On Mon, 2018-05-21 at 11:45 +0100, Frediano Ziglio wrote:
> Check if the current display server is active or not and stream only if
> active.
> This will allow to support multiple display servers running.
> When multiple display servers are running only one have the GPU
> associated and is writing to the screen.
> Stop capturing and release resources when the display server is not
> active and vice versa.
> 
> Signed-off-by: Frediano Ziglio <fziglio@redhat.com>
> ---
>  src/Makefile.am               |  2 ++
>  src/eventfd.cpp               | 38 ++++++++++++++++++++
>  src/eventfd.hpp               | 34 ++++++++++++++++++
>  src/spice-streaming-agent.cpp | 65 +++++++++++++++++++++++++++++++----
>  4 files changed, 132 insertions(+), 7 deletions(-)
>  create mode 100644 src/eventfd.cpp
>  create mode 100644 src/eventfd.hpp
> 
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 276478f..ffc52b2 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -62,4 +62,6 @@ spice_streaming_agent_SOURCES = \
>  	jpeg.hpp \
>  	stream-port.cpp \
>  	stream-port.hpp \
> +	eventfd.cpp \
> +	eventfd.hpp \
>  	$(NULL)
> diff --git a/src/eventfd.cpp b/src/eventfd.cpp
> new file mode 100644
> index 0000000..e3c1e72
> --- /dev/null
> +++ b/src/eventfd.cpp
> @@ -0,0 +1,38 @@
> +/* Linux event file descriptors
> + *
> + * \copyright
> + * Copyright 2018 Red Hat Inc. All rights reserved.
> + */
> +#include <config.h>
> +#include "eventfd.hpp"
> +
> +#include <stdexcept>
> +#include <sys/eventfd.h>
> +
> +template<typename T>
> +inline void ignore_result(T /* unused result */) {}
> +
> +namespace spice {
> +namespace streaming_agent {
> +
> +EventFD::EventFD():
> +    fd(eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK))
> +{
> +    if (fd < 0) {
> +        throw std::runtime_error("Failed to create eventfd");

Use an error derived from our error hierarchy (inheriting Error) and
include the actual error string in the exception.

> +    }
> +}
> +
> +void EventFD::signal()
> +{
> +    uint64_t n = 1;
> +    ignore_result(write(fd, &n, sizeof(n)));
> +}
> +
> +void EventFD::ack()
> +{
> +    uint64_t n;
> +    ignore_result(read(fd, &n, sizeof(n)));
> +}
> +
> +}} // namespace spice::streaming_agent
> diff --git a/src/eventfd.hpp b/src/eventfd.hpp
> new file mode 100644
> index 0000000..48a175c
> --- /dev/null
> +++ b/src/eventfd.hpp
> @@ -0,0 +1,34 @@
> +/* Linux event file descriptors

The description is very vague. See also the docstring note below.

> + *
> + * \copyright
> + * Copyright 2018 Red Hat Inc. All rights reserved.
> + */
> +#ifndef SPICE_STREAMING_AGENT_EVENTFD_HPP
> +#define SPICE_STREAMING_AGENT_EVENTFD_HPP
> +
> +#include <unistd.h>
> +
> +namespace spice {
> +namespace streaming_agent {

The class could really use a docstring saying what it's about (i.e. a
portable eventfd implementation allowing to pass signals through a file
descriptor).

> +class EventFD {
> +public:
> +    EventFD();
> +    ~EventFD() { close(fd); }
> +    void signal();
> +    void ack();
> +    /**
> +     * Returns underlying file descriptor.
> +     * Do not close, should just be used for polling.
> +     */
> +    int raw_fd() const { return fd; }
> +private:
> +    EventFD(const EventFD&) = delete;
> +    void operator=(const EventFD&) = delete;
> +
> +    int fd;
> +};
> +
> +}} // namespace spice::streaming_agent
> +
> +#endif // SPICE_STREAMING_AGENT_EVENTFD_HPP
> diff --git a/src/spice-streaming-agent.cpp b/src/spice-streaming-agent.cpp
> index 6388bb2..240b9c7 100644
> --- a/src/spice-streaming-agent.cpp
> +++ b/src/spice-streaming-agent.cpp
> @@ -10,6 +10,7 @@
>  #include "stream-port.hpp"
>  #include "error.hpp"
>  #include "xorg-utils.hpp"
> +#include "eventfd.hpp"
>  
>  #include <spice/stream-device.h>
>  #include <spice/enums.h>
> @@ -36,6 +37,7 @@
>  #include <thread>
>  #include <vector>
>  #include <string>
> +#include <atomic>
>  #include <functional>
>  #include <X11/Xlib.h>
>  #include <X11/extensions/Xfixes.h>
> @@ -61,12 +63,21 @@ static bool quit_requested = false;
>  static bool log_binary = false;
>  static bool log_frames = false;
>  static std::set<SpiceVideoCodecType> client_codecs;
> +static EventFD update_fd;
> +static const char vt_property_name[] = "XFree86_has_VT";
> +static const char *stream_port_name = "/dev/virtio-ports/org.spice-space.stream.0";

Why did you move this to be static instead of local in a scope?
Limiting the scope of variables is a good thing...

> +static Atom vt_property = None;
> +static std::atomic_bool vt_active;

Adding more static variables here goes directly against the intention
to modularize the streaming agent. I think more refactoring needs to
happen before this can be implemented cleanly.

> -static bool have_something_to_read(StreamPort &stream_port, bool blocking)
> +static bool have_something_to_read(StreamPort &stream_port, bool blocking, int &fd)
>  {
> -    struct pollfd pollfd = {stream_port.fd, POLLIN, 0};
> +    struct pollfd pollfds[2] = {
> +        { stream_port.fd, POLLIN, 0 },
> +        { update_fd.raw_fd(), POLLIN, 0 },
> +    };
>  
> -    if (poll(&pollfd, 1, blocking ? -1 : 0) < 0) {
> +    fd = -1;
> +    if (poll(pollfds, 2, blocking ? -1 : 0) < 0) {
>          if (errno == EINTR) {
>              // report nothing to read, next iteration of the enclosing loop will retry
>              return false;
> @@ -75,7 +86,13 @@ static bool have_something_to_read(StreamPort &stream_port, bool blocking)
>          throw IOError("poll failed on the device", errno);
>      }
>  
> -    if (pollfd.revents & POLLIN) {
> +    if (pollfds[1].revents & POLLIN) {
> +        fd = update_fd.raw_fd();
> +        return true;
> +    }
> +
> +    if (pollfds[0].revents & POLLIN) {
> +        fd = stream_port.fd;
>          return true;
>      }
>  
> @@ -173,8 +190,18 @@ static void read_command_from_device(StreamPort &stream_port)
>  static void read_command(StreamPort &stream_port, bool blocking)
>  {
>      while (!quit_requested) {
> -        if (have_something_to_read(stream_port, blocking)) {
> -            read_command_from_device(stream_port);
> +        int fd;
> +        if (have_something_to_read(stream_port, blocking, fd)) {
> +            if (fd == stream_port.fd) {
> +                read_command_from_device(stream_port);
> +            } else if (fd == update_fd.raw_fd()) {
> +                update_fd.ack();
> +                bool vt_active = ::vt_active.load(std::memory_order_relaxed);
> +                if (!vt_active) {
> +                    throw std::runtime_error("VT disabled");

Error class and a useless error message again... it holds for most of
the exceptions added in this series, so I won't mention it again...

> +                }
> +                continue;
> +            }

Adding this second (and unrelated, besides the need to share the poll
and the control loop) fd here doesn't help with the separation of code
into modules. It seems hard, I'm thinking of ways to solve it and have
some foggy ideas, we can discuss it later or I may post some refactor
patch if I come up with something usable.

>              break;
>          }
>  
> @@ -305,10 +332,22 @@ send_cursor(StreamPort &stream_port, unsigned width, unsigned height, int hotspo
>  static void cursor_changes(StreamPort *stream_port, Display *display, int event_base)
>  {
>      unsigned long last_serial = 0;
> +    Window rootwindow = DefaultRootWindow(display);
>  
>      while (1) {
>          XEvent event;
>          XNextEvent(display, &event);
> +
> +        if (event.type == PropertyNotify) {
> +            if (vt_property == None || event.xproperty.atom != vt_property)
> +                continue;
> +            // update vt property, activate screen read if needed
> +            vt_active.store(get_win_prop_int(display, rootwindow, vt_property) != 0, std::memory_order_relaxed);
> +            std::atomic_thread_fence(std::memory_order_acquire);
> +            update_fd.signal();
> +            continue;
> +        }
> +

This looks shoehorned here, at least the cursor thread should be
renamed to a generic X thread. Not having many better ideas though,
apart from actually using logind as Christophe suggested, which might
be better altogether?

>          if (event.type != event_base + 1) {
>              continue;
>          }
> @@ -416,7 +455,6 @@ do_capture(StreamPort &stream_port, FILE *f_log)
>  
>  int main(int argc, char* argv[])
>  {
> -    const char *stream_port_name = "/dev/virtio-ports/org.spice-space.stream.0";
>      int opt;
>      const char *log_filename = NULL;
>      int logmask = LOG_UPTO(LOG_WARNING);
> @@ -522,6 +560,19 @@ int main(int argc, char* argv[])
>      Window rootwindow = DefaultRootWindow(display);
>      XFixesSelectCursorInput(display, rootwindow, XFixesDisplayCursorNotifyMask);
>  
> +    vt_property = XInternAtom(display, vt_property_name, True);
> +    if (vt_property == None) {
> +        syslog(LOG_ERR, "VT property not found\n");
> +        return EXIT_FAILURE;

I'd prefer to use exceptions consistently for handling errors...

> +    }
> +    XSelectInput(display, rootwindow, PropertyChangeMask);
> +
> +    vt_active.store(get_win_prop_int(display, rootwindow, vt_property) != 0, std::memory_order_relaxed);
> +    if (!::vt_active.load(std::memory_order_relaxed)) {
> +        syslog(LOG_ERR, "VT is disabled");
> +        return EXIT_FAILURE;
> +    }
> +
>      int ret = EXIT_SUCCESS;
>  
>      try {