Discussion:
[Spice-devel] [RFC spice-vdagent 00/18] GLib integration
Jakub Janků
2018-08-14 18:53:34 UTC
Permalink
Hi all,

as the subject suggests, this series further deepens the integration of GLib into spice-vdagent(d).

Change summary:

* udscs.c and virtio-port.c are built upon common base, VDAgentConnection, that handles the lower-level I/O operations
* systemd-login.c and console-kit.c use GDBus instead of libdbus
* vdagentd.c uses GMainLoop and GLib's commandline option parser

Some patches are rather unrelated and try to clean up the code a bit.


Where I'd need your help:

vdagent_connection_destroy() is in most cases asynchronous (due to the way GCancellable works) and it can therefore take multiple iterations of GMainLoop until the memory associated with VDAgentConnection is freed and the underlying file descriptor gets closed.

This is fine as long as the GMainLoop runs. However, the current code assumes, that the call to udscs_destroy_connection() and vdagent_virtio_port_destroy() are synchronous and invokes these functions even after the GMainLoop quits.

As a result, the memory allocated by VDAgentConnection might not be explicitely freed and the FD might not be closed, when the spice-vdagent(d) exits.

Is this an acceptable behavior?

Possible solutions:
1) use GMainLoop inside vdagent_connection_destroy() in a similar way as in vdagent_connection_flush() or in the clipboard.c code
2) add a callback for VDAgentConnection's destruction and make the clean-up code at the end of main() run while the GMainLoop is still alive, the callback would then quit it (this would also require adding destruction callbacks for udscs_connection, udscs_server and virtio_port)

Is there any better solution?

Cheers,
Jakub

Jakub Janků (18):
vdagentd: parse argv using GLib
vport: add by_user param to vdagent_virtio_port_disconnect_callback
vdagentd: use GMainLoop
build: add GIO dependency
add VDAgentConnection
udscs: add udscs_get_peer_pid()
udscs: use VDAgentConnection
udscs-server: split initialization
udscs: simplify logging
vport: use VDAgentConnection
session-info: add ActiveSessionChangeCb
console-kit: use GDBus
systemd-login: use GDBus
session-info: remove session_info_get_fd()
build: drop DBus dependency
move to GLib memory functions
vdagentd: move code to do_guest_xorg_resolution()
vdagentd: move code to do_agent_file_xfer_status()

Makefile.am | 6 +-
configure.ac | 2 +-
src/udscs.c | 560 +++++++--------------------
src/udscs.h | 58 +--
src/vdagent-connection.c | 301 +++++++++++++++
src/vdagent-connection.h | 103 +++++
src/vdagent/vdagent.c | 3 +-
src/vdagent/x11-randr.c | 43 +--
src/vdagentd/console-kit.c | 613 ++++++++++--------------------
src/vdagentd/dummy-session-info.c | 7 +-
src/vdagentd/session-info.h | 6 +-
src/vdagentd/systemd-login.c | 274 +++++--------
src/vdagentd/uinput.c | 9 +-
src/vdagentd/vdagentd.c | 472 +++++++++++------------
src/vdagentd/virtio-port.c | 404 ++++++--------------
src/vdagentd/virtio-port.h | 27 +-
16 files changed, 1217 insertions(+), 1671 deletions(-)
create mode 100644 src/vdagent-connection.c
create mode 100644 src/vdagent-connection.h
--
2.17.1
Jakub Janků
2018-08-14 18:53:35 UTC
Permalink
All command line options now have long names
as they are required by GLib.

Change types of some global variables that hold the options:
- const char * --> gchar *
- int --> gboolean

Define DEFAULT_UINPUT_DEVICE as "/dev/uinput",
since there's already DEFAULT_VIRTIO_PORT_PATH, VDAGENTD_SOCKET.
---
src/vdagentd/vdagentd.c | 149 ++++++++++++++++++++++------------------
1 file changed, 82 insertions(+), 67 deletions(-)

diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index 682761a..d88bbc7 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -48,6 +48,8 @@
#include "virtio-port.h"
#include "session-info.h"

+#define DEFAULT_UINPUT_DEVICE "/dev/uinput"
+
struct agent_data {
char *session;
int width;
@@ -58,12 +60,16 @@ struct agent_data {

/* variables */
static const char *pidfilename = "/var/run/spice-vdagentd/spice-vdagentd.pid";
-static const char *portdev = DEFAULT_VIRTIO_PORT_PATH;
-static const char *vdagentd_socket = VDAGENTD_SOCKET;
-static const char *uinput_device = "/dev/uinput";
+
+static gchar *portdev = NULL;
+static gchar *vdagentd_socket = NULL;
+static gchar *uinput_device = NULL;
static int debug = 0;
-static int uinput_fake = 0;
-static int only_once = 0;
+static gboolean uinput_fake = FALSE;
+static gboolean only_once = FALSE;
+static gboolean do_daemonize = TRUE;
+static gboolean want_session_info = TRUE;
+
static struct udscs_server *server = NULL;
static struct vdagent_virtio_port *virtio_port = NULL;
static GHashTable *active_xfers = NULL;
@@ -960,29 +966,6 @@ static void agent_read_complete(struct udscs_connection **connp,

/* main */

-static void usage(FILE *fp)
-{
- fprintf(fp,
- "Usage: spice-vdagentd [OPTIONS]\n\n"
- "Spice guest agent daemon, version %s.\n\n"
- "Options:\n"
- " -h print this text\n"
- " -d log debug messages (use twice for extra info)\n"
- " -s <port> set virtio serial port [%s]\n"
- " -S <filename> set vdagent Unix domain socket [%s]\n"
- " -u <dev> set uinput device [%s]\n"
- " -f treat uinput device as fake; no ioctls\n"
- " -x don't daemonize\n"
- " -o only handle one virtio serial session\n"
-#ifdef HAVE_CONSOLE_KIT
- " -X disable console kit integration\n"
-#endif
-#ifdef HAVE_LIBSYSTEMD_LOGIN
- " -X disable systemd-logind integration\n"
-#endif
- ,VERSION, portdev, vdagentd_socket, uinput_device);
-}
-
static void daemonize(void)
{
int x;
@@ -1081,52 +1064,80 @@ static void quit_handler(int sig)
quit = 1;
}

+static gboolean parse_debug_level_cb(const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error)
+{
+ debug++;
+ return TRUE;
+}
+
+static GOptionEntry cmd_entries[] = {
+ { "debug", 'd', G_OPTION_FLAG_NO_ARG,
+ G_OPTION_ARG_CALLBACK, parse_debug_level_cb,
+ "Log debug messages (use twice for extra info)", NULL },
+
+ { "virtio-serial-port-path", 's', 0,
+ G_OPTION_ARG_STRING, &portdev,
+ "Set virtio-serial path (" DEFAULT_VIRTIO_PORT_PATH ")", NULL },
+
+ { "vdagentd-socket", 'S', 0,
+ G_OPTION_ARG_STRING, &vdagentd_socket,
+ "Set spice-vdagentd socket (" VDAGENTD_SOCKET ")", NULL },
+
+ { "uinput-device", 'u', 0,
+ G_OPTION_ARG_STRING, &uinput_device,
+ "Set uinput device (" DEFAULT_UINPUT_DEVICE ")", NULL },
+
+ { "fake-uinput", 'f', 0,
+ G_OPTION_ARG_NONE, &uinput_fake,
+ "Treat uinput device as fake; no ioctls", NULL },
+
+ { "foreground", 'x', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &do_daemonize,
+ "Do not daemonize the agent", NULL},
+
+ { "one-session", 'o', 0,
+ G_OPTION_ARG_NONE, &only_once,
+ "Only handle one virtio serial session", NULL },
+
+#if defined(HAVE_CONSOLE_KIT) || defined (HAVE_LIBSYSTEMD_LOGIN)
+ { "disable-session-integration", 'X', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &want_session_info,
+ "Disable console kit and systemd-logind integration", NULL },
+#endif
+
+ { NULL }
+};
+
int main(int argc, char *argv[])
{
- int c;
- int do_daemonize = 1;
- int want_session_info = 1;
+ GOptionContext *context;
+ GError *err = NULL;
struct sigaction act;
gboolean own_socket = TRUE;

- for (;;) {
- if (-1 == (c = getopt(argc, argv, "-dhxXfos:u:S:")))
- break;
- switch (c) {
- case 'd':
- debug++;
- break;
- case 's':
- portdev = optarg;
- break;
- case 'S':
- vdagentd_socket = optarg;
- break;
- case 'u':
- uinput_device = optarg;
- break;
- case 'f':
- uinput_fake = 1;
- break;
- case 'o':
- only_once = 1;
- break;
- case 'x':
- do_daemonize = 0;
- break;
- case 'X':
- want_session_info = 0;
- break;
- case 'h':
- usage(stdout);
- return 0;
- default:
- fputs("\n", stderr);
- usage(stderr);
- return 1;
- }
+ context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, cmd_entries, NULL);
+ g_option_context_set_summary(context,
+ "Spice guest agent daemon, version " VERSION);
+ g_option_context_parse(context, &argc, &argv, &err);
+ g_option_context_free(context);
+
+ if (err) {
+ g_printerr("Invalid arguments, %s\n", err->message);
+ g_error_free(err);
+ return 1;
}

+ if (portdev == NULL)
+ portdev = g_strdup(DEFAULT_VIRTIO_PORT_PATH);
+ if (vdagentd_socket == NULL)
+ vdagentd_socket = g_strdup(VDAGENTD_SOCKET);
+ if (uinput_device == NULL)
+ uinput_device = g_strdup(DEFAULT_UINPUT_DEVICE);
+
memset(&act, 0, sizeof(act));
act.sa_flags = SA_RESTART;
act.sa_handler = quit_handler;
@@ -1223,5 +1234,9 @@ int main(int argc, char *argv[])
if (do_daemonize)
unlink(pidfilename);

+ g_free(portdev);
+ g_free(vdagentd_socket);
+ g_free(uinput_device);
+
return retval;
}
--
2.17.1
Victor Toso
2018-08-28 05:16:53 UTC
Permalink
Hi,

Just for reference, the output of spice-vdagentd -h before was:

| $ spice-vdagentd -h
| Usage: spice-vdagentd [OPTIONS]
|
| Spice guest agent daemon, version 0.17.0.
|
| Options:
| -h print this text
| -d log debug messages (use twice for extra info)
| -s <port> set virtio serial port [/dev/virtio-ports/com.redhat.spice.0]
| -S <filename> set udcs socket [/var/run/spice-vdagentd/spice-vdagent-sock]
| -u <dev> set uinput device [/dev/uinput]
| -f treat uinput device as fake; no ioctls
| -x don't daemonize
| -o Only handle one virtio serial session.
| -X Disable systemd-logind integration

and not it is:

| $ /usr/sbin/spice-vdagentd -h
| Usage:
| spice-vdagentd [OPTION?]
|
| Spice guest agent daemon, version 0.18.0
|
| Help Options:
| -h, --help Show help options
|
| Application Options:
| -d, --debug Log debug messages (use twice for extra info)
| -s, --virtio-serial-port-path Set virtio-serial path (/dev/virtio-ports/com.redhat.spice.0)
| -S, --vdagentd-socket Set spice-vdagentd socket (/var/run/spice-vdagentd/spice-vdagent-sock)
| -u, --uinput-device Set uinput device (/dev/uinput)
| -f, --fake-uinput Treat uinput device as fake; no ioctls
| -x, --foreground Do not daemonize the agent
| -o, --one-session Only handle one virtio serial session
| -X, --disable-session-integration Disable console kit and systemd-logind integration

I think that after "vdagentd: use GMainLoop" patch, we should
also include glib's command line options.
Post by Jakub Janků
All command line options now have long names
as they are required by GLib.
I think that adding the following to the commit log as a quick
overview of current options and its long names might be helpful.

-d, --debug
-s, --virtio-serial-port-path
-S, --vdagentd-socket
-u, --uinput-device
-f, --fake-uinput
-x, --foreground
-o, --one-session
-X, --disable-session-integration
Post by Jakub Janků
- const char * --> gchar *
- int --> gboolean
Define DEFAULT_UINPUT_DEVICE as "/dev/uinput",
since there's already DEFAULT_VIRTIO_PORT_PATH, VDAGENTD_SOCKET.
Would you mind adding Signed-off-by: Name <email> ?

(more below)
Post by Jakub Janků
---
src/vdagentd/vdagentd.c | 149 ++++++++++++++++++++++------------------
1 file changed, 82 insertions(+), 67 deletions(-)
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index 682761a..d88bbc7 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -48,6 +48,8 @@
#include "virtio-port.h"
#include "session-info.h"
+#define DEFAULT_UINPUT_DEVICE "/dev/uinput"
+
struct agent_data {
char *session;
int width;
@@ -58,12 +60,16 @@ struct agent_data {
/* variables */
static const char *pidfilename = "/var/run/spice-vdagentd/spice-vdagentd.pid";
-static const char *portdev = DEFAULT_VIRTIO_PORT_PATH;
-static const char *vdagentd_socket = VDAGENTD_SOCKET;
-static const char *uinput_device = "/dev/uinput";
+
+static gchar *portdev = NULL;
+static gchar *vdagentd_socket = NULL;
+static gchar *uinput_device = NULL;
static int debug = 0;
-static int uinput_fake = 0;
-static int only_once = 0;
+static gboolean uinput_fake = FALSE;
+static gboolean only_once = FALSE;
+static gboolean do_daemonize = TRUE;
+static gboolean want_session_info = TRUE;
+
static struct udscs_server *server = NULL;
static struct vdagent_virtio_port *virtio_port = NULL;
static GHashTable *active_xfers = NULL;
@@ -960,29 +966,6 @@ static void agent_read_complete(struct udscs_connection **connp,
/* main */
-static void usage(FILE *fp)
-{
- fprintf(fp,
- "Usage: spice-vdagentd [OPTIONS]\n\n"
- "Spice guest agent daemon, version %s.\n\n"
- "Options:\n"
- " -h print this text\n"
- " -d log debug messages (use twice for extra info)\n"
- " -s <port> set virtio serial port [%s]\n"
- " -S <filename> set vdagent Unix domain socket [%s]\n"
- " -u <dev> set uinput device [%s]\n"
- " -f treat uinput device as fake; no ioctls\n"
- " -x don't daemonize\n"
- " -o only handle one virtio serial session\n"
-#ifdef HAVE_CONSOLE_KIT
- " -X disable console kit integration\n"
-#endif
-#ifdef HAVE_LIBSYSTEMD_LOGIN
- " -X disable systemd-logind integration\n"
-#endif
- ,VERSION, portdev, vdagentd_socket, uinput_device);
-}
-
static void daemonize(void)
{
int x;
@@ -1081,52 +1064,80 @@ static void quit_handler(int sig)
quit = 1;
}
+static gboolean parse_debug_level_cb(const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error)
+{
+ debug++;
+ return TRUE;
+}
It is okay to leave this for now as this patches are touching the
command line parsing part but within the context of glib
integration, we might consider removing this in the future and
follow what might be dictated by Glib's logging system.
Post by Jakub Janků
+static GOptionEntry cmd_entries[] = {
+ { "debug", 'd', G_OPTION_FLAG_NO_ARG,
+ G_OPTION_ARG_CALLBACK, parse_debug_level_cb,
+ "Log debug messages (use twice for extra info)", NULL },
+
+ { "virtio-serial-port-path", 's', 0,
+ G_OPTION_ARG_STRING, &portdev,
+ "Set virtio-serial path (" DEFAULT_VIRTIO_PORT_PATH ")", NULL },
+
+ { "vdagentd-socket", 'S', 0,
+ G_OPTION_ARG_STRING, &vdagentd_socket,
+ "Set spice-vdagentd socket (" VDAGENTD_SOCKET ")", NULL },
+
+ { "uinput-device", 'u', 0,
+ G_OPTION_ARG_STRING, &uinput_device,
+ "Set uinput device (" DEFAULT_UINPUT_DEVICE ")", NULL },
+
+ { "fake-uinput", 'f', 0,
+ G_OPTION_ARG_NONE, &uinput_fake,
+ "Treat uinput device as fake; no ioctls", NULL },
+
+ { "foreground", 'x', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &do_daemonize,
+ "Do not daemonize the agent", NULL},
+
+ { "one-session", 'o', 0,
+ G_OPTION_ARG_NONE, &only_once,
+ "Only handle one virtio serial session", NULL },
+
+#if defined(HAVE_CONSOLE_KIT) || defined (HAVE_LIBSYSTEMD_LOGIN)
+ { "disable-session-integration", 'X', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &want_session_info,
+ "Disable console kit and systemd-logind integration", NULL },
+#endif
+
+ { NULL }
+};
+
int main(int argc, char *argv[])
{
- int c;
- int do_daemonize = 1;
- int want_session_info = 1;
+ GOptionContext *context;
+ GError *err = NULL;
struct sigaction act;
gboolean own_socket = TRUE;
- for (;;) {
- if (-1 == (c = getopt(argc, argv, "-dhxXfos:u:S:")))
- break;
- switch (c) {
- debug++;
- break;
- portdev = optarg;
- break;
- vdagentd_socket = optarg;
- break;
- uinput_device = optarg;
- break;
- uinput_fake = 1;
- break;
- only_once = 1;
- break;
- do_daemonize = 0;
- break;
- want_session_info = 0;
- break;
- usage(stdout);
- return 0;
- fputs("\n", stderr);
- usage(stderr);
- return 1;
- }
+ context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, cmd_entries, NULL);
+ g_option_context_set_summary(context,
+ "Spice guest agent daemon, version " VERSION);
+ g_option_context_parse(context, &argc, &argv, &err);
+ g_option_context_free(context);
+
+ if (err) {
+ g_printerr("Invalid arguments, %s\n", err->message);
We don't use g_printerr() or any g_log() in this code yet.

I hope we can move to that whenever we bump glib to 2.50 or above
as it writes to systemd's journal by default for daemons (AFAIK).

Reviewed-by: Victor Toso <***@redhat.com>

Feel free to submit a new version with fixes as standalone patch
with me in cc, to reduce the backlog of this series ;)

Cheers,
Victor
Post by Jakub Janků
+ g_error_free(err);
+ return 1;
}
+ if (portdev == NULL)
+ portdev = g_strdup(DEFAULT_VIRTIO_PORT_PATH);
+ if (vdagentd_socket == NULL)
+ vdagentd_socket = g_strdup(VDAGENTD_SOCKET);
+ if (uinput_device == NULL)
+ uinput_device = g_strdup(DEFAULT_UINPUT_DEVICE);
+
memset(&act, 0, sizeof(act));
act.sa_flags = SA_RESTART;
act.sa_handler = quit_handler;
@@ -1223,5 +1234,9 @@ int main(int argc, char *argv[])
if (do_daemonize)
unlink(pidfilename);
+ g_free(portdev);
+ g_free(vdagentd_socket);
+ g_free(uinput_device);
+
return retval;
}
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Jakub Janku
2018-09-03 16:06:01 UTC
Permalink
Hi,
Post by Victor Toso
Hi,
| $ spice-vdagentd -h
| Usage: spice-vdagentd [OPTIONS]
|
| Spice guest agent daemon, version 0.17.0.
|
| -h print this text
| -d log debug messages (use twice for extra info)
| -s <port> set virtio serial port [/dev/virtio-ports/com.redhat.spice.0]
| -S <filename> set udcs socket [/var/run/spice-vdagentd/spice-vdagent-sock]
| -u <dev> set uinput device [/dev/uinput]
| -f treat uinput device as fake; no ioctls
| -x don't daemonize
| -o Only handle one virtio serial session.
| -X Disable systemd-logind integration
| $ /usr/sbin/spice-vdagentd -h
| spice-vdagentd [OPTION?]
|
| Spice guest agent daemon, version 0.18.0
|
| -h, --help Show help options
|
| -d, --debug Log debug messages (use twice for extra info)
| -s, --virtio-serial-port-path Set virtio-serial path (/dev/virtio-ports/com.redhat.spice.0)
| -S, --vdagentd-socket Set spice-vdagentd socket (/var/run/spice-vdagentd/spice-vdagent-sock)
| -u, --uinput-device Set uinput device (/dev/uinput)
| -f, --fake-uinput Treat uinput device as fake; no ioctls
| -x, --foreground Do not daemonize the agent
| -o, --one-session Only handle one virtio serial session
| -X, --disable-session-integration Disable console kit and systemd-logind integration
I think that after "vdagentd: use GMainLoop" patch, we should
also include glib's command line options.
Not sure what you are referring to here.
GLib uses environment variables instead of command line options,
AFAIK, doesn't it?
There's the gtk_get_option_group() that we use in vdagent.c, but I
don't know of anything similar for GLib.
Post by Victor Toso
Post by Jakub Janků
All command line options now have long names
as they are required by GLib.
I think that adding the following to the commit log as a quick
overview of current options and its long names might be helpful.
Sure.
Post by Victor Toso
-d, --debug
-s, --virtio-serial-port-path
-S, --vdagentd-socket
-u, --uinput-device
-f, --fake-uinput
-x, --foreground
-o, --one-session
-X, --disable-session-integration
Post by Jakub Janků
- const char * --> gchar *
- int --> gboolean
Define DEFAULT_UINPUT_DEVICE as "/dev/uinput",
since there's already DEFAULT_VIRTIO_PORT_PATH, VDAGENTD_SOCKET.
Would you mind adding Signed-off-by: Name <email> ?
(more below)
Post by Jakub Janků
---
src/vdagentd/vdagentd.c | 149 ++++++++++++++++++++++------------------
1 file changed, 82 insertions(+), 67 deletions(-)
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index 682761a..d88bbc7 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -48,6 +48,8 @@
#include "virtio-port.h"
#include "session-info.h"
+#define DEFAULT_UINPUT_DEVICE "/dev/uinput"
+
struct agent_data {
char *session;
int width;
@@ -58,12 +60,16 @@ struct agent_data {
/* variables */
static const char *pidfilename = "/var/run/spice-vdagentd/spice-vdagentd.pid";
-static const char *portdev = DEFAULT_VIRTIO_PORT_PATH;
-static const char *vdagentd_socket = VDAGENTD_SOCKET;
-static const char *uinput_device = "/dev/uinput";
+
+static gchar *portdev = NULL;
+static gchar *vdagentd_socket = NULL;
+static gchar *uinput_device = NULL;
static int debug = 0;
-static int uinput_fake = 0;
-static int only_once = 0;
+static gboolean uinput_fake = FALSE;
+static gboolean only_once = FALSE;
+static gboolean do_daemonize = TRUE;
+static gboolean want_session_info = TRUE;
+
static struct udscs_server *server = NULL;
static struct vdagent_virtio_port *virtio_port = NULL;
static GHashTable *active_xfers = NULL;
@@ -960,29 +966,6 @@ static void agent_read_complete(struct udscs_connection **connp,
/* main */
-static void usage(FILE *fp)
-{
- fprintf(fp,
- "Usage: spice-vdagentd [OPTIONS]\n\n"
- "Spice guest agent daemon, version %s.\n\n"
- "Options:\n"
- " -h print this text\n"
- " -d log debug messages (use twice for extra info)\n"
- " -s <port> set virtio serial port [%s]\n"
- " -S <filename> set vdagent Unix domain socket [%s]\n"
- " -u <dev> set uinput device [%s]\n"
- " -f treat uinput device as fake; no ioctls\n"
- " -x don't daemonize\n"
- " -o only handle one virtio serial session\n"
-#ifdef HAVE_CONSOLE_KIT
- " -X disable console kit integration\n"
-#endif
-#ifdef HAVE_LIBSYSTEMD_LOGIN
- " -X disable systemd-logind integration\n"
-#endif
- ,VERSION, portdev, vdagentd_socket, uinput_device);
-}
-
static void daemonize(void)
{
int x;
@@ -1081,52 +1064,80 @@ static void quit_handler(int sig)
quit = 1;
}
+static gboolean parse_debug_level_cb(const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error)
+{
+ debug++;
+ return TRUE;
+}
It is okay to leave this for now as this patches are touching the
command line parsing part but within the context of glib
integration, we might consider removing this in the future and
follow what might be dictated by Glib's logging system.
This parse function is necessary for the command line options to stay the same.
The higher debug level is used in vdagentd_uinput_create(..., debug > 1, ...).
So removing this function would require adding a new option.
I would do this in a separate patch.
Post by Victor Toso
Post by Jakub Janků
+static GOptionEntry cmd_entries[] = {
+ { "debug", 'd', G_OPTION_FLAG_NO_ARG,
+ G_OPTION_ARG_CALLBACK, parse_debug_level_cb,
+ "Log debug messages (use twice for extra info)", NULL },
+
+ { "virtio-serial-port-path", 's', 0,
+ G_OPTION_ARG_STRING, &portdev,
+ "Set virtio-serial path (" DEFAULT_VIRTIO_PORT_PATH ")", NULL },
+
+ { "vdagentd-socket", 'S', 0,
+ G_OPTION_ARG_STRING, &vdagentd_socket,
+ "Set spice-vdagentd socket (" VDAGENTD_SOCKET ")", NULL },
+
+ { "uinput-device", 'u', 0,
+ G_OPTION_ARG_STRING, &uinput_device,
+ "Set uinput device (" DEFAULT_UINPUT_DEVICE ")", NULL },
+
+ { "fake-uinput", 'f', 0,
+ G_OPTION_ARG_NONE, &uinput_fake,
+ "Treat uinput device as fake; no ioctls", NULL },
+
+ { "foreground", 'x', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &do_daemonize,
+ "Do not daemonize the agent", NULL},
+
+ { "one-session", 'o', 0,
+ G_OPTION_ARG_NONE, &only_once,
+ "Only handle one virtio serial session", NULL },
+
+#if defined(HAVE_CONSOLE_KIT) || defined (HAVE_LIBSYSTEMD_LOGIN)
+ { "disable-session-integration", 'X', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &want_session_info,
+ "Disable console kit and systemd-logind integration", NULL },
+#endif
+
+ { NULL }
+};
+
int main(int argc, char *argv[])
{
- int c;
- int do_daemonize = 1;
- int want_session_info = 1;
+ GOptionContext *context;
+ GError *err = NULL;
struct sigaction act;
gboolean own_socket = TRUE;
- for (;;) {
- if (-1 == (c = getopt(argc, argv, "-dhxXfos:u:S:")))
- break;
- switch (c) {
- debug++;
- break;
- portdev = optarg;
- break;
- vdagentd_socket = optarg;
- break;
- uinput_device = optarg;
- break;
- uinput_fake = 1;
- break;
- only_once = 1;
- break;
- do_daemonize = 0;
- break;
- want_session_info = 0;
- break;
- usage(stdout);
- return 0;
- fputs("\n", stderr);
- usage(stderr);
- return 1;
- }
+ context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, cmd_entries, NULL);
+ g_option_context_set_summary(context,
+ "Spice guest agent daemon, version " VERSION);
+ g_option_context_parse(context, &argc, &argv, &err);
+ g_option_context_free(context);
+
+ if (err) {
+ g_printerr("Invalid arguments, %s\n", err->message);
We don't use g_printerr() or any g_log() in this code yet.
I think I copied it from the vdagent.c where it was added in 0ffca2b
("vdagent: Use glib's commandline parser").
I will change it to syslog().
Post by Victor Toso
I hope we can move to that whenever we bump glib to 2.50 or above
as it writes to systemd's journal by default for daemons (AFAIK).
Feel free to submit a new version with fixes as standalone patch
with me in cc, to reduce the backlog of this series ;)
Cheers,
Victor
Post by Jakub Janků
+ g_error_free(err);
+ return 1;
}
+ if (portdev == NULL)
+ portdev = g_strdup(DEFAULT_VIRTIO_PORT_PATH);
+ if (vdagentd_socket == NULL)
+ vdagentd_socket = g_strdup(VDAGENTD_SOCKET);
+ if (uinput_device == NULL)
+ uinput_device = g_strdup(DEFAULT_UINPUT_DEVICE);
+
memset(&act, 0, sizeof(act));
act.sa_flags = SA_RESTART;
act.sa_handler = quit_handler;
@@ -1223,5 +1234,9 @@ int main(int argc, char *argv[])
if (do_daemonize)
unlink(pidfilename);
+ g_free(portdev);
+ g_free(vdagentd_socket);
+ g_free(uinput_device);
+
return retval;
}
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Cheers,
Jakub
Victor Toso
2018-09-04 05:02:41 UTC
Permalink
Hi,
Post by Jakub Janku
Hi,
Post by Victor Toso
I think that after "vdagentd: use GMainLoop" patch, we should
also include glib's command line options.
Not sure what you are referring to here.
GLib uses environment variables instead of command line
options, AFAIK, doesn't it?
There's the gtk_get_option_group() that we use in vdagent.c,
but I don't know of anything similar for GLib.
Yep, there isn't :)
I probably got confused with gtk/glib at the time.
Post by Jakub Janku
Post by Victor Toso
Post by Jakub Janků
All command line options now have long names
as they are required by GLib.
I think that adding the following to the commit log as a quick
overview of current options and its long names might be helpful.
Sure.
Post by Victor Toso
-d, --debug
-s, --virtio-serial-port-path
-S, --vdagentd-socket
-u, --uinput-device
-f, --fake-uinput
-x, --foreground
-o, --one-session
-X, --disable-session-integration
Post by Jakub Janků
- const char * --> gchar *
- int --> gboolean
Define DEFAULT_UINPUT_DEVICE as "/dev/uinput",
since there's already DEFAULT_VIRTIO_PORT_PATH, VDAGENTD_SOCKET.
Would you mind adding Signed-off-by: Name <email> ?
(more below)
Post by Jakub Janků
---
src/vdagentd/vdagentd.c | 149 ++++++++++++++++++++++------------------
1 file changed, 82 insertions(+), 67 deletions(-)
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index 682761a..d88bbc7 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -48,6 +48,8 @@
#include "virtio-port.h"
#include "session-info.h"
+#define DEFAULT_UINPUT_DEVICE "/dev/uinput"
+
struct agent_data {
char *session;
int width;
@@ -58,12 +60,16 @@ struct agent_data {
/* variables */
static const char *pidfilename = "/var/run/spice-vdagentd/spice-vdagentd.pid";
-static const char *portdev = DEFAULT_VIRTIO_PORT_PATH;
-static const char *vdagentd_socket = VDAGENTD_SOCKET;
-static const char *uinput_device = "/dev/uinput";
+
+static gchar *portdev = NULL;
+static gchar *vdagentd_socket = NULL;
+static gchar *uinput_device = NULL;
static int debug = 0;
-static int uinput_fake = 0;
-static int only_once = 0;
+static gboolean uinput_fake = FALSE;
+static gboolean only_once = FALSE;
+static gboolean do_daemonize = TRUE;
+static gboolean want_session_info = TRUE;
+
static struct udscs_server *server = NULL;
static struct vdagent_virtio_port *virtio_port = NULL;
static GHashTable *active_xfers = NULL;
@@ -960,29 +966,6 @@ static void agent_read_complete(struct udscs_connection **connp,
/* main */
-static void usage(FILE *fp)
-{
- fprintf(fp,
- "Usage: spice-vdagentd [OPTIONS]\n\n"
- "Spice guest agent daemon, version %s.\n\n"
- "Options:\n"
- " -h print this text\n"
- " -d log debug messages (use twice for extra info)\n"
- " -s <port> set virtio serial port [%s]\n"
- " -S <filename> set vdagent Unix domain socket [%s]\n"
- " -u <dev> set uinput device [%s]\n"
- " -f treat uinput device as fake; no ioctls\n"
- " -x don't daemonize\n"
- " -o only handle one virtio serial session\n"
-#ifdef HAVE_CONSOLE_KIT
- " -X disable console kit integration\n"
-#endif
-#ifdef HAVE_LIBSYSTEMD_LOGIN
- " -X disable systemd-logind integration\n"
-#endif
- ,VERSION, portdev, vdagentd_socket, uinput_device);
-}
-
static void daemonize(void)
{
int x;
@@ -1081,52 +1064,80 @@ static void quit_handler(int sig)
quit = 1;
}
+static gboolean parse_debug_level_cb(const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error)
+{
+ debug++;
+ return TRUE;
+}
It is okay to leave this for now as this patches are touching the
command line parsing part but within the context of glib
integration, we might consider removing this in the future and
follow what might be dictated by Glib's logging system.
This parse function is necessary for the command line options
to stay the same. The higher debug level is used in
vdagentd_uinput_create(..., debug > 1, ...). So removing this
function would require adding a new option. I would do this in
a separate patch.
Yes, no need to do it now. It was more like a comment for future
enhancements. This change would need glib's output to go to
systemd journal fields by default and I think that's the case
only with glib 2.50, but I could be wrong.
Post by Jakub Janku
Post by Victor Toso
Post by Jakub Janků
+static GOptionEntry cmd_entries[] = {
+ { "debug", 'd', G_OPTION_FLAG_NO_ARG,
+ G_OPTION_ARG_CALLBACK, parse_debug_level_cb,
+ "Log debug messages (use twice for extra info)", NULL },
+
+ { "virtio-serial-port-path", 's', 0,
+ G_OPTION_ARG_STRING, &portdev,
+ "Set virtio-serial path (" DEFAULT_VIRTIO_PORT_PATH ")", NULL },
+
+ { "vdagentd-socket", 'S', 0,
+ G_OPTION_ARG_STRING, &vdagentd_socket,
+ "Set spice-vdagentd socket (" VDAGENTD_SOCKET ")", NULL },
+
+ { "uinput-device", 'u', 0,
+ G_OPTION_ARG_STRING, &uinput_device,
+ "Set uinput device (" DEFAULT_UINPUT_DEVICE ")", NULL },
+
+ { "fake-uinput", 'f', 0,
+ G_OPTION_ARG_NONE, &uinput_fake,
+ "Treat uinput device as fake; no ioctls", NULL },
+
+ { "foreground", 'x', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &do_daemonize,
+ "Do not daemonize the agent", NULL},
+
+ { "one-session", 'o', 0,
+ G_OPTION_ARG_NONE, &only_once,
+ "Only handle one virtio serial session", NULL },
+
+#if defined(HAVE_CONSOLE_KIT) || defined (HAVE_LIBSYSTEMD_LOGIN)
+ { "disable-session-integration", 'X', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &want_session_info,
+ "Disable console kit and systemd-logind integration", NULL },
+#endif
+
+ { NULL }
+};
+
int main(int argc, char *argv[])
{
- int c;
- int do_daemonize = 1;
- int want_session_info = 1;
+ GOptionContext *context;
+ GError *err = NULL;
struct sigaction act;
gboolean own_socket = TRUE;
- for (;;) {
- if (-1 == (c = getopt(argc, argv, "-dhxXfos:u:S:")))
- break;
- switch (c) {
- debug++;
- break;
- portdev = optarg;
- break;
- vdagentd_socket = optarg;
- break;
- uinput_device = optarg;
- break;
- uinput_fake = 1;
- break;
- only_once = 1;
- break;
- do_daemonize = 0;
- break;
- want_session_info = 0;
- break;
- usage(stdout);
- return 0;
- fputs("\n", stderr);
- usage(stderr);
- return 1;
- }
+ context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, cmd_entries, NULL);
+ g_option_context_set_summary(context,
+ "Spice guest agent daemon, version " VERSION);
+ g_option_context_parse(context, &argc, &argv, &err);
+ g_option_context_free(context);
+
+ if (err) {
+ g_printerr("Invalid arguments, %s\n", err->message);
We don't use g_printerr() or any g_log() in this code yet.
I think I copied it from the vdagent.c where it was added in 0ffca2b
("vdagent: Use glib's commandline parser").
I will change it to syslog().
Thanks,
Victor
Post by Jakub Janku
Post by Victor Toso
I hope we can move to that whenever we bump glib to 2.50 or above
as it writes to systemd's journal by default for daemons (AFAIK).
Feel free to submit a new version with fixes as standalone patch
with me in cc, to reduce the backlog of this series ;)
Cheers,
Victor
Post by Jakub Janků
+ g_error_free(err);
+ return 1;
}
+ if (portdev == NULL)
+ portdev = g_strdup(DEFAULT_VIRTIO_PORT_PATH);
+ if (vdagentd_socket == NULL)
+ vdagentd_socket = g_strdup(VDAGENTD_SOCKET);
+ if (uinput_device == NULL)
+ uinput_device = g_strdup(DEFAULT_UINPUT_DEVICE);
+
memset(&act, 0, sizeof(act));
act.sa_flags = SA_RESTART;
act.sa_handler = quit_handler;
@@ -1223,5 +1234,9 @@ int main(int argc, char *argv[])
if (do_daemonize)
unlink(pidfilename);
+ g_free(portdev);
+ g_free(vdagentd_socket);
+ g_free(uinput_device);
+
return retval;
}
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Cheers,
Jakub
Jakub Janku
2018-09-04 12:44:29 UTC
Permalink
Post by Victor Toso
Hi,
Post by Jakub Janku
Hi,
Post by Victor Toso
I think that after "vdagentd: use GMainLoop" patch, we should
also include glib's command line options.
Not sure what you are referring to here.
GLib uses environment variables instead of command line
options, AFAIK, doesn't it?
There's the gtk_get_option_group() that we use in vdagent.c,
but I don't know of anything similar for GLib.
Yep, there isn't :)
I probably got confused with gtk/glib at the time.
Post by Jakub Janku
Post by Victor Toso
Post by Jakub Janků
All command line options now have long names
as they are required by GLib.
I think that adding the following to the commit log as a quick
overview of current options and its long names might be helpful.
Sure.
Post by Victor Toso
-d, --debug
-s, --virtio-serial-port-path
-S, --vdagentd-socket
-u, --uinput-device
-f, --fake-uinput
-x, --foreground
-o, --one-session
-X, --disable-session-integration
Post by Jakub Janků
- const char * --> gchar *
- int --> gboolean
Define DEFAULT_UINPUT_DEVICE as "/dev/uinput",
since there's already DEFAULT_VIRTIO_PORT_PATH, VDAGENTD_SOCKET.
Would you mind adding Signed-off-by: Name <email> ?
(more below)
Post by Jakub Janků
---
src/vdagentd/vdagentd.c | 149 ++++++++++++++++++++++------------------
1 file changed, 82 insertions(+), 67 deletions(-)
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index 682761a..d88bbc7 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -48,6 +48,8 @@
#include "virtio-port.h"
#include "session-info.h"
+#define DEFAULT_UINPUT_DEVICE "/dev/uinput"
+
struct agent_data {
char *session;
int width;
@@ -58,12 +60,16 @@ struct agent_data {
/* variables */
static const char *pidfilename = "/var/run/spice-vdagentd/spice-vdagentd.pid";
-static const char *portdev = DEFAULT_VIRTIO_PORT_PATH;
-static const char *vdagentd_socket = VDAGENTD_SOCKET;
-static const char *uinput_device = "/dev/uinput";
+
+static gchar *portdev = NULL;
+static gchar *vdagentd_socket = NULL;
+static gchar *uinput_device = NULL;
static int debug = 0;
-static int uinput_fake = 0;
-static int only_once = 0;
+static gboolean uinput_fake = FALSE;
+static gboolean only_once = FALSE;
+static gboolean do_daemonize = TRUE;
+static gboolean want_session_info = TRUE;
+
static struct udscs_server *server = NULL;
static struct vdagent_virtio_port *virtio_port = NULL;
static GHashTable *active_xfers = NULL;
@@ -960,29 +966,6 @@ static void agent_read_complete(struct udscs_connection **connp,
/* main */
-static void usage(FILE *fp)
-{
- fprintf(fp,
- "Usage: spice-vdagentd [OPTIONS]\n\n"
- "Spice guest agent daemon, version %s.\n\n"
- "Options:\n"
- " -h print this text\n"
- " -d log debug messages (use twice for extra info)\n"
- " -s <port> set virtio serial port [%s]\n"
- " -S <filename> set vdagent Unix domain socket [%s]\n"
- " -u <dev> set uinput device [%s]\n"
- " -f treat uinput device as fake; no ioctls\n"
- " -x don't daemonize\n"
- " -o only handle one virtio serial session\n"
-#ifdef HAVE_CONSOLE_KIT
- " -X disable console kit integration\n"
-#endif
-#ifdef HAVE_LIBSYSTEMD_LOGIN
- " -X disable systemd-logind integration\n"
-#endif
- ,VERSION, portdev, vdagentd_socket, uinput_device);
-}
-
static void daemonize(void)
{
int x;
@@ -1081,52 +1064,80 @@ static void quit_handler(int sig)
quit = 1;
}
+static gboolean parse_debug_level_cb(const gchar *option_name,
+ const gchar *value,
+ gpointer data,
+ GError **error)
+{
+ debug++;
+ return TRUE;
+}
It is okay to leave this for now as this patches are touching the
command line parsing part but within the context of glib
integration, we might consider removing this in the future and
follow what might be dictated by Glib's logging system.
This parse function is necessary for the command line options
to stay the same. The higher debug level is used in
vdagentd_uinput_create(..., debug > 1, ...). So removing this
function would require adding a new option. I would do this in
a separate patch.
Yes, no need to do it now. It was more like a comment for future
enhancements. This change would need glib's output to go to
systemd journal fields by default and I think that's the case
only with glib 2.50, but I could be wrong.
Post by Jakub Janku
Post by Victor Toso
Post by Jakub Janků
+static GOptionEntry cmd_entries[] = {
+ { "debug", 'd', G_OPTION_FLAG_NO_ARG,
+ G_OPTION_ARG_CALLBACK, parse_debug_level_cb,
+ "Log debug messages (use twice for extra info)", NULL },
+
+ { "virtio-serial-port-path", 's', 0,
+ G_OPTION_ARG_STRING, &portdev,
+ "Set virtio-serial path (" DEFAULT_VIRTIO_PORT_PATH ")", NULL },
+
+ { "vdagentd-socket", 'S', 0,
+ G_OPTION_ARG_STRING, &vdagentd_socket,
+ "Set spice-vdagentd socket (" VDAGENTD_SOCKET ")", NULL },
+
+ { "uinput-device", 'u', 0,
+ G_OPTION_ARG_STRING, &uinput_device,
+ "Set uinput device (" DEFAULT_UINPUT_DEVICE ")", NULL },
+
+ { "fake-uinput", 'f', 0,
+ G_OPTION_ARG_NONE, &uinput_fake,
+ "Treat uinput device as fake; no ioctls", NULL },
+
+ { "foreground", 'x', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &do_daemonize,
+ "Do not daemonize the agent", NULL},
+
+ { "one-session", 'o', 0,
+ G_OPTION_ARG_NONE, &only_once,
+ "Only handle one virtio serial session", NULL },
+
+#if defined(HAVE_CONSOLE_KIT) || defined (HAVE_LIBSYSTEMD_LOGIN)
+ { "disable-session-integration", 'X', G_OPTION_FLAG_REVERSE,
+ G_OPTION_ARG_NONE, &want_session_info,
+ "Disable console kit and systemd-logind integration", NULL },
+#endif
+
+ { NULL }
+};
+
int main(int argc, char *argv[])
{
- int c;
- int do_daemonize = 1;
- int want_session_info = 1;
+ GOptionContext *context;
+ GError *err = NULL;
struct sigaction act;
gboolean own_socket = TRUE;
- for (;;) {
- if (-1 == (c = getopt(argc, argv, "-dhxXfos:u:S:")))
- break;
- switch (c) {
- debug++;
- break;
- portdev = optarg;
- break;
- vdagentd_socket = optarg;
- break;
- uinput_device = optarg;
- break;
- uinput_fake = 1;
- break;
- only_once = 1;
- break;
- do_daemonize = 0;
- break;
- want_session_info = 0;
- break;
- usage(stdout);
- return 0;
- fputs("\n", stderr);
- usage(stderr);
- return 1;
- }
+ context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, cmd_entries, NULL);
+ g_option_context_set_summary(context,
+ "Spice guest agent daemon, version " VERSION);
+ g_option_context_parse(context, &argc, &argv, &err);
+ g_option_context_free(context);
+
+ if (err) {
+ g_printerr("Invalid arguments, %s\n", err->message);
We don't use g_printerr() or any g_log() in this code yet.
I think I copied it from the vdagent.c where it was added in 0ffca2b
("vdagent: Use glib's commandline parser").
I will change it to syslog().
One thing I did not realize while I was responding to you review yesterday:
If the vdagentd was started with wrong args, we probably want to
output the "Invalid arguments" message to the stderr. However,
syslog() does not always do that, it depends on the do_daemonize var
when we call openlog(). And the do_daemonize is set during the
parsing. If the parsing fails, do_daemonize might not be set
correctly. So using g_printerr() is correct in this case, IMHO.

Jakub
Post by Victor Toso
Thanks,
Victor
Post by Jakub Janku
Post by Victor Toso
I hope we can move to that whenever we bump glib to 2.50 or above
as it writes to systemd's journal by default for daemons (AFAIK).
Feel free to submit a new version with fixes as standalone patch
with me in cc, to reduce the backlog of this series ;)
Cheers,
Victor
Post by Jakub Janků
+ g_error_free(err);
+ return 1;
}
+ if (portdev == NULL)
+ portdev = g_strdup(DEFAULT_VIRTIO_PORT_PATH);
+ if (vdagentd_socket == NULL)
+ vdagentd_socket = g_strdup(VDAGENTD_SOCKET);
+ if (uinput_device == NULL)
+ uinput_device = g_strdup(DEFAULT_UINPUT_DEVICE);
+
memset(&act, 0, sizeof(act));
act.sa_flags = SA_RESTART;
act.sa_handler = quit_handler;
@@ -1223,5 +1234,9 @@ int main(int argc, char *argv[])
if (do_daemonize)
unlink(pidfilename);
+ g_free(portdev);
+ g_free(vdagentd_socket);
+ g_free(uinput_device);
+
return retval;
}
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Cheers,
Jakub
Victor Toso
2018-09-04 13:23:21 UTC
Permalink
Post by Jakub Janku
Post by Victor Toso
Post by Jakub Janku
Post by Victor Toso
Post by Jakub Janků
+ if (err) {
+ g_printerr("Invalid arguments, %s\n", err->message);
We don't use g_printerr() or any g_log() in this code
yet.
I think I copied it from the vdagent.c where it was added
in 0ffca2b ("vdagent: Use glib's commandline parser").
I will change it to syslog().
One thing I did not realize while I was responding to you
review yesterday: If the vdagentd was started with wrong args,
we probably want to output the "Invalid arguments" message to
the stderr. However, syslog() does not always do that, it
depends on the do_daemonize var when we call openlog().
Before this commit, usage() was doing fprintf() on stderr;
g_printerr() will do fputs() on stderr, so it should be fine.
Post by Jakub Janku
do_daemonize is set during the parsing. If the parsing fails,
do_daemonize might not be set correctly. So using g_printerr()
is correct in this case, IMHO.
Thanks,
Post by Jakub Janku
Jakub
Post by Victor Toso
Thanks,
Victor
Post by Jakub Janku
Post by Victor Toso
I hope we can move to that whenever we bump glib to 2.50 or above
as it writes to systemd's journal by default for daemons (AFAIK).
Feel free to submit a new version with fixes as standalone patch
with me in cc, to reduce the backlog of this series ;)
Cheers,
Victor
Post by Jakub Janků
+ g_error_free(err);
+ return 1;
}
+ if (portdev == NULL)
+ portdev = g_strdup(DEFAULT_VIRTIO_PORT_PATH);
+ if (vdagentd_socket == NULL)
+ vdagentd_socket = g_strdup(VDAGENTD_SOCKET);
+ if (uinput_device == NULL)
+ uinput_device = g_strdup(DEFAULT_UINPUT_DEVICE);
+
memset(&act, 0, sizeof(act));
act.sa_flags = SA_RESTART;
act.sa_handler = quit_handler;
@@ -1223,5 +1234,9 @@ int main(int argc, char *argv[])
if (do_daemonize)
unlink(pidfilename);
+ g_free(portdev);
+ g_free(vdagentd_socket);
+ g_free(uinput_device);
+
return retval;
}
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Cheers,
Jakub
Jakub Janků
2018-08-14 18:53:36 UTC
Permalink
If the virtio port is destroyed explicitly
by calling vdagent_virtio_port_destroy(),
by_user is set to TRUE, otherwise to FALSE.

This will be used later with GMainLoop.
---
src/vdagentd/virtio-port.c | 24 +++++++++++++++---------
src/vdagentd/virtio-port.h | 10 +++++-----
2 files changed, 20 insertions(+), 14 deletions(-)

diff --git a/src/vdagentd/virtio-port.c b/src/vdagentd/virtio-port.c
index 3dc6f44..642c848 100644
--- a/src/vdagentd/virtio-port.c
+++ b/src/vdagentd/virtio-port.c
@@ -122,7 +122,8 @@ error:
return NULL;
}

-void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
+static void virtio_port_destroy(struct vdagent_virtio_port **vportp,
+ gboolean by_user)
{
struct vdagent_virtio_port_buf *wbuf, *next_wbuf;
struct vdagent_virtio_port *vport = *vportp;
@@ -132,7 +133,7 @@ void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
return;

if (vport->disconnect_callback)
- vport->disconnect_callback(vport);
+ vport->disconnect_callback(vport, by_user);

wbuf = vport->write_buf;
while (wbuf) {
@@ -151,6 +152,11 @@ void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
*vportp = NULL;
}

+void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
+{
+ virtio_port_destroy(vportp, TRUE);
+}
+
int vdagent_virtio_port_fill_fds(struct vdagent_virtio_port *vport,
fd_set *readfds, fd_set *writefds)
{
@@ -321,7 +327,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)
port->message_data = malloc(port->message_header.size);
if (!port->message_data) {
syslog(LOG_ERR, "out of memory, disconnecting virtio");
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
}
@@ -335,7 +341,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)

if (avail > read) {
syslog(LOG_ERR, "chunk larger than message, lost sync?");
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}

@@ -353,7 +359,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)
int r = vport->read_callback(vport, vport->chunk_header.port,
&port->message_header, port->message_data);
if (r == -1) {
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, TRUE);
return;
}
}
@@ -420,7 +426,7 @@ static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp)
return;
}
if (n <= 0) {
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
vport->opening = 0;
@@ -433,13 +439,13 @@ static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp)
if (vport->chunk_header.size > VD_AGENT_MAX_DATA_SIZE) {
syslog(LOG_ERR, "chunk size %u too large",
vport->chunk_header.size);
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
if (vport->chunk_header.port >= VDP_END_PORT) {
syslog(LOG_ERR, "chunk port %u out of range",
vport->chunk_header.port);
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
}
@@ -487,7 +493,7 @@ static void vdagent_virtio_port_do_write(struct vdagent_virtio_port **vportp)
if (errno == EINTR)
return;
syslog(LOG_ERR, "writing to vdagent virtio port: %m");
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
if (n > 0)
diff --git a/src/vdagentd/virtio-port.h b/src/vdagentd/virtio-port.h
index f899e30..3c701d6 100644
--- a/src/vdagentd/virtio-port.h
+++ b/src/vdagentd/virtio-port.h
@@ -41,14 +41,14 @@ typedef int (*vdagent_virtio_port_read_callback)(
uint8_t *data);

/* Callbacks with this type will be called when the port is disconnected.
+ If the disconnect is initiated by calling vdagent_virtio_port_destroy()
+ or by returning -1 from the vdagent_virtio_port_read_callback,
+ @by_user is set to TRUE, otherwise FALSE.
Note:
1) vdagent_virtio_port will destroy the port in question itself after
- this callback has completed!
- 2) This callback is always called, even if the disconnect is initiated
- by the vdagent_virtio_port user through returning -1 from a read
- callback, or by explicitly calling vdagent_virtio_port_destroy */
+ this callback has completed! */
typedef void (*vdagent_virtio_port_disconnect_callback)(
- struct vdagent_virtio_port *conn);
+ struct vdagent_virtio_port *conn, gboolean by_user);


/* Create a vdagent virtio port object for port portname */
--
2.17.1
Victor Toso
2018-08-28 05:52:01 UTC
Permalink
Hi,

Took me a while to get the rationale behind this so some more
info in the commit log might help.
If the virtio port is destroyed explicitly by calling
vdagent_virtio_port_destroy(), by_user is set to TRUE,
otherwise to FALSE.
One issue I had was around the 'by_user' name and its meaning.
Basically, if we want to virtio_port_destroy() without any
runtime failure, by_user would be True.

I think a GError* err would fit better, as if err != NULL we
could show err->message and retry if possible. It is also true
that, under a runtime failure, most of the times before
virtio_port_destroy() is called there is a syslog(LOG_ERR, ...)
so I think GError or even a const gchar *err_msg; would be better
fit but I might be missing something.
This will be used later with GMainLoop.
Another line about how this is related to GMainLoop might help
out while reading the patch ;)
---
src/vdagentd/virtio-port.c | 24 +++++++++++++++---------
src/vdagentd/virtio-port.h | 10 +++++-----
2 files changed, 20 insertions(+), 14 deletions(-)
diff --git a/src/vdagentd/virtio-port.c b/src/vdagentd/virtio-port.c
index 3dc6f44..642c848 100644
--- a/src/vdagentd/virtio-port.c
+++ b/src/vdagentd/virtio-port.c
return NULL;
}
-void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
+static void virtio_port_destroy(struct vdagent_virtio_port **vportp,
+ gboolean by_user)
{
struct vdagent_virtio_port_buf *wbuf, *next_wbuf;
struct vdagent_virtio_port *vport = *vportp;
@@ -132,7 +133,7 @@ void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
return;
if (vport->disconnect_callback)
- vport->disconnect_callback(vport);
+ vport->disconnect_callback(vport, by_user);
wbuf = vport->write_buf;
while (wbuf) {
@@ -151,6 +152,11 @@ void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
*vportp = NULL;
}
+void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
+{
+ virtio_port_destroy(vportp, TRUE);
+}
+
int vdagent_virtio_port_fill_fds(struct vdagent_virtio_port *vport,
fd_set *readfds, fd_set *writefds)
{
@@ -321,7 +327,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)
port->message_data = malloc(port->message_header.size);
if (!port->message_data) {
syslog(LOG_ERR, "out of memory, disconnecting virtio");
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
}
@@ -335,7 +341,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)
if (avail > read) {
syslog(LOG_ERR, "chunk larger than message, lost sync?");
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
@@ -353,7 +359,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)
int r = vport->read_callback(vport, vport->chunk_header.port,
&port->message_header, port->message_data);
if (r == -1) {
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, TRUE);
I wonder if this can't be an actual failure too? (i.e by_user =
FALSE)

Reviewed-by: Victor Toso <***@redhat.com>

Cheers,
Victor
return;
}
}
@@ -420,7 +426,7 @@ static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp)
return;
}
if (n <= 0) {
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
vport->opening = 0;
@@ -433,13 +439,13 @@ static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp)
if (vport->chunk_header.size > VD_AGENT_MAX_DATA_SIZE) {
syslog(LOG_ERR, "chunk size %u too large",
vport->chunk_header.size);
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
if (vport->chunk_header.port >= VDP_END_PORT) {
syslog(LOG_ERR, "chunk port %u out of range",
vport->chunk_header.port);
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
}
@@ -487,7 +493,7 @@ static void vdagent_virtio_port_do_write(struct vdagent_virtio_port **vportp)
if (errno == EINTR)
return;
syslog(LOG_ERR, "writing to vdagent virtio port: %m");
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
if (n > 0)
diff --git a/src/vdagentd/virtio-port.h b/src/vdagentd/virtio-port.h
index f899e30..3c701d6 100644
--- a/src/vdagentd/virtio-port.h
+++ b/src/vdagentd/virtio-port.h
@@ -41,14 +41,14 @@ typedef int (*vdagent_virtio_port_read_callback)(
uint8_t *data);
/* Callbacks with this type will be called when the port is disconnected.
+ If the disconnect is initiated by calling vdagent_virtio_port_destroy()
+ or by returning -1 from the vdagent_virtio_port_read_callback,
1) vdagent_virtio_port will destroy the port in question itself after
- this callback has completed!
- 2) This callback is always called, even if the disconnect is initiated
- by the vdagent_virtio_port user through returning -1 from a read
- callback, or by explicitly calling vdagent_virtio_port_destroy */
+ this callback has completed! */
typedef void (*vdagent_virtio_port_disconnect_callback)(
- struct vdagent_virtio_port *conn);
+ struct vdagent_virtio_port *conn, gboolean by_user);
/* Create a vdagent virtio port object for port portname */
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Jakub Janku
2018-09-03 16:20:44 UTC
Permalink
Hi,
Post by Victor Toso
Hi,
Took me a while to get the rationale behind this so some more
info in the commit log might help.
OK, will do, sorry.
Post by Victor Toso
If the virtio port is destroyed explicitly by calling
vdagent_virtio_port_destroy(), by_user is set to TRUE,
otherwise to FALSE.
One issue I had was around the 'by_user' name and its meaning.
Basically, if we want to virtio_port_destroy() without any
runtime failure, by_user would be True.
That's true. Without any runtime failure in virtio-port.c.
Post by Victor Toso
I think a GError* err would fit better, as if err != NULL we
could show err->message and retry if possible. It is also true
that, under a runtime failure, most of the times before
virtio_port_destroy() is called there is a syslog(LOG_ERR, ...)
so I think GError or even a const gchar *err_msg; would be better
fit but I might be missing something.
I agree, this is a better solution.
Post by Victor Toso
This will be used later with GMainLoop.
Another line about how this is related to GMainLoop might help
out while reading the patch ;)
---
src/vdagentd/virtio-port.c | 24 +++++++++++++++---------
src/vdagentd/virtio-port.h | 10 +++++-----
2 files changed, 20 insertions(+), 14 deletions(-)
diff --git a/src/vdagentd/virtio-port.c b/src/vdagentd/virtio-port.c
index 3dc6f44..642c848 100644
--- a/src/vdagentd/virtio-port.c
+++ b/src/vdagentd/virtio-port.c
return NULL;
}
-void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
+static void virtio_port_destroy(struct vdagent_virtio_port **vportp,
+ gboolean by_user)
{
struct vdagent_virtio_port_buf *wbuf, *next_wbuf;
struct vdagent_virtio_port *vport = *vportp;
@@ -132,7 +133,7 @@ void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
return;
if (vport->disconnect_callback)
- vport->disconnect_callback(vport);
+ vport->disconnect_callback(vport, by_user);
wbuf = vport->write_buf;
while (wbuf) {
@@ -151,6 +152,11 @@ void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
*vportp = NULL;
}
+void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
+{
+ virtio_port_destroy(vportp, TRUE);
+}
+
int vdagent_virtio_port_fill_fds(struct vdagent_virtio_port *vport,
fd_set *readfds, fd_set *writefds)
{
@@ -321,7 +327,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)
port->message_data = malloc(port->message_header.size);
if (!port->message_data) {
syslog(LOG_ERR, "out of memory, disconnecting virtio");
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
}
@@ -335,7 +341,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)
if (avail > read) {
syslog(LOG_ERR, "chunk larger than message, lost sync?");
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
@@ -353,7 +359,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)
int r = vport->read_callback(vport, vport->chunk_header.port,
&port->message_header, port->message_data);
if (r == -1) {
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, TRUE);
I wonder if this can't be an actual failure too? (i.e by_user =
FALSE)
I don't think by_user should be set to FALSE in this case.
There's no error in the virtio-port. The *user* of virtio-port
voluntarily decided to destroy the connection.
Apart from that, the current implementation never returns -1 in the
virtio_port_read_complete(), so it doesn't matter that much, I
guess...
Post by Victor Toso
Cheers,
Victor
return;
}
}
@@ -420,7 +426,7 @@ static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp)
return;
}
if (n <= 0) {
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
vport->opening = 0;
@@ -433,13 +439,13 @@ static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp)
if (vport->chunk_header.size > VD_AGENT_MAX_DATA_SIZE) {
syslog(LOG_ERR, "chunk size %u too large",
vport->chunk_header.size);
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
if (vport->chunk_header.port >= VDP_END_PORT) {
syslog(LOG_ERR, "chunk port %u out of range",
vport->chunk_header.port);
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
}
@@ -487,7 +493,7 @@ static void vdagent_virtio_port_do_write(struct vdagent_virtio_port **vportp)
if (errno == EINTR)
return;
syslog(LOG_ERR, "writing to vdagent virtio port: %m");
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
if (n > 0)
diff --git a/src/vdagentd/virtio-port.h b/src/vdagentd/virtio-port.h
index f899e30..3c701d6 100644
--- a/src/vdagentd/virtio-port.h
+++ b/src/vdagentd/virtio-port.h
@@ -41,14 +41,14 @@ typedef int (*vdagent_virtio_port_read_callback)(
uint8_t *data);
/* Callbacks with this type will be called when the port is disconnected.
+ If the disconnect is initiated by calling vdagent_virtio_port_destroy()
+ or by returning -1 from the vdagent_virtio_port_read_callback,
1) vdagent_virtio_port will destroy the port in question itself after
- this callback has completed!
- 2) This callback is always called, even if the disconnect is initiated
- by the vdagent_virtio_port user through returning -1 from a read
- callback, or by explicitly calling vdagent_virtio_port_destroy */
+ this callback has completed! */
typedef void (*vdagent_virtio_port_disconnect_callback)(
- struct vdagent_virtio_port *conn);
+ struct vdagent_virtio_port *conn, gboolean by_user);
/* Create a vdagent virtio port object for port portname */
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Victor Toso
2018-09-04 05:13:15 UTC
Permalink
Hi,
Post by Jakub Janku
Hi,
Post by Victor Toso
Hi,
Took me a while to get the rationale behind this so some more
info in the commit log might help.
OK, will do, sorry.
No worries
Post by Jakub Janku
Post by Victor Toso
If the virtio port is destroyed explicitly by calling
vdagent_virtio_port_destroy(), by_user is set to TRUE,
otherwise to FALSE.
One issue I had was around the 'by_user' name and its meaning.
Basically, if we want to virtio_port_destroy() without any
runtime failure, by_user would be True.
That's true. Without any runtime failure in virtio-port.c.
Post by Victor Toso
I think a GError* err would fit better, as if err != NULL we
could show err->message and retry if possible. It is also true
that, under a runtime failure, most of the times before
virtio_port_destroy() is called there is a syslog(LOG_ERR, ...)
so I think GError or even a const gchar *err_msg; would be better
fit but I might be missing something.
I agree, this is a better solution.
Post by Victor Toso
This will be used later with GMainLoop.
Another line about how this is related to GMainLoop might help
out while reading the patch ;)
---
src/vdagentd/virtio-port.c | 24 +++++++++++++++---------
src/vdagentd/virtio-port.h | 10 +++++-----
2 files changed, 20 insertions(+), 14 deletions(-)
diff --git a/src/vdagentd/virtio-port.c b/src/vdagentd/virtio-port.c
index 3dc6f44..642c848 100644
--- a/src/vdagentd/virtio-port.c
+++ b/src/vdagentd/virtio-port.c
return NULL;
}
-void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
+static void virtio_port_destroy(struct vdagent_virtio_port **vportp,
+ gboolean by_user)
{
struct vdagent_virtio_port_buf *wbuf, *next_wbuf;
struct vdagent_virtio_port *vport = *vportp;
@@ -132,7 +133,7 @@ void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
return;
if (vport->disconnect_callback)
- vport->disconnect_callback(vport);
+ vport->disconnect_callback(vport, by_user);
wbuf = vport->write_buf;
while (wbuf) {
@@ -151,6 +152,11 @@ void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
*vportp = NULL;
}
+void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
+{
+ virtio_port_destroy(vportp, TRUE);
+}
+
int vdagent_virtio_port_fill_fds(struct vdagent_virtio_port *vport,
fd_set *readfds, fd_set *writefds)
{
@@ -321,7 +327,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)
port->message_data = malloc(port->message_header.size);
if (!port->message_data) {
syslog(LOG_ERR, "out of memory, disconnecting virtio");
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
}
@@ -335,7 +341,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)
if (avail > read) {
syslog(LOG_ERR, "chunk larger than message, lost sync?");
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
@@ -353,7 +359,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)
int r = vport->read_callback(vport, vport->chunk_header.port,
&port->message_header, port->message_data);
if (r == -1) {
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, TRUE);
I wonder if this can't be an actual failure too? (i.e by_user =
FALSE)
I don't think by_user should be set to FALSE in this case.
There's no error in the virtio-port. The *user* of virtio-port
voluntarily decided to destroy the connection.
Right
Post by Jakub Janku
Apart from that, the current implementation never returns -1 in the
virtio_port_read_complete(), so it doesn't matter that much, I
guess...
Ouch. Seems that it is like this for long time :(
Post by Jakub Janku
Post by Victor Toso
Cheers,
Victor
return;
}
}
@@ -420,7 +426,7 @@ static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp)
return;
}
if (n <= 0) {
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
vport->opening = 0;
@@ -433,13 +439,13 @@ static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp)
if (vport->chunk_header.size > VD_AGENT_MAX_DATA_SIZE) {
syslog(LOG_ERR, "chunk size %u too large",
vport->chunk_header.size);
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
if (vport->chunk_header.port >= VDP_END_PORT) {
syslog(LOG_ERR, "chunk port %u out of range",
vport->chunk_header.port);
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
}
@@ -487,7 +493,7 @@ static void vdagent_virtio_port_do_write(struct vdagent_virtio_port **vportp)
if (errno == EINTR)
return;
syslog(LOG_ERR, "writing to vdagent virtio port: %m");
- vdagent_virtio_port_destroy(vportp);
+ virtio_port_destroy(vportp, FALSE);
return;
}
if (n > 0)
diff --git a/src/vdagentd/virtio-port.h b/src/vdagentd/virtio-port.h
index f899e30..3c701d6 100644
--- a/src/vdagentd/virtio-port.h
+++ b/src/vdagentd/virtio-port.h
@@ -41,14 +41,14 @@ typedef int (*vdagent_virtio_port_read_callback)(
uint8_t *data);
/* Callbacks with this type will be called when the port is disconnected.
+ If the disconnect is initiated by calling vdagent_virtio_port_destroy()
+ or by returning -1 from the vdagent_virtio_port_read_callback,
1) vdagent_virtio_port will destroy the port in question itself after
- this callback has completed!
- 2) This callback is always called, even if the disconnect is initiated
- by the vdagent_virtio_port user through returning -1 from a read
- callback, or by explicitly calling vdagent_virtio_port_destroy */
+ this callback has completed! */
typedef void (*vdagent_virtio_port_disconnect_callback)(
- struct vdagent_virtio_port *conn);
+ struct vdagent_virtio_port *conn, gboolean by_user);
/* Create a vdagent virtio port object for port portname */
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Jakub Janků
2018-08-14 18:53:37 UTC
Permalink
This is purely a preparatory patch as it renders
the vdagentd non-functional.

Remove main while loop with FD polling.

udscs, virtio-port and session-info will be integrated
into the GMainLoop in the following commits.

Use g_unix_signal_add() to handle SIGINT, SIGHUP, SIGTERM.
SIGQUIT handling is not supported by GLib.
---
src/vdagentd/vdagentd.c | 129 ++++++++++++++--------------------------
1 file changed, 44 insertions(+), 85 deletions(-)

diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index d88bbc7..8abc63c 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -31,10 +31,9 @@
#include <errno.h>
#include <signal.h>
#include <syslog.h>
-#include <sys/select.h>
#include <sys/stat.h>
#include <spice/vd_agent.h>
-#include <glib.h>
+#include <glib-unix.h>

#ifdef WITH_SYSTEMD_SOCKET_ACTIVATION
#include <systemd/sd-daemon.h>
@@ -82,11 +81,12 @@ static const char *active_session = NULL;
static unsigned int session_count = 0;
static struct udscs_connection *active_session_conn = NULL;
static int agent_owns_clipboard[256] = { 0, };
-static int quit = 0;
static int retval = 0;
static int client_connected = 0;
static int max_clipboard = -1;

+static GMainLoop *loop;
+
/* utility functions */
static void virtio_msg_uint32_to_le(uint8_t *_msg, uint32_t size, uint32_t offset)
{
@@ -175,7 +175,7 @@ void do_client_mouse(struct vdagentd_uinput **uinputp, VDAgentMouseState *mouse)
if (!*uinputp) {
syslog(LOG_CRIT, "Fatal uinput error");
retval = 1;
- quit = 1;
+ g_main_loop_quit(loop);
}
}
}
@@ -588,6 +588,33 @@ static int virtio_port_read_complete(
return 0;
}

+static void virtio_port_disconnect_cb(struct vdagent_virtio_port *vport,
+ gboolean by_user)
+{
+ virtio_port = NULL;
+
+ if (!g_main_loop_is_running(loop))
+ return;
+
+ if (by_user == FALSE) {
+ /* virtio_port was destroyed because of an internal error */
+ gboolean old_client_connected = client_connected;
+ syslog(LOG_CRIT, "AIIEEE lost spice client connection, reconnecting");
+ virtio_port = vdagent_virtio_port_create(portdev,
+ virtio_port_read_complete,
+ virtio_port_disconnect_cb);
+ if (virtio_port == NULL) {
+ syslog(LOG_CRIT, "Fatal error opening vdagent virtio channel");
+ retval = 1;
+ g_main_loop_quit(loop);
+ return;
+ }
+ do_client_disconnect();
+ client_connected = old_client_connected;
+ } else if (only_once)
+ g_main_loop_quit(loop);
+}
+
static void virtio_write_clipboard(uint8_t selection, uint32_t msg_type,
uint32_t data_type, uint8_t *data, uint32_t data_size)
{
@@ -727,7 +754,7 @@ static void check_xorg_resolution(void)
if (!uinput) {
syslog(LOG_CRIT, "Fatal uinput error");
retval = 1;
- quit = 1;
+ g_main_loop_quit(loop);
return;
}

@@ -735,11 +762,11 @@ static void check_xorg_resolution(void)
syslog(LOG_INFO, "opening vdagent virtio channel");
virtio_port = vdagent_virtio_port_create(portdev,
virtio_port_read_complete,
- NULL);
+ virtio_port_disconnect_cb);
if (!virtio_port) {
syslog(LOG_CRIT, "Fatal error opening vdagent virtio channel");
retval = 1;
- quit = 1;
+ g_main_loop_quit(loop);
return;
}
send_capabilities(virtio_port, 1);
@@ -992,76 +1019,10 @@ static void daemonize(void)
}
}

-static void main_loop(void)
+static gboolean signal_handler(gpointer user_data)
{
- fd_set readfds, writefds;
- int n, nfds;
- int ck_fd = 0;
- int once = 0;
-
- while (!quit) {
- FD_ZERO(&readfds);
- FD_ZERO(&writefds);
-
- nfds = udscs_server_fill_fds(server, &readfds, &writefds);
- n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
- if (n >= nfds)
- nfds = n + 1;
-
- if (session_info) {
- ck_fd = session_info_get_fd(session_info);
- FD_SET(ck_fd, &readfds);
- if (ck_fd >= nfds)
- nfds = ck_fd + 1;
- }
-
- n = select(nfds, &readfds, &writefds, NULL, NULL);
- if (n == -1) {
- if (errno == EINTR)
- continue;
- syslog(LOG_CRIT, "Fatal error select: %m");
- retval = 1;
- break;
- }
-
- udscs_server_handle_fds(server, &readfds, &writefds);
-
- if (virtio_port) {
- once = 1;
- vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds);
- if (!virtio_port) {
- int old_client_connected = client_connected;
- syslog(LOG_CRIT,
- "AIIEEE lost spice client connection, reconnecting");
- virtio_port = vdagent_virtio_port_create(portdev,
- virtio_port_read_complete,
- NULL);
- if (!virtio_port) {
- syslog(LOG_CRIT,
- "Fatal error opening vdagent virtio channel");
- retval = 1;
- break;
- }
- do_client_disconnect();
- client_connected = old_client_connected;
- }
- }
- else if (only_once && once)
- {
- syslog(LOG_INFO, "Exiting after one client session.");
- break;
- }
-
- if (session_info && FD_ISSET(ck_fd, &readfds)) {
- active_session = session_info_get_active_session(session_info);
- update_active_session_connection(NULL);
- }
- }
-}
-
-static void quit_handler(int sig)
-{
- quit = 1;
+ g_main_loop_quit(loop);
+ return G_SOURCE_REMOVE;
}

static gboolean parse_debug_level_cb(const gchar *option_name,
@@ -1115,7 +1076,6 @@ int main(int argc, char *argv[])
{
GOptionContext *context;
GError *err = NULL;
- struct sigaction act;
gboolean own_socket = TRUE;

context = g_option_context_new(NULL);
@@ -1138,13 +1098,9 @@ int main(int argc, char *argv[])
if (uinput_device == NULL)
uinput_device = g_strdup(DEFAULT_UINPUT_DEVICE);

- memset(&act, 0, sizeof(act));
- act.sa_flags = SA_RESTART;
- act.sa_handler = quit_handler;
- sigaction(SIGINT, &act, NULL);
- sigaction(SIGHUP, &act, NULL);
- sigaction(SIGTERM, &act, NULL);
- sigaction(SIGQUIT, &act, NULL);
+ g_unix_signal_add(SIGINT, signal_handler, NULL);
+ g_unix_signal_add(SIGHUP, signal_handler, NULL);
+ g_unix_signal_add(SIGTERM, signal_handler, NULL);

openlog("spice-vdagentd", do_daemonize ? 0 : LOG_PERROR, LOG_USER);

@@ -1214,7 +1170,9 @@ int main(int argc, char *argv[])
syslog(LOG_WARNING, "no session info, max 1 session agent allowed");

active_xfers = g_hash_table_new(g_direct_hash, g_direct_equal);
- main_loop();
+
+ loop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(loop);

release_clipboards();

@@ -1223,6 +1181,7 @@ int main(int argc, char *argv[])
vdagent_virtio_port_destroy(&virtio_port);
session_info_destroy(session_info);
udscs_destroy_server(server);
+ g_main_loop_unref(loop);

/* leave the socket around if it was provided by systemd */
if (own_socket) {
--
2.17.1
Victor Toso
2018-08-28 07:38:56 UTC
Permalink
Hi,
Post by Jakub Janků
This is purely a preparatory patch as it renders
the vdagentd non-functional.
I would rather not break it unless it really helps a lot. It was
possible to do it for vdagent code at 3fcf2e944ae3bf7, not sure
why we can't here.

If it is really the case that it is better to have a commit with
non functional vdagentd, the commit log must state the rationale
behind it and which commit it is expected to have it working
again (the immediate follow-up is better, I guess)

Reviewed-by: Victor Toso <***@redhat.com>

Victor
Post by Jakub Janků
Remove main while loop with FD polling.
udscs, virtio-port and session-info will be integrated
into the GMainLoop in the following commits.
Use g_unix_signal_add() to handle SIGINT, SIGHUP, SIGTERM.
SIGQUIT handling is not supported by GLib.
---
src/vdagentd/vdagentd.c | 129 ++++++++++++++--------------------------
1 file changed, 44 insertions(+), 85 deletions(-)
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index d88bbc7..8abc63c 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -31,10 +31,9 @@
#include <errno.h>
#include <signal.h>
#include <syslog.h>
-#include <sys/select.h>
#include <sys/stat.h>
#include <spice/vd_agent.h>
-#include <glib.h>
+#include <glib-unix.h>
#ifdef WITH_SYSTEMD_SOCKET_ACTIVATION
#include <systemd/sd-daemon.h>
@@ -82,11 +81,12 @@ static const char *active_session = NULL;
static unsigned int session_count = 0;
static struct udscs_connection *active_session_conn = NULL;
static int agent_owns_clipboard[256] = { 0, };
-static int quit = 0;
static int retval = 0;
static int client_connected = 0;
static int max_clipboard = -1;
+static GMainLoop *loop;
+
/* utility functions */
static void virtio_msg_uint32_to_le(uint8_t *_msg, uint32_t size, uint32_t offset)
{
@@ -175,7 +175,7 @@ void do_client_mouse(struct vdagentd_uinput **uinputp, VDAgentMouseState *mouse)
if (!*uinputp) {
syslog(LOG_CRIT, "Fatal uinput error");
retval = 1;
- quit = 1;
+ g_main_loop_quit(loop);
}
}
}
@@ -588,6 +588,33 @@ static int virtio_port_read_complete(
return 0;
}
+static void virtio_port_disconnect_cb(struct vdagent_virtio_port *vport,
+ gboolean by_user)
+{
+ virtio_port = NULL;
+
+ if (!g_main_loop_is_running(loop))
+ return;
+
+ if (by_user == FALSE) {
+ /* virtio_port was destroyed because of an internal error */
+ gboolean old_client_connected = client_connected;
+ syslog(LOG_CRIT, "AIIEEE lost spice client connection, reconnecting");
+ virtio_port = vdagent_virtio_port_create(portdev,
+ virtio_port_read_complete,
+ virtio_port_disconnect_cb);
+ if (virtio_port == NULL) {
+ syslog(LOG_CRIT, "Fatal error opening vdagent virtio channel");
+ retval = 1;
+ g_main_loop_quit(loop);
+ return;
+ }
+ do_client_disconnect();
+ client_connected = old_client_connected;
+ } else if (only_once)
+ g_main_loop_quit(loop);
+}
+
static void virtio_write_clipboard(uint8_t selection, uint32_t msg_type,
uint32_t data_type, uint8_t *data, uint32_t data_size)
{
@@ -727,7 +754,7 @@ static void check_xorg_resolution(void)
if (!uinput) {
syslog(LOG_CRIT, "Fatal uinput error");
retval = 1;
- quit = 1;
+ g_main_loop_quit(loop);
return;
}
@@ -735,11 +762,11 @@ static void check_xorg_resolution(void)
syslog(LOG_INFO, "opening vdagent virtio channel");
virtio_port = vdagent_virtio_port_create(portdev,
virtio_port_read_complete,
- NULL);
+ virtio_port_disconnect_cb);
if (!virtio_port) {
syslog(LOG_CRIT, "Fatal error opening vdagent virtio channel");
retval = 1;
- quit = 1;
+ g_main_loop_quit(loop);
return;
}
send_capabilities(virtio_port, 1);
@@ -992,76 +1019,10 @@ static void daemonize(void)
}
}
-static void main_loop(void)
+static gboolean signal_handler(gpointer user_data)
{
- fd_set readfds, writefds;
- int n, nfds;
- int ck_fd = 0;
- int once = 0;
-
- while (!quit) {
- FD_ZERO(&readfds);
- FD_ZERO(&writefds);
-
- nfds = udscs_server_fill_fds(server, &readfds, &writefds);
- n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
- if (n >= nfds)
- nfds = n + 1;
-
- if (session_info) {
- ck_fd = session_info_get_fd(session_info);
- FD_SET(ck_fd, &readfds);
- if (ck_fd >= nfds)
- nfds = ck_fd + 1;
- }
-
- n = select(nfds, &readfds, &writefds, NULL, NULL);
- if (n == -1) {
- if (errno == EINTR)
- continue;
- syslog(LOG_CRIT, "Fatal error select: %m");
- retval = 1;
- break;
- }
-
- udscs_server_handle_fds(server, &readfds, &writefds);
-
- if (virtio_port) {
- once = 1;
- vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds);
- if (!virtio_port) {
- int old_client_connected = client_connected;
- syslog(LOG_CRIT,
- "AIIEEE lost spice client connection, reconnecting");
- virtio_port = vdagent_virtio_port_create(portdev,
- virtio_port_read_complete,
- NULL);
- if (!virtio_port) {
- syslog(LOG_CRIT,
- "Fatal error opening vdagent virtio channel");
- retval = 1;
- break;
- }
- do_client_disconnect();
- client_connected = old_client_connected;
- }
- }
- else if (only_once && once)
- {
- syslog(LOG_INFO, "Exiting after one client session.");
- break;
- }
-
- if (session_info && FD_ISSET(ck_fd, &readfds)) {
- active_session = session_info_get_active_session(session_info);
- update_active_session_connection(NULL);
- }
- }
-}
-
-static void quit_handler(int sig)
-{
- quit = 1;
+ g_main_loop_quit(loop);
+ return G_SOURCE_REMOVE;
}
static gboolean parse_debug_level_cb(const gchar *option_name,
@@ -1115,7 +1076,6 @@ int main(int argc, char *argv[])
{
GOptionContext *context;
GError *err = NULL;
- struct sigaction act;
gboolean own_socket = TRUE;
context = g_option_context_new(NULL);
@@ -1138,13 +1098,9 @@ int main(int argc, char *argv[])
if (uinput_device == NULL)
uinput_device = g_strdup(DEFAULT_UINPUT_DEVICE);
- memset(&act, 0, sizeof(act));
- act.sa_flags = SA_RESTART;
- act.sa_handler = quit_handler;
- sigaction(SIGINT, &act, NULL);
- sigaction(SIGHUP, &act, NULL);
- sigaction(SIGTERM, &act, NULL);
- sigaction(SIGQUIT, &act, NULL);
+ g_unix_signal_add(SIGINT, signal_handler, NULL);
+ g_unix_signal_add(SIGHUP, signal_handler, NULL);
+ g_unix_signal_add(SIGTERM, signal_handler, NULL);
openlog("spice-vdagentd", do_daemonize ? 0 : LOG_PERROR, LOG_USER);
@@ -1214,7 +1170,9 @@ int main(int argc, char *argv[])
syslog(LOG_WARNING, "no session info, max 1 session agent allowed");
active_xfers = g_hash_table_new(g_direct_hash, g_direct_equal);
- main_loop();
+
+ loop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(loop);
release_clipboards();
@@ -1223,6 +1181,7 @@ int main(int argc, char *argv[])
vdagent_virtio_port_destroy(&virtio_port);
session_info_destroy(session_info);
udscs_destroy_server(server);
+ g_main_loop_unref(loop);
/* leave the socket around if it was provided by systemd */
if (own_socket) {
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Jakub Janku
2018-09-03 16:38:58 UTC
Permalink
Hi Victor,
Post by Victor Toso
Hi,
Post by Jakub Janků
This is purely a preparatory patch as it renders
the vdagentd non-functional.
I would rather not break it unless it really helps a lot. It was
possible to do it for vdagent code at 3fcf2e944ae3bf7, not sure
why we can't here.
We surely can :) but the situation in vdagent.c was simpler, IMHO.
In vdagent.c, we started using GMainLoop and g_io_add_watch() both in
one commit which was relatively small.

If we wanted to do the same here, we'd have to squash these 3 commits:
* "vdagentd: use GMainLoop"
* "udscs: use VDAgentConnection"
* "vport: use VDAgentConnection"
Apart from that, g_io_add_watch() would need to be used with the
session_info_get_fd().

The resulting patch would be huge, so I opted for this variant.
I don't know what's the best way to go, I'll leave it up to you ;)

Cheers,
Jakub
Post by Victor Toso
If it is really the case that it is better to have a commit with
non functional vdagentd, the commit log must state the rationale
behind it and which commit it is expected to have it working
again (the immediate follow-up is better, I guess)
Victor
Post by Jakub Janků
Remove main while loop with FD polling.
udscs, virtio-port and session-info will be integrated
into the GMainLoop in the following commits.
Use g_unix_signal_add() to handle SIGINT, SIGHUP, SIGTERM.
SIGQUIT handling is not supported by GLib.
---
src/vdagentd/vdagentd.c | 129 ++++++++++++++--------------------------
1 file changed, 44 insertions(+), 85 deletions(-)
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index d88bbc7..8abc63c 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -31,10 +31,9 @@
#include <errno.h>
#include <signal.h>
#include <syslog.h>
-#include <sys/select.h>
#include <sys/stat.h>
#include <spice/vd_agent.h>
-#include <glib.h>
+#include <glib-unix.h>
#ifdef WITH_SYSTEMD_SOCKET_ACTIVATION
#include <systemd/sd-daemon.h>
@@ -82,11 +81,12 @@ static const char *active_session = NULL;
static unsigned int session_count = 0;
static struct udscs_connection *active_session_conn = NULL;
static int agent_owns_clipboard[256] = { 0, };
-static int quit = 0;
static int retval = 0;
static int client_connected = 0;
static int max_clipboard = -1;
+static GMainLoop *loop;
+
/* utility functions */
static void virtio_msg_uint32_to_le(uint8_t *_msg, uint32_t size, uint32_t offset)
{
@@ -175,7 +175,7 @@ void do_client_mouse(struct vdagentd_uinput **uinputp, VDAgentMouseState *mouse)
if (!*uinputp) {
syslog(LOG_CRIT, "Fatal uinput error");
retval = 1;
- quit = 1;
+ g_main_loop_quit(loop);
}
}
}
@@ -588,6 +588,33 @@ static int virtio_port_read_complete(
return 0;
}
+static void virtio_port_disconnect_cb(struct vdagent_virtio_port *vport,
+ gboolean by_user)
+{
+ virtio_port = NULL;
+
+ if (!g_main_loop_is_running(loop))
+ return;
+
+ if (by_user == FALSE) {
+ /* virtio_port was destroyed because of an internal error */
+ gboolean old_client_connected = client_connected;
+ syslog(LOG_CRIT, "AIIEEE lost spice client connection, reconnecting");
+ virtio_port = vdagent_virtio_port_create(portdev,
+ virtio_port_read_complete,
+ virtio_port_disconnect_cb);
+ if (virtio_port == NULL) {
+ syslog(LOG_CRIT, "Fatal error opening vdagent virtio channel");
+ retval = 1;
+ g_main_loop_quit(loop);
+ return;
+ }
+ do_client_disconnect();
+ client_connected = old_client_connected;
+ } else if (only_once)
+ g_main_loop_quit(loop);
+}
+
static void virtio_write_clipboard(uint8_t selection, uint32_t msg_type,
uint32_t data_type, uint8_t *data, uint32_t data_size)
{
@@ -727,7 +754,7 @@ static void check_xorg_resolution(void)
if (!uinput) {
syslog(LOG_CRIT, "Fatal uinput error");
retval = 1;
- quit = 1;
+ g_main_loop_quit(loop);
return;
}
@@ -735,11 +762,11 @@ static void check_xorg_resolution(void)
syslog(LOG_INFO, "opening vdagent virtio channel");
virtio_port = vdagent_virtio_port_create(portdev,
virtio_port_read_complete,
- NULL);
+ virtio_port_disconnect_cb);
if (!virtio_port) {
syslog(LOG_CRIT, "Fatal error opening vdagent virtio channel");
retval = 1;
- quit = 1;
+ g_main_loop_quit(loop);
return;
}
send_capabilities(virtio_port, 1);
@@ -992,76 +1019,10 @@ static void daemonize(void)
}
}
-static void main_loop(void)
+static gboolean signal_handler(gpointer user_data)
{
- fd_set readfds, writefds;
- int n, nfds;
- int ck_fd = 0;
- int once = 0;
-
- while (!quit) {
- FD_ZERO(&readfds);
- FD_ZERO(&writefds);
-
- nfds = udscs_server_fill_fds(server, &readfds, &writefds);
- n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
- if (n >= nfds)
- nfds = n + 1;
-
- if (session_info) {
- ck_fd = session_info_get_fd(session_info);
- FD_SET(ck_fd, &readfds);
- if (ck_fd >= nfds)
- nfds = ck_fd + 1;
- }
-
- n = select(nfds, &readfds, &writefds, NULL, NULL);
- if (n == -1) {
- if (errno == EINTR)
- continue;
- syslog(LOG_CRIT, "Fatal error select: %m");
- retval = 1;
- break;
- }
-
- udscs_server_handle_fds(server, &readfds, &writefds);
-
- if (virtio_port) {
- once = 1;
- vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds);
- if (!virtio_port) {
- int old_client_connected = client_connected;
- syslog(LOG_CRIT,
- "AIIEEE lost spice client connection, reconnecting");
- virtio_port = vdagent_virtio_port_create(portdev,
- virtio_port_read_complete,
- NULL);
- if (!virtio_port) {
- syslog(LOG_CRIT,
- "Fatal error opening vdagent virtio channel");
- retval = 1;
- break;
- }
- do_client_disconnect();
- client_connected = old_client_connected;
- }
- }
- else if (only_once && once)
- {
- syslog(LOG_INFO, "Exiting after one client session.");
- break;
- }
-
- if (session_info && FD_ISSET(ck_fd, &readfds)) {
- active_session = session_info_get_active_session(session_info);
- update_active_session_connection(NULL);
- }
- }
-}
-
-static void quit_handler(int sig)
-{
- quit = 1;
+ g_main_loop_quit(loop);
+ return G_SOURCE_REMOVE;
}
static gboolean parse_debug_level_cb(const gchar *option_name,
@@ -1115,7 +1076,6 @@ int main(int argc, char *argv[])
{
GOptionContext *context;
GError *err = NULL;
- struct sigaction act;
gboolean own_socket = TRUE;
context = g_option_context_new(NULL);
@@ -1138,13 +1098,9 @@ int main(int argc, char *argv[])
if (uinput_device == NULL)
uinput_device = g_strdup(DEFAULT_UINPUT_DEVICE);
- memset(&act, 0, sizeof(act));
- act.sa_flags = SA_RESTART;
- act.sa_handler = quit_handler;
- sigaction(SIGINT, &act, NULL);
- sigaction(SIGHUP, &act, NULL);
- sigaction(SIGTERM, &act, NULL);
- sigaction(SIGQUIT, &act, NULL);
+ g_unix_signal_add(SIGINT, signal_handler, NULL);
+ g_unix_signal_add(SIGHUP, signal_handler, NULL);
+ g_unix_signal_add(SIGTERM, signal_handler, NULL);
openlog("spice-vdagentd", do_daemonize ? 0 : LOG_PERROR, LOG_USER);
@@ -1214,7 +1170,9 @@ int main(int argc, char *argv[])
syslog(LOG_WARNING, "no session info, max 1 session agent allowed");
active_xfers = g_hash_table_new(g_direct_hash, g_direct_equal);
- main_loop();
+
+ loop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(loop);
release_clipboards();
@@ -1223,6 +1181,7 @@ int main(int argc, char *argv[])
vdagent_virtio_port_destroy(&virtio_port);
session_info_destroy(session_info);
udscs_destroy_server(server);
+ g_main_loop_unref(loop);
/* leave the socket around if it was provided by systemd */
if (own_socket) {
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Victor Toso
2018-09-04 05:44:22 UTC
Permalink
Hi,
Post by Jakub Janku
Hi Victor,
Post by Victor Toso
Hi,
Post by Jakub Janků
This is purely a preparatory patch as it renders
the vdagentd non-functional.
I would rather not break it unless it really helps a lot. It was
possible to do it for vdagent code at 3fcf2e944ae3bf7, not sure
why we can't here.
We surely can :) but the situation in vdagent.c was simpler, IMHO.
In vdagent.c, we started using GMainLoop and g_io_add_watch() both in
one commit which was relatively small.
* "vdagentd: use GMainLoop"
* "udscs: use VDAgentConnection"
* "vport: use VDAgentConnection"
Apart from that, g_io_add_watch() would need to be used with the
session_info_get_fd().
The resulting patch would be huge, so I opted for this variant.
I don't know what's the best way to go, I'll leave it up to you ;)
Right, I trust your judgment and I'll review with that in mind in
the next iteration but for sure, if something is not working we
should add it in commit log.

eg:
- This commit does not build and $reason_to_have_it
- This commit breaks $feature and $reason_to_have_it
- This commit introduces $bug and $reason_to_have_it

And future commit in the series that makes code stable should
have that info too.

Besides helping review and another set of eyes looking for an
alternative to that, etc. this helps for sure when try to find a
problematic commit with git bisect.

Thanks again for your work on this.
Victor
Post by Jakub Janku
Cheers,
Jakub
Post by Victor Toso
If it is really the case that it is better to have a commit with
non functional vdagentd, the commit log must state the rationale
behind it and which commit it is expected to have it working
again (the immediate follow-up is better, I guess)
Victor
Post by Jakub Janků
Remove main while loop with FD polling.
udscs, virtio-port and session-info will be integrated
into the GMainLoop in the following commits.
Use g_unix_signal_add() to handle SIGINT, SIGHUP, SIGTERM.
SIGQUIT handling is not supported by GLib.
---
src/vdagentd/vdagentd.c | 129 ++++++++++++++--------------------------
1 file changed, 44 insertions(+), 85 deletions(-)
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index d88bbc7..8abc63c 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -31,10 +31,9 @@
#include <errno.h>
#include <signal.h>
#include <syslog.h>
-#include <sys/select.h>
#include <sys/stat.h>
#include <spice/vd_agent.h>
-#include <glib.h>
+#include <glib-unix.h>
#ifdef WITH_SYSTEMD_SOCKET_ACTIVATION
#include <systemd/sd-daemon.h>
@@ -82,11 +81,12 @@ static const char *active_session = NULL;
static unsigned int session_count = 0;
static struct udscs_connection *active_session_conn = NULL;
static int agent_owns_clipboard[256] = { 0, };
-static int quit = 0;
static int retval = 0;
static int client_connected = 0;
static int max_clipboard = -1;
+static GMainLoop *loop;
+
/* utility functions */
static void virtio_msg_uint32_to_le(uint8_t *_msg, uint32_t size, uint32_t offset)
{
@@ -175,7 +175,7 @@ void do_client_mouse(struct vdagentd_uinput **uinputp, VDAgentMouseState *mouse)
if (!*uinputp) {
syslog(LOG_CRIT, "Fatal uinput error");
retval = 1;
- quit = 1;
+ g_main_loop_quit(loop);
}
}
}
@@ -588,6 +588,33 @@ static int virtio_port_read_complete(
return 0;
}
+static void virtio_port_disconnect_cb(struct vdagent_virtio_port *vport,
+ gboolean by_user)
+{
+ virtio_port = NULL;
+
+ if (!g_main_loop_is_running(loop))
+ return;
+
+ if (by_user == FALSE) {
+ /* virtio_port was destroyed because of an internal error */
+ gboolean old_client_connected = client_connected;
+ syslog(LOG_CRIT, "AIIEEE lost spice client connection, reconnecting");
+ virtio_port = vdagent_virtio_port_create(portdev,
+ virtio_port_read_complete,
+ virtio_port_disconnect_cb);
+ if (virtio_port == NULL) {
+ syslog(LOG_CRIT, "Fatal error opening vdagent virtio channel");
+ retval = 1;
+ g_main_loop_quit(loop);
+ return;
+ }
+ do_client_disconnect();
+ client_connected = old_client_connected;
+ } else if (only_once)
+ g_main_loop_quit(loop);
+}
+
static void virtio_write_clipboard(uint8_t selection, uint32_t msg_type,
uint32_t data_type, uint8_t *data, uint32_t data_size)
{
@@ -727,7 +754,7 @@ static void check_xorg_resolution(void)
if (!uinput) {
syslog(LOG_CRIT, "Fatal uinput error");
retval = 1;
- quit = 1;
+ g_main_loop_quit(loop);
return;
}
@@ -735,11 +762,11 @@ static void check_xorg_resolution(void)
syslog(LOG_INFO, "opening vdagent virtio channel");
virtio_port = vdagent_virtio_port_create(portdev,
virtio_port_read_complete,
- NULL);
+ virtio_port_disconnect_cb);
if (!virtio_port) {
syslog(LOG_CRIT, "Fatal error opening vdagent virtio channel");
retval = 1;
- quit = 1;
+ g_main_loop_quit(loop);
return;
}
send_capabilities(virtio_port, 1);
@@ -992,76 +1019,10 @@ static void daemonize(void)
}
}
-static void main_loop(void)
+static gboolean signal_handler(gpointer user_data)
{
- fd_set readfds, writefds;
- int n, nfds;
- int ck_fd = 0;
- int once = 0;
-
- while (!quit) {
- FD_ZERO(&readfds);
- FD_ZERO(&writefds);
-
- nfds = udscs_server_fill_fds(server, &readfds, &writefds);
- n = vdagent_virtio_port_fill_fds(virtio_port, &readfds, &writefds);
- if (n >= nfds)
- nfds = n + 1;
-
- if (session_info) {
- ck_fd = session_info_get_fd(session_info);
- FD_SET(ck_fd, &readfds);
- if (ck_fd >= nfds)
- nfds = ck_fd + 1;
- }
-
- n = select(nfds, &readfds, &writefds, NULL, NULL);
- if (n == -1) {
- if (errno == EINTR)
- continue;
- syslog(LOG_CRIT, "Fatal error select: %m");
- retval = 1;
- break;
- }
-
- udscs_server_handle_fds(server, &readfds, &writefds);
-
- if (virtio_port) {
- once = 1;
- vdagent_virtio_port_handle_fds(&virtio_port, &readfds, &writefds);
- if (!virtio_port) {
- int old_client_connected = client_connected;
- syslog(LOG_CRIT,
- "AIIEEE lost spice client connection, reconnecting");
- virtio_port = vdagent_virtio_port_create(portdev,
- virtio_port_read_complete,
- NULL);
- if (!virtio_port) {
- syslog(LOG_CRIT,
- "Fatal error opening vdagent virtio channel");
- retval = 1;
- break;
- }
- do_client_disconnect();
- client_connected = old_client_connected;
- }
- }
- else if (only_once && once)
- {
- syslog(LOG_INFO, "Exiting after one client session.");
- break;
- }
-
- if (session_info && FD_ISSET(ck_fd, &readfds)) {
- active_session = session_info_get_active_session(session_info);
- update_active_session_connection(NULL);
- }
- }
-}
-
-static void quit_handler(int sig)
-{
- quit = 1;
+ g_main_loop_quit(loop);
+ return G_SOURCE_REMOVE;
}
static gboolean parse_debug_level_cb(const gchar *option_name,
@@ -1115,7 +1076,6 @@ int main(int argc, char *argv[])
{
GOptionContext *context;
GError *err = NULL;
- struct sigaction act;
gboolean own_socket = TRUE;
context = g_option_context_new(NULL);
@@ -1138,13 +1098,9 @@ int main(int argc, char *argv[])
if (uinput_device == NULL)
uinput_device = g_strdup(DEFAULT_UINPUT_DEVICE);
- memset(&act, 0, sizeof(act));
- act.sa_flags = SA_RESTART;
- act.sa_handler = quit_handler;
- sigaction(SIGINT, &act, NULL);
- sigaction(SIGHUP, &act, NULL);
- sigaction(SIGTERM, &act, NULL);
- sigaction(SIGQUIT, &act, NULL);
+ g_unix_signal_add(SIGINT, signal_handler, NULL);
+ g_unix_signal_add(SIGHUP, signal_handler, NULL);
+ g_unix_signal_add(SIGTERM, signal_handler, NULL);
openlog("spice-vdagentd", do_daemonize ? 0 : LOG_PERROR, LOG_USER);
@@ -1214,7 +1170,9 @@ int main(int argc, char *argv[])
syslog(LOG_WARNING, "no session info, max 1 session agent allowed");
active_xfers = g_hash_table_new(g_direct_hash, g_direct_equal);
- main_loop();
+
+ loop = g_main_loop_new(NULL, FALSE);
+ g_main_loop_run(loop);
release_clipboards();
@@ -1223,6 +1181,7 @@ int main(int argc, char *argv[])
vdagent_virtio_port_destroy(&virtio_port);
session_info_destroy(session_info);
udscs_destroy_server(server);
+ g_main_loop_unref(loop);
/* leave the socket around if it was provided by systemd */
if (own_socket) {
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Jakub Janků
2018-08-14 18:53:38 UTC
Permalink
Require UNIX-specific GIO package to build spice-vdagent.

This includes

- GDBus which is going to be used instead of libdbus in
console-kit.c and systemd-login.c

- I/O stream classes and networking APIs that are going to
be used in udscs.c and virtio-port.c instead of low-level
POSIX I/O functions dealing with file descriptors.
---
Makefile.am | 2 ++
configure.ac | 1 +
2 files changed, 3 insertions(+)

diff --git a/Makefile.am b/Makefile.am
index 3e405bc..fa54bbc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -51,6 +51,7 @@ src_spice_vdagentd_CFLAGS = \
$(PCIACCESS_CFLAGS) \
$(SPICE_CFLAGS) \
$(GLIB2_CFLAGS) \
+ $(GIO2_CFLAGS) \
$(PIE_CFLAGS) \
-I$(srcdir)/src \
$(NULL)
@@ -62,6 +63,7 @@ src_spice_vdagentd_LDADD = \
$(PCIACCESS_LIBS) \
$(SPICE_LIBS) \
$(GLIB2_LIBS) \
+ $(GIO2_LIBS) \
$(PIE_LDFLAGS) \
$(NULL)

diff --git a/configure.ac b/configure.ac
index 7cb44db..cf8169b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -101,6 +101,7 @@ AC_ARG_ENABLE([static-uinput],
[enable_static_uinput="no"])

PKG_CHECK_MODULES([GLIB2], [glib-2.0 >= 2.34])
+PKG_CHECK_MODULES([GIO2], [gio-unix-2.0])
PKG_CHECK_MODULES(X, [xfixes xrandr >= 1.3 xinerama x11])
PKG_CHECK_MODULES(SPICE, [spice-protocol >= 0.12.13])
PKG_CHECK_MODULES(ALSA, [alsa >= 1.0.22])
--
2.17.1
Jakub Janků
2018-08-14 18:53:39 UTC
Permalink
Add a set of helper functions built around GIO that can be used to
easily write messages to and read from the given FD.

Since VDAgentConnection uses GIO,
it integrates well with GMainLoop.

Read messages must begin with a header of a fixed size.
Message body size can vary.

User of VDAgentConnection is notified
through callbacks about the following events:
- message header read
- message body read
- I/O error

A new VDAgentConnection can be constructed using
vdagent_connection_new() based on a GIOStream.

A new GIOStream can be obtained using
vdagent_file_open() or vdagent_socket_connect().

vdagent_connection_destroy() destroyes the connection.
However, due to the asynchronous nature of used GIO functions,
this does NOT close the underlying FD immediately.
---
Makefile.am | 2 +
src/vdagent-connection.c | 301 +++++++++++++++++++++++++++++++++++++++
src/vdagent-connection.h | 103 ++++++++++++++
3 files changed, 406 insertions(+)
create mode 100644 src/vdagent-connection.c
create mode 100644 src/vdagent-connection.h

diff --git a/Makefile.am b/Makefile.am
index fa54bbc..b291b19 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,6 +7,8 @@ sbin_PROGRAMS = src/spice-vdagentd
common_sources = \
src/udscs.c \
src/udscs.h \
+ src/vdagent-connection.c \
+ src/vdagent-connection.h \
src/vdagentd-proto-strings.h \
src/vdagentd-proto.h \
$(NULL)
diff --git a/src/vdagent-connection.c b/src/vdagent-connection.c
new file mode 100644
index 0000000..0eb2ec9
--- /dev/null
+++ b/src/vdagent-connection.c
@@ -0,0 +1,301 @@
+/* vdagent-connection.c
+
+ Copyright 2018 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <syslog.h>
+#include <fcntl.h>
+#include <glib/gstdio.h>
+#include <gio/gunixinputstream.h>
+#include <gio/gunixoutputstream.h>
+#include <gio/gunixsocketaddress.h>
+
+#include "vdagent-connection.h"
+
+struct VDAgentConnection {
+ GIOStream *io_stream;
+ gboolean opening;
+ GCancellable *cancellable;
+
+ GQueue *write_queue;
+ GMainLoop *flush_loop;
+
+ VDAgentConnReadCb read_cb;
+ gpointer read_buff;
+ gpointer header_buff;
+ gsize header_size;
+ VDAgentConnHeaderReadCb header_read_cb;
+
+ VDAgentConnErrorCb error_cb;
+
+ GCredentials *credentials;
+
+ gpointer user_data;
+};
+
+static void request_message_write(VDAgentConnection *conn);
+static void request_message_read(VDAgentConnection *conn);
+
+GIOStream *vdagent_file_open(const gchar *path)
+{
+ gint fd;
+
+ fd = g_open(path, O_RDWR);
+ if (fd == -1) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return NULL;
+ }
+
+ return g_simple_io_stream_new(g_unix_input_stream_new(fd, TRUE),
+ g_unix_output_stream_new(fd, TRUE));
+}
+
+GIOStream *vdagent_socket_connect(const gchar *address)
+{
+ GSocketConnection *socket_conn;
+ GSocketClient *client;
+ GSocketConnectable *connectable;
+ GError *err = NULL;
+
+ connectable = G_SOCKET_CONNECTABLE(g_unix_socket_address_new(address));
+ client = g_object_new(G_TYPE_SOCKET_CLIENT,
+ "family", G_SOCKET_FAMILY_UNIX,
+ "type", G_SOCKET_TYPE_STREAM,
+ NULL);
+
+ socket_conn = g_socket_client_connect(client, connectable, NULL, &err);
+ g_object_unref(client);
+ g_object_unref(connectable);
+ if (err) {
+ syslog(LOG_ERR, "%s: %s", __func__, err->message);
+ g_error_free(err);
+ }
+ return G_IO_STREAM(socket_conn);
+}
+
+VDAgentConnection *vdagent_connection_new(
+ GIOStream *io_stream,
+ gboolean wait_on_opening,
+ gsize header_size,
+ VDAgentConnHeaderReadCb header_read_cb,
+ VDAgentConnReadCb read_cb,
+ VDAgentConnErrorCb error_cb,
+ gpointer user_data)
+{
+ VDAgentConnection *conn;
+
+ conn = g_new(VDAgentConnection, 1);
+ conn->io_stream = io_stream;
+ conn->cancellable = g_cancellable_new();
+ conn->opening = wait_on_opening;
+ conn->write_queue = g_queue_new();
+ conn->flush_loop = NULL;
+ conn->read_cb = read_cb;
+ conn->read_buff = NULL;
+ conn->header_buff = g_malloc(header_size);
+ conn->header_size = header_size;
+ conn->header_read_cb = header_read_cb;
+ conn->error_cb = error_cb;
+ conn->credentials = NULL;
+ conn->user_data = user_data;
+
+ request_message_read(conn);
+
+ return conn;
+}
+
+static gboolean connection_has_pending(VDAgentConnection *conn)
+{
+ GInputStream *in = g_io_stream_get_input_stream(conn->io_stream);
+ GOutputStream *out = g_io_stream_get_output_stream(conn->io_stream);
+
+ return g_input_stream_has_pending(in) || g_output_stream_has_pending(out);
+}
+
+/* Free up all resources used by VDAgentConnection
+ * once all I/O operations have finished. */
+static void connection_finalize(VDAgentConnection *conn)
+{
+ g_object_unref(conn->cancellable);
+ g_queue_free_full(conn->write_queue, (GDestroyNotify)g_bytes_unref);
+ g_clear_pointer(&conn->flush_loop, g_main_loop_quit);
+ g_clear_object(&conn->credentials);
+ g_free(conn->header_buff);
+ g_free(conn->read_buff);
+ g_object_unref(conn->io_stream);
+ g_free(conn);
+}
+
+void vdagent_connection_destroy(VDAgentConnection *conn)
+{
+ /* If there's a pending I/O operation on either of the streams, cancel it,
+ * connection_finalize() will be invoked in the next GMainLoop iteration(s). */
+ if (connection_has_pending(conn))
+ g_cancellable_cancel(conn->cancellable);
+ else
+ connection_finalize(conn);
+}
+
+static void handle_io_error(VDAgentConnection *conn, GError *err)
+{
+ if (g_cancellable_is_cancelled(conn->cancellable)) {
+ if (!connection_has_pending(conn))
+ connection_finalize(conn);
+ } else {
+ syslog(LOG_ERR, "vdagent-connection: I/O error: %s", err->message);
+ conn->error_cb(conn->user_data);
+ }
+ g_error_free(err);
+}
+
+GCredentials *vdagent_connection_get_peer_credentials(VDAgentConnection *conn)
+{
+ GSocket *socket;
+ GError *err = NULL;
+
+ g_return_val_if_fail(G_IS_SOCKET_CONNECTION(conn->io_stream), NULL);
+
+ if (conn->credentials)
+ return conn->credentials;
+
+ socket = g_socket_connection_get_socket(G_SOCKET_CONNECTION(conn->io_stream));
+ conn->credentials = g_socket_get_credentials(socket, &err);
+ if (err) {
+ syslog(LOG_ERR, "%s: %s", __func__, err->message);
+ g_error_free(err);
+ }
+ return conn->credentials;
+}
+
+static void message_write_cb(GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ VDAgentConnection *conn = user_data;
+ GOutputStream *out = G_OUTPUT_STREAM(source_object);
+ GError *err = NULL;
+
+ g_output_stream_write_all_finish(out, res, NULL, &err);
+ g_bytes_unref(g_queue_pop_head(conn->write_queue));
+
+ if (err)
+ return handle_io_error(conn, err);
+
+ conn->opening = FALSE;
+
+ if (g_queue_is_empty(conn->write_queue))
+ g_clear_pointer(&conn->flush_loop, g_main_loop_quit);
+ else
+ request_message_write(conn);
+}
+
+static void request_message_write(VDAgentConnection *conn)
+{
+ GBytes *msg;
+ GOutputStream *out;
+
+ msg = g_queue_peek_head(conn->write_queue);
+ out = g_io_stream_get_output_stream(conn->io_stream);
+
+ g_output_stream_write_all_async(out,
+ g_bytes_get_data(msg, NULL), g_bytes_get_size(msg),
+ G_PRIORITY_DEFAULT, conn->cancellable, message_write_cb, conn);
+}
+
+void vdagent_connection_write(VDAgentConnection *conn,
+ gpointer data,
+ gsize size)
+{
+ g_queue_push_tail(conn->write_queue, g_bytes_new_take(data, size));
+
+ if (g_queue_get_length(conn->write_queue) == 1)
+ request_message_write(conn);
+}
+
+void vdagent_connection_flush(VDAgentConnection *conn)
+{
+ GMainLoop *loop;
+ /* TODO: allow multiple flush calls at once? */
+ g_return_if_fail(conn->flush_loop == NULL);
+
+ if (g_queue_is_empty(conn->write_queue))
+ return;
+
+ loop = conn->flush_loop = g_main_loop_new(NULL, FALSE);
+ /* When using GTK+, this should be wrapped with
+ * gdk_threads_leave() and gdk_threads_enter(),
+ * but since flush is used in virtio-port.c only
+ * let's leave it as it is for now. */
+ g_main_loop_run(loop);
+ g_main_loop_unref(loop);
+}
+
+static void message_read_cb(GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ VDAgentConnection *conn = user_data;
+ GInputStream *in = G_INPUT_STREAM(source_object);
+ GError *err = NULL;
+ gsize bytes_read, data_size;
+
+ g_input_stream_read_all_finish(in, res, &bytes_read, &err);
+ if (err)
+ return handle_io_error(conn, err);
+ if (bytes_read == 0) {
+ /* see virtio-port.c for the rationale behind this */
+ if (conn->opening) {
+ g_usleep(10000);
+ request_message_read(conn);
+ } else {
+ conn->error_cb(conn->user_data);
+ }
+ return;
+ }
+ conn->opening = FALSE;
+
+ if (conn->read_buff == NULL) {
+ /* we've read the message header, now let's read its body */
+ if (!conn->header_read_cb(conn->header_buff, &data_size, conn->user_data))
+ return;
+ if (data_size > 0) {
+ conn->read_buff = g_malloc(data_size);
+ /* TODO: if allocation fails, we could try g_input_stream_skip()
+ * and hope that the message wasn't crucial for proper functiong.
+ * An example might be when a user tries to copy large clipboard.
+ * Not sure whether it's worth implementing.
+ * Other stuff might just as well fall apart
+ * when the system is running out of memory? */
+ g_input_stream_read_all_async(in, conn->read_buff, data_size,
+ G_PRIORITY_DEFAULT, conn->cancellable, message_read_cb, conn);
+ return;
+ }
+ }
+
+ if (!conn->read_cb(conn->header_buff, conn->read_buff, conn->user_data))
+ return;
+ g_clear_pointer(&conn->read_buff, g_free);
+ request_message_read(conn);
+}
+
+static void request_message_read(VDAgentConnection *conn)
+{
+ GInputStream *in;
+ in = g_io_stream_get_input_stream(conn->io_stream);
+
+ g_input_stream_read_all_async(in, conn->header_buff, conn->header_size,
+ G_PRIORITY_DEFAULT, conn->cancellable, message_read_cb, conn);
+}
diff --git a/src/vdagent-connection.h b/src/vdagent-connection.h
new file mode 100644
index 0000000..6fc0081
--- /dev/null
+++ b/src/vdagent-connection.h
@@ -0,0 +1,103 @@
+/* vdagent-connection.h
+
+ Copyright 2018 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __VDAGENT_CONNECTION_H
+#define __VDAGENT_CONNECTION_H
+
+#include <glib.h>
+#include <gio/gio.h>
+
+typedef struct VDAgentConnection VDAgentConnection;
+
+/* Called when a message header has been read.
+ *
+ * If the handler wishes to continue reading,
+ * it must set @body_size to the size of message's body and return TRUE.
+ * Once @body_size bytes are read, VDAgentConnReadCb() is invoked.
+ *
+ * Otherwise the handler should return FALSE
+ * and call vdagent_connection_destroy().
+ *
+ * @header_buff is owned by VDAgentConnection and must not be freed. */
+typedef gboolean (*VDAgentConnHeaderReadCb)(gpointer header_buff,
+ gsize *body_size,
+ gpointer user_data);
+
+/* Called when a full message has been read.
+ *
+ * If the handler wished to continue reading, it must return TRUE,
+ * otherwise FALSE and call vdagent_connection_destroy().
+ *
+ * @header, @data are owned by VDAgentConnection and must not be freed. */
+typedef gboolean (*VDAgentConnReadCb)(gpointer header,
+ gpointer data,
+ gpointer user_data);
+
+/* Called when an error occured during read or wirte.
+ * The handler is expected to call vdagent_connection_destroy(). */
+typedef void (*VDAgentConnErrorCb)(gpointer user_data);
+
+/* Open a file in @path for read and write.
+ * Returns a GIOStream to the given file or NULL on error. */
+GIOStream *vdagent_file_open(const gchar *path);
+
+/* Create a socket and initiate a new connection to the socket on @address.
+ * Returns a GIOStream corresponding to the new connection or NULL on error. */
+GIOStream *vdagent_socket_connect(const gchar *address);
+
+/* Create new VDAgentConnection and start reading incoming messages.
+ *
+ * If @wait_on_opening is set to TRUE, EOF won't be treated as an error
+ * until the first message is successfully read or written to the @io_stream.
+ *
+ * @user_data will be passed to the supplied callbacks. */
+VDAgentConnection *vdagent_connection_new(
+ GIOStream *io_stream,
+ gboolean wait_on_opening,
+ gsize header_size,
+ VDAgentConnHeaderReadCb header_read_cb,
+ VDAgentConnReadCb read_cb,
+ VDAgentConnErrorCb error_cb,
+ gpointer user_data);
+
+/* Free up all resources associated with the VDAgentConnection.
+ *
+ * This operation can be asynchronous. */
+void vdagent_connection_destroy(VDAgentConnection *conn);
+
+/* Append a message to write queue.
+ *
+ * VDAgentConnection takes ownership of the @data
+ * and frees it once the message is flushed. */
+void vdagent_connection_write(VDAgentConnection *conn,
+ gpointer data,
+ gsize size);
+
+/* Waits until all queued messages get written to the output stream.
+ *
+ * Note: other GSources can be triggered during this call */
+void vdagent_connection_flush(VDAgentConnection *conn);
+
+/* Returns the credentials of the foreign process connected to the socket.
+ *
+ * It is an error to call this function with a VDAgentConnection
+ * that isn't based on a GIOStream of G_TYPE_SOCKET_CONNECTION. */
+GCredentials *vdagent_connection_get_peer_credentials(
+ VDAgentConnection *conn);
+
+#endif
--
2.17.1
Victor Toso
2018-08-28 08:04:06 UTC
Permalink
Hi,
Post by Jakub Janků
Add a set of helper functions built around GIO that can be used to
easily write messages to and read from the given FD.
Since VDAgentConnection uses GIO,
it integrates well with GMainLoop.
Read messages must begin with a header of a fixed size.
Message body size can vary.
User of VDAgentConnection is notified
- message header read
- message body read
- I/O error
A new VDAgentConnection can be constructed using
vdagent_connection_new() based on a GIOStream.
A new GIOStream can be obtained using
vdagent_file_open() or vdagent_socket_connect().
vdagent_connection_destroy() destroyes the connection.
However, due to the asynchronous nature of used GIO functions,
this does NOT close the underlying FD immediately.
Yep, commented about it on 00/18 but I take that making this a
GObject might help. Not giving a full review here, just small
note after looking at the patch and the follow up ones.
Post by Jakub Janků
---
Makefile.am | 2 +
src/vdagent-connection.c | 301 +++++++++++++++++++++++++++++++++++++++
src/vdagent-connection.h | 103 ++++++++++++++
3 files changed, 406 insertions(+)
create mode 100644 src/vdagent-connection.c
create mode 100644 src/vdagent-connection.h
diff --git a/Makefile.am b/Makefile.am
index fa54bbc..b291b19 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,6 +7,8 @@ sbin_PROGRAMS = src/spice-vdagentd
common_sources = \
src/udscs.c \
src/udscs.h \
+ src/vdagent-connection.c \
+ src/vdagent-connection.h \
src/vdagentd-proto-strings.h \
src/vdagentd-proto.h \
$(NULL)
diff --git a/src/vdagent-connection.c b/src/vdagent-connection.c
new file mode 100644
index 0000000..0eb2ec9
--- /dev/null
+++ b/src/vdagent-connection.c
@@ -0,0 +1,301 @@
+/* vdagent-connection.c
+
+ Copyright 2018 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <syslog.h>
+#include <fcntl.h>
+#include <glib/gstdio.h>
+#include <gio/gunixinputstream.h>
+#include <gio/gunixoutputstream.h>
+#include <gio/gunixsocketaddress.h>
+
+#include "vdagent-connection.h"
+
+struct VDAgentConnection {
+ GIOStream *io_stream;
+ gboolean opening;
+ GCancellable *cancellable;
+
+ GQueue *write_queue;
+ GMainLoop *flush_loop;
+
+ VDAgentConnReadCb read_cb;
+ gpointer read_buff;
+ gpointer header_buff;
+ gsize header_size;
+ VDAgentConnHeaderReadCb header_read_cb;
+
+ VDAgentConnErrorCb error_cb;
+
+ GCredentials *credentials;
+
+ gpointer user_data;
+};
+
+static void request_message_write(VDAgentConnection *conn);
+static void request_message_read(VDAgentConnection *conn);
+
+GIOStream *vdagent_file_open(const gchar *path)
+{
+ gint fd;
+
+ fd = g_open(path, O_RDWR);
+ if (fd == -1) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return NULL;
+ }
+
+ return g_simple_io_stream_new(g_unix_input_stream_new(fd, TRUE),
+ g_unix_output_stream_new(fd, TRUE));
+}
+
+GIOStream *vdagent_socket_connect(const gchar *address)
+{
+ GSocketConnection *socket_conn;
+ GSocketClient *client;
+ GSocketConnectable *connectable;
+ GError *err = NULL;
+
+ connectable = G_SOCKET_CONNECTABLE(g_unix_socket_address_new(address));
+ client = g_object_new(G_TYPE_SOCKET_CLIENT,
+ "family", G_SOCKET_FAMILY_UNIX,
+ "type", G_SOCKET_TYPE_STREAM,
+ NULL);
+
+ socket_conn = g_socket_client_connect(client, connectable, NULL, &err);
+ g_object_unref(client);
+ g_object_unref(connectable);
+ if (err) {
+ syslog(LOG_ERR, "%s: %s", __func__, err->message);
+ g_error_free(err);
+ }
+ return G_IO_STREAM(socket_conn);
+}
Not convinced that this API is really needed? The function can be
kept but to be used internally by vdagent_connection_new()
itself, I guess. I don't see other components using GIOStream
unless to pass it to vdagent_connection_new ().

Might make sense to make socket's address as VDAgentConnection's
property which should be set on g_object_new () too.

Reviewed-by: Victor Toso <***@redhat.com>

Victor
Post by Jakub Janků
+
+VDAgentConnection *vdagent_connection_new(
+ GIOStream *io_stream,
+ gboolean wait_on_opening,
+ gsize header_size,
+ VDAgentConnHeaderReadCb header_read_cb,
+ VDAgentConnReadCb read_cb,
+ VDAgentConnErrorCb error_cb,
+ gpointer user_data)
+{
+ VDAgentConnection *conn;
+
+ conn = g_new(VDAgentConnection, 1);
+ conn->io_stream = io_stream;
+ conn->cancellable = g_cancellable_new();
+ conn->opening = wait_on_opening;
+ conn->write_queue = g_queue_new();
+ conn->flush_loop = NULL;
+ conn->read_cb = read_cb;
+ conn->read_buff = NULL;
+ conn->header_buff = g_malloc(header_size);
+ conn->header_size = header_size;
+ conn->header_read_cb = header_read_cb;
+ conn->error_cb = error_cb;
+ conn->credentials = NULL;
+ conn->user_data = user_data;
+
+ request_message_read(conn);
+
+ return conn;
+}
+
+static gboolean connection_has_pending(VDAgentConnection *conn)
+{
+ GInputStream *in = g_io_stream_get_input_stream(conn->io_stream);
+ GOutputStream *out = g_io_stream_get_output_stream(conn->io_stream);
+
+ return g_input_stream_has_pending(in) || g_output_stream_has_pending(out);
+}
+
+/* Free up all resources used by VDAgentConnection
+ * once all I/O operations have finished. */
+static void connection_finalize(VDAgentConnection *conn)
+{
+ g_object_unref(conn->cancellable);
+ g_queue_free_full(conn->write_queue, (GDestroyNotify)g_bytes_unref);
+ g_clear_pointer(&conn->flush_loop, g_main_loop_quit);
+ g_clear_object(&conn->credentials);
+ g_free(conn->header_buff);
+ g_free(conn->read_buff);
+ g_object_unref(conn->io_stream);
+ g_free(conn);
+}
+
+void vdagent_connection_destroy(VDAgentConnection *conn)
+{
+ /* If there's a pending I/O operation on either of the streams, cancel it,
+ * connection_finalize() will be invoked in the next GMainLoop iteration(s). */
+ if (connection_has_pending(conn))
+ g_cancellable_cancel(conn->cancellable);
+ else
+ connection_finalize(conn);
+}
+
+static void handle_io_error(VDAgentConnection *conn, GError *err)
+{
+ if (g_cancellable_is_cancelled(conn->cancellable)) {
+ if (!connection_has_pending(conn))
+ connection_finalize(conn);
+ } else {
+ syslog(LOG_ERR, "vdagent-connection: I/O error: %s", err->message);
+ conn->error_cb(conn->user_data);
+ }
+ g_error_free(err);
+}
+
+GCredentials *vdagent_connection_get_peer_credentials(VDAgentConnection *conn)
+{
+ GSocket *socket;
+ GError *err = NULL;
+
+ g_return_val_if_fail(G_IS_SOCKET_CONNECTION(conn->io_stream), NULL);
+
+ if (conn->credentials)
+ return conn->credentials;
+
+ socket = g_socket_connection_get_socket(G_SOCKET_CONNECTION(conn->io_stream));
+ conn->credentials = g_socket_get_credentials(socket, &err);
+ if (err) {
+ syslog(LOG_ERR, "%s: %s", __func__, err->message);
+ g_error_free(err);
+ }
+ return conn->credentials;
+}
+
+static void message_write_cb(GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ VDAgentConnection *conn = user_data;
+ GOutputStream *out = G_OUTPUT_STREAM(source_object);
+ GError *err = NULL;
+
+ g_output_stream_write_all_finish(out, res, NULL, &err);
+ g_bytes_unref(g_queue_pop_head(conn->write_queue));
+
+ if (err)
+ return handle_io_error(conn, err);
+
+ conn->opening = FALSE;
+
+ if (g_queue_is_empty(conn->write_queue))
+ g_clear_pointer(&conn->flush_loop, g_main_loop_quit);
+ else
+ request_message_write(conn);
+}
+
+static void request_message_write(VDAgentConnection *conn)
+{
+ GBytes *msg;
+ GOutputStream *out;
+
+ msg = g_queue_peek_head(conn->write_queue);
+ out = g_io_stream_get_output_stream(conn->io_stream);
+
+ g_output_stream_write_all_async(out,
+ g_bytes_get_data(msg, NULL), g_bytes_get_size(msg),
+ G_PRIORITY_DEFAULT, conn->cancellable, message_write_cb, conn);
+}
+
+void vdagent_connection_write(VDAgentConnection *conn,
+ gpointer data,
+ gsize size)
+{
+ g_queue_push_tail(conn->write_queue, g_bytes_new_take(data, size));
+
+ if (g_queue_get_length(conn->write_queue) == 1)
+ request_message_write(conn);
+}
+
+void vdagent_connection_flush(VDAgentConnection *conn)
+{
+ GMainLoop *loop;
+ /* TODO: allow multiple flush calls at once? */
+ g_return_if_fail(conn->flush_loop == NULL);
+
+ if (g_queue_is_empty(conn->write_queue))
+ return;
+
+ loop = conn->flush_loop = g_main_loop_new(NULL, FALSE);
+ /* When using GTK+, this should be wrapped with
+ * gdk_threads_leave() and gdk_threads_enter(),
+ * but since flush is used in virtio-port.c only
+ * let's leave it as it is for now. */
+ g_main_loop_run(loop);
+ g_main_loop_unref(loop);
+}
+
+static void message_read_cb(GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ VDAgentConnection *conn = user_data;
+ GInputStream *in = G_INPUT_STREAM(source_object);
+ GError *err = NULL;
+ gsize bytes_read, data_size;
+
+ g_input_stream_read_all_finish(in, res, &bytes_read, &err);
+ if (err)
+ return handle_io_error(conn, err);
+ if (bytes_read == 0) {
+ /* see virtio-port.c for the rationale behind this */
+ if (conn->opening) {
+ g_usleep(10000);
+ request_message_read(conn);
+ } else {
+ conn->error_cb(conn->user_data);
+ }
+ return;
+ }
+ conn->opening = FALSE;
+
+ if (conn->read_buff == NULL) {
+ /* we've read the message header, now let's read its body */
+ if (!conn->header_read_cb(conn->header_buff, &data_size, conn->user_data))
+ return;
+ if (data_size > 0) {
+ conn->read_buff = g_malloc(data_size);
+ /* TODO: if allocation fails, we could try g_input_stream_skip()
+ * and hope that the message wasn't crucial for proper functiong.
+ * An example might be when a user tries to copy large clipboard.
+ * Not sure whether it's worth implementing.
+ * Other stuff might just as well fall apart
+ * when the system is running out of memory? */
+ g_input_stream_read_all_async(in, conn->read_buff, data_size,
+ G_PRIORITY_DEFAULT, conn->cancellable, message_read_cb, conn);
+ return;
+ }
+ }
+
+ if (!conn->read_cb(conn->header_buff, conn->read_buff, conn->user_data))
+ return;
+ g_clear_pointer(&conn->read_buff, g_free);
+ request_message_read(conn);
+}
+
+static void request_message_read(VDAgentConnection *conn)
+{
+ GInputStream *in;
+ in = g_io_stream_get_input_stream(conn->io_stream);
+
+ g_input_stream_read_all_async(in, conn->header_buff, conn->header_size,
+ G_PRIORITY_DEFAULT, conn->cancellable, message_read_cb, conn);
+}
diff --git a/src/vdagent-connection.h b/src/vdagent-connection.h
new file mode 100644
index 0000000..6fc0081
--- /dev/null
+++ b/src/vdagent-connection.h
@@ -0,0 +1,103 @@
+/* vdagent-connection.h
+
+ Copyright 2018 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __VDAGENT_CONNECTION_H
+#define __VDAGENT_CONNECTION_H
+
+#include <glib.h>
+#include <gio/gio.h>
+
+typedef struct VDAgentConnection VDAgentConnection;
+
+/* Called when a message header has been read.
+ *
+ * If the handler wishes to continue reading,
+ *
+ * Otherwise the handler should return FALSE
+ * and call vdagent_connection_destroy().
+ *
+typedef gboolean (*VDAgentConnHeaderReadCb)(gpointer header_buff,
+ gsize *body_size,
+ gpointer user_data);
+
+/* Called when a full message has been read.
+ *
+ * If the handler wished to continue reading, it must return TRUE,
+ * otherwise FALSE and call vdagent_connection_destroy().
+ *
+typedef gboolean (*VDAgentConnReadCb)(gpointer header,
+ gpointer data,
+ gpointer user_data);
+
+/* Called when an error occured during read or wirte.
+ * The handler is expected to call vdagent_connection_destroy(). */
+typedef void (*VDAgentConnErrorCb)(gpointer user_data);
+
+ * Returns a GIOStream to the given file or NULL on error. */
+GIOStream *vdagent_file_open(const gchar *path);
+
+ * Returns a GIOStream corresponding to the new connection or NULL on error. */
+GIOStream *vdagent_socket_connect(const gchar *address);
+
+/* Create new VDAgentConnection and start reading incoming messages.
+ *
+ *
+VDAgentConnection *vdagent_connection_new(
+ GIOStream *io_stream,
+ gboolean wait_on_opening,
+ gsize header_size,
+ VDAgentConnHeaderReadCb header_read_cb,
+ VDAgentConnReadCb read_cb,
+ VDAgentConnErrorCb error_cb,
+ gpointer user_data);
+
+/* Free up all resources associated with the VDAgentConnection.
+ *
+ * This operation can be asynchronous. */
+void vdagent_connection_destroy(VDAgentConnection *conn);
+
+/* Append a message to write queue.
+ *
+ * and frees it once the message is flushed. */
+void vdagent_connection_write(VDAgentConnection *conn,
+ gpointer data,
+ gsize size);
+
+/* Waits until all queued messages get written to the output stream.
+ *
+ * Note: other GSources can be triggered during this call */
+void vdagent_connection_flush(VDAgentConnection *conn);
+
+/* Returns the credentials of the foreign process connected to the socket.
+ *
+ * It is an error to call this function with a VDAgentConnection
+ * that isn't based on a GIOStream of G_TYPE_SOCKET_CONNECTION. */
+GCredentials *vdagent_connection_get_peer_credentials(
+ VDAgentConnection *conn);
+
+#endif
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Jakub Janku
2018-09-03 16:49:38 UTC
Permalink
Hey,
Post by Victor Toso
Hi,
Post by Jakub Janků
Add a set of helper functions built around GIO that can be used to
easily write messages to and read from the given FD.
Since VDAgentConnection uses GIO,
it integrates well with GMainLoop.
Read messages must begin with a header of a fixed size.
Message body size can vary.
User of VDAgentConnection is notified
- message header read
- message body read
- I/O error
A new VDAgentConnection can be constructed using
vdagent_connection_new() based on a GIOStream.
A new GIOStream can be obtained using
vdagent_file_open() or vdagent_socket_connect().
vdagent_connection_destroy() destroyes the connection.
However, due to the asynchronous nature of used GIO functions,
this does NOT close the underlying FD immediately.
Yep, commented about it on 00/18 but I take that making this a
GObject might help. Not giving a full review here, just small
note after looking at the patch and the follow up ones.
Post by Jakub Janků
---
Makefile.am | 2 +
src/vdagent-connection.c | 301 +++++++++++++++++++++++++++++++++++++++
src/vdagent-connection.h | 103 ++++++++++++++
3 files changed, 406 insertions(+)
create mode 100644 src/vdagent-connection.c
create mode 100644 src/vdagent-connection.h
diff --git a/Makefile.am b/Makefile.am
index fa54bbc..b291b19 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,6 +7,8 @@ sbin_PROGRAMS = src/spice-vdagentd
common_sources = \
src/udscs.c \
src/udscs.h \
+ src/vdagent-connection.c \
+ src/vdagent-connection.h \
src/vdagentd-proto-strings.h \
src/vdagentd-proto.h \
$(NULL)
diff --git a/src/vdagent-connection.c b/src/vdagent-connection.c
new file mode 100644
index 0000000..0eb2ec9
--- /dev/null
+++ b/src/vdagent-connection.c
@@ -0,0 +1,301 @@
+/* vdagent-connection.c
+
+ Copyright 2018 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <syslog.h>
+#include <fcntl.h>
+#include <glib/gstdio.h>
+#include <gio/gunixinputstream.h>
+#include <gio/gunixoutputstream.h>
+#include <gio/gunixsocketaddress.h>
+
+#include "vdagent-connection.h"
+
+struct VDAgentConnection {
+ GIOStream *io_stream;
+ gboolean opening;
+ GCancellable *cancellable;
+
+ GQueue *write_queue;
+ GMainLoop *flush_loop;
+
+ VDAgentConnReadCb read_cb;
+ gpointer read_buff;
+ gpointer header_buff;
+ gsize header_size;
+ VDAgentConnHeaderReadCb header_read_cb;
+
+ VDAgentConnErrorCb error_cb;
+
+ GCredentials *credentials;
+
+ gpointer user_data;
+};
+
+static void request_message_write(VDAgentConnection *conn);
+static void request_message_read(VDAgentConnection *conn);
+
+GIOStream *vdagent_file_open(const gchar *path)
+{
+ gint fd;
+
+ fd = g_open(path, O_RDWR);
+ if (fd == -1) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return NULL;
+ }
+
+ return g_simple_io_stream_new(g_unix_input_stream_new(fd, TRUE),
+ g_unix_output_stream_new(fd, TRUE));
+}
+
+GIOStream *vdagent_socket_connect(const gchar *address)
+{
+ GSocketConnection *socket_conn;
+ GSocketClient *client;
+ GSocketConnectable *connectable;
+ GError *err = NULL;
+
+ connectable = G_SOCKET_CONNECTABLE(g_unix_socket_address_new(address));
+ client = g_object_new(G_TYPE_SOCKET_CLIENT,
+ "family", G_SOCKET_FAMILY_UNIX,
+ "type", G_SOCKET_TYPE_STREAM,
+ NULL);
+
+ socket_conn = g_socket_client_connect(client, connectable, NULL, &err);
+ g_object_unref(client);
+ g_object_unref(connectable);
+ if (err) {
+ syslog(LOG_ERR, "%s: %s", __func__, err->message);
+ g_error_free(err);
+ }
+ return G_IO_STREAM(socket_conn);
+}
Not convinced that this API is really needed? The function can be
kept but to be used internally by vdagent_connection_new()
itself, I guess. I don't see other components using GIOStream
unless to pass it to vdagent_connection_new ().
The rationale behind this was actually purely cosmetic.
We could have something like vdagent_connection_new_file() and
vdagent_connection_new_socket(),
but both functions would need to have 7 arguments and we would need to
initialize all the members of VDAgentConnection struct in both of the
functions.
So having just one common vdagent_connection_new() function seemed
cleaner to me.
Post by Victor Toso
Might make sense to make socket's address as VDAgentConnection's
property which should be set on g_object_new () too.
Victor
Post by Jakub Janků
+
+VDAgentConnection *vdagent_connection_new(
+ GIOStream *io_stream,
+ gboolean wait_on_opening,
+ gsize header_size,
+ VDAgentConnHeaderReadCb header_read_cb,
+ VDAgentConnReadCb read_cb,
+ VDAgentConnErrorCb error_cb,
+ gpointer user_data)
+{
+ VDAgentConnection *conn;
+
+ conn = g_new(VDAgentConnection, 1);
+ conn->io_stream = io_stream;
+ conn->cancellable = g_cancellable_new();
+ conn->opening = wait_on_opening;
+ conn->write_queue = g_queue_new();
+ conn->flush_loop = NULL;
+ conn->read_cb = read_cb;
+ conn->read_buff = NULL;
+ conn->header_buff = g_malloc(header_size);
+ conn->header_size = header_size;
+ conn->header_read_cb = header_read_cb;
+ conn->error_cb = error_cb;
+ conn->credentials = NULL;
+ conn->user_data = user_data;
+
+ request_message_read(conn);
+
+ return conn;
+}
+
+static gboolean connection_has_pending(VDAgentConnection *conn)
+{
+ GInputStream *in = g_io_stream_get_input_stream(conn->io_stream);
+ GOutputStream *out = g_io_stream_get_output_stream(conn->io_stream);
+
+ return g_input_stream_has_pending(in) || g_output_stream_has_pending(out);
+}
+
+/* Free up all resources used by VDAgentConnection
+ * once all I/O operations have finished. */
+static void connection_finalize(VDAgentConnection *conn)
+{
+ g_object_unref(conn->cancellable);
+ g_queue_free_full(conn->write_queue, (GDestroyNotify)g_bytes_unref);
+ g_clear_pointer(&conn->flush_loop, g_main_loop_quit);
+ g_clear_object(&conn->credentials);
+ g_free(conn->header_buff);
+ g_free(conn->read_buff);
+ g_object_unref(conn->io_stream);
+ g_free(conn);
+}
+
+void vdagent_connection_destroy(VDAgentConnection *conn)
+{
+ /* If there's a pending I/O operation on either of the streams, cancel it,
+ * connection_finalize() will be invoked in the next GMainLoop iteration(s). */
+ if (connection_has_pending(conn))
+ g_cancellable_cancel(conn->cancellable);
+ else
+ connection_finalize(conn);
+}
+
+static void handle_io_error(VDAgentConnection *conn, GError *err)
+{
+ if (g_cancellable_is_cancelled(conn->cancellable)) {
+ if (!connection_has_pending(conn))
+ connection_finalize(conn);
+ } else {
+ syslog(LOG_ERR, "vdagent-connection: I/O error: %s", err->message);
+ conn->error_cb(conn->user_data);
+ }
+ g_error_free(err);
+}
+
+GCredentials *vdagent_connection_get_peer_credentials(VDAgentConnection *conn)
+{
+ GSocket *socket;
+ GError *err = NULL;
+
+ g_return_val_if_fail(G_IS_SOCKET_CONNECTION(conn->io_stream), NULL);
+
+ if (conn->credentials)
+ return conn->credentials;
+
+ socket = g_socket_connection_get_socket(G_SOCKET_CONNECTION(conn->io_stream));
+ conn->credentials = g_socket_get_credentials(socket, &err);
+ if (err) {
+ syslog(LOG_ERR, "%s: %s", __func__, err->message);
+ g_error_free(err);
+ }
+ return conn->credentials;
+}
+
+static void message_write_cb(GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ VDAgentConnection *conn = user_data;
+ GOutputStream *out = G_OUTPUT_STREAM(source_object);
+ GError *err = NULL;
+
+ g_output_stream_write_all_finish(out, res, NULL, &err);
+ g_bytes_unref(g_queue_pop_head(conn->write_queue));
+
+ if (err)
+ return handle_io_error(conn, err);
+
+ conn->opening = FALSE;
+
+ if (g_queue_is_empty(conn->write_queue))
+ g_clear_pointer(&conn->flush_loop, g_main_loop_quit);
+ else
+ request_message_write(conn);
+}
+
+static void request_message_write(VDAgentConnection *conn)
+{
+ GBytes *msg;
+ GOutputStream *out;
+
+ msg = g_queue_peek_head(conn->write_queue);
+ out = g_io_stream_get_output_stream(conn->io_stream);
+
+ g_output_stream_write_all_async(out,
+ g_bytes_get_data(msg, NULL), g_bytes_get_size(msg),
+ G_PRIORITY_DEFAULT, conn->cancellable, message_write_cb, conn);
+}
+
+void vdagent_connection_write(VDAgentConnection *conn,
+ gpointer data,
+ gsize size)
+{
+ g_queue_push_tail(conn->write_queue, g_bytes_new_take(data, size));
+
+ if (g_queue_get_length(conn->write_queue) == 1)
+ request_message_write(conn);
+}
+
+void vdagent_connection_flush(VDAgentConnection *conn)
+{
+ GMainLoop *loop;
+ /* TODO: allow multiple flush calls at once? */
+ g_return_if_fail(conn->flush_loop == NULL);
+
+ if (g_queue_is_empty(conn->write_queue))
+ return;
+
+ loop = conn->flush_loop = g_main_loop_new(NULL, FALSE);
+ /* When using GTK+, this should be wrapped with
+ * gdk_threads_leave() and gdk_threads_enter(),
+ * but since flush is used in virtio-port.c only
+ * let's leave it as it is for now. */
+ g_main_loop_run(loop);
+ g_main_loop_unref(loop);
+}
+
+static void message_read_cb(GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ VDAgentConnection *conn = user_data;
+ GInputStream *in = G_INPUT_STREAM(source_object);
+ GError *err = NULL;
+ gsize bytes_read, data_size;
+
+ g_input_stream_read_all_finish(in, res, &bytes_read, &err);
+ if (err)
+ return handle_io_error(conn, err);
+ if (bytes_read == 0) {
+ /* see virtio-port.c for the rationale behind this */
+ if (conn->opening) {
+ g_usleep(10000);
+ request_message_read(conn);
+ } else {
+ conn->error_cb(conn->user_data);
+ }
+ return;
+ }
+ conn->opening = FALSE;
+
+ if (conn->read_buff == NULL) {
+ /* we've read the message header, now let's read its body */
+ if (!conn->header_read_cb(conn->header_buff, &data_size, conn->user_data))
+ return;
+ if (data_size > 0) {
+ conn->read_buff = g_malloc(data_size);
+ /* TODO: if allocation fails, we could try g_input_stream_skip()
+ * and hope that the message wasn't crucial for proper functiong.
+ * An example might be when a user tries to copy large clipboard.
+ * Not sure whether it's worth implementing.
+ * Other stuff might just as well fall apart
+ * when the system is running out of memory? */
+ g_input_stream_read_all_async(in, conn->read_buff, data_size,
+ G_PRIORITY_DEFAULT, conn->cancellable, message_read_cb, conn);
+ return;
+ }
+ }
+
+ if (!conn->read_cb(conn->header_buff, conn->read_buff, conn->user_data))
+ return;
+ g_clear_pointer(&conn->read_buff, g_free);
+ request_message_read(conn);
+}
+
+static void request_message_read(VDAgentConnection *conn)
+{
+ GInputStream *in;
+ in = g_io_stream_get_input_stream(conn->io_stream);
+
+ g_input_stream_read_all_async(in, conn->header_buff, conn->header_size,
+ G_PRIORITY_DEFAULT, conn->cancellable, message_read_cb, conn);
+}
diff --git a/src/vdagent-connection.h b/src/vdagent-connection.h
new file mode 100644
index 0000000..6fc0081
--- /dev/null
+++ b/src/vdagent-connection.h
@@ -0,0 +1,103 @@
+/* vdagent-connection.h
+
+ Copyright 2018 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 3 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __VDAGENT_CONNECTION_H
+#define __VDAGENT_CONNECTION_H
+
+#include <glib.h>
+#include <gio/gio.h>
+
+typedef struct VDAgentConnection VDAgentConnection;
+
+/* Called when a message header has been read.
+ *
+ * If the handler wishes to continue reading,
+ *
+ * Otherwise the handler should return FALSE
+ * and call vdagent_connection_destroy().
+ *
+typedef gboolean (*VDAgentConnHeaderReadCb)(gpointer header_buff,
+ gsize *body_size,
+ gpointer user_data);
+
+/* Called when a full message has been read.
+ *
+ * If the handler wished to continue reading, it must return TRUE,
+ * otherwise FALSE and call vdagent_connection_destroy().
+ *
+typedef gboolean (*VDAgentConnReadCb)(gpointer header,
+ gpointer data,
+ gpointer user_data);
+
+/* Called when an error occured during read or wirte.
+ * The handler is expected to call vdagent_connection_destroy(). */
+typedef void (*VDAgentConnErrorCb)(gpointer user_data);
+
+ * Returns a GIOStream to the given file or NULL on error. */
+GIOStream *vdagent_file_open(const gchar *path);
+
+ * Returns a GIOStream corresponding to the new connection or NULL on error. */
+GIOStream *vdagent_socket_connect(const gchar *address);
+
+/* Create new VDAgentConnection and start reading incoming messages.
+ *
+ *
+VDAgentConnection *vdagent_connection_new(
+ GIOStream *io_stream,
+ gboolean wait_on_opening,
+ gsize header_size,
+ VDAgentConnHeaderReadCb header_read_cb,
+ VDAgentConnReadCb read_cb,
+ VDAgentConnErrorCb error_cb,
+ gpointer user_data);
+
+/* Free up all resources associated with the VDAgentConnection.
+ *
+ * This operation can be asynchronous. */
+void vdagent_connection_destroy(VDAgentConnection *conn);
+
+/* Append a message to write queue.
+ *
+ * and frees it once the message is flushed. */
+void vdagent_connection_write(VDAgentConnection *conn,
+ gpointer data,
+ gsize size);
+
+/* Waits until all queued messages get written to the output stream.
+ *
+ * Note: other GSources can be triggered during this call */
+void vdagent_connection_flush(VDAgentConnection *conn);
+
+/* Returns the credentials of the foreign process connected to the socket.
+ *
+ * It is an error to call this function with a VDAgentConnection
+ * that isn't based on a GIOStream of G_TYPE_SOCKET_CONNECTION. */
+GCredentials *vdagent_connection_get_peer_credentials(
+ VDAgentConnection *conn);
+
+#endif
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Jakub Janků
2018-08-14 18:53:40 UTC
Permalink
udscs_get_peer_cred() is currently used only in
vdagentd.c to obtain PID, so let's drop udscs_get_peer_cred()
and add udscs_get_peer_pid() instead.
---
src/udscs.c | 6 +++---
src/udscs.h | 4 ++--
src/vdagentd/vdagentd.c | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/udscs.c b/src/udscs.c
index c439f23..a77da99 100644
--- a/src/udscs.c
+++ b/src/udscs.c
@@ -490,9 +490,9 @@ void udscs_destroy_server(struct udscs_server *server)
free(server);
}

-struct ucred udscs_get_peer_cred(struct udscs_connection *conn)
+int udscs_get_peer_pid(struct udscs_connection *conn)
{
- return conn->peer_cred;
+ return (int)conn->peer_cred.pid;
}

static void udscs_server_accept(struct udscs_server *server) {
@@ -541,7 +541,7 @@ static void udscs_server_accept(struct udscs_server *server) {

if (server->debug)
syslog(LOG_DEBUG, "new client accepted: %p, pid: %d",
- new_conn, (int)new_conn->peer_cred.pid);
+ new_conn, udscs_get_peer_pid(new_conn));

if (server->connect_callback)
server->connect_callback(new_conn);
diff --git a/src/udscs.h b/src/udscs.h
index 48ecf90..4f47b7f 100644
--- a/src/udscs.h
+++ b/src/udscs.h
@@ -171,8 +171,8 @@ int udscs_server_fill_fds(struct udscs_server *server, fd_set *readfds,
void udscs_server_handle_fds(struct udscs_server *server, fd_set *readfds,
fd_set *writefds);

-/* Returns the peer's user credentials. */
-struct ucred udscs_get_peer_cred(struct udscs_connection *conn);
+/* Returns the peer's PID. */
+int udscs_get_peer_pid(struct udscs_connection *conn);

#endif

diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index 8abc63c..53d5516 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -885,7 +885,7 @@ static void agent_connect(struct udscs_connection *conn)
}

if (session_info) {
- uint32_t pid = udscs_get_peer_cred(conn).pid;
+ uint32_t pid = udscs_get_peer_pid(conn);
agent_data->session = session_info_session_for_pid(session_info, pid);
}
--
2.17.1
Victor Toso
2018-08-28 06:40:15 UTC
Permalink
Hi,
Post by Jakub Janků
udscs_get_peer_cred() is currently used only in
vdagentd.c to obtain PID, so let's drop udscs_get_peer_cred()
and add udscs_get_peer_pid() instead.
Acked-by: Victor Toso <***@redhat.com>

Pushed: https://gitlab.freedesktop.org/spice/linux/vd_agent/commit/359cceda914b37

Cheers,
Victor
Post by Jakub Janků
---
src/udscs.c | 6 +++---
src/udscs.h | 4 ++--
src/vdagentd/vdagentd.c | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/udscs.c b/src/udscs.c
index c439f23..a77da99 100644
--- a/src/udscs.c
+++ b/src/udscs.c
@@ -490,9 +490,9 @@ void udscs_destroy_server(struct udscs_server *server)
free(server);
}
-struct ucred udscs_get_peer_cred(struct udscs_connection *conn)
+int udscs_get_peer_pid(struct udscs_connection *conn)
{
- return conn->peer_cred;
+ return (int)conn->peer_cred.pid;
}
static void udscs_server_accept(struct udscs_server *server) {
@@ -541,7 +541,7 @@ static void udscs_server_accept(struct udscs_server *server) {
if (server->debug)
syslog(LOG_DEBUG, "new client accepted: %p, pid: %d",
- new_conn, (int)new_conn->peer_cred.pid);
+ new_conn, udscs_get_peer_pid(new_conn));
if (server->connect_callback)
server->connect_callback(new_conn);
diff --git a/src/udscs.h b/src/udscs.h
index 48ecf90..4f47b7f 100644
--- a/src/udscs.h
+++ b/src/udscs.h
@@ -171,8 +171,8 @@ int udscs_server_fill_fds(struct udscs_server *server, fd_set *readfds,
void udscs_server_handle_fds(struct udscs_server *server, fd_set *readfds,
fd_set *writefds);
-/* Returns the peer's user credentials. */
-struct ucred udscs_get_peer_cred(struct udscs_connection *conn);
+/* Returns the peer's PID. */
+int udscs_get_peer_pid(struct udscs_connection *conn);
#endif
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index 8abc63c..53d5516 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -885,7 +885,7 @@ static void agent_connect(struct udscs_connection *conn)
}
if (session_info) {
- uint32_t pid = udscs_get_peer_cred(conn).pid;
+ uint32_t pid = udscs_get_peer_pid(conn);
agent_data->session = session_info_session_for_pid(session_info, pid);
}
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Jakub Janků
2018-08-14 18:53:41 UTC
Permalink
Rewrite udscs.c using VDAgentConnection to integrate it
into GMainLoop and simplify the code.

udscs_destroy_connection() does NOT close
the underlying FD immediately.
Apart from that, the behavior stays the same.

Drop support for select() in udscs_server, remove
udscs_server_fill_fds(), udscs_server_handle_fds().
---
src/udscs.c | 515 ++++++++++++----------------------------
src/udscs.h | 15 --
src/vdagentd/vdagentd.c | 7 +-
3 files changed, 150 insertions(+), 387 deletions(-)

diff --git a/src/udscs.c b/src/udscs.c
index a77da99..45565b6 100644
--- a/src/udscs.c
+++ b/src/udscs.c
@@ -24,43 +24,21 @@
#include <config.h>
#endif

-#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
-#include <unistd.h>
-#include <errno.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <glib.h>
#include <glib-unix.h>
-#include "udscs.h"
-
-struct udscs_buf {
- uint8_t *buf;
- size_t pos;
- size_t size;
+#include <gio/gunixsocketaddress.h>

- struct udscs_buf *next;
-};
+#include "udscs.h"
+#include "vdagent-connection.h"

struct udscs_connection {
- int fd;
const char * const *type_to_string;
int no_types;
int debug;
void *user_data;
-#ifndef UDSCS_NO_SERVER
- struct ucred peer_cred;
-#endif

- /* Read stuff, single buffer, separate header and data buffer */
- int header_read;
- struct udscs_message_header header;
- struct udscs_buf data;
-
- /* Writes are stored in a linked list of buffers, with both the header
- + data for a single message in 1 buffer. */
- struct udscs_buf *write_buf;
+ VDAgentConnection *conn;

/* Callbacks */
udscs_read_callback read_callback;
@@ -68,25 +46,61 @@ struct udscs_connection {

struct udscs_connection *next;
struct udscs_connection *prev;
-
- GIOChannel *io_channel;
- guint write_watch_id;
- guint read_watch_id;
};

-static gboolean udscs_io_channel_cb(GIOChannel *source,
- GIOCondition condition,
- gpointer data);
+static gboolean conn_header_read_cb(gpointer header_buff,
+ gsize *body_size,
+ gpointer user_data)
+{
+ struct udscs_message_header *header = header_buff;
+ *body_size = header->size;
+ return TRUE;
+}
+
+static gboolean conn_read_cb(gpointer header_buff,
+ gpointer data,
+ gpointer user_data)
+{
+ struct udscs_connection *conn = user_data;
+ struct udscs_message_header *header = header_buff;
+
+ if (conn->debug) {
+ if (header->type < conn->no_types)
+ syslog(LOG_DEBUG,
+ "%p received %s, arg1: %u, arg2: %u, size %u",
+ conn, conn->type_to_string[header->type],
+ header->arg1, header->arg2, header->size);
+ else
+ syslog(LOG_DEBUG,
+ "%p received invalid message %u, arg1: %u, arg2: %u, size %u",
+ conn, header->type, header->arg1, header->arg2,
+ header->size);
+ }
+
+ if (conn->read_callback) {
+ conn->read_callback(&conn, header, data);
+ }
+ return conn != NULL;
+}
+
+static void conn_error_cb(gpointer user_data)
+{
+ struct udscs_connection *conn = user_data;
+ udscs_destroy_connection(&conn);
+}

struct udscs_connection *udscs_connect(const char *socketname,
udscs_read_callback read_callback,
udscs_disconnect_callback disconnect_callback,
const char * const type_to_string[], int no_types, int debug)
{
- int c;
- struct sockaddr_un address;
+ GIOStream *io_stream;
struct udscs_connection *conn;

+ io_stream = vdagent_socket_connect(socketname);
+ if (io_stream == NULL)
+ return NULL;
+
conn = calloc(1, sizeof(*conn));
if (!conn)
return NULL;
@@ -94,36 +108,13 @@ struct udscs_connection *udscs_connect(const char *socketname,
conn->type_to_string = type_to_string;
conn->no_types = no_types;
conn->debug = debug;
-
- conn->fd = socket(PF_UNIX, SOCK_STREAM, 0);
- if (conn->fd == -1) {
- syslog(LOG_ERR, "creating unix domain socket: %m");
- free(conn);
- return NULL;
- }
-
- address.sun_family = AF_UNIX;
- snprintf(address.sun_path, sizeof(address.sun_path), "%s", socketname);
- c = connect(conn->fd, (struct sockaddr *)&address, sizeof(address));
- if (c != 0) {
- if (conn->debug) {
- syslog(LOG_DEBUG, "connect %s: %m", socketname);
- }
- free(conn);
- return NULL;
- }
-
- conn->io_channel = g_io_channel_unix_new(conn->fd);
- if (!conn->io_channel) {
- udscs_destroy_connection(&conn);
- return NULL;
- }
- conn->read_watch_id =
- g_io_add_watch(conn->io_channel,
- G_IO_IN | G_IO_ERR | G_IO_NVAL,
- udscs_io_channel_cb,
- conn);
-
+ conn->conn = vdagent_connection_new(io_stream,
+ FALSE,
+ sizeof(struct udscs_message_header),
+ conn_header_read_cb,
+ conn_read_cb,
+ conn_error_cb,
+ conn);
conn->read_callback = read_callback;
conn->disconnect_callback = disconnect_callback;

@@ -135,7 +126,6 @@ struct udscs_connection *udscs_connect(const char *socketname,

void udscs_destroy_connection(struct udscs_connection **connp)
{
- struct udscs_buf *wbuf, *next_wbuf;
struct udscs_connection *conn = *connp;

if (!conn)
@@ -144,29 +134,12 @@ void udscs_destroy_connection(struct udscs_connection **connp)
if (conn->disconnect_callback)
conn->disconnect_callback(conn);

- wbuf = conn->write_buf;
- while (wbuf) {
- next_wbuf = wbuf->next;
- free(wbuf->buf);
- free(wbuf);
- wbuf = next_wbuf;
- }
-
- free(conn->data.buf);
- conn->data.buf = NULL;
-
if (conn->next)
conn->next->prev = conn->prev;
if (conn->prev)
conn->prev->next = conn->next;

- close(conn->fd);
-
- if (conn->write_watch_id != 0)
- g_source_remove(conn->write_watch_id);
- if (conn->read_watch_id != 0)
- g_source_remove(conn->read_watch_id);
- g_clear_pointer(&conn->io_channel, g_io_channel_unref);
+ vdagent_connection_destroy(conn->conn);

if (conn->debug)
syslog(LOG_DEBUG, "%p disconnected", conn);
@@ -191,29 +164,22 @@ void *udscs_get_user_data(struct udscs_connection *conn)
int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1,
uint32_t arg2, const uint8_t *data, uint32_t size)
{
- struct udscs_buf *wbuf, *new_wbuf;
+ guchar *buff;
+ guint buff_size;
struct udscs_message_header header;

- new_wbuf = malloc(sizeof(*new_wbuf));
- if (!new_wbuf)
+ buff_size = sizeof(header) + size;
+ buff = malloc(buff_size);
+ if (buff == NULL)
return -1;

- new_wbuf->pos = 0;
- new_wbuf->size = sizeof(header) + size;
- new_wbuf->next = NULL;
- new_wbuf->buf = malloc(new_wbuf->size);
- if (!new_wbuf->buf) {
- free(new_wbuf);
- return -1;
- }
-
header.type = type;
header.arg1 = arg1;
header.arg2 = arg2;
header.size = size;

- memcpy(new_wbuf->buf, &header, sizeof(header));
- memcpy(new_wbuf->buf + sizeof(header), data, size);
+ memcpy(buff, &header, sizeof(header));
+ memcpy(buff + sizeof(header), data, size);

if (conn->debug) {
if (type < conn->no_types)
@@ -225,173 +191,17 @@ int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1,
conn, type, arg1, arg2, size);
}

- if (conn->io_channel && conn->write_watch_id == 0)
- conn->write_watch_id =
- g_io_add_watch(conn->io_channel,
- G_IO_OUT | G_IO_ERR | G_IO_NVAL,
- udscs_io_channel_cb,
- conn);
-
- if (!conn->write_buf) {
- conn->write_buf = new_wbuf;
- return 0;
- }
-
- /* maybe we should limit the write_buf stack depth ? */
- wbuf = conn->write_buf;
- while (wbuf->next)
- wbuf = wbuf->next;
-
- wbuf->next = new_wbuf;
-
+ vdagent_connection_write(conn->conn, buff, buff_size);
return 0;
}

-/* A helper for udscs_do_read() */
-static void udscs_read_complete(struct udscs_connection **connp)
-{
- struct udscs_connection *conn = *connp;
-
- if (conn->debug) {
- if (conn->header.type < conn->no_types)
- syslog(LOG_DEBUG,
- "%p received %s, arg1: %u, arg2: %u, size %u",
- conn, conn->type_to_string[conn->header.type],
- conn->header.arg1, conn->header.arg2, conn->header.size);
- else
- syslog(LOG_DEBUG,
- "%p received invalid message %u, arg1: %u, arg2: %u, size %u",
- conn, conn->header.type, conn->header.arg1, conn->header.arg2,
- conn->header.size);
- }
-
- if (conn->read_callback) {
- conn->read_callback(connp, &conn->header, conn->data.buf);
- if (!*connp) /* Was the connection disconnected by the callback ? */
- return;
- }
-
- free(conn->data.buf);
- memset(&conn->data, 0, sizeof(conn->data)); /* data.buf = NULL */
- conn->header_read = 0;
-}
-
-static void udscs_do_read(struct udscs_connection **connp)
-{
- ssize_t n;
- size_t to_read;
- uint8_t *dest;
- struct udscs_connection *conn = *connp;
-
- if (conn->header_read < sizeof(conn->header)) {
- to_read = sizeof(conn->header) - conn->header_read;
- dest = (uint8_t *)&conn->header + conn->header_read;
- } else {
- to_read = conn->data.size - conn->data.pos;
- dest = conn->data.buf + conn->data.pos;
- }
-
- n = read(conn->fd, dest, to_read);
- if (n < 0) {
- if (errno == EINTR)
- return;
- syslog(LOG_ERR, "reading unix domain socket: %m, disconnecting %p",
- conn);
- }
- if (n <= 0) {
- udscs_destroy_connection(connp);
- return;
- }
-
- if (conn->header_read < sizeof(conn->header)) {
- conn->header_read += n;
- if (conn->header_read == sizeof(conn->header)) {
- if (conn->header.size == 0) {
- udscs_read_complete(connp);
- return;
- }
- conn->data.pos = 0;
- conn->data.size = conn->header.size;
- conn->data.buf = malloc(conn->data.size);
- if (!conn->data.buf) {
- syslog(LOG_ERR, "out of memory, disconnecting %p", conn);
- udscs_destroy_connection(connp);
- return;
- }
- }
- } else {
- conn->data.pos += n;
- if (conn->data.pos == conn->data.size)
- udscs_read_complete(connp);
- }
-}
-
-static void udscs_do_write(struct udscs_connection **connp)
-{
- ssize_t n;
- size_t to_write;
- struct udscs_connection *conn = *connp;
-
- struct udscs_buf* wbuf = conn->write_buf;
- if (!wbuf) {
- syslog(LOG_ERR,
- "%p do_write called on a connection without a write buf ?!",
- conn);
- return;
- }
-
- to_write = wbuf->size - wbuf->pos;
- n = write(conn->fd, wbuf->buf + wbuf->pos, to_write);
- if (n < 0) {
- if (errno == EINTR)
- return;
- syslog(LOG_ERR, "writing to unix domain socket: %m, disconnecting %p",
- conn);
- udscs_destroy_connection(connp);
- return;
- }
-
- wbuf->pos += n;
- if (wbuf->pos == wbuf->size) {
- conn->write_buf = wbuf->next;
- free(wbuf->buf);
- free(wbuf);
- }
-}
-
-static gboolean udscs_io_channel_cb(GIOChannel *source,
- GIOCondition condition,
- gpointer data)
-{
- struct udscs_connection *conn = data;
-
- if (condition & G_IO_IN) {
- udscs_do_read(&conn);
- if (conn == NULL)
- return G_SOURCE_REMOVE;
- return G_SOURCE_CONTINUE;
- }
- if (condition & G_IO_OUT) {
- udscs_do_write(&conn);
- if (conn == NULL)
- return G_SOURCE_REMOVE;
- if (conn->write_buf)
- return G_SOURCE_CONTINUE;
- conn->write_watch_id = 0;
- return G_SOURCE_REMOVE;
- }
-
- udscs_destroy_connection(&conn);
- return G_SOURCE_REMOVE;
-}
-
-
#ifndef UDSCS_NO_SERVER

/* ---------- Server-side implementation ---------- */

struct udscs_server {
- int fd;
+ GSocketService *service;
+
const char * const *type_to_string;
int no_types;
int debug;
@@ -401,7 +211,12 @@ struct udscs_server {
udscs_disconnect_callback disconnect_callback;
};

-struct udscs_server *udscs_create_server_for_fd(int fd,
+static gboolean udscs_server_accept_cb(GSocketService *service,
+ GSocketConnection *socket_conn,
+ GObject *source_object,
+ gpointer user_data);
+
+static struct udscs_server *udscs_server_new(
udscs_connect_callback connect_callback,
udscs_read_callback read_callback,
udscs_disconnect_callback disconnect_callback,
@@ -409,11 +224,6 @@ struct udscs_server *udscs_create_server_for_fd(int fd,
{
struct udscs_server *server;

- if (fd <= 0) {
- syslog(LOG_ERR, "Invalid file descriptor: %i", fd);
- return NULL;
- }
-
server = calloc(1, sizeof(*server));
if (!server)
return NULL;
@@ -421,53 +231,76 @@ struct udscs_server *udscs_create_server_for_fd(int fd,
server->type_to_string = type_to_string;
server->no_types = no_types;
server->debug = debug;
- server->fd = fd;
server->connect_callback = connect_callback;
server->read_callback = read_callback;
server->disconnect_callback = disconnect_callback;
+ server->service = g_socket_service_new();
+
+ g_signal_connect(server->service, "incoming",
+ G_CALLBACK(udscs_server_accept_cb), server);

return server;
}

-struct udscs_server *udscs_create_server(const char *socketname,
+struct udscs_server *udscs_create_server_for_fd(int fd,
udscs_connect_callback connect_callback,
udscs_read_callback read_callback,
udscs_disconnect_callback disconnect_callback,
const char * const type_to_string[], int no_types, int debug)
{
- int c;
- int fd;
- struct sockaddr_un address;
struct udscs_server *server;
+ GSocket *socket;
+ GError *err = NULL;

- fd = socket(PF_UNIX, SOCK_STREAM, 0);
- if (fd == -1) {
- syslog(LOG_ERR, "creating unix domain socket: %m");
+ server = udscs_server_new(connect_callback, read_callback, disconnect_callback,
+ type_to_string, no_types, debug);
+ if (server == NULL)
return NULL;
- }

- address.sun_family = AF_UNIX;
- snprintf(address.sun_path, sizeof(address.sun_path), "%s", socketname);
- c = bind(fd, (struct sockaddr *)&address, sizeof(address));
- if (c != 0) {
- syslog(LOG_ERR, "bind %s: %m", socketname);
- close(fd);
- return NULL;
- }
+ socket = g_socket_new_from_fd(fd, &err);
+ if (err)
+ goto error;
+ g_socket_listener_add_socket(G_SOCKET_LISTENER(server->service),
+ socket, NULL, &err);
+ g_object_unref(socket);
+ if (err)
+ goto error;

- c = listen(fd, 5);
- if (c != 0) {
- syslog(LOG_ERR, "listen: %m");
- close(fd);
- return NULL;
- }
+ return server;
+error:
+ syslog(LOG_ERR, "%s: %s", __func__, err->message);
+ g_error_free(err);
+ udscs_destroy_server(server);
+ return NULL;
+}

- server = udscs_create_server_for_fd(fd, connect_callback, read_callback,
- disconnect_callback, type_to_string,
- no_types, debug);
+struct udscs_server *udscs_create_server(const char *socketname,
+ udscs_connect_callback connect_callback,
+ udscs_read_callback read_callback,
+ udscs_disconnect_callback disconnect_callback,
+ const char * const type_to_string[], int no_types, int debug)
+{
+ struct udscs_server *server;
+ GSocketAddress *socket_addr;
+ GError *err = NULL;
+
+ server = udscs_server_new(connect_callback, read_callback, disconnect_callback,
+ type_to_string, no_types, debug);
+ if (server == NULL)
+ return NULL;

- if (!server) {
- close(fd);
+ socket_addr = g_unix_socket_address_new(socketname);
+ g_socket_listener_add_address(G_SOCKET_LISTENER(server->service),
+ socket_addr,
+ G_SOCKET_TYPE_STREAM,
+ G_SOCKET_PROTOCOL_DEFAULT,
+ NULL, NULL, &err);
+ g_object_unref(socket_addr);
+ if (err) {
+ syslog(LOG_ERR, "%s: %s", __func__, err->message);
+ g_error_free(err);
+ udscs_destroy_server(server);
+ return NULL;
}

return server;
@@ -486,50 +319,49 @@ void udscs_destroy_server(struct udscs_server *server)
udscs_destroy_connection(&conn);
conn = next_conn;
}
- close(server->fd);
+ g_object_unref(server->service);
free(server);
}

int udscs_get_peer_pid(struct udscs_connection *conn)
{
- return (int)conn->peer_cred.pid;
+ GCredentials *cred = vdagent_connection_get_peer_credentials(conn->conn);
+ return cred ? g_credentials_get_unix_pid(cred, NULL) : -1;
}

-static void udscs_server_accept(struct udscs_server *server) {
+static gboolean udscs_server_accept_cb(GSocketService *service,
+ GSocketConnection *socket_conn,
+ GObject *source_object,
+ gpointer user_data)
+{
+ struct udscs_server *server = user_data;
struct udscs_connection *new_conn, *conn;
- struct sockaddr_un address;
- socklen_t length = sizeof(address);
- int r, fd;
-
- fd = accept(server->fd, (struct sockaddr *)&address, &length);
- if (fd == -1) {
- if (errno == EINTR)
- return;
- syslog(LOG_ERR, "accept: %m");
- return;
- }

new_conn = calloc(1, sizeof(*conn));
if (!new_conn) {
syslog(LOG_ERR, "out of memory, disconnecting new client");
- close(fd);
- return;
+ return TRUE;
}

- new_conn->fd = fd;
new_conn->type_to_string = server->type_to_string;
new_conn->no_types = server->no_types;
new_conn->debug = server->debug;
new_conn->read_callback = server->read_callback;
new_conn->disconnect_callback = server->disconnect_callback;

- length = sizeof(new_conn->peer_cred);
- r = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &new_conn->peer_cred, &length);
- if (r != 0) {
- syslog(LOG_ERR, "Could not get peercred, disconnecting new client");
- close(fd);
- free(new_conn);
- return;
+ g_object_ref(socket_conn);
+ new_conn->conn = vdagent_connection_new(G_IO_STREAM(socket_conn),
+ FALSE,
+ sizeof(struct udscs_message_header),
+ conn_header_read_cb,
+ conn_read_cb,
+ conn_error_cb,
+ new_conn);
+
+ if (udscs_get_peer_pid(new_conn) == -1) {
+ syslog(LOG_ERR, "Could not get peer PID, disconnecting new client");
+ vdagent_connection_destroy(new_conn->conn);
+ return TRUE;
}

conn = &server->connections_head;
@@ -545,59 +377,8 @@ static void udscs_server_accept(struct udscs_server *server) {

if (server->connect_callback)
server->connect_callback(new_conn);
-}
-
-int udscs_server_fill_fds(struct udscs_server *server, fd_set *readfds,
- fd_set *writefds)
-{
- struct udscs_connection *conn;
- int nfds;
-
- if (!server)
- return -1;
-
- nfds = server->fd + 1;
- FD_SET(server->fd, readfds);
-
- conn = server->connections_head.next;
- while (conn) {
- FD_SET(conn->fd, readfds);
- if (conn->write_buf)
- FD_SET(conn->fd, writefds);

- if (conn->fd >= nfds)
- nfds = conn->fd + 1;
-
- conn = conn->next;
- }
-
- return nfds;
-}
-
-void udscs_server_handle_fds(struct udscs_server *server, fd_set *readfds,
- fd_set *writefds)
-{
- struct udscs_connection *conn, *next_conn;
-
- if (!server)
- return;
-
- if (FD_ISSET(server->fd, readfds))
- udscs_server_accept(server);
-
- conn = server->connections_head.next;
- while (conn) {
- /* conn may be destroyed by udscs_do_read() or udscs_do_write()
- * (when disconnected), so get the next connection first. */
- next_conn = conn->next;
-
- if (FD_ISSET(conn->fd, readfds))
- udscs_do_read(&conn);
- if (conn && FD_ISSET(conn->fd, writefds))
- udscs_do_write(&conn);
-
- conn = next_conn;
- }
+ return TRUE;
}

int udscs_server_write_all(struct udscs_server *server,
diff --git a/src/udscs.h b/src/udscs.h
index 4f47b7f..dba3fb9 100644
--- a/src/udscs.h
+++ b/src/udscs.h
@@ -22,9 +22,7 @@
#ifndef __UDSCS_H
#define __UDSCS_H

-#include <stdio.h>
#include <stdint.h>
-#include <sys/select.h>
#include <sys/socket.h>


@@ -158,19 +156,6 @@ typedef int (*udscs_for_all_clients_callback)(struct udscs_connection **connp,
int udscs_server_for_all_clients(struct udscs_server *server,
udscs_for_all_clients_callback func, void *priv);

-/* Given a udscs server, fill the fd_sets pointed to by readfds and
- * writefds for select() usage.
- * Return value: value of the highest fd + 1 or -1 if server is NULL
- */
-int udscs_server_fill_fds(struct udscs_server *server, fd_set *readfds,
- fd_set *writefds);
-
-/* Handle any events flagged by select for the given udscs server.
- * Does nothing if server is NULL.
- */
-void udscs_server_handle_fds(struct udscs_server *server, fd_set *readfds,
- fd_set *writefds);
-
/* Returns the peer's PID. */
int udscs_get_peer_pid(struct udscs_connection *conn);

diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index 53d5516..027406f 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -1130,13 +1130,10 @@ int main(int argc, char *argv[])
}

if (!server) {
- if (errno == EADDRINUSE) {
- syslog(LOG_CRIT, "Fatal the server socket %s exists already. Delete it?",
- vdagentd_socket);
- } else if (errno == ENOMEM) {
+ if (errno == ENOMEM) {
syslog(LOG_CRIT, "Fatal could not allocate memory for udscs server");
} else {
- syslog(LOG_CRIT, "Fatal could not create the server socket %s: %m",
+ syslog(LOG_CRIT, "Fatal could not create the server socket %s",
vdagentd_socket);
}
return 1;
--
2.17.1
Jakub Janků
2018-08-14 18:53:42 UTC
Permalink
Divide creation of udscs_server into 2 steps:

1) udscs_server_new()
- allocates new udscs_server struct and inits it

2) udscs_server_listen_to_fd(), udscs_server_listen_to_address()
- starts accepting new connections

udscs_server_listen_to_address() creates a new socket and
binds it to the given address.

Remove udscs_create_server() and udscs_create_server_for_fd().

TODO: maybe squash with the previous commit
if this is the way to go?
---
src/udscs.c | 46 ++++++++++-------------------------------
src/udscs.h | 25 +++++++++-------------
src/vdagentd/vdagentd.c | 25 +++++++++-------------
3 files changed, 31 insertions(+), 65 deletions(-)

diff --git a/src/udscs.c b/src/udscs.c
index 45565b6..4a657c9 100644
--- a/src/udscs.c
+++ b/src/udscs.c
@@ -216,7 +216,7 @@ static gboolean udscs_server_accept_cb(GSocketService *service,
GObject *source_object,
gpointer user_data);

-static struct udscs_server *udscs_server_new(
+struct udscs_server *udscs_server_new(
udscs_connect_callback connect_callback,
udscs_read_callback read_callback,
udscs_disconnect_callback disconnect_callback,
@@ -242,55 +242,34 @@ static struct udscs_server *udscs_server_new(
return server;
}

-struct udscs_server *udscs_create_server_for_fd(int fd,
- udscs_connect_callback connect_callback,
- udscs_read_callback read_callback,
- udscs_disconnect_callback disconnect_callback,
- const char * const type_to_string[], int no_types, int debug)
+void udscs_server_listen_to_socket(struct udscs_server **server, gint fd)
{
- struct udscs_server *server;
GSocket *socket;
GError *err = NULL;

- server = udscs_server_new(connect_callback, read_callback, disconnect_callback,
- type_to_string, no_types, debug);
- if (server == NULL)
- return NULL;
-
socket = g_socket_new_from_fd(fd, &err);
if (err)
goto error;
- g_socket_listener_add_socket(G_SOCKET_LISTENER(server->service),
+ g_socket_listener_add_socket(G_SOCKET_LISTENER((*server)->service),
socket, NULL, &err);
g_object_unref(socket);
- if (err)
- goto error;
+ if (!err)
+ return;

- return server;
error:
syslog(LOG_ERR, "%s: %s", __func__, err->message);
g_error_free(err);
- udscs_destroy_server(server);
- return NULL;
+ g_clear_pointer(server, udscs_destroy_server);
}

-struct udscs_server *udscs_create_server(const char *socketname,
- udscs_connect_callback connect_callback,
- udscs_read_callback read_callback,
- udscs_disconnect_callback disconnect_callback,
- const char * const type_to_string[], int no_types, int debug)
+void udscs_server_listen_to_address(struct udscs_server **server,
+ const gchar *address)
{
- struct udscs_server *server;
GSocketAddress *socket_addr;
GError *err = NULL;

- server = udscs_server_new(connect_callback, read_callback, disconnect_callback,
- type_to_string, no_types, debug);
- if (server == NULL)
- return NULL;
-
- socket_addr = g_unix_socket_address_new(socketname);
- g_socket_listener_add_address(G_SOCKET_LISTENER(server->service),
+ socket_addr = g_unix_socket_address_new(address);
+ g_socket_listener_add_address(G_SOCKET_LISTENER((*server)->service),
socket_addr,
G_SOCKET_TYPE_STREAM,
G_SOCKET_PROTOCOL_DEFAULT,
@@ -299,11 +278,8 @@ struct udscs_server *udscs_create_server(const char *socketname,
if (err) {
syslog(LOG_ERR, "%s: %s", __func__, err->message);
g_error_free(err);
- udscs_destroy_server(server);
- return NULL;
+ g_clear_pointer(server, udscs_destroy_server);
}
-
- return server;
}

void udscs_destroy_server(struct udscs_server *server)
diff --git a/src/udscs.h b/src/udscs.h
index dba3fb9..d02d5b4 100644
--- a/src/udscs.h
+++ b/src/udscs.h
@@ -102,20 +102,7 @@ struct udscs_server;
*/
typedef void (*udscs_connect_callback)(struct udscs_connection *conn);

-/* Create a server for the given file descriptor. This allows us to use
- * pre-configured sockets for use with systemd socket activation, etc.
- *
- * See udscs_create_server() for more information
- */
-struct udscs_server *udscs_create_server_for_fd(int fd,
- udscs_connect_callback connect_callback,
- udscs_read_callback read_callback,
- udscs_disconnect_callback disconnect_callback,
- const char * const type_to_string[], int no_types, int debug);
-
-/* Create the unix domain socket specified by socketname and
- * start listening on it.
- * Only sockets bound to a pathname are supported.
+/* Initialize a new udscs_server struct.
*
* If debug is true then the events on this socket and related individual
* connections will be traced.
@@ -124,12 +111,20 @@ struct udscs_server *udscs_create_server_for_fd(int fd,
* and type_to_string must point to a string array of size no_types for
* converting the message ids to their names.
*/
-struct udscs_server *udscs_create_server(const char *socketname,
+struct udscs_server *udscs_server_new(
udscs_connect_callback connect_callback,
udscs_read_callback read_callback,
udscs_disconnect_callback disconnect_callback,
const char * const type_to_string[], int no_types, int debug);

+/* Start listening on a pre-configured socket specified by the given @fd.
+ * This can be used with systemd socket activation, etc. */
+void udscs_server_listen_to_socket(struct udscs_server **server, gint fd);
+
+/* Create a new socket, bind it to @address and start listening on it. */
+void udscs_server_listen_to_address(struct udscs_server **server,
+ const gchar *address);
+
/* Close all the server's connections and releases the corresponding
* resources.
* Does nothing if server is NULL.
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index 027406f..fd54723 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -1105,6 +1105,12 @@ int main(int argc, char *argv[])
openlog("spice-vdagentd", do_daemonize ? 0 : LOG_PERROR, LOG_USER);

/* Setup communication with vdagent process(es) */
+ server = udscs_server_new(agent_connect, agent_read_complete, agent_disconnect,
+ vdagentd_messages, VDAGENTD_NO_MESSAGES, debug);
+ if (server == NULL) {
+ syslog(LOG_CRIT, "Fatal could not allocate memory for udscs server");
+ return 1;
+ }
#ifdef WITH_SYSTEMD_SOCKET_ACTIVATION
int n_fds;
/* try to retrieve pre-configured sockets from systemd */
@@ -1113,29 +1119,18 @@ int main(int argc, char *argv[])
syslog(LOG_CRIT, "Received too many sockets from systemd (%i)", n_fds);
return 1;
} else if (n_fds == 1) {
- server = udscs_create_server_for_fd(SD_LISTEN_FDS_START, agent_connect,
- agent_read_complete,
- agent_disconnect,
- vdagentd_messages,
- VDAGENTD_NO_MESSAGES, debug);
+ udscs_server_listen_to_socket(&server, SD_LISTEN_FDS_START);
own_socket = FALSE;
} else
/* systemd socket activation not enabled, create our own */
#endif /* WITH_SYSTEMD_SOCKET_ACTIVATION */
{
- server = udscs_create_server(vdagentd_socket, agent_connect,
- agent_read_complete, agent_disconnect,
- vdagentd_messages, VDAGENTD_NO_MESSAGES,
- debug);
+ udscs_server_listen_to_address(&server, vdagentd_socket);
}

if (!server) {
- if (errno == ENOMEM) {
- syslog(LOG_CRIT, "Fatal could not allocate memory for udscs server");
- } else {
- syslog(LOG_CRIT, "Fatal could not create the server socket %s",
- vdagentd_socket);
- }
+ syslog(LOG_CRIT, "Fatal could not create the server socket %s",
+ vdagentd_socket);
return 1;
}
--
2.17.1
Jakub Janků
2018-08-14 18:53:44 UTC
Permalink
Rewrite virtio-port.c using VDAgentConnection to integrate it
into GMainLoop and simplify the code.

virtio_port_destroy() does NOT close the underlying FD immediately.
GSources attached to GMainContext can be processed during
vdagent_virtio_port_flush() call.
Apart from that, the behavior stays the same.

Drop support for select(), remove
udscs_server_fill_fds(), udscs_server_handle_fds().
---
src/vdagentd/virtio-port.c | 369 +++++++++++--------------------------
src/vdagentd/virtio-port.h | 17 --
2 files changed, 110 insertions(+), 276 deletions(-)

diff --git a/src/vdagentd/virtio-port.c b/src/vdagentd/virtio-port.c
index 642c848..9731086 100644
--- a/src/vdagentd/virtio-port.c
+++ b/src/vdagentd/virtio-port.c
@@ -19,27 +19,19 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

-#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-#include <glib.h>
+#include <glib-unix.h>

+#include "vdagent-connection.h"
#include "virtio-port.h"


struct vdagent_virtio_port_buf {
uint8_t *buf;
- size_t pos;
size_t size;
size_t write_pos;
-
- struct vdagent_virtio_port_buf *next;
};

/* Data to keep track of the assembling of vdagent messages per chunk port,
@@ -52,21 +44,11 @@ struct vdagent_virtio_port_chunk_port_data {
};

struct vdagent_virtio_port {
- int fd;
- int opening;
- int is_uds;
-
- /* Chunk read stuff, single buffer, separate header and data buffer */
- int chunk_header_read;
- int chunk_data_pos;
- VDIChunkHeader chunk_header;
- uint8_t chunk_data[VD_AGENT_MAX_DATA_SIZE];
+ VDAgentConnection *conn;

/* Per chunk port data */
struct vdagent_virtio_port_chunk_port_data port_data[VDP_END_PORT];

- /* Writes are stored in a linked list of buffers, with both the header
- + data for a single message in 1 buffer. */
struct vdagent_virtio_port_buf *write_buf;

/* Callbacks */
@@ -74,58 +56,106 @@ struct vdagent_virtio_port {
vdagent_virtio_port_disconnect_callback disconnect_callback;
};

-static void vdagent_virtio_port_do_write(struct vdagent_virtio_port **vportp);
-static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp);
+static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp,
+ VDIChunkHeader *chunk_header,
+ const guchar *chunk_data);
+
+static void virtio_port_destroy(struct vdagent_virtio_port **vportp, int by_user);
+
+static gboolean conn_header_read_cb(gpointer header_buff,
+ gsize *body_size,
+ gpointer user_data)
+{
+ struct vdagent_virtio_port *vport = user_data;
+ VDIChunkHeader *header = header_buff;
+
+ header->size = GUINT32_FROM_LE(header->size);
+ header->port = GUINT32_FROM_LE(header->port);
+
+ if (header->size > VD_AGENT_MAX_DATA_SIZE) {
+ syslog(LOG_ERR, "chunk size %u too large", header->size);
+ virtio_port_destroy(&vport, FALSE);
+ return FALSE;
+ }
+ if (header->port >= VDP_END_PORT) {
+ syslog(LOG_ERR, "chunk port %u out of range", header->port);
+ virtio_port_destroy(&vport, FALSE);
+ return FALSE;
+ }
+
+ *body_size = header->size;
+ return TRUE;
+}
+
+static gboolean conn_read_cb(gpointer header,
+ gpointer data,
+ gpointer user_data)
+{
+ struct vdagent_virtio_port *vport = user_data;
+ vdagent_virtio_port_do_chunk(&vport, header, data);
+ return vport != NULL;
+}
+
+static void conn_error_cb(gpointer user_data)
+{
+ struct vdagent_virtio_port *vport = user_data;
+ virtio_port_destroy(&vport, FALSE);
+}

struct vdagent_virtio_port *vdagent_virtio_port_create(const char *portname,
vdagent_virtio_port_read_callback read_callback,
vdagent_virtio_port_disconnect_callback disconnect_callback)
{
struct vdagent_virtio_port *vport;
- struct sockaddr_un address;
- int c;
+ GIOStream *io_stream;
+
+ io_stream = vdagent_file_open(portname);
+ if (io_stream == NULL) {
+ io_stream = vdagent_socket_connect(portname);
+ if (io_stream == NULL)
+ return NULL;
+ }

vport = calloc(1, sizeof(*vport));
if (!vport)
return 0;

- vport->fd = open(portname, O_RDWR);
- if (vport->fd == -1) {
- vport->fd = socket(PF_UNIX, SOCK_STREAM, 0);
- if (vport->fd == -1) {
- goto error;
- }
- address.sun_family = AF_UNIX;
- snprintf(address.sun_path, sizeof(address.sun_path), "%s", portname);
- c = connect(vport->fd, (struct sockaddr *)&address, sizeof(address));
- if (c == 0) {
- vport->is_uds = 1;
- } else {
- goto error;
- }
- } else {
- vport->is_uds = 0;
- }
- vport->opening = 1;
-
+ /* When calling vdagent_connection_new(),
+ * @wait_on_opening MUST be set to TRUE:
+ *
+ * When we open the virtio serial port, the following happens:
+ * 1) The linux kernel virtio_console driver sends a
+ * VIRTIO_CONSOLE_PORT_OPEN message to qemu
+ * 2) qemu's spicevmc chardev driver calls qemu_spice_add_interface to
+ * register the agent chardev with the spice-server
+ * 3) spice-server then calls the spicevmc chardev driver's state
+ * callback to let it know it is ready to receive data
+ * 4) The state callback sends a CHR_EVENT_OPENED to the virtio-console
+ * chardev backend
+ * 5) The virtio-console chardev backend sends VIRTIO_CONSOLE_PORT_OPEN
+ * to the linux kernel virtio_console driver
+ *
+ * Until steps 1 - 5 have completed the linux kernel virtio_console
+ * driver sees the virtio serial port as being in a disconnected state
+ * and read will return 0 ! So if we blindly assume that a read 0 means
+ * that the channel is closed we will hit a race here.
+ */
+ vport->conn = vdagent_connection_new(io_stream,
+ TRUE,
+ sizeof(VDIChunkHeader),
+ conn_header_read_cb,
+ conn_read_cb,
+ conn_error_cb,
+ vport);
vport->read_callback = read_callback;
vport->disconnect_callback = disconnect_callback;

return vport;
-
-error:
- syslog(LOG_ERR, "open %s: %m", portname);
- if (vport->fd != -1) {
- close(vport->fd);
- }
- free(vport);
- return NULL;
}

static void virtio_port_destroy(struct vdagent_virtio_port **vportp,
gboolean by_user)
{
- struct vdagent_virtio_port_buf *wbuf, *next_wbuf;
struct vdagent_virtio_port *vport = *vportp;
int i;

@@ -135,19 +165,16 @@ static void virtio_port_destroy(struct vdagent_virtio_port **vportp,
if (vport->disconnect_callback)
vport->disconnect_callback(vport, by_user);

- wbuf = vport->write_buf;
- while (wbuf) {
- next_wbuf = wbuf->next;
- free(wbuf->buf);
- free(wbuf);
- wbuf = next_wbuf;
+ if (vport->write_buf) {
+ free(vport->write_buf->buf);
+ free(vport->write_buf);
}

for (i = 0; i < VDP_END_PORT; i++) {
free(vport->port_data[i].message_data);
}

- close(vport->fd);
+ vdagent_connection_destroy(vport->conn);
free(vport);
*vportp = NULL;
}
@@ -157,47 +184,6 @@ void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
virtio_port_destroy(vportp, TRUE);
}

-int vdagent_virtio_port_fill_fds(struct vdagent_virtio_port *vport,
- fd_set *readfds, fd_set *writefds)
-{
- if (!vport)
- return -1;
-
- FD_SET(vport->fd, readfds);
- if (vport->write_buf)
- FD_SET(vport->fd, writefds);
-
- return vport->fd + 1;
-}
-
-void vdagent_virtio_port_handle_fds(struct vdagent_virtio_port **vportp,
- fd_set *readfds, fd_set *writefds)
-{
- if (!*vportp)
- return;
-
- if (FD_ISSET((*vportp)->fd, readfds))
- vdagent_virtio_port_do_read(vportp);
-
- if (*vportp && FD_ISSET((*vportp)->fd, writefds))
- vdagent_virtio_port_do_write(vportp);
-}
-
-static struct vdagent_virtio_port_buf* vdagent_virtio_port_get_last_wbuf(
- struct vdagent_virtio_port *vport)
-{
- struct vdagent_virtio_port_buf *wbuf;
-
- wbuf = vport->write_buf;
- if (!wbuf)
- return NULL;
-
- while (wbuf->next)
- wbuf = wbuf->next;
-
- return wbuf;
-}
-
int vdagent_virtio_port_write_start(
struct vdagent_virtio_port *vport,
uint32_t port_nr,
@@ -205,7 +191,7 @@ int vdagent_virtio_port_write_start(
uint32_t message_opaque,
uint32_t data_size)
{
- struct vdagent_virtio_port_buf *wbuf, *new_wbuf;
+ struct vdagent_virtio_port_buf *new_wbuf;
VDIChunkHeader chunk_header;
VDAgentMessage message_header;

@@ -213,10 +199,8 @@ int vdagent_virtio_port_write_start(
if (!new_wbuf)
return -1;

- new_wbuf->pos = 0;
new_wbuf->write_pos = 0;
new_wbuf->size = sizeof(chunk_header) + sizeof(message_header) + data_size;
- new_wbuf->next = NULL;
new_wbuf->buf = malloc(new_wbuf->size);
if (!new_wbuf->buf) {
free(new_wbuf);
@@ -237,14 +221,7 @@ int vdagent_virtio_port_write_start(
sizeof(message_header));
new_wbuf->write_pos += sizeof(message_header);

- if (!vport->write_buf) {
- vport->write_buf = new_wbuf;
- return 0;
- }
-
- wbuf = vdagent_virtio_port_get_last_wbuf(vport);
- wbuf->next = new_wbuf;
-
+ vport->write_buf = new_wbuf;
return 0;
}

@@ -253,7 +230,10 @@ int vdagent_virtio_port_write_append(struct vdagent_virtio_port *vport,
{
struct vdagent_virtio_port_buf *wbuf;

- wbuf = vdagent_virtio_port_get_last_wbuf(vport);
+ if (size == 0)
+ return 0;
+
+ wbuf = vport->write_buf;
if (!wbuf) {
syslog(LOG_ERR, "can't append without a buffer");
return -1;
@@ -266,6 +246,11 @@ int vdagent_virtio_port_write_append(struct vdagent_virtio_port *vport,

memcpy(wbuf->buf + wbuf->write_pos, data, size);
wbuf->write_pos += size;
+
+ if (wbuf->write_pos == wbuf->size) {
+ vdagent_connection_write(vport->conn, wbuf->buf, wbuf->size);
+ g_clear_pointer(&vport->write_buf, free);
+ }
return 0;
}

@@ -287,8 +272,8 @@ int vdagent_virtio_port_write(

void vdagent_virtio_port_flush(struct vdagent_virtio_port **vportp)
{
- while (*vportp && (*vportp)->write_buf)
- vdagent_virtio_port_do_write(vportp);
+ if (*vportp)
+ vdagent_connection_flush((*vportp)->conn);
}

void vdagent_virtio_port_reset(struct vdagent_virtio_port *vport, int port)
@@ -301,20 +286,22 @@ void vdagent_virtio_port_reset(struct vdagent_virtio_port *vport, int port)
memset(&vport->port_data[port], 0, sizeof(vport->port_data[0]));
}

-static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)
+static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp,
+ VDIChunkHeader *chunk_header,
+ const guchar *chunk_data)
{
int avail, read, pos = 0;
struct vdagent_virtio_port *vport = *vportp;
struct vdagent_virtio_port_chunk_port_data *port =
- &vport->port_data[vport->chunk_header.port];
+ &vport->port_data[chunk_header->port];

if (port->message_header_read < sizeof(port->message_header)) {
read = sizeof(port->message_header) - port->message_header_read;
- if (read > vport->chunk_header.size) {
- read = vport->chunk_header.size;
+ if (read > chunk_header->size) {
+ read = chunk_header->size;
}
memcpy((uint8_t *)&port->message_header + port->message_header_read,
- vport->chunk_data, read);
+ chunk_data, read);
port->message_header_read += read;
if (port->message_header_read == sizeof(port->message_header)) {

@@ -337,7 +324,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)

if (port->message_header_read == sizeof(port->message_header)) {
read = port->message_header.size - port->message_data_pos;
- avail = vport->chunk_header.size - pos;
+ avail = chunk_header->size - pos;

if (avail > read) {
syslog(LOG_ERR, "chunk larger than message, lost sync?");
@@ -350,13 +337,13 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)

if (read) {
memcpy(port->message_data + port->message_data_pos,
- vport->chunk_data + pos, read);
+ chunk_data + pos, read);
port->message_data_pos += read;
}

if (port->message_data_pos == port->message_header.size) {
if (vport->read_callback) {
- int r = vport->read_callback(vport, vport->chunk_header.port,
+ int r = vport->read_callback(vport, chunk_header->port,
&port->message_header, port->message_data);
if (r == -1) {
virtio_port_destroy(vportp, TRUE);
@@ -370,139 +357,3 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp)
}
}
}
-
-static int vport_read(struct vdagent_virtio_port *vport, uint8_t *buf, int len)
-{
- if (vport->is_uds) {
- return recv(vport->fd, buf, len, 0);
- } else {
- return read(vport->fd, buf, len);
- }
-}
-
-static void vdagent_virtio_port_do_read(struct vdagent_virtio_port **vportp)
-{
- ssize_t n;
- size_t to_read;
- uint8_t *dest;
- struct vdagent_virtio_port *vport = *vportp;
-
- if (vport->chunk_header_read < sizeof(vport->chunk_header)) {
- to_read = sizeof(vport->chunk_header) - vport->chunk_header_read;
- dest = (uint8_t *)&vport->chunk_header + vport->chunk_header_read;
- } else {
- to_read = vport->chunk_header.size - vport->chunk_data_pos;
- dest = vport->chunk_data + vport->chunk_data_pos;
- }
-
- n = vport_read(vport, dest, to_read);
- if (n < 0) {
- if (errno == EINTR)
- return;
- syslog(LOG_ERR, "reading from vdagent virtio port: %m");
- }
- if (n == 0 && vport->opening) {
- /* When we open the virtio serial port, the following happens:
- 1) The linux kernel virtio_console driver sends a
- VIRTIO_CONSOLE_PORT_OPEN message to qemu
- 2) qemu's spicevmc chardev driver calls qemu_spice_add_interface to
- register the agent chardev with the spice-server
- 3) spice-server then calls the spicevmc chardev driver's state
- callback to let it know it is ready to receive data
- 4) The state callback sends a CHR_EVENT_OPENED to the virtio-console
- chardev backend
- 5) The virtio-console chardev backend sends VIRTIO_CONSOLE_PORT_OPEN
- to the linux kernel virtio_console driver
-
- Until steps 1 - 5 have completed the linux kernel virtio_console
- driver sees the virtio serial port as being in a disconnected state
- and read will return 0 ! So if we blindly assume that a read 0 means
- that the channel is closed we will hit a race here.
-
- Therefore we ignore read returning 0 until we've successfully read
- or written some data. If we hit this race we also sleep a bit here
- to avoid busy waiting until the above steps complete */
- usleep(10000);
- return;
- }
- if (n <= 0) {
- virtio_port_destroy(vportp, FALSE);
- return;
- }
- vport->opening = 0;
-
- if (vport->chunk_header_read < sizeof(vport->chunk_header)) {
- vport->chunk_header_read += n;
- if (vport->chunk_header_read == sizeof(vport->chunk_header)) {
- vport->chunk_header.size = GUINT32_FROM_LE(vport->chunk_header.size);
- vport->chunk_header.port = GUINT32_FROM_LE(vport->chunk_header.port);
- if (vport->chunk_header.size > VD_AGENT_MAX_DATA_SIZE) {
- syslog(LOG_ERR, "chunk size %u too large",
- vport->chunk_header.size);
- virtio_port_destroy(vportp, FALSE);
- return;
- }
- if (vport->chunk_header.port >= VDP_END_PORT) {
- syslog(LOG_ERR, "chunk port %u out of range",
- vport->chunk_header.port);
- virtio_port_destroy(vportp, FALSE);
- return;
- }
- }
- } else {
- vport->chunk_data_pos += n;
- if (vport->chunk_data_pos == vport->chunk_header.size) {
- vdagent_virtio_port_do_chunk(vportp);
- if (!*vportp)
- return;
- vport->chunk_header_read = 0;
- vport->chunk_data_pos = 0;
- }
- }
-}
-
-static int vport_write(struct vdagent_virtio_port *vport, uint8_t *buf, int len)
-{
- if (vport->is_uds) {
- return send(vport->fd, buf, len, 0);
- } else {
- return write(vport->fd, buf, len);
- }
-}
-
-static void vdagent_virtio_port_do_write(struct vdagent_virtio_port **vportp)
-{
- ssize_t n;
- size_t to_write;
- struct vdagent_virtio_port *vport = *vportp;
-
- struct vdagent_virtio_port_buf* wbuf = vport->write_buf;
- if (!wbuf) {
- syslog(LOG_ERR, "do_write called on a port without a write buf ?!");
- return;
- }
-
- if (wbuf->write_pos != wbuf->size) {
- syslog(LOG_ERR, "do_write: buffer is incomplete!!");
- return;
- }
-
- to_write = wbuf->size - wbuf->pos;
- n = vport_write(vport, wbuf->buf + wbuf->pos, to_write);
- if (n < 0) {
- if (errno == EINTR)
- return;
- syslog(LOG_ERR, "writing to vdagent virtio port: %m");
- virtio_port_destroy(vportp, FALSE);
- return;
- }
- if (n > 0)
- vport->opening = 0;
-
- wbuf->pos += n;
- if (wbuf->pos == wbuf->size) {
- vport->write_buf = wbuf->next;
- free(wbuf->buf);
- free(wbuf);
- }
-}
diff --git a/src/vdagentd/virtio-port.h b/src/vdagentd/virtio-port.h
index 3c701d6..87b75cf 100644
--- a/src/vdagentd/virtio-port.h
+++ b/src/vdagentd/virtio-port.h
@@ -22,9 +22,7 @@
#ifndef __VIRTIO_PORT_H
#define __VIRTIO_PORT_H

-#include <stdio.h>
#include <stdint.h>
-#include <sys/select.h>
#include <spice/vd_agent.h>

struct vdagent_virtio_port;
@@ -60,21 +58,6 @@ struct vdagent_virtio_port *vdagent_virtio_port_create(const char *portname,
void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp);


-/* Given a vdagent_virtio_port fill the fd_sets pointed to by readfds and
- writefds for select() usage.
-
- Return value: value of the highest fd + 1 */
-int vdagent_virtio_port_fill_fds(struct vdagent_virtio_port *vport,
- fd_set *readfds, fd_set *writefds);
-
-/* Handle any events flagged by select for the given vdagent_virtio_port.
- Note the port may be destroyed (when disconnected) by this call
- in this case the disconnect calllback will get called before the
- destruction and the contents of connp will be made NULL */
-void vdagent_virtio_port_handle_fds(struct vdagent_virtio_port **vportp,
- fd_set *readfds, fd_set *writefds);
-
-
/* Queue a message for delivery, either bit by bit, or all at once

Returns 0 on success -1 on error (only happens when malloc fails) */
--
2.17.1
Jakub Janků
2018-08-14 18:53:43 UTC
Permalink
Remove type_to_string, no_types arguments from
udscs_connect() and udscs_server_new().
udscs is used only in vdagent.c and vdagentd.c
and in both cases the args are the same
(vdagentd_messages, VDAGENTD_NO_MESSAGES).

Add debug_print_message_header().
---
src/udscs.c | 52 ++++++++++++++---------------------------
src/udscs.h | 14 ++++-------
src/vdagent/vdagent.c | 3 +--
src/vdagentd/vdagentd.c | 5 ++--
4 files changed, 25 insertions(+), 49 deletions(-)

diff --git a/src/udscs.c b/src/udscs.c
index 4a657c9..0b80317 100644
--- a/src/udscs.c
+++ b/src/udscs.c
@@ -31,10 +31,9 @@

#include "udscs.h"
#include "vdagent-connection.h"
+#include "vdagentd-proto-strings.h"

struct udscs_connection {
- const char * const *type_to_string;
- int no_types;
int debug;
void *user_data;

@@ -48,6 +47,17 @@ struct udscs_connection {
struct udscs_connection *prev;
};

+static void debug_print_message_header(struct udscs_connection *conn,
+ struct udscs_message_header *header,
+ const gchar *direction)
+{
+ const gchar *type = header->type < G_N_ELEMENTS(vdagentd_messages) ?
+ vdagentd_messages[header->type] : "invalid message";
+
+ syslog(LOG_DEBUG, "%p %s %s, arg1: %u, arg2: %u, size %u",
+ conn, direction, type, header->arg1, header->arg2, header->size);
+}
+
static gboolean conn_header_read_cb(gpointer header_buff,
gsize *body_size,
gpointer user_data)
@@ -64,18 +74,8 @@ static gboolean conn_read_cb(gpointer header_buff,
struct udscs_connection *conn = user_data;
struct udscs_message_header *header = header_buff;

- if (conn->debug) {
- if (header->type < conn->no_types)
- syslog(LOG_DEBUG,
- "%p received %s, arg1: %u, arg2: %u, size %u",
- conn, conn->type_to_string[header->type],
- header->arg1, header->arg2, header->size);
- else
- syslog(LOG_DEBUG,
- "%p received invalid message %u, arg1: %u, arg2: %u, size %u",
- conn, header->type, header->arg1, header->arg2,
- header->size);
- }
+ if (conn->debug)
+ debug_print_message_header(conn, header, "received");

if (conn->read_callback) {
conn->read_callback(&conn, header, data);
@@ -92,7 +92,7 @@ static void conn_error_cb(gpointer user_data)
struct udscs_connection *udscs_connect(const char *socketname,
udscs_read_callback read_callback,
udscs_disconnect_callback disconnect_callback,
- const char * const type_to_string[], int no_types, int debug)
+ int debug)
{
GIOStream *io_stream;
struct udscs_connection *conn;
@@ -104,9 +104,6 @@ struct udscs_connection *udscs_connect(const char *socketname,
conn = calloc(1, sizeof(*conn));
if (!conn)
return NULL;
-
- conn->type_to_string = type_to_string;
- conn->no_types = no_types;
conn->debug = debug;
conn->conn = vdagent_connection_new(io_stream,
FALSE,
@@ -181,15 +178,8 @@ int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1,
memcpy(buff, &header, sizeof(header));
memcpy(buff + sizeof(header), data, size);

- if (conn->debug) {
- if (type < conn->no_types)
- syslog(LOG_DEBUG, "%p sent %s, arg1: %u, arg2: %u, size %u",
- conn, conn->type_to_string[type], arg1, arg2, size);
- else
- syslog(LOG_DEBUG,
- "%p sent invalid message %u, arg1: %u, arg2: %u, size %u",
- conn, type, arg1, arg2, size);
- }
+ if (conn->debug)
+ debug_print_message_header(conn, &header, "sent");

vdagent_connection_write(conn->conn, buff, buff_size);
return 0;
@@ -202,8 +192,6 @@ int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1,
struct udscs_server {
GSocketService *service;

- const char * const *type_to_string;
- int no_types;
int debug;
struct udscs_connection connections_head;
udscs_connect_callback connect_callback;
@@ -220,7 +208,7 @@ struct udscs_server *udscs_server_new(
udscs_connect_callback connect_callback,
udscs_read_callback read_callback,
udscs_disconnect_callback disconnect_callback,
- const char * const type_to_string[], int no_types, int debug)
+ int debug)
{
struct udscs_server *server;

@@ -228,8 +216,6 @@ struct udscs_server *udscs_server_new(
if (!server)
return NULL;

- server->type_to_string = type_to_string;
- server->no_types = no_types;
server->debug = debug;
server->connect_callback = connect_callback;
server->read_callback = read_callback;
@@ -319,8 +305,6 @@ static gboolean udscs_server_accept_cb(GSocketService *service,
return TRUE;
}

- new_conn->type_to_string = server->type_to_string;
- new_conn->no_types = server->no_types;
new_conn->debug = server->debug;
new_conn->read_callback = server->read_callback;
new_conn->disconnect_callback = server->disconnect_callback;
diff --git a/src/udscs.h b/src/udscs.h
index d02d5b4..0a1bad4 100644
--- a/src/udscs.h
+++ b/src/udscs.h
@@ -59,15 +59,12 @@ typedef void (*udscs_disconnect_callback)(struct udscs_connection *conn);
* Only sockets bound to a pathname are supported.
*
* If debug is true then the events on this connection will be traced.
- * This includes the incoming and outgoing message names. So when debug is true
- * no_types must be set to the value of the highest valid message id + 1,
- * and type_to_string must point to a string array of size no_types for
- * converting the message ids to their names.
+ * This includes the incoming and outgoing message names.
*/
struct udscs_connection *udscs_connect(const char *socketname,
udscs_read_callback read_callback,
udscs_disconnect_callback disconnect_callback,
- const char * const type_to_string[], int no_types, int debug);
+ int debug);

/* Close the connection, releases the corresponding resources and
* sets *connp to NULL.
@@ -106,16 +103,13 @@ typedef void (*udscs_connect_callback)(struct udscs_connection *conn);
*
* If debug is true then the events on this socket and related individual
* connections will be traced.
- * This includes the incoming and outgoing message names. So when debug is true
- * no_types must be set to the value of the highest valid message id + 1,
- * and type_to_string must point to a string array of size no_types for
- * converting the message ids to their names.
+ * This includes the incoming and outgoing message names.
*/
struct udscs_server *udscs_server_new(
udscs_connect_callback connect_callback,
udscs_read_callback read_callback,
udscs_disconnect_callback disconnect_callback,
- const char * const type_to_string[], int no_types, int debug);
+ int debug);

/* Start listening on a pre-configured socket specified by the given @fd.
* This can be used with systemd socket activation, etc. */
diff --git a/src/vdagent/vdagent.c b/src/vdagent/vdagent.c
index 3f8ef31..cb66749 100644
--- a/src/vdagent/vdagent.c
+++ b/src/vdagent/vdagent.c
@@ -42,7 +42,6 @@

#include "udscs.h"
#include "vdagentd-proto.h"
-#include "vdagentd-proto-strings.h"
#include "audio.h"
#include "x11.h"
#include "file-xfers.h"
@@ -369,7 +368,7 @@ static gboolean vdagent_init_async_cb(gpointer user_data)

agent->conn = udscs_connect(vdagentd_socket,
daemon_read_complete, daemon_disconnect_cb,
- vdagentd_messages, VDAGENTD_NO_MESSAGES, debug);
+ debug);
if (agent->conn == NULL) {
g_timeout_add_seconds(1, vdagent_init_async_cb, agent);
return G_SOURCE_REMOVE;
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index fd54723..8893f3b 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -41,7 +41,6 @@

#include "udscs.h"
#include "vdagentd-proto.h"
-#include "vdagentd-proto-strings.h"
#include "uinput.h"
#include "xorg-conf.h"
#include "virtio-port.h"
@@ -1105,8 +1104,8 @@ int main(int argc, char *argv[])
openlog("spice-vdagentd", do_daemonize ? 0 : LOG_PERROR, LOG_USER);

/* Setup communication with vdagent process(es) */
- server = udscs_server_new(agent_connect, agent_read_complete, agent_disconnect,
- vdagentd_messages, VDAGENTD_NO_MESSAGES, debug);
+ server = udscs_server_new(agent_connect, agent_read_complete,
+ agent_disconnect, debug);
if (server == NULL) {
syslog(LOG_CRIT, "Fatal could not allocate memory for udscs server");
return 1;
--
2.17.1
Victor Toso
2018-09-19 07:23:19 UTC
Permalink
Hi,

Forgot to reply to this one, small suggestions below.
Post by Jakub Janků
Remove type_to_string, no_types arguments from
udscs_connect() and udscs_server_new().
udscs is used only in vdagent.c and vdagentd.c
and in both cases the args are the same
(vdagentd_messages, VDAGENTD_NO_MESSAGES).
Add debug_print_message_header().
If not a problem, please add SoB
Post by Jakub Janků
---
src/udscs.c | 52 ++++++++++++++---------------------------
src/udscs.h | 14 ++++-------
src/vdagent/vdagent.c | 3 +--
src/vdagentd/vdagentd.c | 5 ++--
4 files changed, 25 insertions(+), 49 deletions(-)
diff --git a/src/udscs.c b/src/udscs.c
index 4a657c9..0b80317 100644
--- a/src/udscs.c
+++ b/src/udscs.c
@@ -31,10 +31,9 @@
#include "udscs.h"
#include "vdagent-connection.h"
+#include "vdagentd-proto-strings.h"
struct udscs_connection {
- const char * const *type_to_string;
- int no_types;
int debug;
void *user_data;
@@ -48,6 +47,17 @@ struct udscs_connection {
struct udscs_connection *prev;
};
debug_print_message_header() will be called only if conn->debug
is set. We could add that check here and remove where this is
called? something like:

if (conn == NULL || !conn->debug)
return;
Post by Jakub Janků
+static void debug_print_message_header(struct udscs_connection *conn,
+ struct udscs_message_header *header,
+ const gchar *direction)
+{
+ const gchar *type = header->type < G_N_ELEMENTS(vdagentd_messages) ?
+ vdagentd_messages[header->type] : "invalid message";
I find more readable to start type with "invalid message" and use
a if to set it, eg:

const gchar *type = "invalid message";

if (header->type < G_N_ELEMENTS(vdagentd_messages) {
type = vdagentd_messages[header->type];
}
Post by Jakub Janků
+
+ syslog(LOG_DEBUG, "%p %s %s, arg1: %u, arg2: %u, size %u",
+ conn, direction, type, header->arg1, header->arg2, header->size);
+}
+
static gboolean conn_header_read_cb(gpointer header_buff,
gsize *body_size,
gpointer user_data)
@@ -64,18 +74,8 @@ static gboolean conn_read_cb(gpointer header_buff,
struct udscs_connection *conn = user_data;
struct udscs_message_header *header = header_buff;
- if (conn->debug) {
- if (header->type < conn->no_types)
- syslog(LOG_DEBUG,
- "%p received %s, arg1: %u, arg2: %u, size %u",
- conn, conn->type_to_string[header->type],
- header->arg1, header->arg2, header->size);
- else
- syslog(LOG_DEBUG,
- "%p received invalid message %u, arg1: %u, arg2: %u, size %u",
- conn, header->type, header->arg1, header->arg2,
- header->size);
- }
+ if (conn->debug)
+ debug_print_message_header(conn, header, "received");
if (conn->read_callback) {
conn->read_callback(&conn, header, data);
@@ -92,7 +92,7 @@ static void conn_error_cb(gpointer user_data)
struct udscs_connection *udscs_connect(const char *socketname,
udscs_read_callback read_callback,
udscs_disconnect_callback disconnect_callback,
- const char * const type_to_string[], int no_types, int debug)
+ int debug)
{
GIOStream *io_stream;
struct udscs_connection *conn;
@@ -104,9 +104,6 @@ struct udscs_connection *udscs_connect(const char *socketname,
conn = calloc(1, sizeof(*conn));
if (!conn)
return NULL;
-
- conn->type_to_string = type_to_string;
- conn->no_types = no_types;
conn->debug = debug;
conn->conn = vdagent_connection_new(io_stream,
FALSE,
@@ -181,15 +178,8 @@ int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1,
memcpy(buff, &header, sizeof(header));
memcpy(buff + sizeof(header), data, size);
- if (conn->debug) {
- if (type < conn->no_types)
- syslog(LOG_DEBUG, "%p sent %s, arg1: %u, arg2: %u, size %u",
- conn, conn->type_to_string[type], arg1, arg2, size);
- else
- syslog(LOG_DEBUG,
- "%p sent invalid message %u, arg1: %u, arg2: %u, size %u",
- conn, type, arg1, arg2, size);
- }
+ if (conn->debug)
+ debug_print_message_header(conn, &header, "sent");
vdagent_connection_write(conn->conn, buff, buff_size);
return 0;
@@ -202,8 +192,6 @@ int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1,
struct udscs_server {
GSocketService *service;
- const char * const *type_to_string;
- int no_types;
int debug;
struct udscs_connection connections_head;
udscs_connect_callback connect_callback;
@@ -220,7 +208,7 @@ struct udscs_server *udscs_server_new(
udscs_connect_callback connect_callback,
udscs_read_callback read_callback,
udscs_disconnect_callback disconnect_callback,
- const char * const type_to_string[], int no_types, int debug)
+ int debug)
{
struct udscs_server *server;
@@ -228,8 +216,6 @@ struct udscs_server *udscs_server_new(
if (!server)
return NULL;
- server->type_to_string = type_to_string;
- server->no_types = no_types;
server->debug = debug;
server->connect_callback = connect_callback;
server->read_callback = read_callback;
@@ -319,8 +305,6 @@ static gboolean udscs_server_accept_cb(GSocketService *service,
return TRUE;
}
- new_conn->type_to_string = server->type_to_string;
- new_conn->no_types = server->no_types;
new_conn->debug = server->debug;
new_conn->read_callback = server->read_callback;
new_conn->disconnect_callback = server->disconnect_callback;
diff --git a/src/udscs.h b/src/udscs.h
index d02d5b4..0a1bad4 100644
--- a/src/udscs.h
+++ b/src/udscs.h
@@ -59,15 +59,12 @@ typedef void (*udscs_disconnect_callback)(struct udscs_connection *conn);
* Only sockets bound to a pathname are supported.
*
* If debug is true then the events on this connection will be traced.
- * This includes the incoming and outgoing message names. So when debug is true
- * no_types must be set to the value of the highest valid message id + 1,
- * and type_to_string must point to a string array of size no_types for
- * converting the message ids to their names.
+ * This includes the incoming and outgoing message names.
*/
struct udscs_connection *udscs_connect(const char *socketname,
udscs_read_callback read_callback,
udscs_disconnect_callback disconnect_callback,
- const char * const type_to_string[], int no_types, int debug);
+ int debug);
/* Close the connection, releases the corresponding resources and
* sets *connp to NULL.
@@ -106,16 +103,13 @@ typedef void (*udscs_connect_callback)(struct udscs_connection *conn);
*
* If debug is true then the events on this socket and related individual
* connections will be traced.
- * This includes the incoming and outgoing message names. So when debug is true
- * no_types must be set to the value of the highest valid message id + 1,
- * and type_to_string must point to a string array of size no_types for
- * converting the message ids to their names.
+ * This includes the incoming and outgoing message names.
*/
struct udscs_server *udscs_server_new(
udscs_connect_callback connect_callback,
udscs_read_callback read_callback,
udscs_disconnect_callback disconnect_callback,
- const char * const type_to_string[], int no_types, int debug);
+ int debug);
* This can be used with systemd socket activation, etc. */
diff --git a/src/vdagent/vdagent.c b/src/vdagent/vdagent.c
index 3f8ef31..cb66749 100644
--- a/src/vdagent/vdagent.c
+++ b/src/vdagent/vdagent.c
@@ -42,7 +42,6 @@
#include "udscs.h"
#include "vdagentd-proto.h"
-#include "vdagentd-proto-strings.h"
#include "audio.h"
#include "x11.h"
#include "file-xfers.h"
@@ -369,7 +368,7 @@ static gboolean vdagent_init_async_cb(gpointer user_data)
agent->conn = udscs_connect(vdagentd_socket,
daemon_read_complete, daemon_disconnect_cb,
- vdagentd_messages, VDAGENTD_NO_MESSAGES, debug);
+ debug);
if (agent->conn == NULL) {
g_timeout_add_seconds(1, vdagent_init_async_cb, agent);
return G_SOURCE_REMOVE;
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index fd54723..8893f3b 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -41,7 +41,6 @@
#include "udscs.h"
#include "vdagentd-proto.h"
-#include "vdagentd-proto-strings.h"
#include "uinput.h"
#include "xorg-conf.h"
#include "virtio-port.h"
@@ -1105,8 +1104,8 @@ int main(int argc, char *argv[])
openlog("spice-vdagentd", do_daemonize ? 0 : LOG_PERROR, LOG_USER);
/* Setup communication with vdagent process(es) */
- server = udscs_server_new(agent_connect, agent_read_complete, agent_disconnect,
- vdagentd_messages, VDAGENTD_NO_MESSAGES, debug);
+ server = udscs_server_new(agent_connect, agent_read_complete,
+ agent_disconnect, debug);
if (server == NULL) {
syslog(LOG_CRIT, "Fatal could not allocate memory for udscs server");
return 1;
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Jakub Janků
2018-08-14 18:53:45 UTC
Permalink
This is necessary for the following GDBus integration,
which drops session_info_get_fd(). The vdagentd therefore
won't be able to detect session changes using select().
---
src/vdagentd/console-kit.c | 2 +-
src/vdagentd/dummy-session-info.c | 2 +-
src/vdagentd/session-info.h | 4 +++-
src/vdagentd/systemd-login.c | 2 +-
src/vdagentd/vdagentd.c | 8 +++++++-
5 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/src/vdagentd/console-kit.c b/src/vdagentd/console-kit.c
index 381b97e..ef49db5 100644
--- a/src/vdagentd/console-kit.c
+++ b/src/vdagentd/console-kit.c
@@ -217,7 +217,7 @@ si_dbus_read_signals(struct session_info *info)
}
}

-struct session_info *session_info_create(int verbose)
+struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb)
{
struct session_info *info;
DBusError error;
diff --git a/src/vdagentd/dummy-session-info.c b/src/vdagentd/dummy-session-info.c
index 1be27ae..b2306d7 100644
--- a/src/vdagentd/dummy-session-info.c
+++ b/src/vdagentd/dummy-session-info.c
@@ -21,7 +21,7 @@

#include "session-info.h"

-struct session_info *session_info_create(int verbose)
+struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb)
{
return NULL;
}
diff --git a/src/vdagentd/session-info.h b/src/vdagentd/session-info.h
index c8edb86..2f21347 100644
--- a/src/vdagentd/session-info.h
+++ b/src/vdagentd/session-info.h
@@ -28,7 +28,9 @@

struct session_info;

-struct session_info *session_info_create(int verbose);
+typedef void (*ActiveSessionChangeCb)(const gchar *session);
+
+struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb);
void session_info_destroy(struct session_info *ck);

int session_info_get_fd(struct session_info *ck);
diff --git a/src/vdagentd/systemd-login.c b/src/vdagentd/systemd-login.c
index 9719c0b..88de1a6 100644
--- a/src/vdagentd/systemd-login.c
+++ b/src/vdagentd/systemd-login.c
@@ -225,7 +225,7 @@ si_dbus_read_signals(struct session_info *si)
}
}

-struct session_info *session_info_create(int verbose)
+struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb)
{
struct session_info *si;
int r;
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index 8893f3b..6d117a8 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -990,6 +990,12 @@ static void agent_read_complete(struct udscs_connection **connp,
}
}

+static void active_session_change_cb(const gchar *session)
+{
+ active_session = session;
+ update_active_session_connection(NULL);
+}
+
/* main */

static void daemonize(void)
@@ -1156,7 +1162,7 @@ int main(int argc, char *argv[])
#endif

if (want_session_info)
- session_info = session_info_create(debug);
+ session_info = session_info_create(debug, active_session_change_cb);
if (!session_info)
syslog(LOG_WARNING, "no session info, max 1 session agent allowed");
--
2.17.1
Jakub Janků
2018-08-14 18:53:48 UTC
Permalink
In console-kit.c, FD polling is handled internally by GDBus.

In systemd-login.c, FD is integrated into the GMainLoop
using GIOChannel.

Users of session-info.h are notified by ActiveSessionChangeCb
when the active session changes.

This renders the session_info_get_fd() obsolete.
---
src/vdagentd/console-kit.c | 5 -----
src/vdagentd/dummy-session-info.c | 5 -----
src/vdagentd/session-info.h | 2 --
src/vdagentd/systemd-login.c | 5 -----
4 files changed, 17 deletions(-)

diff --git a/src/vdagentd/console-kit.c b/src/vdagentd/console-kit.c
index 4c02ebe..390491e 100644
--- a/src/vdagentd/console-kit.c
+++ b/src/vdagentd/console-kit.c
@@ -199,11 +199,6 @@ void session_info_destroy(struct session_info *info)
free(info);
}

-int session_info_get_fd(struct session_info *info)
-{
- return -1;
-}
-
/* Invoke a method on a remote object through DBus and wait for reply.
*
* Return type must be one of G_VARIANT_TYPE_STRING, G_VARIANT_TYPE_SIGNATURE,
diff --git a/src/vdagentd/dummy-session-info.c b/src/vdagentd/dummy-session-info.c
index b2306d7..d1a2435 100644
--- a/src/vdagentd/dummy-session-info.c
+++ b/src/vdagentd/dummy-session-info.c
@@ -30,11 +30,6 @@ void session_info_destroy(struct session_info *si)
{
}

-int session_info_get_fd(struct session_info *si)
-{
- return -1;
-}
-
const char *session_info_get_active_session(struct session_info *si)
{
return NULL;
diff --git a/src/vdagentd/session-info.h b/src/vdagentd/session-info.h
index 2f21347..70bf4a5 100644
--- a/src/vdagentd/session-info.h
+++ b/src/vdagentd/session-info.h
@@ -33,8 +33,6 @@ typedef void (*ActiveSessionChangeCb)(const gchar *session);
struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb);
void session_info_destroy(struct session_info *ck);

-int session_info_get_fd(struct session_info *ck);
-
const char *session_info_get_active_session(struct session_info *ck);
/* Note result must be free()-ed by caller */
char *session_info_session_for_pid(struct session_info *ck, uint32_t pid);
diff --git a/src/vdagentd/systemd-login.c b/src/vdagentd/systemd-login.c
index 2afd0dd..0940230 100644
--- a/src/vdagentd/systemd-login.c
+++ b/src/vdagentd/systemd-login.c
@@ -143,11 +143,6 @@ void session_info_destroy(struct session_info *si)
free(si);
}

-int session_info_get_fd(struct session_info *si)
-{
- return sd_login_monitor_get_fd(si->mon);
-}
-
const char *session_info_get_active_session(struct session_info *si)
{
int r;
--
2.17.1
Jakub Janků
2018-08-14 18:53:47 UTC
Permalink
Rewrite systemd-login.c using GDBus instead of libdbus.

Use GDBusProxy. This is less efficient than the previous impl
as it caches all properties and listens to all signals of the
given DBus object. But it is just so much more convenient...
---
src/vdagentd/systemd-login.c | 254 +++++++++++------------------------
1 file changed, 79 insertions(+), 175 deletions(-)

diff --git a/src/vdagentd/systemd-login.c b/src/vdagentd/systemd-login.c
index 88de1a6..2afd0dd 100644
--- a/src/vdagentd/systemd-login.c
+++ b/src/vdagentd/systemd-login.c
@@ -20,23 +20,21 @@
*/

#include "session-info.h"
-#include <errno.h>
#include <stdlib.h>
-#include <string.h>
#include <syslog.h>
#include <systemd/sd-login.h>
-#include <dbus/dbus.h>
+#include <gio/gio.h>
+#include <glib-unix.h>

struct session_info {
int verbose;
sd_login_monitor *mon;
char *session;
- struct {
- DBusConnection *system_connection;
- char *match_session_signals;
- } dbus;
+ GDBusProxy *dbus_proxy;
gboolean session_is_locked;
- gboolean session_locked_hint;
+ GIOChannel *io_channel;
+ guint io_watch_id;
+ ActiveSessionChangeCb session_change_cb;
};

#define LOGIND_INTERFACE "org.freedesktop.login1"
@@ -44,185 +42,66 @@ struct session_info {
#define LOGIND_SESSION_INTERFACE "org.freedesktop.login1.Session"
#define LOGIND_SESSION_OBJ_TEMPLATE "/org/freedesktop/login1/session/_3%s"

-#define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"
-
#define SESSION_SIGNAL_LOCK "Lock"
#define SESSION_SIGNAL_UNLOCK "Unlock"

#define SESSION_PROP_LOCKED_HINT "LockedHint"

-/* dbus related */
-static DBusConnection *si_dbus_get_system_bus(void)
-{
- DBusConnection *connection;
- DBusError error;
-
- dbus_error_init(&error);
- connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
- if (connection == NULL || dbus_error_is_set(&error)) {
- if (dbus_error_is_set(&error)) {
- syslog(LOG_WARNING, "Unable to connect to system bus: %s",
- error.message);
- dbus_error_free(&error);
- } else {
- syslog(LOG_WARNING, "Unable to connect to system bus");
- }
- return NULL;
- }
- return connection;
-}
-
-static void si_dbus_match_remove(struct session_info *si)
-{
- DBusError error;
- if (si->dbus.match_session_signals == NULL)
- return;
-
- dbus_error_init(&error);
- dbus_bus_remove_match(si->dbus.system_connection,
- si->dbus.match_session_signals,
- &error);
-
- g_free(si->dbus.match_session_signals);
- si->dbus.match_session_signals = NULL;
-}
-
-static void si_dbus_match_rule_update(struct session_info *si)
+static void session_signal_cb(GDBusProxy *proxy,
+ gchar *sender_name,
+ gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
{
- DBusError error;
-
- if (si->dbus.system_connection == NULL ||
- si->session == NULL)
- return;
-
- si_dbus_match_remove(si);
-
- si->dbus.match_session_signals =
- g_strdup_printf ("type='signal',interface='%s',path='"
- LOGIND_SESSION_OBJ_TEMPLATE"'",
- LOGIND_SESSION_INTERFACE,
- si->session);
- if (si->verbose)
- syslog(LOG_DEBUG, "logind match: %s", si->dbus.match_session_signals);
-
- dbus_error_init(&error);
- dbus_bus_add_match(si->dbus.system_connection,
- si->dbus.match_session_signals,
- &error);
- if (dbus_error_is_set(&error)) {
- syslog(LOG_WARNING, "Unable to add dbus rule match: %s",
- error.message);
- dbus_error_free(&error);
- g_free(si->dbus.match_session_signals);
- si->dbus.match_session_signals = NULL;
+ struct session_info *si = user_data;
+
+ if (g_strcmp0(signal_name, SESSION_SIGNAL_LOCK) == 0) {
+ si->session_is_locked = TRUE;
+ } else if (g_strcmp0(signal_name, SESSION_SIGNAL_UNLOCK) == 0) {
+ si->session_is_locked = FALSE;
+ } else if (si->verbose) {
+ syslog(LOG_DEBUG, "(systemd-login) Signal not handled: %s", signal_name);
}
}

-static void
-si_dbus_read_properties(struct session_info *si)
+static void si_dbus_proxy_update(struct session_info *si)
{
- dbus_bool_t locked_hint, ret;
- DBusMessageIter iter, iter_variant;
- gint type;
- DBusError error;
- DBusMessage *message = NULL;
- DBusMessage *reply = NULL;
+ GError *err = NULL;
gchar *session_object;
- const gchar *interface, *property;
+
+ g_clear_object(&si->dbus_proxy);

if (si->session == NULL)
return;

session_object = g_strdup_printf(LOGIND_SESSION_OBJ_TEMPLATE, si->session);
- message = dbus_message_new_method_call(LOGIND_INTERFACE,
- session_object,
- DBUS_PROPERTIES_INTERFACE,
- "Get");
- g_free (session_object);
- if (message == NULL) {
- syslog(LOG_ERR, "Unable to create dbus message");
- goto exit;
- }
-
- interface = LOGIND_SESSION_INTERFACE;
- property = SESSION_PROP_LOCKED_HINT;
- ret = dbus_message_append_args(message,
- DBUS_TYPE_STRING, &interface,
- DBUS_TYPE_STRING, &property,
- DBUS_TYPE_INVALID);
- if (!ret) {
- syslog(LOG_ERR, "Unable to request locked-hint");
- goto exit;
- }
-
- dbus_error_init(&error);
- reply = dbus_connection_send_with_reply_and_block(si->dbus.system_connection,
- message,
- -1,
- &error);
- if (reply == NULL) {
- if (dbus_error_is_set(&error)) {
- syslog(LOG_ERR, "Properties.Get failed (locked-hint) due %s", error.message);
- dbus_error_free(&error);
- } else {
- syslog(LOG_ERR, "Properties.Get failed (locked-hint)");
- }
- goto exit;
- }
-
- dbus_message_iter_init(reply, &iter);
- type = dbus_message_iter_get_arg_type(&iter);
- if (type != DBUS_TYPE_VARIANT) {
- syslog(LOG_ERR, "expected a variant, got a '%c' instead", type);
- goto exit;
- }
-
- dbus_message_iter_recurse(&iter, &iter_variant);
- type = dbus_message_iter_get_arg_type(&iter_variant);
- if (type != DBUS_TYPE_BOOLEAN) {
- syslog(LOG_ERR, "expected a boolean, got a '%c' instead", type);
- goto exit;
- }
- dbus_message_iter_get_basic(&iter_variant, &locked_hint);
-
- si->session_locked_hint = (locked_hint) ? TRUE : FALSE;
-exit:
- if (reply != NULL) {
- dbus_message_unref(reply);
- }
-
- if (message != NULL) {
- dbus_message_unref(message);
+ si->dbus_proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ NULL,
+ LOGIND_INTERFACE,
+ session_object,
+ LOGIND_SESSION_INTERFACE,
+ NULL,
+ &err);
+ g_free(session_object);
+ if (err) {
+ syslog(LOG_ERR, "(systemd-login) %s: %s", __func__, err->message);
+ g_error_free(err);
+ return;
}
+ g_signal_connect(si->dbus_proxy, "g-signal",
+ G_CALLBACK(session_signal_cb), si);
}

-static void
-si_dbus_read_signals(struct session_info *si)
+static gboolean io_channel_cb(GIOChannel *source,
+ GIOCondition condition,
+ gpointer data)
{
- DBusMessage *message = NULL;
-
- dbus_connection_read_write(si->dbus.system_connection, 0);
- message = dbus_connection_pop_message(si->dbus.system_connection);
- while (message != NULL) {
- const char *member;
-
- member = dbus_message_get_member (message);
- if (g_strcmp0(member, SESSION_SIGNAL_LOCK) == 0) {
- si->session_is_locked = TRUE;
- } else if (g_strcmp0(member, SESSION_SIGNAL_UNLOCK) == 0) {
- si->session_is_locked = FALSE;
- } else {
- if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) {
- syslog(LOG_WARNING, "(systemd-login) received non signal message");
- } else if (si->verbose) {
- syslog(LOG_DEBUG, "(systemd-login) Signal not handled: %s", member);
- }
- }
-
- dbus_message_unref(message);
- dbus_connection_read_write(si->dbus.system_connection, 0);
- message = dbus_connection_pop_message(si->dbus.system_connection);
- }
+ struct session_info *si = data;
+ session_info_get_active_session(si);
+ if (si->session_change_cb)
+ si->session_change_cb(si->session);
+ return G_SOURCE_CONTINUE;
}

struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb)
@@ -236,6 +115,7 @@ struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb)

si->verbose = verbose;
si->session_is_locked = FALSE;
+ si->session_change_cb = cb;

r = sd_login_monitor_new("session", &si->mon);
if (r < 0) {
@@ -244,7 +124,9 @@ struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb)
return NULL;
}

- si->dbus.system_connection = si_dbus_get_system_bus();
+ si->io_channel = g_io_channel_unix_new(sd_login_monitor_get_fd(si->mon));
+ si->io_watch_id = g_io_add_watch(si->io_channel, G_IO_IN, io_channel_cb, si);
+
return si;
}

@@ -253,8 +135,9 @@ void session_info_destroy(struct session_info *si)
if (!si)
return;

- si_dbus_match_remove(si);
- dbus_connection_close(si->dbus.system_connection);
+ g_clear_object(&si->dbus_proxy);
+ g_source_remove(si->io_watch_id);
+ g_io_channel_unref(si->io_channel);
sd_login_monitor_unref(si->mon);
free(si->session);
free(si);
@@ -284,7 +167,7 @@ const char *session_info_get_active_session(struct session_info *si)
sd_login_monitor_flush(si->mon);
free(old_session);

- si_dbus_match_rule_update(si);
+ si_dbus_proxy_update(si);
return si->session;
}

@@ -303,16 +186,37 @@ char *session_info_session_for_pid(struct session_info *si, uint32_t pid)
return session;
}

+gboolean get_locked_hint_property(struct session_info *si)
+{
+ GVariant *prop;
+ gboolean locked = FALSE;
+
+ if (si->dbus_proxy == NULL)
+ return locked;
+
+ prop = g_dbus_proxy_get_cached_property(si->dbus_proxy,
+ SESSION_PROP_LOCKED_HINT);
+ if (prop) {
+ if (g_variant_is_of_type(prop, G_VARIANT_TYPE_BOOLEAN))
+ locked = g_variant_get_boolean(prop);
+ else
+ syslog(LOG_ERR, "(systemd-login) unexpected LockedHint property type");
+
+ g_variant_unref(prop);
+ } else {
+ syslog(LOG_ERR, "(systemd-login) cannot get LockedHint property");
+ }
+
+ return locked;
+}
+
gboolean session_info_session_is_locked(struct session_info *si)
{
gboolean locked;

g_return_val_if_fail (si != NULL, FALSE);

- si_dbus_read_signals(si);
- si_dbus_read_properties(si);
-
- locked = (si->session_is_locked || si->session_locked_hint);
+ locked = (si->session_is_locked || get_locked_hint_property(si));
if (si->verbose) {
syslog(LOG_DEBUG, "(systemd-login) session is locked: %s",
locked ? "yes" : "no");
--
2.17.1
Jakub Janků
2018-08-14 18:53:49 UTC
Permalink
Both console-kit.c and systemd-login.c
have been moved to GDBus.
---
Makefile.am | 2 --
configure.ac | 1 -
2 files changed, 3 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index b291b19..64ed406 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -47,7 +47,6 @@ src_spice_vdagent_SOURCES = \
$(NULL)

src_spice_vdagentd_CFLAGS = \
- $(DBUS_CFLAGS) \
$(LIBSYSTEMD_DAEMON_CFLAGS) \
$(LIBSYSTEMD_LOGIN_CFLAGS) \
$(PCIACCESS_CFLAGS) \
@@ -59,7 +58,6 @@ src_spice_vdagentd_CFLAGS = \
$(NULL)

src_spice_vdagentd_LDADD = \
- $(DBUS_LIBS) \
$(LIBSYSTEMD_DAEMON_LIBS) \
$(LIBSYSTEMD_LOGIN_LIBS) \
$(PCIACCESS_LIBS) \
diff --git a/configure.ac b/configure.ac
index cf8169b..3f28532 100644
--- a/configure.ac
+++ b/configure.ac
@@ -105,7 +105,6 @@ PKG_CHECK_MODULES([GIO2], [gio-unix-2.0])
PKG_CHECK_MODULES(X, [xfixes xrandr >= 1.3 xinerama x11])
PKG_CHECK_MODULES(SPICE, [spice-protocol >= 0.12.13])
PKG_CHECK_MODULES(ALSA, [alsa >= 1.0.22])
-PKG_CHECK_MODULES([DBUS], [dbus-1])

if test "$with_session_info" = "auto" || test "$with_session_info" = "systemd"; then
PKG_CHECK_MODULES([LIBSYSTEMD_LOGIN],
--
2.17.1
Jakub Janků
2018-08-14 18:53:46 UTC
Permalink
Rewrite console-kit.c using GDBus instead of libdbus.
GDBus provides a more convenient way to access DBus objects.

All DBus method calls share a fair amount of common code here,
so add a helper function dbus_call_method_for_string().
---
src/vdagentd/console-kit.c | 597 ++++++++++++-------------------------
1 file changed, 192 insertions(+), 405 deletions(-)

diff --git a/src/vdagentd/console-kit.c b/src/vdagentd/console-kit.c
index ef49db5..4c02ebe 100644
--- a/src/vdagentd/console-kit.c
+++ b/src/vdagentd/console-kit.c
@@ -20,24 +20,21 @@
*/

#include "session-info.h"
-#include <dbus/dbus.h>
-#include <stdbool.h>
-#include <unistd.h>
#include <stdlib.h>
-#include <string.h>
#include <syslog.h>
#include <glib.h>
+#include <gio/gio.h>

struct session_info {
- DBusConnection *connection;
- int fd;
+ GDBusConnection *dbus;
char *seat;
char *active_session;
int verbose;
- gchar *match_seat_signals;
- gchar *match_session_signals;
gboolean session_is_locked;
gboolean session_idle_hint;
+ guint seat_signals_subs_id;
+ guint session_signals_subs_id;
+ ActiveSessionChangeCb session_change_cb;
};

#define INTERFACE_CONSOLE_KIT "org.freedesktop.ConsoleKit"
@@ -58,169 +55,112 @@ struct session_info {
#define SESSION_SIGNAL_IDLE_HINT_CHANGED "IdleHintChanged"

static char *console_kit_get_first_seat(struct session_info *info);
-static char *console_kit_check_active_session_change(struct session_info *info);
+static void si_dbus_signal_listeners_update(struct session_info *info);

-static void si_dbus_match_remove(struct session_info *info)
+static void si_dbus_signals_unsubscribe(struct session_info *info)
{
- DBusError error;
- if (info->match_seat_signals != NULL) {
- dbus_error_init(&error);
- dbus_bus_remove_match(info->connection,
- info->match_seat_signals,
- &error);
- if (info->verbose)
- syslog(LOG_DEBUG, "(console-kit) seat match removed: %s",
- info->match_seat_signals);
- g_free(info->match_seat_signals);
- info->match_seat_signals = NULL;
+ if (info->seat_signals_subs_id) {
+ g_dbus_connection_signal_unsubscribe(info->dbus,
+ info->seat_signals_subs_id);
+ info->seat_signals_subs_id = 0;
}
-
- if (info->match_session_signals != NULL) {
- dbus_error_init(&error);
- dbus_bus_remove_match(info->connection,
- info->match_session_signals,
- &error);
-
- if (info->verbose)
- syslog(LOG_DEBUG, "(console-kit) session match removed: %s",
- info->match_session_signals);
- g_free(info->match_session_signals);
- info->match_session_signals = NULL;
+ if (info->session_signals_subs_id) {
+ g_dbus_connection_signal_unsubscribe(info->dbus,
+ info->session_signals_subs_id);
+ info->session_signals_subs_id = 0;
}
}

-static void si_dbus_match_rule_update(struct session_info *info)
+static void active_session_changed_cb(GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
{
- DBusError error;
+ struct session_info *info = user_data;

- if (info->connection == NULL)
- return;
+ g_clear_pointer(&info->active_session, g_free);

- si_dbus_match_remove(info);
-
- /* Seat signals */
- if (info->seat != NULL) {
- info->match_seat_signals =
- g_strdup_printf ("type='signal',interface='%s',path='%s',"
- "member='ActiveSessionChanged'",
- INTERFACE_CONSOLE_KIT_SEAT,
- info->seat);
- if (info->verbose)
- syslog(LOG_DEBUG, "(console-kit) seat match: %s",
- info->match_seat_signals);
-
- dbus_error_init(&error);
- dbus_bus_add_match(info->connection,
- info->match_seat_signals,
- &error);
- if (dbus_error_is_set(&error)) {
- syslog(LOG_WARNING, "Unable to add dbus rule match: %s",
- error.message);
- dbus_error_free(&error);
- g_free(info->match_seat_signals);
- info->match_seat_signals = NULL;
- }
+ /* s = G_VARIANT_TYPE_STRING, o = G_VARIANT_TYPE_OBJECT_PATH */
+ if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(s)")) &&
+ !g_variant_is_of_type(parameters, G_VARIANT_TYPE("(o)"))) {
+ syslog(LOG_ERR, "ActiveSessionChanged message has unexpected type");
+ return;
}

- /* Session signals */
- if (info->active_session != NULL) {
- info->match_session_signals =
- g_strdup_printf ("type='signal',interface='%s',path='%s'",
- INTERFACE_CONSOLE_KIT_SESSION,
- info->active_session);
- if (info->verbose)
- syslog(LOG_DEBUG, "(console-kit) session match: %s",
- info->match_session_signals);
-
- dbus_error_init(&error);
- dbus_bus_add_match(info->connection,
- info->match_session_signals,
- &error);
- if (dbus_error_is_set(&error)) {
- syslog(LOG_WARNING, "Unable to add dbus rule match: %s",
- error.message);
- dbus_error_free(&error);
- g_free(info->match_session_signals);
- info->match_session_signals = NULL;
- }
- }
+ g_variant_get(parameters, "(s)", &info->active_session);
+ if (info->active_session)
+ si_dbus_signal_listeners_update(info);
+ else
+ syslog(LOG_WARNING, "(console-kit) received invalid session. "
+ "No active-session at the moment");
+
+ if (info->session_change_cb)
+ info->session_change_cb(info->active_session);
}

-static void
-si_dbus_read_signals(struct session_info *info)
+static void session_signal_cb(GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
{
- DBusMessage *message = NULL;
-
- dbus_connection_read_write(info->connection, 0);
- message = dbus_connection_pop_message(info->connection);
- while (message != NULL) {
- const char *member;
-
- member = dbus_message_get_member (message);
- if (g_strcmp0(member, SEAT_SIGNAL_ACTIVE_SESSION_CHANGED) == 0) {
- DBusMessageIter iter;
- gint type;
- gchar *session;
-
- free(info->active_session);
- info->active_session = NULL;
-
- dbus_message_iter_init(message, &iter);
- type = dbus_message_iter_get_arg_type(&iter);
- /* Session should be an object path, but there is a bug in
- ConsoleKit where it sends a string rather than an object_path
- accept object_path too in case the bug ever gets fixed */
- if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
- dbus_message_iter_get_basic(&iter, &session);
- if (session != NULL && session[0] != '\0') {
- info->active_session = g_strdup(session);
- si_dbus_match_rule_update(info);
- } else {
- syslog(LOG_WARNING, "(console-kit) received invalid session. "
- "No active-session at the moment");
- }
- } else {
- syslog(LOG_ERR,
- "ActiveSessionChanged message has unexpected type: '%c'",
- type);
- }
- } else if (g_strcmp0(member, SESSION_SIGNAL_LOCK) == 0) {
- info->session_is_locked = TRUE;
- } else if (g_strcmp0(member, SESSION_SIGNAL_UNLOCK) == 0) {
- info->session_is_locked = FALSE;
- } else if (g_strcmp0(member, SESSION_SIGNAL_IDLE_HINT_CHANGED) == 0) {
- DBusMessageIter iter;
- gint type;
- dbus_bool_t idle_hint;
-
- dbus_message_iter_init(message, &iter);
- type = dbus_message_iter_get_arg_type(&iter);
- if (type == DBUS_TYPE_BOOLEAN) {
- dbus_message_iter_get_basic(&iter, &idle_hint);
- info->session_idle_hint = (idle_hint);
- } else {
- syslog(LOG_ERR,
- "(console-kit) IdleHintChanged has unexpected type: '%c'",
- type);
- }
- } else {
- if (dbus_message_get_type(message) != DBUS_MESSAGE_TYPE_SIGNAL) {
- syslog(LOG_WARNING, "(console-kit) received non signal message");
- } else if (info->verbose) {
- syslog(LOG_DEBUG, "(console-kit) Signal not handled: %s", member);
- }
- }
-
- dbus_message_unref(message);
- dbus_connection_read_write(info->connection, 0);
- message = dbus_connection_pop_message(info->connection);
+ struct session_info *info = user_data;
+
+ if (!g_strcmp0(signal_name, SESSION_SIGNAL_IDLE_HINT_CHANGED)) {
+ if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(b)")))
+ g_variant_get(parameters, "(b)", &info->session_idle_hint);
+ else
+ syslog(LOG_ERR, "(console-kit) IdleHintChanged has unexpected type");
+ return;
}
+
+ if (!g_strcmp0(signal_name, SESSION_SIGNAL_LOCK))
+ info->session_is_locked = TRUE;
+ else if (!g_strcmp0(signal_name, SESSION_SIGNAL_UNLOCK))
+ info->session_is_locked = FALSE;
+}
+
+static void si_dbus_signal_listeners_update(struct session_info *info)
+{
+ if (info->dbus == NULL)
+ return;
+
+ si_dbus_signals_unsubscribe(info);
+
+ if (info->seat != NULL)
+ info->seat_signals_subs_id =
+ g_dbus_connection_signal_subscribe(info->dbus,
+ INTERFACE_CONSOLE_KIT,
+ INTERFACE_CONSOLE_KIT_SEAT,
+ SEAT_SIGNAL_ACTIVE_SESSION_CHANGED,
+ info->seat,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ active_session_changed_cb,
+ info, NULL);
+
+ if (info->active_session != NULL)
+ info->session_signals_subs_id =
+ g_dbus_connection_signal_subscribe(info->dbus,
+ INTERFACE_CONSOLE_KIT,
+ INTERFACE_CONSOLE_KIT_SESSION,
+ NULL,
+ info->active_session,
+ NULL,
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ session_signal_cb,
+ info, NULL);
}

struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb)
{
struct session_info *info;
- DBusError error;
+ GError *err = NULL;

info = calloc(1, sizeof(*info));
if (!info)
@@ -229,32 +169,21 @@ struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb)
info->verbose = verbose;
info->session_is_locked = FALSE;
info->session_idle_hint = FALSE;
-
- dbus_error_init(&error);
- info->connection = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
- if (info->connection == NULL || dbus_error_is_set(&error)) {
- if (dbus_error_is_set(&error)) {
- syslog(LOG_ERR, "Unable to connect to system bus: %s",
- error.message);
- dbus_error_free(&error);
- } else
- syslog(LOG_ERR, "Unable to connect to system bus");
+ info->session_change_cb = cb;
+ info->dbus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
+ if (err) {
+ syslog(LOG_ERR, "%s: %s", __func__, err->message);
+ g_error_free(err);
free(info);
return NULL;
}

- if (!dbus_connection_get_unix_fd(info->connection, &info->fd)) {
- syslog(LOG_ERR, "Unable to get connection fd");
- session_info_destroy(info);
- return NULL;
- }
-
if (!console_kit_get_first_seat(info)) {
session_info_destroy(info);
return NULL;
}

- si_dbus_match_rule_update(info);
+ si_dbus_signal_listeners_update(info);
return info;
}

@@ -263,8 +192,8 @@ void session_info_destroy(struct session_info *info)
if (!info)
return;

- si_dbus_match_remove(info);
- dbus_connection_close(info->connection);
+ si_dbus_signals_unsubscribe(info);
+ g_object_unref(info->dbus);
free(info->seat);
free(info->active_session);
free(info);
@@ -272,217 +201,120 @@ void session_info_destroy(struct session_info *info)

int session_info_get_fd(struct session_info *info)
{
- return info->fd;
+ return -1;
}

-static char *console_kit_get_first_seat(struct session_info *info)
+/* Invoke a method on a remote object through DBus and wait for reply.
+ *
+ * Return type must be one of G_VARIANT_TYPE_STRING, G_VARIANT_TYPE_SIGNATURE,
+ * G_VARIANT_TYPE_OBJECT_PATH, G_VARIANT_TYPE_OBJECT_PATH_ARRAY.
+ *
+ * In the case of G_VARIANT_TYPE_OBJECT_PATH_ARRAY,
+ * first element of the array is returned.
+ *
+ * Returns a newly-allocated string extracted from reply, or NULL on error */
+static gchar *dbus_call_method_for_string(GDBusConnection *dbus,
+ const gchar *bus_name,
+ const gchar *obj_path,
+ const gchar *interface,
+ const gchar *method,
+ GVariant *msg_body,
+ const GVariantType *ret_type)
{
- DBusError error;
- DBusMessage *message = NULL;
- DBusMessage *reply = NULL;
- DBusMessageIter iter, subiter;
- int type;
- char *seat = NULL;
-
-
- message = dbus_message_new_method_call(INTERFACE_CONSOLE_KIT,
- OBJ_PATH_CONSOLE_KIT_MANAGER,
- INTERFACE_CONSOLE_KIT_MANAGER,
- "GetSeats");
- if (message == NULL) {
- syslog(LOG_ERR, "Unable to create dbus message");
- goto exit;
- }
+ GDBusMessage *message, *reply;
+ GError *err = NULL;
+ GVariant *body;
+ gchar *ret_str;

- dbus_error_init(&error);
- reply = dbus_connection_send_with_reply_and_block(info->connection,
- message,
- -1,
- &error);
- if (reply == NULL || dbus_error_is_set(&error)) {
- if (dbus_error_is_set(&error)) {
- syslog(LOG_ERR, "GetSeats failed: %s", error.message);
- dbus_error_free(&error);
- } else
- syslog(LOG_ERR, "GetSeats failed");
- goto exit;
- }
+ message = g_dbus_message_new_method_call(bus_name, obj_path,
+ interface, method);
+ g_dbus_message_set_body(message, msg_body);

- dbus_message_iter_init(reply, &iter);
- type = dbus_message_iter_get_arg_type(&iter);
- if (type != DBUS_TYPE_ARRAY) {
- syslog(LOG_ERR,
- "expected an array return value, got a '%c' instead", type);
- goto exit;
- }
+ reply = g_dbus_connection_send_message_with_reply_sync(dbus, message,
+ G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, NULL, NULL, &err);

- dbus_message_iter_recurse(&iter, &subiter);
- type = dbus_message_iter_get_arg_type(&subiter);
- if (type != DBUS_TYPE_OBJECT_PATH) {
- syslog(LOG_ERR,
- "expected an object path element, got a '%c' instead", type);
- goto exit;
+ g_object_unref(message);
+
+ if (reply && g_dbus_message_to_gerror(reply, &err)) {
+ /* Remote process replied with an error (G_DBUS_MESSAGE_TYPE_ERROR). */
+ g_object_unref(reply);
+ }
+ if (err) {
+ syslog(LOG_ERR, "%s call failed: %s", method, err->message);
+ g_error_free(err);
+ return NULL;
}

- dbus_message_iter_get_basic(&subiter, &seat);
- info->seat = strdup(seat);
+ /* Reply always comes as a tuple, unpack it. */
+ body = g_variant_get_child_value(g_dbus_message_get_body(reply), 0);
+ g_object_unref(reply);

-exit:
- if (reply != NULL) {
- dbus_message_unref(reply);
+ if (g_variant_is_of_type(body, ret_type) == FALSE) {
+ syslog(LOG_ERR, "%s call returned a value of unexpected type: \"%s\"",
+ method, g_variant_get_type_string(body));
+ g_variant_unref(body);
+ return NULL;
}

- if (message != NULL) {
- dbus_message_unref(message);
+ if (g_variant_is_of_type(body, G_VARIANT_TYPE_OBJECT_PATH_ARRAY)) {
+ /* Get the first item in the array, this is NULL for an empty array. */
+ const gchar **objv = g_variant_get_objv(body, NULL);
+ ret_str = g_strdup(objv[0]);
+ g_free(objv);
+ } else {
+ ret_str = g_strdup(g_variant_get_string(body, NULL));
}

+ g_variant_unref(body);
+ return ret_str;
+}
+
+static char *console_kit_get_first_seat(struct session_info *info)
+{
+ info->seat =
+ dbus_call_method_for_string(info->dbus,
+ INTERFACE_CONSOLE_KIT,
+ OBJ_PATH_CONSOLE_KIT_MANAGER,
+ INTERFACE_CONSOLE_KIT_MANAGER,
+ "GetSeats", NULL,
+ G_VARIANT_TYPE_OBJECT_PATH_ARRAY);
+
syslog(LOG_INFO, "(console-kit) seat: %s", info->seat);
return info->seat;
}

const char *session_info_get_active_session(struct session_info *info)
{
- DBusError error;
- DBusMessage *message = NULL;
- DBusMessage *reply = NULL;
- char *session = NULL;
-
if (!info)
return NULL;

if (info->active_session)
- return console_kit_check_active_session_change(info);
-
- message = dbus_message_new_method_call(INTERFACE_CONSOLE_KIT,
- info->seat,
- INTERFACE_CONSOLE_KIT_SEAT,
- "GetActiveSession");
- if (message == NULL) {
- syslog(LOG_ERR, "Unable to create dbus message");
- goto exit;
- }
+ return info->active_session;

- dbus_error_init(&error);
- reply = dbus_connection_send_with_reply_and_block(info->connection,
- message,
- -1,
- &error);
- if (reply == NULL || dbus_error_is_set(&error)) {
- if (dbus_error_is_set(&error)) {
- syslog(LOG_ERR, "GetActiveSession failed: %s", error.message);
- dbus_error_free(&error);
- } else
- syslog(LOG_ERR, "GetActiveSession failed");
- goto exit;
- }
+ info->active_session =
+ dbus_call_method_for_string(info->dbus,
+ INTERFACE_CONSOLE_KIT,
+ info->seat,
+ INTERFACE_CONSOLE_KIT_SEAT,
+ "GetActiveSession", NULL,
+ G_VARIANT_TYPE_OBJECT_PATH);

- dbus_error_init(&error);
- if (!dbus_message_get_args(reply,
- &error,
- DBUS_TYPE_OBJECT_PATH, &session,
- DBUS_TYPE_INVALID)) {
- if (dbus_error_is_set(&error)) {
- syslog(LOG_ERR, "error get ssid from reply: %s", error.message);
- dbus_error_free(&error);
- } else
- syslog(LOG_ERR, "error getting ssid from reply");
- session = NULL;
- goto exit;
- }
-
- info->active_session = strdup(session);
- si_dbus_match_rule_update(info);
-
-exit:
- if (reply != NULL) {
- dbus_message_unref(reply);
- }
-
- if (message != NULL) {
- dbus_message_unref(message);
- }
-
- /* In case the session was changed while we were running */
- return console_kit_check_active_session_change(info);
+ si_dbus_signal_listeners_update(info);
+ return info->active_session;
}

char *session_info_session_for_pid(struct session_info *info, uint32_t pid)
{
- DBusError error;
- DBusMessage *message = NULL;
- DBusMessage *reply = NULL;
- DBusMessageIter args;
- char *ssid = NULL;
-
if (!info)
return NULL;

- message = dbus_message_new_method_call(INTERFACE_CONSOLE_KIT,
- OBJ_PATH_CONSOLE_KIT_MANAGER,
- INTERFACE_CONSOLE_KIT_MANAGER,
- "GetSessionForUnixProcess");
- if (message == NULL) {
- syslog(LOG_ERR, "Unable to create dbus message");
- goto exit;
- }
-
- dbus_message_iter_init_append(message, &args);
- if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT32, &pid)) {
- syslog(LOG_ERR, "Unable to append dbus message args");
- goto exit;
- }
-
- dbus_error_init(&error);
- reply = dbus_connection_send_with_reply_and_block(info->connection,
- message,
- -1,
- &error);
- if (reply == NULL || dbus_error_is_set(&error)) {
- if (dbus_error_is_set(&error)) {
- syslog(LOG_ERR, "GetSessionForUnixProcess failed: %s",
- error.message);
- dbus_error_free(&error);
- } else
- syslog(LOG_ERR, "GetSessionForUnixProcess failed");
- goto exit;
- }
-
- dbus_error_init(&error);
- if (!dbus_message_get_args(reply,
- &error,
- DBUS_TYPE_OBJECT_PATH, &ssid,
- DBUS_TYPE_INVALID)) {
- if (dbus_error_is_set(&error)) {
- syslog(LOG_ERR, "error get ssid from reply: %s", error.message);
- dbus_error_free(&error);
- } else
- syslog(LOG_ERR, "error getting ssid from reply");
- ssid = NULL;
- goto exit;
- }
-
- ssid = strdup(ssid);
-
-exit:
- if (reply != NULL) {
- dbus_message_unref(reply);
- }
-
- if (message != NULL) {
- dbus_message_unref(message);
- }
-
- return ssid;
-}
-
-static char *console_kit_check_active_session_change(struct session_info *info)
-{
- si_dbus_read_signals(info);
- if (info->verbose)
- syslog(LOG_DEBUG, "(console-kit) active-session: '%s'",
- (info->active_session ? info->active_session : "None"));
-
- return info->active_session;
+ return dbus_call_method_for_string(info->dbus,
+ INTERFACE_CONSOLE_KIT,
+ OBJ_PATH_CONSOLE_KIT_MANAGER,
+ INTERFACE_CONSOLE_KIT_MANAGER,
+ "GetSessionForUnixProcess",
+ g_variant_new("(u)", pid),
+ G_VARIANT_TYPE_OBJECT_PATH);
}

gboolean session_info_session_is_locked(struct session_info *info)
@@ -496,7 +328,6 @@ gboolean session_info_session_is_locked(struct session_info *info)
* IdleHintChanged. So use the IdleHint value.
* systemd-login uses locked-hint which is not implemented in ConsoleKit,
* see https://github.com/ConsoleKit2/ConsoleKit2/issues/89 */
- si_dbus_read_signals(info);
locked = info->session_idle_hint;
if (info->verbose) {
syslog(LOG_DEBUG, "(console-kit) session is locked: %s",
@@ -509,69 +340,25 @@ gboolean session_info_session_is_locked(struct session_info *info)
* in order to verify if active session belongs to user (non greeter) */
gboolean session_info_is_user(struct session_info *info)
{
- DBusError error;
- DBusMessage *message = NULL;
- DBusMessage *reply = NULL;
- gchar *session_type = NULL;
- gboolean ret = TRUE;
+ gchar *session_type;
+ gboolean ret;

g_return_val_if_fail (info != NULL, TRUE);
- g_return_val_if_fail (info->connection != NULL, TRUE);
g_return_val_if_fail (info->active_session != NULL, TRUE);

- message = dbus_message_new_method_call(INTERFACE_CONSOLE_KIT,
- info->active_session,
- INTERFACE_CONSOLE_KIT_SESSION,
- "GetSessionType");
- if (message == NULL) {
- syslog(LOG_ERR,
- "(console-kit) Unable to create dbus message for GetSessionType");
- return TRUE;
- }
-
- dbus_error_init(&error);
- reply = dbus_connection_send_with_reply_and_block(info->connection,
- message,
- -1,
- &error);
- if (reply == NULL || dbus_error_is_set(&error)) {
- if (dbus_error_is_set(&error)) {
- syslog(LOG_ERR, "GetSessionType failed: %s", error.message);
- dbus_error_free(&error);
- } else
- syslog(LOG_ERR, "GetSessionType failed");
- goto exit;
- }
-
- dbus_error_init(&error);
- if (!dbus_message_get_args(reply,
- &error,
- DBUS_TYPE_STRING, &session_type,
- DBUS_TYPE_INVALID)) {
- if (dbus_error_is_set(&error)) {
- syslog(LOG_ERR,
- "(console-kit) fail to get session-type from reply: %s",
- error.message);
- dbus_error_free(&error);
- } else {
- syslog(LOG_ERR, "(console-kit) fail to get session-type from reply");
- }
- session_type = NULL;
- goto exit;
- }
+ session_type =
+ dbus_call_method_for_string(info->dbus,
+ INTERFACE_CONSOLE_KIT,
+ info->active_session,
+ INTERFACE_CONSOLE_KIT_SESSION,
+ "GetSessionType", NULL,
+ G_VARIANT_TYPE_STRING);

/* Empty session_type means user */
if (info->verbose)
syslog(LOG_DEBUG, "(console-kit) session-type is '%s'", session_type);

- ret = (g_strcmp0 (session_type, "LoginWindow") != 0);
-
-exit:
- if (reply != NULL) {
- dbus_message_unref(reply);
- }
- if (message != NULL) {
- dbus_message_unref(message);
- }
+ ret = (g_strcmp0(session_type, "LoginWindow") != 0);
+ g_free(session_type);
return ret;
}
--
2.17.1
Jakub Janků
2018-08-14 18:53:50 UTC
Permalink
Some older parts of the code currently use
memory functions defined in stdlib.h
and usually handle allocation errors.

On the other hand, newer parts of the code
and GLib/GTK+ functions themselves commonly use
wrappers provided by GLib that terminate
the program when there isn't enough memory.

So it doesn't make much sense to check for
ENOMEM somewhere, while the program can be
terminated at any time elsewhere.

Unify the code and use GLib wrappers only.

malloc --> g_malloc, g_new
calloc --> g_malloc0, g_new0
free --> g_free

Use g_try_malloc only when allocating
message buffers, where the agent could
theoretically continue functioning even
after the allocation fails.
(e.g. when trying to send large
clipboard data buffer)

Use g_clear_pointer where possible
for simplification.
---
src/udscs.c | 23 ++++-----------
src/vdagent/x11-randr.c | 43 ++++++++++-----------------
src/vdagentd/console-kit.c | 13 ++++----
src/vdagentd/systemd-login.c | 13 ++++----
src/vdagentd/uinput.c | 9 ++----
src/vdagentd/vdagentd.c | 57 +++++++++---------------------------
src/vdagentd/virtio-port.c | 37 ++++++++---------------
7 files changed, 60 insertions(+), 135 deletions(-)

diff --git a/src/udscs.c b/src/udscs.c
index 0b80317..681e80f 100644
--- a/src/udscs.c
+++ b/src/udscs.c
@@ -101,9 +101,7 @@ struct udscs_connection *udscs_connect(const char *socketname,
if (io_stream == NULL)
return NULL;

- conn = calloc(1, sizeof(*conn));
- if (!conn)
- return NULL;
+ conn = g_new0(struct udscs_connection, 1);
conn->debug = debug;
conn->conn = vdagent_connection_new(io_stream,
FALSE,
@@ -141,8 +139,7 @@ void udscs_destroy_connection(struct udscs_connection **connp)
if (conn->debug)
syslog(LOG_DEBUG, "%p disconnected", conn);

- free(conn);
- *connp = NULL;
+ g_clear_pointer(connp, g_free);
}

void udscs_set_user_data(struct udscs_connection *conn, void *data)
@@ -166,7 +163,7 @@ int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1,
struct udscs_message_header header;

buff_size = sizeof(header) + size;
- buff = malloc(buff_size);
+ buff = g_try_malloc(buff_size);
if (buff == NULL)
return -1;

@@ -212,10 +209,7 @@ struct udscs_server *udscs_server_new(
{
struct udscs_server *server;

- server = calloc(1, sizeof(*server));
- if (!server)
- return NULL;
-
+ server = g_new0(struct udscs_server, 1);
server->debug = debug;
server->connect_callback = connect_callback;
server->read_callback = read_callback;
@@ -282,7 +276,7 @@ void udscs_destroy_server(struct udscs_server *server)
conn = next_conn;
}
g_object_unref(server->service);
- free(server);
+ g_free(server);
}

int udscs_get_peer_pid(struct udscs_connection *conn)
@@ -299,12 +293,7 @@ static gboolean udscs_server_accept_cb(GSocketService *service,
struct udscs_server *server = user_data;
struct udscs_connection *new_conn, *conn;

- new_conn = calloc(1, sizeof(*conn));
- if (!new_conn) {
- syslog(LOG_ERR, "out of memory, disconnecting new client");
- return TRUE;
- }
-
+ new_conn = g_new0(struct udscs_connection, 1);
new_conn->debug = server->debug;
new_conn->read_callback = server->read_callback;
new_conn->disconnect_callback = server->disconnect_callback;
diff --git a/src/vdagent/x11-randr.c b/src/vdagent/x11-randr.c
index 803cf73..29ce6fc 100644
--- a/src/vdagent/x11-randr.c
+++ b/src/vdagent/x11-randr.c
@@ -81,13 +81,13 @@ static void free_randr_resources(struct vdagent_x11 *x11)
for (i = 0 ; i < x11->randr.res->noutput; ++i) {
XRRFreeOutputInfo(x11->randr.outputs[i]);
}
- free(x11->randr.outputs);
+ g_free(x11->randr.outputs);
}
if (x11->randr.crtcs != NULL) {
for (i = 0 ; i < x11->randr.res->ncrtc; ++i) {
XRRFreeCrtcInfo(x11->randr.crtcs[i]);
}
- free(x11->randr.crtcs);
+ g_free(x11->randr.crtcs);
}
XRRFreeScreenResources(x11->randr.res);
x11->randr.res = NULL;
@@ -105,8 +105,8 @@ static void update_randr_res(struct vdagent_x11 *x11, int poll)
x11->randr.res = XRRGetScreenResources(x11->display, x11->root_window[0]);
else
x11->randr.res = XRRGetScreenResourcesCurrent(x11->display, x11->root_window[0]);
- x11->randr.outputs = malloc(x11->randr.res->noutput * sizeof(*x11->randr.outputs));
- x11->randr.crtcs = malloc(x11->randr.res->ncrtc * sizeof(*x11->randr.crtcs));
+ x11->randr.outputs = g_malloc(x11->randr.res->noutput * sizeof(*x11->randr.outputs));
+ x11->randr.crtcs = g_malloc(x11->randr.res->ncrtc * sizeof(*x11->randr.crtcs));
for (i = 0 ; i < x11->randr.res->noutput; ++i) {
x11->randr.outputs[i] = XRRGetOutputInfo(x11->display, x11->randr.res,
x11->randr.res->outputs[i]);
@@ -696,7 +696,7 @@ static VDAgentMonitorsConfig *get_current_mon_config(struct vdagent_x11 *x11)

error:
syslog(LOG_ERR, "error: inconsistent or stale data from X");
- free(mon_config);
+ g_free(mon_config);
return NULL;
}

@@ -844,12 +844,12 @@ void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
if (!fallback) {
syslog(LOG_WARNING, "Restoring previous config");
vdagent_x11_set_monitor_config(x11, curr, 1);
- free(curr);
+ g_free(curr);
/* Remember this config failed, if the client is maximized or
fullscreen it will keep sending the failing config. */
- free(x11->randr.failed_conf);
+ g_free(x11->randr.failed_conf);
x11->randr.failed_conf =
- malloc(config_size(mon_config->num_of_monitors));
+ g_malloc(config_size(mon_config->num_of_monitors));
if (x11->randr.failed_conf)
memcpy(x11->randr.failed_conf, mon_config,
config_size(mon_config->num_of_monitors));
@@ -895,7 +895,7 @@ exit:

/* Flush output buffers and consume any pending events */
vdagent_x11_do_read(x11);
- free(curr);
+ g_free(curr);
}

void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11, int update)
@@ -914,11 +914,7 @@ void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11, int update)
goto no_info;

screen_count = curr->num_of_monitors;
- res = malloc(screen_count * sizeof(*res));
- if (!res) {
- free(curr);
- goto no_mem;
- }
+ res = g_malloc(screen_count * sizeof(*res));

for (i = 0; i < screen_count; i++) {
res[i].width = curr->monitors[i].width;
@@ -926,7 +922,7 @@ void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11, int update)
res[i].x = curr->monitors[i].x;
res[i].y = curr->monitors[i].y;
}
- free(curr);
+ g_free(curr);
width = x11->width[0];
height = x11->height[0];
} else if (x11->has_xinerama) {
@@ -935,17 +931,13 @@ void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11, int update)
screen_info = XineramaQueryScreens(x11->display, &screen_count);
if (!screen_info)
goto no_info;
- res = malloc(screen_count * sizeof(*res));
- if (!res) {
- XFree(screen_info);
- goto no_mem;
- }
+ res = g_malloc(screen_count * sizeof(*res));
for (i = 0; i < screen_count; i++) {
if (screen_info[i].screen_number >= screen_count) {
syslog(LOG_ERR, "Invalid screen number in xinerama screen info (%d >= %d)",
screen_info[i].screen_number, screen_count);
XFree(screen_info);
- free(res);
+ g_free(res);
return;
}
res[screen_info[i].screen_number].width = screen_info[i].width;
@@ -959,9 +951,7 @@ void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11, int update)
} else {
no_info:
screen_count = x11->screen_count;
- res = malloc(screen_count * sizeof(*res));
- if (!res)
- goto no_mem;
+ res = g_malloc(screen_count * sizeof(*res));
for (i = 0; i < screen_count; i++) {
res[i].width = x11->width[i];
res[i].height = x11->height[i];
@@ -982,8 +972,5 @@ no_info:

udscs_write(x11->vdagentd, VDAGENTD_GUEST_XORG_RESOLUTION, width, height,
(uint8_t *)res, screen_count * sizeof(*res));
- free(res);
- return;
-no_mem:
- syslog(LOG_ERR, "out of memory while trying to send resolutions, not sending resolutions.");
+ g_free(res);
}
diff --git a/src/vdagentd/console-kit.c b/src/vdagentd/console-kit.c
index 390491e..8094fa9 100644
--- a/src/vdagentd/console-kit.c
+++ b/src/vdagentd/console-kit.c
@@ -162,10 +162,7 @@ struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb)
struct session_info *info;
GError *err = NULL;

- info = calloc(1, sizeof(*info));
- if (!info)
- return NULL;
-
+ info = g_new0(struct session_info, 1);
info->verbose = verbose;
info->session_is_locked = FALSE;
info->session_idle_hint = FALSE;
@@ -174,7 +171,7 @@ struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb)
if (err) {
syslog(LOG_ERR, "%s: %s", __func__, err->message);
g_error_free(err);
- free(info);
+ g_free(info);
return NULL;
}

@@ -194,9 +191,9 @@ void session_info_destroy(struct session_info *info)

si_dbus_signals_unsubscribe(info);
g_object_unref(info->dbus);
- free(info->seat);
- free(info->active_session);
- free(info);
+ g_free(info->seat);
+ g_free(info->active_session);
+ g_free(info);
}

/* Invoke a method on a remote object through DBus and wait for reply.
diff --git a/src/vdagentd/systemd-login.c b/src/vdagentd/systemd-login.c
index 0940230..7a9a13f 100644
--- a/src/vdagentd/systemd-login.c
+++ b/src/vdagentd/systemd-login.c
@@ -109,10 +109,7 @@ struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb)
struct session_info *si;
int r;

- si = calloc(1, sizeof(*si));
- if (!si)
- return NULL;
-
+ si = g_new0(struct session_info, 1);
si->verbose = verbose;
si->session_is_locked = FALSE;
si->session_change_cb = cb;
@@ -120,7 +117,7 @@ struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb)
r = sd_login_monitor_new("session", &si->mon);
if (r < 0) {
syslog(LOG_ERR, "Error creating login monitor: %s", strerror(-r));
- free(si);
+ g_free(si);
return NULL;
}

@@ -139,8 +136,8 @@ void session_info_destroy(struct session_info *si)
g_source_remove(si->io_watch_id);
g_io_channel_unref(si->io_channel);
sd_login_monitor_unref(si->mon);
- free(si->session);
- free(si);
+ g_free(si->session);
+ g_free(si);
}

const char *session_info_get_active_session(struct session_info *si)
@@ -160,7 +157,7 @@ const char *session_info_get_active_session(struct session_info *si)
syslog(LOG_INFO, "Active session: %s", si->session);

sd_login_monitor_flush(si->mon);
- free(old_session);
+ g_free(old_session);

si_dbus_proxy_update(si);
return si->session;
diff --git a/src/vdagentd/uinput.c b/src/vdagentd/uinput.c
index e2966c4..880a679 100644
--- a/src/vdagentd/uinput.c
+++ b/src/vdagentd/uinput.c
@@ -31,6 +31,7 @@
#include <linux/input.h>
#include <linux/uinput.h>
#include <spice/vd_agent.h>
+#include <glib.h>
#include "uinput.h"

struct vdagentd_uinput {
@@ -52,10 +53,7 @@ struct vdagentd_uinput *vdagentd_uinput_create(const char *devname,
{
struct vdagentd_uinput *uinput;

- uinput = calloc(1, sizeof(*uinput));
- if (!uinput)
- return NULL;
-
+ uinput = g_new0(struct vdagentd_uinput, 1);
uinput->devname = devname;
uinput->fd = -1; /* Gets opened by vdagentd_uinput_update_size() */
uinput->debug = debug;
@@ -76,8 +74,7 @@ void vdagentd_uinput_destroy(struct vdagentd_uinput **uinputp)

if (uinput->fd != -1)
close(uinput->fd);
- free(uinput);
- *uinputp = NULL;
+ g_clear_pointer(uinputp, g_free);
}

void vdagentd_uinput_update_size(struct vdagentd_uinput **uinputp,
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index 6d117a8..b8f617c 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -123,12 +123,7 @@ static void send_capabilities(struct vdagent_virtio_port *vport,
uint32_t size;

size = sizeof(*caps) + VD_AGENT_CAPS_BYTES;
- caps = calloc(1, size);
- if (!caps) {
- syslog(LOG_ERR, "out of memory allocating capabilities array (write)");
- return;
- }
-
+ caps = g_malloc0(size);
caps->request = request;
VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MOUSE_STATE);
VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG);
@@ -144,7 +139,7 @@ static void send_capabilities(struct vdagent_virtio_port *vport,
vdagent_virtio_port_write(vport, VDP_CLIENT_PORT,
VD_AGENT_ANNOUNCE_CAPABILITIES, 0,
(uint8_t *)caps, size);
- free(caps);
+ g_free(caps);
}

static void do_client_disconnect(void)
@@ -197,12 +192,8 @@ static void do_client_monitors(struct vdagent_virtio_port *vport, int port_nr,

if (!mon_config ||
mon_config->num_of_monitors != new_monitors->num_of_monitors) {
- free(mon_config);
- mon_config = malloc(size);
- if (!mon_config) {
- syslog(LOG_ERR, "out of memory allocating monitors config");
- return;
- }
+ g_free(mon_config);
+ mon_config = g_malloc(size);
}
memcpy(mon_config, new_monitors, size);

@@ -239,13 +230,8 @@ static void do_client_capabilities(struct vdagent_virtio_port *vport,

if (capabilities_size != new_size) {
capabilities_size = new_size;
- free(capabilities);
- capabilities = malloc(capabilities_size * sizeof(uint32_t));
- if (!capabilities) {
- syslog(LOG_ERR, "oom allocating capabilities array (read)");
- capabilities_size = 0;
- return;
- }
+ g_free(capabilities);
+ capabilities = g_malloc(capabilities_size * sizeof(uint32_t));
}
memcpy(capabilities, caps->caps, capabilities_size * sizeof(uint32_t));
if (caps->request) {
@@ -327,7 +313,7 @@ static void send_file_xfer_status(struct vdagent_virtio_port *vport,
data_size = 0;
}

- status = malloc(sizeof(*status) + data_size);
+ status = g_malloc(sizeof(*status) + data_size);
status->id = GUINT32_TO_LE(id);
status->result = GUINT32_TO_LE(xfer_status);
if (data)
@@ -341,7 +327,7 @@ static void send_file_xfer_status(struct vdagent_virtio_port *vport,
VD_AGENT_FILE_XFER_STATUS, 0,
(uint8_t *)status, sizeof(*status) + data_size);

- free(status);
+ g_free(status);
}

static void do_client_file_xfer(struct vdagent_virtio_port *vport,
@@ -875,13 +861,7 @@ static gboolean remove_active_xfers(gpointer key, gpointer value, gpointer conn)
static void agent_connect(struct udscs_connection *conn)
{
struct agent_data *agent_data;
-
- agent_data = calloc(1, sizeof(*agent_data));
- if (!agent_data) {
- syslog(LOG_ERR, "Out of memory allocating agent data, disconnecting");
- udscs_destroy_connection(&conn);
- return;
- }
+ agent_data = g_new0(struct agent_data, 1);

if (session_info) {
uint32_t pid = udscs_get_peer_pid(conn);
@@ -900,12 +880,11 @@ static void agent_disconnect(struct udscs_connection *conn)

g_hash_table_foreach_remove(active_xfers, remove_active_xfers, conn);

- free(agent_data->session);
- agent_data->session = NULL;
+ g_clear_pointer(&agent_data->session, g_free);
update_active_session_connection(NULL);

- free(agent_data->screen_info);
- free(agent_data);
+ g_free(agent_data->screen_info);
+ g_free(agent_data);
}

static void agent_read_complete(struct udscs_connection **connp,
@@ -934,12 +913,8 @@ static void agent_read_complete(struct udscs_connection **connp,
return;
}

- free(agent_data->screen_info);
- res = malloc(n * sizeof(*res));
- if (!res) {
- syslog(LOG_ERR, "out of memory allocating screen info");
- n = 0;
- }
+ g_free(agent_data->screen_info);
+ res = g_malloc(n * sizeof(*res));
memcpy(res, data, n * sizeof(*res));
agent_data->width = header->arg1;
agent_data->height = header->arg2;
@@ -1112,10 +1087,6 @@ int main(int argc, char *argv[])
/* Setup communication with vdagent process(es) */
server = udscs_server_new(agent_connect, agent_read_complete,
agent_disconnect, debug);
- if (server == NULL) {
- syslog(LOG_CRIT, "Fatal could not allocate memory for udscs server");
- return 1;
- }
#ifdef WITH_SYSTEMD_SOCKET_ACTIVATION
int n_fds;
/* try to retrieve pre-configured sockets from systemd */
diff --git a/src/vdagentd/virtio-port.c b/src/vdagentd/virtio-port.c
index 9731086..723d9ad 100644
--- a/src/vdagentd/virtio-port.c
+++ b/src/vdagentd/virtio-port.c
@@ -116,10 +116,7 @@ struct vdagent_virtio_port *vdagent_virtio_port_create(const char *portname,
return NULL;
}

- vport = calloc(1, sizeof(*vport));
- if (!vport)
- return 0;
-
+ vport = g_new0(struct vdagent_virtio_port, 1);
/* When calling vdagent_connection_new(),
* @wait_on_opening MUST be set to TRUE:
*
@@ -166,17 +163,16 @@ static void virtio_port_destroy(struct vdagent_virtio_port **vportp,
vport->disconnect_callback(vport, by_user);

if (vport->write_buf) {
- free(vport->write_buf->buf);
- free(vport->write_buf);
+ g_free(vport->write_buf->buf);
+ g_free(vport->write_buf);
}

for (i = 0; i < VDP_END_PORT; i++) {
- free(vport->port_data[i].message_data);
+ g_free(vport->port_data[i].message_data);
}

vdagent_connection_destroy(vport->conn);
- free(vport);
- *vportp = NULL;
+ g_clear_pointer(vportp, g_free);
}

void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
@@ -195,15 +191,12 @@ int vdagent_virtio_port_write_start(
VDIChunkHeader chunk_header;
VDAgentMessage message_header;

- new_wbuf = malloc(sizeof(*new_wbuf));
- if (!new_wbuf)
- return -1;
-
+ new_wbuf = g_new(struct vdagent_virtio_port_buf, 1);
new_wbuf->write_pos = 0;
new_wbuf->size = sizeof(chunk_header) + sizeof(message_header) + data_size;
- new_wbuf->buf = malloc(new_wbuf->size);
+ new_wbuf->buf =g_try_malloc(new_wbuf->size);
if (!new_wbuf->buf) {
- free(new_wbuf);
+ g_free(new_wbuf);
return -1;
}

@@ -249,7 +242,7 @@ int vdagent_virtio_port_write_append(struct vdagent_virtio_port *vport,

if (wbuf->write_pos == wbuf->size) {
vdagent_connection_write(vport->conn, wbuf->buf, wbuf->size);
- g_clear_pointer(&vport->write_buf, free);
+ g_clear_pointer(&vport->write_buf, g_free);
}
return 0;
}
@@ -282,7 +275,7 @@ void vdagent_virtio_port_reset(struct vdagent_virtio_port *vport, int port)
syslog(LOG_ERR, "vdagent_virtio_port_reset port out of range");
return;
}
- free(vport->port_data[port].message_data);
+ g_free(vport->port_data[port].message_data);
memset(&vport->port_data[port], 0, sizeof(vport->port_data[0]));
}

@@ -311,12 +304,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp,
port->message_header.size = GUINT32_FROM_LE(port->message_header.size);

if (port->message_header.size) {
- port->message_data = malloc(port->message_header.size);
- if (!port->message_data) {
- syslog(LOG_ERR, "out of memory, disconnecting virtio");
- virtio_port_destroy(vportp, FALSE);
- return;
- }
+ port->message_data = g_malloc(port->message_header.size);
}
}
pos = read;
@@ -352,8 +340,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp,
}
port->message_header_read = 0;
port->message_data_pos = 0;
- free(port->message_data);
- port->message_data = NULL;
+ g_clear_pointer(&port->message_data, g_free);
}
}
}
--
2.17.1
Victor Toso
2018-08-28 06:16:09 UTC
Permalink
Hi,

This one I would also take, together with Patch 0001, outside of
this series in order to reduce the patches a bit and focus more
on the Glib integration.

The memory functions in glib are not bounded to GMainLoop so it
should be fine.
Post by Jakub Janků
Some older parts of the code currently use
memory functions defined in stdlib.h
and usually handle allocation errors.
On the other hand, newer parts of the code
and GLib/GTK+ functions themselves commonly use
wrappers provided by GLib that terminate
the program when there isn't enough memory.
So it doesn't make much sense to check for
ENOMEM somewhere, while the program can be
terminated at any time elsewhere.
Unify the code and use GLib wrappers only.
malloc --> g_malloc, g_new
calloc --> g_malloc0, g_new0
free --> g_free
Use g_try_malloc only when allocating
message buffers, where the agent could
theoretically continue functioning even
after the allocation fails.
(e.g. when trying to send large
clipboard data buffer)
No, I don't think we should. For the clipboard example, the best
would be to either have a clipboard-size-limit (pretty sure there
is one) or we should use some limit in the spice-protocol itself.

The use case of g-try-malloc is for systems with small amount of
resources, such as embedded systems (IMHO).
Post by Jakub Janků
Use g_clear_pointer where possible
for simplification.
Yep. If possible, S-o-B too.

Small comment below.
Post by Jakub Janků
---
src/udscs.c | 23 ++++-----------
src/vdagent/x11-randr.c | 43 ++++++++++-----------------
src/vdagentd/console-kit.c | 13 ++++----
src/vdagentd/systemd-login.c | 13 ++++----
src/vdagentd/uinput.c | 9 ++----
src/vdagentd/vdagentd.c | 57 +++++++++---------------------------
src/vdagentd/virtio-port.c | 37 ++++++++---------------
7 files changed, 60 insertions(+), 135 deletions(-)
diff --git a/src/udscs.c b/src/udscs.c
index 0b80317..681e80f 100644
--- a/src/udscs.c
+++ b/src/udscs.c
@@ -101,9 +101,7 @@ struct udscs_connection *udscs_connect(const char *socketname,
if (io_stream == NULL)
return NULL;
- conn = calloc(1, sizeof(*conn));
- if (!conn)
- return NULL;
+ conn = g_new0(struct udscs_connection, 1);
conn->debug = debug;
conn->conn = vdagent_connection_new(io_stream,
FALSE,
@@ -141,8 +139,7 @@ void udscs_destroy_connection(struct udscs_connection **connp)
if (conn->debug)
syslog(LOG_DEBUG, "%p disconnected", conn);
- free(conn);
- *connp = NULL;
+ g_clear_pointer(connp, g_free);
}
void udscs_set_user_data(struct udscs_connection *conn, void *data)
@@ -166,7 +163,7 @@ int udscs_write(struct udscs_connection *conn, uint32_t type, uint32_t arg1,
struct udscs_message_header header;
buff_size = sizeof(header) + size;
- buff = malloc(buff_size);
+ buff = g_try_malloc(buff_size);
if (buff == NULL)
return -1;
@@ -212,10 +209,7 @@ struct udscs_server *udscs_server_new(
{
struct udscs_server *server;
- server = calloc(1, sizeof(*server));
- if (!server)
- return NULL;
-
+ server = g_new0(struct udscs_server, 1);
server->debug = debug;
server->connect_callback = connect_callback;
server->read_callback = read_callback;
@@ -282,7 +276,7 @@ void udscs_destroy_server(struct udscs_server *server)
conn = next_conn;
}
g_object_unref(server->service);
- free(server);
+ g_free(server);
}
int udscs_get_peer_pid(struct udscs_connection *conn)
@@ -299,12 +293,7 @@ static gboolean udscs_server_accept_cb(GSocketService *service,
struct udscs_server *server = user_data;
struct udscs_connection *new_conn, *conn;
- new_conn = calloc(1, sizeof(*conn));
- if (!new_conn) {
- syslog(LOG_ERR, "out of memory, disconnecting new client");
- return TRUE;
- }
-
+ new_conn = g_new0(struct udscs_connection, 1);
new_conn->debug = server->debug;
new_conn->read_callback = server->read_callback;
new_conn->disconnect_callback = server->disconnect_callback;
diff --git a/src/vdagent/x11-randr.c b/src/vdagent/x11-randr.c
index 803cf73..29ce6fc 100644
--- a/src/vdagent/x11-randr.c
+++ b/src/vdagent/x11-randr.c
@@ -81,13 +81,13 @@ static void free_randr_resources(struct vdagent_x11 *x11)
for (i = 0 ; i < x11->randr.res->noutput; ++i) {
XRRFreeOutputInfo(x11->randr.outputs[i]);
}
- free(x11->randr.outputs);
+ g_free(x11->randr.outputs);
}
if (x11->randr.crtcs != NULL) {
for (i = 0 ; i < x11->randr.res->ncrtc; ++i) {
XRRFreeCrtcInfo(x11->randr.crtcs[i]);
}
- free(x11->randr.crtcs);
+ g_free(x11->randr.crtcs);
}
XRRFreeScreenResources(x11->randr.res);
x11->randr.res = NULL;
@@ -105,8 +105,8 @@ static void update_randr_res(struct vdagent_x11 *x11, int poll)
x11->randr.res = XRRGetScreenResources(x11->display, x11->root_window[0]);
else
x11->randr.res = XRRGetScreenResourcesCurrent(x11->display, x11->root_window[0]);
- x11->randr.outputs = malloc(x11->randr.res->noutput * sizeof(*x11->randr.outputs));
- x11->randr.crtcs = malloc(x11->randr.res->ncrtc * sizeof(*x11->randr.crtcs));
+ x11->randr.outputs = g_malloc(x11->randr.res->noutput * sizeof(*x11->randr.outputs));
+ x11->randr.crtcs = g_malloc(x11->randr.res->ncrtc * sizeof(*x11->randr.crtcs));
for (i = 0 ; i < x11->randr.res->noutput; ++i) {
x11->randr.outputs[i] = XRRGetOutputInfo(x11->display, x11->randr.res,
x11->randr.res->outputs[i]);
@@ -696,7 +696,7 @@ static VDAgentMonitorsConfig *get_current_mon_config(struct vdagent_x11 *x11)
syslog(LOG_ERR, "error: inconsistent or stale data from X");
- free(mon_config);
+ g_free(mon_config);
return NULL;
}
@@ -844,12 +844,12 @@ void vdagent_x11_set_monitor_config(struct vdagent_x11 *x11,
if (!fallback) {
syslog(LOG_WARNING, "Restoring previous config");
vdagent_x11_set_monitor_config(x11, curr, 1);
- free(curr);
+ g_free(curr);
/* Remember this config failed, if the client is maximized or
fullscreen it will keep sending the failing config. */
- free(x11->randr.failed_conf);
+ g_free(x11->randr.failed_conf);
x11->randr.failed_conf =
- malloc(config_size(mon_config->num_of_monitors));
+ g_malloc(config_size(mon_config->num_of_monitors));
if (x11->randr.failed_conf)
memcpy(x11->randr.failed_conf, mon_config,
config_size(mon_config->num_of_monitors));
/* Flush output buffers and consume any pending events */
vdagent_x11_do_read(x11);
- free(curr);
+ g_free(curr);
}
void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11, int update)
@@ -914,11 +914,7 @@ void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11, int update)
goto no_info;
screen_count = curr->num_of_monitors;
- res = malloc(screen_count * sizeof(*res));
- if (!res) {
- free(curr);
- goto no_mem;
- }
+ res = g_malloc(screen_count * sizeof(*res));
for (i = 0; i < screen_count; i++) {
res[i].width = curr->monitors[i].width;
@@ -926,7 +922,7 @@ void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11, int update)
res[i].x = curr->monitors[i].x;
res[i].y = curr->monitors[i].y;
}
- free(curr);
+ g_free(curr);
width = x11->width[0];
height = x11->height[0];
} else if (x11->has_xinerama) {
@@ -935,17 +931,13 @@ void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11, int update)
screen_info = XineramaQueryScreens(x11->display, &screen_count);
if (!screen_info)
goto no_info;
- res = malloc(screen_count * sizeof(*res));
- if (!res) {
- XFree(screen_info);
- goto no_mem;
- }
+ res = g_malloc(screen_count * sizeof(*res));
for (i = 0; i < screen_count; i++) {
if (screen_info[i].screen_number >= screen_count) {
syslog(LOG_ERR, "Invalid screen number in xinerama screen info (%d >= %d)",
screen_info[i].screen_number, screen_count);
XFree(screen_info);
- free(res);
+ g_free(res);
return;
}
res[screen_info[i].screen_number].width = screen_info[i].width;
@@ -959,9 +951,7 @@ void vdagent_x11_send_daemon_guest_xorg_res(struct vdagent_x11 *x11, int update)
} else {
screen_count = x11->screen_count;
- res = malloc(screen_count * sizeof(*res));
- if (!res)
- goto no_mem;
+ res = g_malloc(screen_count * sizeof(*res));
for (i = 0; i < screen_count; i++) {
res[i].width = x11->width[i];
res[i].height = x11->height[i];
udscs_write(x11->vdagentd, VDAGENTD_GUEST_XORG_RESOLUTION, width, height,
(uint8_t *)res, screen_count * sizeof(*res));
- free(res);
- return;
- syslog(LOG_ERR, "out of memory while trying to send resolutions, not sending resolutions.");
+ g_free(res);
}
diff --git a/src/vdagentd/console-kit.c b/src/vdagentd/console-kit.c
index 390491e..8094fa9 100644
--- a/src/vdagentd/console-kit.c
+++ b/src/vdagentd/console-kit.c
@@ -162,10 +162,7 @@ struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb)
struct session_info *info;
GError *err = NULL;
- info = calloc(1, sizeof(*info));
- if (!info)
- return NULL;
-
+ info = g_new0(struct session_info, 1);
info->verbose = verbose;
info->session_is_locked = FALSE;
info->session_idle_hint = FALSE;
@@ -174,7 +171,7 @@ struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb)
if (err) {
syslog(LOG_ERR, "%s: %s", __func__, err->message);
g_error_free(err);
- free(info);
+ g_free(info);
return NULL;
}
@@ -194,9 +191,9 @@ void session_info_destroy(struct session_info *info)
si_dbus_signals_unsubscribe(info);
g_object_unref(info->dbus);
- free(info->seat);
- free(info->active_session);
- free(info);
+ g_free(info->seat);
+ g_free(info->active_session);
+ g_free(info);
}
/* Invoke a method on a remote object through DBus and wait for reply.
diff --git a/src/vdagentd/systemd-login.c b/src/vdagentd/systemd-login.c
index 0940230..7a9a13f 100644
--- a/src/vdagentd/systemd-login.c
+++ b/src/vdagentd/systemd-login.c
@@ -109,10 +109,7 @@ struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb)
struct session_info *si;
int r;
- si = calloc(1, sizeof(*si));
- if (!si)
- return NULL;
-
+ si = g_new0(struct session_info, 1);
si->verbose = verbose;
si->session_is_locked = FALSE;
si->session_change_cb = cb;
@@ -120,7 +117,7 @@ struct session_info *session_info_create(int verbose, ActiveSessionChangeCb cb)
r = sd_login_monitor_new("session", &si->mon);
if (r < 0) {
syslog(LOG_ERR, "Error creating login monitor: %s", strerror(-r));
- free(si);
+ g_free(si);
return NULL;
}
@@ -139,8 +136,8 @@ void session_info_destroy(struct session_info *si)
g_source_remove(si->io_watch_id);
g_io_channel_unref(si->io_channel);
sd_login_monitor_unref(si->mon);
- free(si->session);
- free(si);
+ g_free(si->session);
+ g_free(si);
}
const char *session_info_get_active_session(struct session_info *si)
@@ -160,7 +157,7 @@ const char *session_info_get_active_session(struct session_info *si)
syslog(LOG_INFO, "Active session: %s", si->session);
sd_login_monitor_flush(si->mon);
- free(old_session);
+ g_free(old_session);
si_dbus_proxy_update(si);
return si->session;
diff --git a/src/vdagentd/uinput.c b/src/vdagentd/uinput.c
index e2966c4..880a679 100644
--- a/src/vdagentd/uinput.c
+++ b/src/vdagentd/uinput.c
@@ -31,6 +31,7 @@
#include <linux/input.h>
#include <linux/uinput.h>
#include <spice/vd_agent.h>
+#include <glib.h>
#include "uinput.h"
struct vdagentd_uinput {
@@ -52,10 +53,7 @@ struct vdagentd_uinput *vdagentd_uinput_create(const char *devname,
{
struct vdagentd_uinput *uinput;
- uinput = calloc(1, sizeof(*uinput));
- if (!uinput)
- return NULL;
-
+ uinput = g_new0(struct vdagentd_uinput, 1);
uinput->devname = devname;
uinput->fd = -1; /* Gets opened by vdagentd_uinput_update_size() */
uinput->debug = debug;
@@ -76,8 +74,7 @@ void vdagentd_uinput_destroy(struct vdagentd_uinput **uinputp)
if (uinput->fd != -1)
close(uinput->fd);
- free(uinput);
- *uinputp = NULL;
+ g_clear_pointer(uinputp, g_free);
}
void vdagentd_uinput_update_size(struct vdagentd_uinput **uinputp,
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index 6d117a8..b8f617c 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -123,12 +123,7 @@ static void send_capabilities(struct vdagent_virtio_port *vport,
uint32_t size;
size = sizeof(*caps) + VD_AGENT_CAPS_BYTES;
- caps = calloc(1, size);
- if (!caps) {
- syslog(LOG_ERR, "out of memory allocating capabilities array (write)");
- return;
- }
-
+ caps = g_malloc0(size);
caps->request = request;
VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MOUSE_STATE);
VD_AGENT_SET_CAPABILITY(caps->caps, VD_AGENT_CAP_MONITORS_CONFIG);
@@ -144,7 +139,7 @@ static void send_capabilities(struct vdagent_virtio_port *vport,
vdagent_virtio_port_write(vport, VDP_CLIENT_PORT,
VD_AGENT_ANNOUNCE_CAPABILITIES, 0,
(uint8_t *)caps, size);
- free(caps);
+ g_free(caps);
}
static void do_client_disconnect(void)
@@ -197,12 +192,8 @@ static void do_client_monitors(struct vdagent_virtio_port *vport, int port_nr,
if (!mon_config ||
mon_config->num_of_monitors != new_monitors->num_of_monitors) {
- free(mon_config);
- mon_config = malloc(size);
- if (!mon_config) {
- syslog(LOG_ERR, "out of memory allocating monitors config");
- return;
- }
+ g_free(mon_config);
+ mon_config = g_malloc(size);
}
memcpy(mon_config, new_monitors, size);
@@ -239,13 +230,8 @@ static void do_client_capabilities(struct vdagent_virtio_port *vport,
if (capabilities_size != new_size) {
capabilities_size = new_size;
- free(capabilities);
- capabilities = malloc(capabilities_size * sizeof(uint32_t));
- if (!capabilities) {
- syslog(LOG_ERR, "oom allocating capabilities array (read)");
- capabilities_size = 0;
- return;
- }
+ g_free(capabilities);
+ capabilities = g_malloc(capabilities_size * sizeof(uint32_t));
}
memcpy(capabilities, caps->caps, capabilities_size * sizeof(uint32_t));
if (caps->request) {
@@ -327,7 +313,7 @@ static void send_file_xfer_status(struct vdagent_virtio_port *vport,
data_size = 0;
}
- status = malloc(sizeof(*status) + data_size);
+ status = g_malloc(sizeof(*status) + data_size);
status->id = GUINT32_TO_LE(id);
status->result = GUINT32_TO_LE(xfer_status);
if (data)
@@ -341,7 +327,7 @@ static void send_file_xfer_status(struct vdagent_virtio_port *vport,
VD_AGENT_FILE_XFER_STATUS, 0,
(uint8_t *)status, sizeof(*status) + data_size);
- free(status);
+ g_free(status);
}
static void do_client_file_xfer(struct vdagent_virtio_port *vport,
@@ -875,13 +861,7 @@ static gboolean remove_active_xfers(gpointer key, gpointer value, gpointer conn)
static void agent_connect(struct udscs_connection *conn)
{
struct agent_data *agent_data;
-
- agent_data = calloc(1, sizeof(*agent_data));
- if (!agent_data) {
- syslog(LOG_ERR, "Out of memory allocating agent data, disconnecting");
- udscs_destroy_connection(&conn);
- return;
- }
+ agent_data = g_new0(struct agent_data, 1);
if (session_info) {
uint32_t pid = udscs_get_peer_pid(conn);
@@ -900,12 +880,11 @@ static void agent_disconnect(struct udscs_connection *conn)
g_hash_table_foreach_remove(active_xfers, remove_active_xfers, conn);
- free(agent_data->session);
- agent_data->session = NULL;
+ g_clear_pointer(&agent_data->session, g_free);
update_active_session_connection(NULL);
- free(agent_data->screen_info);
- free(agent_data);
+ g_free(agent_data->screen_info);
+ g_free(agent_data);
}
static void agent_read_complete(struct udscs_connection **connp,
@@ -934,12 +913,8 @@ static void agent_read_complete(struct udscs_connection **connp,
return;
}
- free(agent_data->screen_info);
- res = malloc(n * sizeof(*res));
- if (!res) {
- syslog(LOG_ERR, "out of memory allocating screen info");
- n = 0;
- }
+ g_free(agent_data->screen_info);
+ res = g_malloc(n * sizeof(*res));
memcpy(res, data, n * sizeof(*res));
agent_data->width = header->arg1;
agent_data->height = header->arg2;
@@ -1112,10 +1087,6 @@ int main(int argc, char *argv[])
/* Setup communication with vdagent process(es) */
server = udscs_server_new(agent_connect, agent_read_complete,
agent_disconnect, debug);
- if (server == NULL) {
- syslog(LOG_CRIT, "Fatal could not allocate memory for udscs server");
- return 1;
- }
#ifdef WITH_SYSTEMD_SOCKET_ACTIVATION
int n_fds;
/* try to retrieve pre-configured sockets from systemd */
diff --git a/src/vdagentd/virtio-port.c b/src/vdagentd/virtio-port.c
index 9731086..723d9ad 100644
--- a/src/vdagentd/virtio-port.c
+++ b/src/vdagentd/virtio-port.c
@@ -116,10 +116,7 @@ struct vdagent_virtio_port *vdagent_virtio_port_create(const char *portname,
return NULL;
}
- vport = calloc(1, sizeof(*vport));
- if (!vport)
- return 0;
-
+ vport = g_new0(struct vdagent_virtio_port, 1);
/* When calling vdagent_connection_new(),
*
@@ -166,17 +163,16 @@ static void virtio_port_destroy(struct vdagent_virtio_port **vportp,
vport->disconnect_callback(vport, by_user);
if (vport->write_buf) {
- free(vport->write_buf->buf);
- free(vport->write_buf);
+ g_free(vport->write_buf->buf);
+ g_free(vport->write_buf);
}
for (i = 0; i < VDP_END_PORT; i++) {
- free(vport->port_data[i].message_data);
+ g_free(vport->port_data[i].message_data);
}
vdagent_connection_destroy(vport->conn);
- free(vport);
- *vportp = NULL;
+ g_clear_pointer(vportp, g_free);
}
void vdagent_virtio_port_destroy(struct vdagent_virtio_port **vportp)
@@ -195,15 +191,12 @@ int vdagent_virtio_port_write_start(
VDIChunkHeader chunk_header;
VDAgentMessage message_header;
- new_wbuf = malloc(sizeof(*new_wbuf));
- if (!new_wbuf)
- return -1;
-
+ new_wbuf = g_new(struct vdagent_virtio_port_buf, 1);
new_wbuf->write_pos = 0;
new_wbuf->size = sizeof(chunk_header) + sizeof(message_header) + data_size;
- new_wbuf->buf = malloc(new_wbuf->size);
+ new_wbuf->buf =g_try_malloc(new_wbuf->size);
if (!new_wbuf->buf) {
- free(new_wbuf);
+ g_free(new_wbuf);
return -1;
Functions like this one, we can remove the return value after
using g_malloc() as that would abort() in case of failure
(ENOMEM). In that case, a patch for that function only would be
easier to review/revert if necessary.

Reviewed-by: Victor Toso <***@redhat.com>

Cheers,
Victor
Post by Jakub Janků
}
@@ -249,7 +242,7 @@ int vdagent_virtio_port_write_append(struct vdagent_virtio_port *vport,
if (wbuf->write_pos == wbuf->size) {
vdagent_connection_write(vport->conn, wbuf->buf, wbuf->size);
- g_clear_pointer(&vport->write_buf, free);
+ g_clear_pointer(&vport->write_buf, g_free);
}
return 0;
}
@@ -282,7 +275,7 @@ void vdagent_virtio_port_reset(struct vdagent_virtio_port *vport, int port)
syslog(LOG_ERR, "vdagent_virtio_port_reset port out of range");
return;
}
- free(vport->port_data[port].message_data);
+ g_free(vport->port_data[port].message_data);
memset(&vport->port_data[port], 0, sizeof(vport->port_data[0]));
}
@@ -311,12 +304,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp,
port->message_header.size = GUINT32_FROM_LE(port->message_header.size);
if (port->message_header.size) {
- port->message_data = malloc(port->message_header.size);
- if (!port->message_data) {
- syslog(LOG_ERR, "out of memory, disconnecting virtio");
- virtio_port_destroy(vportp, FALSE);
- return;
- }
+ port->message_data = g_malloc(port->message_header.size);
}
}
pos = read;
@@ -352,8 +340,7 @@ static void vdagent_virtio_port_do_chunk(struct vdagent_virtio_port **vportp,
}
port->message_header_read = 0;
port->message_data_pos = 0;
- free(port->message_data);
- port->message_data = NULL;
+ g_clear_pointer(&port->message_data, g_free);
}
}
}
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Jakub Janků
2018-08-14 18:53:51 UTC
Permalink
Move code handling VDAGENTD_GUEST_XORG_RESOLUTION
from agent_read_complete() to a separate function
to make the switch statement less cluttered.

Remove unnecessary res variable.
---
src/vdagentd/vdagentd.c | 61 +++++++++++++++++++++--------------------
1 file changed, 31 insertions(+), 30 deletions(-)

diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index b8f617c..e975abd 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -887,43 +887,44 @@ static void agent_disconnect(struct udscs_connection *conn)
g_free(agent_data);
}

-static void agent_read_complete(struct udscs_connection **connp,
- struct udscs_message_header *header, uint8_t *data)
+static void do_guest_xorg_resolution(struct udscs_connection **connp,
+ struct udscs_message_header *header,
+ guint8 *data)
{
struct agent_data *agent_data = udscs_get_user_data(*connp);
+ guint n = header->size / sizeof(struct vdagentd_guest_xorg_resolution);

- switch (header->type) {
- case VDAGENTD_GUEST_XORG_RESOLUTION: {
- struct vdagentd_guest_xorg_resolution *res;
- int n = header->size / sizeof(*res);
-
- /* Detect older version session agent, but don't disconnect, as
- that stops it from getting the VDAGENTD_VERSION message, and then
- it will never re-exec the new version... */
- if (header->arg1 == 0 && header->arg2 == 0) {
- syslog(LOG_INFO, "got old session agent xorg resolution message, "
- "ignoring");
- return;
- }
+ /* Detect older version session agent, but don't disconnect, as
+ * that stops it from getting the VDAGENTD_VERSION message, and then
+ * it will never re-exec the new version... */
+ if (header->arg1 == 0 && header->arg2 == 0) {
+ syslog(LOG_INFO, "got old session agent xorg resolution message, ignoring");
+ return;
+ }

- if (header->size != n * sizeof(*res)) {
- syslog(LOG_ERR, "guest xorg resolution message has wrong size, "
- "disconnecting agent");
- udscs_destroy_connection(connp);
- return;
- }
+ if (header->size != n * sizeof(struct vdagentd_guest_xorg_resolution)) {
+ syslog(LOG_ERR, "guest xorg resolution message has wrong size, "
+ "disconnecting agent");
+ udscs_destroy_connection(connp);
+ return;
+ }
+
+ g_free(agent_data->screen_info);
+ agent_data->screen_info = g_memdup(data, header->size);
+ agent_data->width = header->arg1;
+ agent_data->height = header->arg2;
+ agent_data->screen_count = n;

- g_free(agent_data->screen_info);
- res = g_malloc(n * sizeof(*res));
- memcpy(res, data, n * sizeof(*res));
- agent_data->width = header->arg1;
- agent_data->height = header->arg2;
- agent_data->screen_info = res;
- agent_data->screen_count = n;
+ check_xorg_resolution();
+}

- check_xorg_resolution();
+static void agent_read_complete(struct udscs_connection **connp,
+ struct udscs_message_header *header, uint8_t *data)
+{
+ switch (header->type) {
+ case VDAGENTD_GUEST_XORG_RESOLUTION:
+ do_guest_xorg_resolution(connp, header, data);
break;
- }
case VDAGENTD_CLIPBOARD_GRAB:
case VDAGENTD_CLIPBOARD_REQUEST:
case VDAGENTD_CLIPBOARD_DATA:
--
2.17.1
Victor Toso
2018-08-28 06:24:20 UTC
Permalink
Hi,
Post by Jakub Janků
Move code handling VDAGENTD_GUEST_XORG_RESOLUTION
from agent_read_complete() to a separate function
to make the switch statement less cluttered.
Remove unnecessary res variable.
Please, send this outside of glib integration series, I'd be
happy to apply before :)

Reviewed-by: Victor Toso <***@redhat.com>

Victor
Post by Jakub Janků
---
src/vdagentd/vdagentd.c | 61 +++++++++++++++++++++--------------------
1 file changed, 31 insertions(+), 30 deletions(-)
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index b8f617c..e975abd 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -887,43 +887,44 @@ static void agent_disconnect(struct udscs_connection *conn)
g_free(agent_data);
}
-static void agent_read_complete(struct udscs_connection **connp,
- struct udscs_message_header *header, uint8_t *data)
+static void do_guest_xorg_resolution(struct udscs_connection **connp,
+ struct udscs_message_header *header,
+ guint8 *data)
{
struct agent_data *agent_data = udscs_get_user_data(*connp);
+ guint n = header->size / sizeof(struct vdagentd_guest_xorg_resolution);
- switch (header->type) {
- case VDAGENTD_GUEST_XORG_RESOLUTION: {
- struct vdagentd_guest_xorg_resolution *res;
- int n = header->size / sizeof(*res);
-
- /* Detect older version session agent, but don't disconnect, as
- that stops it from getting the VDAGENTD_VERSION message, and then
- it will never re-exec the new version... */
- if (header->arg1 == 0 && header->arg2 == 0) {
- syslog(LOG_INFO, "got old session agent xorg resolution message, "
- "ignoring");
- return;
- }
+ /* Detect older version session agent, but don't disconnect, as
+ * that stops it from getting the VDAGENTD_VERSION message, and then
+ * it will never re-exec the new version... */
+ if (header->arg1 == 0 && header->arg2 == 0) {
+ syslog(LOG_INFO, "got old session agent xorg resolution message, ignoring");
+ return;
+ }
- if (header->size != n * sizeof(*res)) {
- syslog(LOG_ERR, "guest xorg resolution message has wrong size, "
- "disconnecting agent");
- udscs_destroy_connection(connp);
- return;
- }
+ if (header->size != n * sizeof(struct vdagentd_guest_xorg_resolution)) {
+ syslog(LOG_ERR, "guest xorg resolution message has wrong size, "
+ "disconnecting agent");
+ udscs_destroy_connection(connp);
+ return;
+ }
+
+ g_free(agent_data->screen_info);
+ agent_data->screen_info = g_memdup(data, header->size);
+ agent_data->width = header->arg1;
+ agent_data->height = header->arg2;
+ agent_data->screen_count = n;
- g_free(agent_data->screen_info);
- res = g_malloc(n * sizeof(*res));
- memcpy(res, data, n * sizeof(*res));
- agent_data->width = header->arg1;
- agent_data->height = header->arg2;
- agent_data->screen_info = res;
- agent_data->screen_count = n;
+ check_xorg_resolution();
+}
- check_xorg_resolution();
+static void agent_read_complete(struct udscs_connection **connp,
+ struct udscs_message_header *header, uint8_t *data)
+{
+ switch (header->type) {
+ do_guest_xorg_resolution(connp, header, data);
break;
- }
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Jakub Janků
2018-08-14 18:53:52 UTC
Permalink
Move code handling VDAGENTD_FILE_XFER_STATUS
from agent_read_complete() to a separate function
to make the switch statement less cluttered.

Improve formatting a bit.
---
src/vdagentd/vdagentd.c | 61 +++++++++++++++++++++++++----------------
1 file changed, 37 insertions(+), 24 deletions(-)

diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index e975abd..56c82b2 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -918,6 +918,41 @@ static void do_guest_xorg_resolution(struct udscs_connection **connp,
check_xorg_resolution();
}

+static void do_agent_file_xfer_status(struct udscs_connection **connp,
+ struct udscs_message_header *header,
+ guint8 *data)
+{
+ gpointer task_id = GUINT_TO_POINTER(GUINT32_TO_LE(header->arg1));
+
+ /* header->arg1 = file xfer task id, header->arg2 = file xfer status */
+ switch (header->arg2) {
+ case VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE: {
+ uint64_t free_space = GUINT64_TO_LE(*((uint64_t*)data));
+ send_file_xfer_status(virtio_port,
+ "Not enough free space. Cancelling file-xfer %u",
+ header->arg1, header->arg2,
+ (uint8_t*)&free_space, sizeof(uint64_t));
+ break;
+ }
+ case VD_AGENT_FILE_XFER_STATUS_DISABLED:
+ send_file_xfer_status(virtio_port,
+ "File-xfer is disabled. Cancelling file-xfer %u",
+ header->arg1, header->arg2,
+ NULL, 0);
+ break;
+ default:
+ send_file_xfer_status(virtio_port,
+ NULL,
+ header->arg1, header->arg2,
+ NULL, 0);
+ }
+
+ if (header->arg2 == VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA)
+ g_hash_table_insert(active_xfers, task_id, *connp);
+ else
+ g_hash_table_remove(active_xfers, task_id);
+}
+
static void agent_read_complete(struct udscs_connection **connp,
struct udscs_message_header *header, uint8_t *data)
{
@@ -934,31 +969,9 @@ static void agent_read_complete(struct udscs_connection **connp,
return;
}
break;
- case VDAGENTD_FILE_XFER_STATUS:{
- /* header->arg1 = file xfer task id, header->arg2 = file xfer status */
- switch (header->arg2) {
- case VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE: {
- uint64_t free_space = GUINT64_TO_LE(*((uint64_t*)data));
- send_file_xfer_status(virtio_port, "Not enough free space. Cancelling file-xfer %u",
- header->arg1, header->arg2,
- (uint8_t*)&free_space, sizeof(uint64_t));
- break;
- }
- case VD_AGENT_FILE_XFER_STATUS_DISABLED:
- send_file_xfer_status(virtio_port, "File-xfer is disabled, cancelling",
- header->arg1, header->arg2, NULL, 0);
- break;
- default:
- send_file_xfer_status(virtio_port, NULL, header->arg1, header->arg2, NULL, 0);
- }
-
- if (header->arg2 == VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA)
- g_hash_table_insert(active_xfers, GUINT_TO_POINTER(GUINT32_TO_LE(header->arg1)),
- *connp);
- else
- g_hash_table_remove(active_xfers, GUINT_TO_POINTER(GUINT32_TO_LE(header->arg1)));
+ case VDAGENTD_FILE_XFER_STATUS:
+ do_agent_file_xfer_status(connp, header, data);
break;
- }

default:
syslog(LOG_ERR, "unknown message from vdagent: %u, ignoring",
--
2.17.1
Victor Toso
2018-08-28 06:25:20 UTC
Permalink
Hi,
Post by Jakub Janků
Move code handling VDAGENTD_FILE_XFER_STATUS
from agent_read_complete() to a separate function
to make the switch statement less cluttered.
Improve formatting a bit.
If you send this outside of glib integration series, I'd be happy
to apply before the glib integration too

Reviewed-by: Victor Toso <***@redhat.com>

Victor
Post by Jakub Janků
---
src/vdagentd/vdagentd.c | 61 +++++++++++++++++++++++++----------------
1 file changed, 37 insertions(+), 24 deletions(-)
diff --git a/src/vdagentd/vdagentd.c b/src/vdagentd/vdagentd.c
index e975abd..56c82b2 100644
--- a/src/vdagentd/vdagentd.c
+++ b/src/vdagentd/vdagentd.c
@@ -918,6 +918,41 @@ static void do_guest_xorg_resolution(struct udscs_connection **connp,
check_xorg_resolution();
}
+static void do_agent_file_xfer_status(struct udscs_connection **connp,
+ struct udscs_message_header *header,
+ guint8 *data)
+{
+ gpointer task_id = GUINT_TO_POINTER(GUINT32_TO_LE(header->arg1));
+
+ /* header->arg1 = file xfer task id, header->arg2 = file xfer status */
+ switch (header->arg2) {
+ case VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE: {
+ uint64_t free_space = GUINT64_TO_LE(*((uint64_t*)data));
+ send_file_xfer_status(virtio_port,
+ "Not enough free space. Cancelling file-xfer %u",
+ header->arg1, header->arg2,
+ (uint8_t*)&free_space, sizeof(uint64_t));
+ break;
+ }
+ send_file_xfer_status(virtio_port,
+ "File-xfer is disabled. Cancelling file-xfer %u",
+ header->arg1, header->arg2,
+ NULL, 0);
+ break;
+ send_file_xfer_status(virtio_port,
+ NULL,
+ header->arg1, header->arg2,
+ NULL, 0);
+ }
+
+ if (header->arg2 == VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA)
+ g_hash_table_insert(active_xfers, task_id, *connp);
+ else
+ g_hash_table_remove(active_xfers, task_id);
+}
+
static void agent_read_complete(struct udscs_connection **connp,
struct udscs_message_header *header, uint8_t *data)
{
@@ -934,31 +969,9 @@ static void agent_read_complete(struct udscs_connection **connp,
return;
}
break;
- case VDAGENTD_FILE_XFER_STATUS:{
- /* header->arg1 = file xfer task id, header->arg2 = file xfer status */
- switch (header->arg2) {
- case VD_AGENT_FILE_XFER_STATUS_NOT_ENOUGH_SPACE: {
- uint64_t free_space = GUINT64_TO_LE(*((uint64_t*)data));
- send_file_xfer_status(virtio_port, "Not enough free space. Cancelling file-xfer %u",
- header->arg1, header->arg2,
- (uint8_t*)&free_space, sizeof(uint64_t));
- break;
- }
- send_file_xfer_status(virtio_port, "File-xfer is disabled, cancelling",
- header->arg1, header->arg2, NULL, 0);
- break;
- send_file_xfer_status(virtio_port, NULL, header->arg1, header->arg2, NULL, 0);
- }
-
- if (header->arg2 == VD_AGENT_FILE_XFER_STATUS_CAN_SEND_DATA)
- g_hash_table_insert(active_xfers, GUINT_TO_POINTER(GUINT32_TO_LE(header->arg1)),
- *connp);
- else
- g_hash_table_remove(active_xfers, GUINT_TO_POINTER(GUINT32_TO_LE(header->arg1)));
+ do_agent_file_xfer_status(connp, header, data);
break;
- }
syslog(LOG_ERR, "unknown message from vdagent: %u, ignoring",
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Victor Toso
2018-08-28 07:33:25 UTC
Permalink
Hi,

First of all, thanks for your work on this :)

I'm still looking into the glib-integration related patches so I
might still reply once more afterwards. To keep things easy to
review, I take we could split this series in three different set
of patches.

- The cleanup code patches, including using glib's memory
function, should be easy to review an apply

- Glib integration patches, might take some interactions as some
careful review/testing are needed

- GDBus integration - Shouldn't be hard to review/test but it
depends on the Glib one.

Would you mind splitting them?
Post by Jakub Janků
Hi all,
as the subject suggests, this series further deepens the
integration of GLib into spice-vdagent(d).
* udscs.c and virtio-port.c are built upon common base,
VDAgentConnection, that handles the lower-level I/O operations
* systemd-login.c and console-kit.c use GDBus instead of libdbus
* vdagentd.c uses GMainLoop and GLib's commandline option parser
Some patches are rather unrelated and try to clean up the code
a bit.
vdagent_connection_destroy() is in most cases asynchronous (due
to the way GCancellable works) and it can therefore take
multiple iterations of GMainLoop until the memory associated
with VDAgentConnection is freed and the underlying file
descriptor gets closed.
This is fine as long as the GMainLoop runs. However, the
current code assumes, that the call to
udscs_destroy_connection() and vdagent_virtio_port_destroy()
are synchronous and invokes these functions even after the
GMainLoop quits.
As a result, the memory allocated by VDAgentConnection might
not be explicitely freed and the FD might not be closed, when
the spice-vdagent(d) exits.
I'm wondering if making VDAgentConnection into a GObject would
solve the problem? The vdagent_connection_destroy() would be made
into g_object_unref(vdagent_connection); When the object is being
cleanup, that's split into Dispose and Finalize cleaning methods.

So, in the end of GMainLoop, the VDAgentConnection should always
be correctly finished.

Note that this suggestion is also around how the code has been
wrote. If you have only one reference to VDAgentConnection, you
have to be careful when you can dealloc its resources but if you
_ref() before an operation and _unref() after, you can consider
that the memory will be there in the callback, simple example:

You have:

g_input_stream_read_all_async(in,
conn->header_buff,
conn->header_size,
G_PRIORITY_DEFAULT,
conn->cancellable,
message_read_cb,
conn);

and in message_read_cb()

VDAgentConnection *conn = user_data;
...
g_input_stream_read_all_finish(in, res, &bytes_read, &err);
if (err)
return handle_io_error(conn, err);

where handle_io_error() is doing:

if (g_cancellable_is_cancelled(conn->cancellable)) {
if (!connection_has_pending(conn))
connection_finalize(conn);


So you have a check to Cancel and after that a
connection_finalize(), to handle the situation of 'have to
cleanup my memory right'

Instead, you could have a VDAgentConnection object and the
initial code would change to:


g_input_stream_read_all_async(in,
...
message_read_cb,
g_object_ref(conn));

and in message_read_cb()

VDAgentConnection *conn = user_data;
...
g_input_stream_read_all_finish(in, res, &bytes_read, &err);
if (err) {
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
/* print/deal with error message/values */
}
/* If needed, this will cleanup everything else, etc. */
g_object_unref(conn);
return;
}
...
g_object_unref(conn);
Post by Jakub Janků
Is this an acceptable behavior?
Not one that we shouldn't fix ;)
Post by Jakub Janků
1) use GMainLoop inside vdagent_connection_destroy() in a
similar way as in vdagent_connection_flush() or in the
clipboard.c code
2) add a callback for VDAgentConnection's destruction and make
the clean-up code at the end of main() run while the GMainLoop
is still alive, the callback would then quit it (this would
also require adding destruction callbacks for udscs_connection,
udscs_server and virtio_port)
Is there any better solution?
Overall I think we should GObjectfy if possible as this helps in
this kind of situation, otherwise we might need to rethink a bit
the design (not sure about [1] or [2] proposed options)

Cheers,
Victor
Post by Jakub Janků
Cheers,
Jakub
vdagentd: parse argv using GLib
vport: add by_user param to vdagent_virtio_port_disconnect_callback
vdagentd: use GMainLoop
build: add GIO dependency
add VDAgentConnection
udscs: add udscs_get_peer_pid()
udscs: use VDAgentConnection
udscs-server: split initialization
udscs: simplify logging
vport: use VDAgentConnection
session-info: add ActiveSessionChangeCb
console-kit: use GDBus
systemd-login: use GDBus
session-info: remove session_info_get_fd()
build: drop DBus dependency
move to GLib memory functions
vdagentd: move code to do_guest_xorg_resolution()
vdagentd: move code to do_agent_file_xfer_status()
Makefile.am | 6 +-
configure.ac | 2 +-
src/udscs.c | 560 +++++++--------------------
src/udscs.h | 58 +--
src/vdagent-connection.c | 301 +++++++++++++++
src/vdagent-connection.h | 103 +++++
src/vdagent/vdagent.c | 3 +-
src/vdagent/x11-randr.c | 43 +--
src/vdagentd/console-kit.c | 613 ++++++++++--------------------
src/vdagentd/dummy-session-info.c | 7 +-
src/vdagentd/session-info.h | 6 +-
src/vdagentd/systemd-login.c | 274 +++++--------
src/vdagentd/uinput.c | 9 +-
src/vdagentd/vdagentd.c | 472 +++++++++++------------
src/vdagentd/virtio-port.c | 404 ++++++--------------
src/vdagentd/virtio-port.h | 27 +-
16 files changed, 1217 insertions(+), 1671 deletions(-)
create mode 100644 src/vdagent-connection.c
create mode 100644 src/vdagent-connection.h
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Jakub Janku
2018-09-03 19:03:38 UTC
Permalink
Post by Victor Toso
Hi,
First of all, thanks for your work on this :)
I'm still looking into the glib-integration related patches so I
might still reply once more afterwards. To keep things easy to
review, I take we could split this series in three different set
of patches.
- The cleanup code patches, including using glib's memory
function, should be easy to review an apply
- Glib integration patches, might take some interactions as some
careful review/testing are needed
- GDBus integration - Shouldn't be hard to review/test but it
depends on the Glib one.
Would you mind splitting them?
Not a problem.
Post by Victor Toso
Post by Jakub Janků
Hi all,
as the subject suggests, this series further deepens the
integration of GLib into spice-vdagent(d).
* udscs.c and virtio-port.c are built upon common base,
VDAgentConnection, that handles the lower-level I/O operations
* systemd-login.c and console-kit.c use GDBus instead of libdbus
* vdagentd.c uses GMainLoop and GLib's commandline option parser
Some patches are rather unrelated and try to clean up the code a bit.
vdagent_connection_destroy() is in most cases asynchronous (due
to the way GCancellable works) and it can therefore take
multiple iterations of GMainLoop until the memory associated
with VDAgentConnection is freed and the underlying file
descriptor gets closed.
This is fine as long as the GMainLoop runs. However, the
current code assumes, that the call to
udscs_destroy_connection() and vdagent_virtio_port_destroy()
are synchronous and invokes these functions even after the
GMainLoop quits.
As a result, the memory allocated by VDAgentConnection might
not be explicitely freed and the FD might not be closed, when
the spice-vdagent(d) exits.
I'm wondering if making VDAgentConnection into a GObject would
solve the problem? The vdagent_connection_destroy() would be made
into g_object_unref(vdagent_connection); When the object is being
cleanup, that's split into Dispose and Finalize cleaning methods.
So, in the end of GMainLoop, the VDAgentConnection should always
be correctly finished.
Note that this suggestion is also around how the code has been
wrote. If you have only one reference to VDAgentConnection, you
have to be careful when you can dealloc its resources but if you
_ref() before an operation and _unref() after, you can consider
g_input_stream_read_all_async(in,
conn->header_buff,
conn->header_size,
G_PRIORITY_DEFAULT,
conn->cancellable,
message_read_cb,
conn);
and in message_read_cb()
VDAgentConnection *conn = user_data;
...
g_input_stream_read_all_finish(in, res, &bytes_read, &err);
if (err)
return handle_io_error(conn, err);
if (g_cancellable_is_cancelled(conn->cancellable)) {
if (!connection_has_pending(conn))
connection_finalize(conn);
So you have a check to Cancel and after that a
connection_finalize(), to handle the situation of 'have to
cleanup my memory right'
Instead, you could have a VDAgentConnection object and the
g_input_stream_read_all_async(in,
...
message_read_cb,
g_object_ref(conn));
and in message_read_cb()
VDAgentConnection *conn = user_data;
...
g_input_stream_read_all_finish(in, res, &bytes_read, &err);
if (err) {
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
/* print/deal with error message/values */
}
/* If needed, this will cleanup everything else, etc. */
g_object_unref(conn);
return;
}
...
g_object_unref(conn);
Post by Jakub Janků
Is this an acceptable behavior?
Not one that we shouldn't fix ;)
Post by Jakub Janků
1) use GMainLoop inside vdagent_connection_destroy() in a
similar way as in vdagent_connection_flush() or in the
clipboard.c code
2) add a callback for VDAgentConnection's destruction and make
the clean-up code at the end of main() run while the GMainLoop
is still alive, the callback would then quit it (this would
also require adding destruction callbacks for udscs_connection,
udscs_server and virtio_port)
Is there any better solution?
Overall I think we should GObjectfy if possible as this helps in
this kind of situation, otherwise we might need to rethink a bit
the design (not sure about [1] or [2] proposed options)
Cheers,
Victor
I do agree that turning VDAgentConnection into a GObject is probably a
good idea, however, I do not see how it could help us in this
situation. Am I missing something?
I'll try to explain the problem again:

Let's consider that vdagentd receives a signal.
signal_handler() in vdagentd.c is called which quits the main loop.
The current loop iteration is finished and the program returns to main().
In main(), vdagent_virtio_port_destroy() is called, which internally
invokes vdagent_connection_destroy().

If the virtio port was connected at the moment we received the signal,
there was also a running GTask which was previously started with
g_input_stream_read_all_async() (because we try to read from the port
the whole time).
This GTask holds a reference to the GIOStream, this means that the
stream won't be closed until the GTask finishes.
However, the GTask cannot be canceled synchronously (see docs for
g_cancellable_cancel()) and the GMainLoop is NOT running at the
moment.
As a result, the reference to GIOStream is not released and the FD is
not closed.

So the problem aren't the references to VDAgentConnection itself, but
rather the GLib's internal references to the GIOStream we use in
VDAgentConnection.

I hope this makes it a bit more clear.

Thanks for the reviews :)
Jakub
Post by Victor Toso
Post by Jakub Janků
vdagentd: parse argv using GLib
vport: add by_user param to vdagent_virtio_port_disconnect_callback
vdagentd: use GMainLoop
build: add GIO dependency
add VDAgentConnection
udscs: add udscs_get_peer_pid()
udscs: use VDAgentConnection
udscs-server: split initialization
udscs: simplify logging
vport: use VDAgentConnection
session-info: add ActiveSessionChangeCb
console-kit: use GDBus
systemd-login: use GDBus
session-info: remove session_info_get_fd()
build: drop DBus dependency
move to GLib memory functions
vdagentd: move code to do_guest_xorg_resolution()
vdagentd: move code to do_agent_file_xfer_status()
Makefile.am | 6 +-
configure.ac | 2 +-
src/udscs.c | 560 +++++++--------------------
src/udscs.h | 58 +--
src/vdagent-connection.c | 301 +++++++++++++++
src/vdagent-connection.h | 103 +++++
src/vdagent/vdagent.c | 3 +-
src/vdagent/x11-randr.c | 43 +--
src/vdagentd/console-kit.c | 613 ++++++++++--------------------
src/vdagentd/dummy-session-info.c | 7 +-
src/vdagentd/session-info.h | 6 +-
src/vdagentd/systemd-login.c | 274 +++++--------
src/vdagentd/uinput.c | 9 +-
src/vdagentd/vdagentd.c | 472 +++++++++++------------
src/vdagentd/virtio-port.c | 404 ++++++--------------
src/vdagentd/virtio-port.h | 27 +-
16 files changed, 1217 insertions(+), 1671 deletions(-)
create mode 100644 src/vdagent-connection.c
create mode 100644 src/vdagent-connection.h
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Victor Toso
2018-09-04 05:32:59 UTC
Permalink
Hi,
Post by Jakub Janku
I do agree that turning VDAgentConnection into a GObject is
probably a good idea, however, I do not see how it could help
us in this situation. Am I missing something? I'll try to
Let's consider that vdagentd receives a signal.
signal_handler() in vdagentd.c is called which quits the main
loop. The current loop iteration is finished and the program
returns to main(). In main(), vdagent_virtio_port_destroy() is
called, which internally invokes vdagent_connection_destroy().
That is:

| static gboolean signal_handler(gpointer user_data)
| {
| g_main_loop_quit(loop);
| return G_SOURCE_REMOVE;
| }

But note that g_main_loop_quit() docs says that:

| Note that sources that have already been dispatched when
| g_main_loop_quit() is called will still be executed.

So, isn't this a matter of having cancelled everything correctly
at the right time?
Post by Jakub Janku
If the virtio port was connected at the moment we received the
signal, there was also a running GTask which was previously
started with g_input_stream_read_all_async() (because we try to
read from the port the whole time).
This GTask holds a reference to the GIOStream, this means that
the stream won't be closed until the GTask finishes.
However, the GTask cannot be canceled synchronously (see docs
for g_cancellable_cancel()) and the GMainLoop is NOT running at
the moment.
It is fine that it isn't sync. What happens is that, it gets
cancelled (because g_main_loop_quit()) and on GIOStream's
callback, you would get the GError::G_IO_ERROR_CANCELLED, that
callback would return and that GSource finished.

When the last GSource is finished is when g_main_loop_run()
returns and main() is executed again.
Post by Jakub Janku
As a result, the reference to GIOStream is not released and the
FD is not closed.
So the problem aren't the references to VDAgentConnection
itself, but rather the GLib's internal references to the
GIOStream we use in VDAgentConnection.
If we follow glib's API we either should be fine or find a bug in
glib
Post by Jakub Janku
I hope this makes it a bit more clear.
Let me know if I get it wrong again ;)

Cheers,
Victor
Post by Jakub Janku
Thanks for the reviews :)
Jakub
Post by Jakub Janků
vdagentd: parse argv using GLib
vport: add by_user param to vdagent_virtio_port_disconnect_callback
vdagentd: use GMainLoop
build: add GIO dependency
add VDAgentConnection
udscs: add udscs_get_peer_pid()
udscs: use VDAgentConnection
udscs-server: split initialization
udscs: simplify logging
vport: use VDAgentConnection
session-info: add ActiveSessionChangeCb
console-kit: use GDBus
systemd-login: use GDBus
session-info: remove session_info_get_fd()
build: drop DBus dependency
move to GLib memory functions
vdagentd: move code to do_guest_xorg_resolution()
vdagentd: move code to do_agent_file_xfer_status()
Makefile.am | 6 +-
configure.ac | 2 +-
src/udscs.c | 560 +++++++--------------------
src/udscs.h | 58 +--
src/vdagent-connection.c | 301 +++++++++++++++
src/vdagent-connection.h | 103 +++++
src/vdagent/vdagent.c | 3 +-
src/vdagent/x11-randr.c | 43 +--
src/vdagentd/console-kit.c | 613 ++++++++++--------------------
src/vdagentd/dummy-session-info.c | 7 +-
src/vdagentd/session-info.h | 6 +-
src/vdagentd/systemd-login.c | 274 +++++--------
src/vdagentd/uinput.c | 9 +-
src/vdagentd/vdagentd.c | 472 +++++++++++------------
src/vdagentd/virtio-port.c | 404 ++++++--------------
src/vdagentd/virtio-port.h | 27 +-
16 files changed, 1217 insertions(+), 1671 deletions(-)
create mode 100644 src/vdagent-connection.c
create mode 100644 src/vdagent-connection.h
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Jakub Janku
2018-09-04 12:11:46 UTC
Permalink
Hi,
Post by Victor Toso
Hi,
Post by Jakub Janku
I do agree that turning VDAgentConnection into a GObject is
probably a good idea, however, I do not see how it could help
us in this situation. Am I missing something? I'll try to
Let's consider that vdagentd receives a signal.
signal_handler() in vdagentd.c is called which quits the main
loop. The current loop iteration is finished and the program
returns to main(). In main(), vdagent_virtio_port_destroy() is
called, which internally invokes vdagent_connection_destroy().
| static gboolean signal_handler(gpointer user_data)
| {
| g_main_loop_quit(loop);
| return G_SOURCE_REMOVE;
| }
| Note that sources that have already been dispatched when
| g_main_loop_quit() is called will still be executed.
So, isn't this a matter of having cancelled everything correctly
at the right time?
Well, it kinda is. I think that the call to g_main_loop_quit() should
be made once the connection_finalize() in vdagent-connection.c is
called.
The question is what's the best way to accomplish this.
Post by Victor Toso
Post by Jakub Janku
If the virtio port was connected at the moment we received the
signal, there was also a running GTask which was previously
started with g_input_stream_read_all_async() (because we try to
read from the port the whole time).
This GTask holds a reference to the GIOStream, this means that
the stream won't be closed until the GTask finishes.
However, the GTask cannot be canceled synchronously (see docs
for g_cancellable_cancel()) and the GMainLoop is NOT running at
the moment.
It is fine that it isn't sync. What happens is that, it gets
cancelled (because g_main_loop_quit()) and on GIOStream's
callback, you would get the GError::G_IO_ERROR_CANCELLED, that
callback would return and that GSource finished.
When the last GSource is finished is when g_main_loop_run()
returns and main() is executed again.
Post by Jakub Janku
As a result, the reference to GIOStream is not released and the
FD is not closed.
So the problem aren't the references to VDAgentConnection
itself, but rather the GLib's internal references to the
GIOStream we use in VDAgentConnection.
If we follow glib's API we either should be fine or find a bug in
glib
It's not that simple I'm afraid.
Have a look at https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html
and the diagram (
Loading Image... )
When signal_handler() is called, the main loop is in the "dispatching"
phase. We can call vdagent_virtio_port_destroy() in the signal
handler, but the problem is that if we call g_main_loop_quit(), the
main loop won't iterate again. And we need that extra iterations
because GCancellable uses file descriptor and hence the main loop
needs to go through that "polling" phase so that we get the
GError::G_IO_ERROR_CANCELLED error.

So in other words, if the loop is quit too early, the GIOStream's
callback won't be called and connection won't be finalized.

Cheers,
Jakub
Post by Victor Toso
Post by Jakub Janku
I hope this makes it a bit more clear.
Let me know if I get it wrong again ;)
Cheers,
Victor
Post by Jakub Janku
Thanks for the reviews :)
Jakub
Post by Jakub Janků
vdagentd: parse argv using GLib
vport: add by_user param to vdagent_virtio_port_disconnect_callback
vdagentd: use GMainLoop
build: add GIO dependency
add VDAgentConnection
udscs: add udscs_get_peer_pid()
udscs: use VDAgentConnection
udscs-server: split initialization
udscs: simplify logging
vport: use VDAgentConnection
session-info: add ActiveSessionChangeCb
console-kit: use GDBus
systemd-login: use GDBus
session-info: remove session_info_get_fd()
build: drop DBus dependency
move to GLib memory functions
vdagentd: move code to do_guest_xorg_resolution()
vdagentd: move code to do_agent_file_xfer_status()
Makefile.am | 6 +-
configure.ac | 2 +-
src/udscs.c | 560 +++++++--------------------
src/udscs.h | 58 +--
src/vdagent-connection.c | 301 +++++++++++++++
src/vdagent-connection.h | 103 +++++
src/vdagent/vdagent.c | 3 +-
src/vdagent/x11-randr.c | 43 +--
src/vdagentd/console-kit.c | 613 ++++++++++--------------------
src/vdagentd/dummy-session-info.c | 7 +-
src/vdagentd/session-info.h | 6 +-
src/vdagentd/systemd-login.c | 274 +++++--------
src/vdagentd/uinput.c | 9 +-
src/vdagentd/vdagentd.c | 472 +++++++++++------------
src/vdagentd/virtio-port.c | 404 ++++++--------------
src/vdagentd/virtio-port.h | 27 +-
16 files changed, 1217 insertions(+), 1671 deletions(-)
create mode 100644 src/vdagent-connection.c
create mode 100644 src/vdagent-connection.h
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Victor Toso
2018-09-04 13:15:51 UTC
Permalink
Hi,
Post by Jakub Janku
Post by Victor Toso
Post by Jakub Janku
As a result, the reference to GIOStream is not released and
the FD is not closed.
So the problem aren't the references to VDAgentConnection
itself, but rather the GLib's internal references to the
GIOStream we use in VDAgentConnection.
If we follow glib's API we either should be fine or find a
bug in glib
It's not that simple I'm afraid.
Have a look at https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html
and the diagram (
https://developer.gnome.org/glib/stable/mainloop-states.gif )
When signal_handler() is called, the main loop is in the "dispatching"
phase. We can call vdagent_virtio_port_destroy() in the signal
handler, but the problem is that if we call g_main_loop_quit(), the
main loop won't iterate again.
And we need that extra iterations because GCancellable uses
file descriptor and hence the main loop needs to go through
that "polling" phase so that we get the
GError::G_IO_ERROR_CANCELLED error.
So in other words, if the loop is quit too early, the
GIOStream's callback won't be called and connection won't be
finalized.
How are you checking that any FD is left open? The session agent
would not run without the system one, so it shouldn't be a
problem in case of terminating the process.

The documentation for this GSource points out that a common use
case is to flush things and call g_main_loop_quit().

https://developer.gnome.org/glib/stable/glib-UNIX-specific-utilities-and-integration.html#g-unix-signal-source-new

Anyway, if needed, you can add a g_idle_add() to g_main_loop_quit
with some comment /* to be sure that GIOStream is closed */
considering that you have called the g_cancellable_cancel(),
it should be plenty.
Post by Jakub Janku
Cheers,
Jakub
Post by Victor Toso
Post by Jakub Janku
I hope this makes it a bit more clear.
Let me know if I get it wrong again ;)
Cheers,
Victor
Post by Jakub Janku
Thanks for the reviews :)
Jakub
Post by Jakub Janků
vdagentd: parse argv using GLib
vport: add by_user param to vdagent_virtio_port_disconnect_callback
vdagentd: use GMainLoop
build: add GIO dependency
add VDAgentConnection
udscs: add udscs_get_peer_pid()
udscs: use VDAgentConnection
udscs-server: split initialization
udscs: simplify logging
vport: use VDAgentConnection
session-info: add ActiveSessionChangeCb
console-kit: use GDBus
systemd-login: use GDBus
session-info: remove session_info_get_fd()
build: drop DBus dependency
move to GLib memory functions
vdagentd: move code to do_guest_xorg_resolution()
vdagentd: move code to do_agent_file_xfer_status()
Makefile.am | 6 +-
configure.ac | 2 +-
src/udscs.c | 560 +++++++--------------------
src/udscs.h | 58 +--
src/vdagent-connection.c | 301 +++++++++++++++
src/vdagent-connection.h | 103 +++++
src/vdagent/vdagent.c | 3 +-
src/vdagent/x11-randr.c | 43 +--
src/vdagentd/console-kit.c | 613 ++++++++++--------------------
src/vdagentd/dummy-session-info.c | 7 +-
src/vdagentd/session-info.h | 6 +-
src/vdagentd/systemd-login.c | 274 +++++--------
src/vdagentd/uinput.c | 9 +-
src/vdagentd/vdagentd.c | 472 +++++++++++------------
src/vdagentd/virtio-port.c | 404 ++++++--------------
src/vdagentd/virtio-port.h | 27 +-
16 files changed, 1217 insertions(+), 1671 deletions(-)
create mode 100644 src/vdagent-connection.c
create mode 100644 src/vdagent-connection.h
--
2.17.1
_______________________________________________
Spice-devel mailing list
https://lists.freedesktop.org/mailman/listinfo/spice-devel
Loading...