On Wed, Oct 4, 2017 at 12:43 PM, Frediano Ziglio <fziglio@xxxxxxxxxx> wrote: >> >> Replace existing main while-loop with GMainLoop. >> >> Use GIOChannel with g_io_add_watch() to manage IO >> from x11 connections in the main loop. >> >> udscs_connect() now internally creates GSources >> by calling g_io_add_watch(). >> This enables GMainLoop integration, >> clients no longer need to call >> udscs_client_fill_fds() and >> udscs_client_handle_fds() in a loop. >> Read callback and udscs_write() functions as before. >> >> Signals are also handled in the main loop, >> using g_unix_signal_add(). >> SIGQUIT is currently not supported by GLib. >> >> This enables further GTK+ integration in the future. >> >> Signed-off-by: Victor Toso <victortoso@xxxxxxxxxx> >> --- >> src/udscs.c | 60 ++++++++++++++++++++++ >> src/vdagent/vdagent.c | 138 >> ++++++++++++++++++++++++-------------------------- >> src/vdagent/vdagent.h | 4 ++ >> 3 files changed, 131 insertions(+), 71 deletions(-) >> >> diff --git a/src/udscs.c b/src/udscs.c >> index 8b16f89..44842f4 100644 >> --- a/src/udscs.c >> +++ b/src/udscs.c >> @@ -31,6 +31,8 @@ >> #include <errno.h> >> #include <sys/socket.h> >> #include <sys/un.h> >> +#include <glib.h> >> +#include <glib-unix.h> >> #include "udscs.h" >> >> struct udscs_buf { >> @@ -66,8 +68,16 @@ 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); >> + >> struct udscs_connection *udscs_connect(const char *socketname, >> udscs_read_callback read_callback, >> udscs_disconnect_callback disconnect_callback, >> @@ -103,6 +113,17 @@ struct udscs_connection *udscs_connect(const char >> *socketname, >> 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->read_callback = read_callback; >> conn->disconnect_callback = disconnect_callback; >> >> @@ -141,6 +162,12 @@ void udscs_destroy_connection(struct udscs_connection >> **connp) >> >> close(conn->fd); >> >> + g_clear_pointer(&conn->io_channel, g_io_channel_unref); >> + 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); >> + > > I would put freeing io_channel as last, but as reference counting > are used is just style. I agree. > >> if (conn->debug) >> syslog(LOG_DEBUG, "%p disconnected", conn); >> >> @@ -198,6 +225,13 @@ 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; >> @@ -353,6 +387,32 @@ int udscs_client_fill_fds(struct udscs_connection *conn, >> fd_set *readfds, >> return conn->fd + 1; >> } >> >> +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 >> >> diff --git a/src/vdagent/vdagent.c b/src/vdagent/vdagent.c >> index fd2ba51..fe443df 100644 >> --- a/src/vdagent/vdagent.c >> +++ b/src/vdagent/vdagent.c >> @@ -34,8 +34,8 @@ >> #include <sys/select.h> >> #include <sys/stat.h> >> #include <spice/vd_agent.h> >> -#include <glib.h> >> #include <poll.h> >> +#include <glib-unix.h> >> >> #include "vdagentd-proto.h" >> #include "vdagentd-proto-strings.h" >> @@ -44,7 +44,6 @@ >> >> G_DEFINE_TYPE (VDAgent, vdagent, G_TYPE_OBJECT); >> >> -static int quit = 0; >> static int version_mismatch = 0; >> >> /* Command line options */ >> @@ -170,7 +169,7 @@ static void daemon_read_complete(struct udscs_connection >> **connp, >> if (strcmp((char *)data, VERSION) != 0) { >> syslog(LOG_INFO, "vdagentd version mismatch: got %s expected >> %s", >> data, VERSION); >> - udscs_destroy_connection(connp); >> + g_main_loop_quit(agent->loop); >> version_mismatch = 1; >> } >> break; >> @@ -228,25 +227,12 @@ static void daemon_read_complete(struct >> udscs_connection **connp, >> } >> } >> >> -static struct udscs_connection *client_setup_sync(VDAgent *agent) >> +static void daemon_disconnect_cb(struct udscs_connection *conn) >> { >> - struct udscs_connection *conn = NULL; >> - >> - while (!quit) { >> - conn = udscs_connect(vdagentd_socket, daemon_read_complete, NULL, >> - vdagentd_messages, VDAGENTD_NO_MESSAGES, >> - debug); >> - if (conn || !do_daemonize || quit) { >> - break; >> - } >> - sleep(1); >> - } >> - return conn; >> -} >> - >> -static void quit_handler(int sig) >> -{ >> - quit = 1; >> + VDAgent *agent = udscs_get_user_data(conn); >> + agent->conn = NULL; >> + if (agent->loop) >> + g_main_loop_quit(agent->loop); >> } >> >> /* When we daemonize, it is useful to have the main process >> @@ -304,13 +290,40 @@ static int file_test(const char *path) >> return stat(path, &buffer); >> } >> >> +static gboolean x11_io_channel_cb(GIOChannel *source, >> + GIOCondition condition, >> + gpointer data) >> +{ >> + VDAgent *agent = data; >> + vdagent_x11_do_read(agent->x11); >> + >> + return G_SOURCE_CONTINUE; >> +} >> + >> +gboolean vdagent_signal_handler(gpointer user_data) >> +{ >> + VDAgent *agent = user_data; >> + do_daemonize = FALSE; > > why you removed quit and used do_daemonize? > Is not that readable setting do_daemonize if you want to > quit. > I understand that it's less readable, but do we really need to keep that extra variable around when the effect of both is the same? What about creating a simple function like vdagent_exit() which would quit the mainloop and set do_daemonize to FALSE? Or renaming do_daemonize to something like keep_running, or try_reconnect to make it more clear? >> + g_main_loop_quit(agent->loop); >> + return G_SOURCE_REMOVE; >> +} >> + >> static void vdagent_init(VDAgent *self) >> { >> + self->loop = g_main_loop_new(NULL, FALSE); >> + >> + g_unix_signal_add(SIGINT, vdagent_signal_handler, self); >> + g_unix_signal_add(SIGHUP, vdagent_signal_handler, self); >> + g_unix_signal_add(SIGTERM, vdagent_signal_handler, self); >> } >> >> static void vdagent_finalize(GObject *gobject) >> { >> VDAgent *self = VDAGENT(gobject); >> + >> + g_clear_pointer(&self->x11_channel, g_io_channel_unref); >> + g_clear_pointer(&self->loop, g_main_loop_unref); >> + >> vdagent_finalize_file_xfer(self); >> vdagent_x11_destroy(self->x11, self->conn == NULL); >> udscs_destroy_connection(&self->conn); >> @@ -324,29 +337,50 @@ static void vdagent_class_init(VDAgentClass *klass) >> gobject_class->finalize = vdagent_finalize; >> } >> >> -static gboolean vdagent_init_sync(VDAgent *agent) >> +static gboolean vdagent_init_async_cb(gpointer user_data) >> { >> - agent->conn = client_setup_sync(agent); >> + VDAgent *agent = user_data; >> + >> + agent->conn = udscs_connect(vdagentd_socket, >> + daemon_read_complete, daemon_disconnect_cb, >> + vdagentd_messages, VDAGENTD_NO_MESSAGES, >> debug); >> if (agent->conn == NULL) >> - return FALSE; >> + return G_SOURCE_CONTINUE; >> udscs_set_user_data(agent->conn, agent); >> >> agent->x11 = vdagent_x11_create(agent->conn, debug, x11_sync); >> if (agent->x11 == NULL) >> - return FALSE; >> + goto err_init; >> + agent->x11_channel = >> g_io_channel_unix_new(vdagent_x11_get_fd(agent->x11)); >> + if (agent->x11_channel == NULL) >> + goto err_init; >> + >> + g_io_add_watch(agent->x11_channel, >> + G_IO_IN, >> + x11_io_channel_cb, >> + agent); >> >> if (!vdagent_init_file_xfer(agent)) >> syslog(LOG_WARNING, "File transfer is disabled"); >> >> - return TRUE; >> + if (agent->parent_socket) { >> + if (write(agent->parent_socket, "OK", 2) != 2) >> + syslog(LOG_WARNING, "Parent already gone."); >> + close(agent->parent_socket); >> + agent->parent_socket = 0; >> + } >> + >> + return G_SOURCE_REMOVE; >> + >> +err_init: >> + g_main_loop_quit(agent->loop); >> + do_daemonize = FALSE; > > here too. > >> + return G_SOURCE_REMOVE; >> } >> >> int main(int argc, char *argv[]) >> { >> - fd_set readfds, writefds; >> - int n, nfds, x11_fd; >> int parent_socket = 0; >> - struct sigaction act; >> GOptionContext *context; >> GError *error = NULL; >> VDAgent *agent; >> @@ -372,14 +406,6 @@ int main(int argc, char *argv[]) >> if (vdagentd_socket == NULL) >> vdagentd_socket = g_strdup(VDAGENTD_SOCKET); >> >> - 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); >> - >> openlog("spice-vdagent", do_daemonize ? LOG_PID : (LOG_PID | >> LOG_PERROR), >> LOG_USER); >> >> @@ -400,44 +426,14 @@ reconnect: >> >> agent = g_object_new(TYPE_VDAGENT, NULL); >> agent->parent_socket = parent_socket; >> - if (!vdagent_init_sync(agent)) { >> - quit = 1; >> - goto end_main; >> - } >> - >> - if (parent_socket) { >> - if (write(parent_socket, "OK", 2) != 2) >> - syslog(LOG_WARNING, "Parent already gone."); >> - close(parent_socket); >> - parent_socket = 0; >> - } >> >> - while (agent->conn && !quit) { >> - FD_ZERO(&readfds); >> - FD_ZERO(&writefds); >> - >> - nfds = udscs_client_fill_fds(agent->conn, &readfds, &writefds); >> - x11_fd = vdagent_x11_get_fd(agent->x11); >> - FD_SET(x11_fd, &readfds); >> - if (x11_fd >= nfds) >> - nfds = x11_fd + 1; >> - >> - n = select(nfds, &readfds, &writefds, NULL, NULL); >> - if (n == -1) { >> - if (errno == EINTR) >> - continue; >> - syslog(LOG_ERR, "Fatal error select: %s", strerror(errno)); >> - break; >> - } >> + g_timeout_add_seconds(1, vdagent_init_async_cb, agent); >> > > this adds an extra 1 second delay to the start of the agent. > I understand you did this to handle signals during initialization too. > An easy fix is to call vdagent_init_async_cb manually, something like > > if (vdagent_init_async_cb(agent) != G_SOURCE_CONTINUE) > g_timeout_add_seconds(1, vdagent_init_async_cb, agent); > > if (!quit) > g_main_loop_run(agent->loop); Yes, that would fix it. Maybe we can initialize the agent outside the mainloop and use sleep() instead (as before). We probably won't handle signals when the connection isn't initialized anyway. > >> - if (FD_ISSET(x11_fd, &readfds)) >> - vdagent_x11_do_read(agent->x11); >> - udscs_client_handle_fds(&agent->conn, &readfds, &writefds); >> - } >> + g_main_loop_run(agent->loop); >> >> -end_main: >> g_clear_object(&agent); >> - if (!quit && do_daemonize) >> + >> + if (do_daemonize) >> goto reconnect; >> >> g_free(fx_dir); >> diff --git a/src/vdagent/vdagent.h b/src/vdagent/vdagent.h >> index 8376315..2a62f6f 100644 >> --- a/src/vdagent/vdagent.h >> +++ b/src/vdagent/vdagent.h >> @@ -20,6 +20,7 @@ >> #define __VDAGENT_H_ >> >> #include <glib-object.h> >> +#include <glib.h> >> #include "udscs.h" >> #include "x11.h" >> #include "file-xfers.h" >> @@ -39,6 +40,9 @@ typedef struct VDAgent { >> struct vdagent_x11 *x11; >> struct vdagent_file_xfers *xfers; >> struct udscs_connection *conn; >> + GIOChannel *x11_channel; >> + >> + GMainLoop *loop; >> >> int parent_socket; >> } VDAgent; > > Frediano Cheers, Jakub _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel