On Oct 16, 2017 4:20 PM, "Frediano Ziglio" <fzig...@redhat.com> wrote:
On Fri, Oct 13, 2017 at 3:49 PM Frediano Ziglio <fzig...@redhat.com> wrote: > > On Fri, Oct 13, 2017, 9:29 AM Frediano Ziglio <fzig...@redhat.com> wrote: > >> > >> > On Wed, Oct 11, 2017 at 1:43 PM, Frediano Ziglio <fzig...@redhat.com> >> 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 <victort...@redhat.com> >> > >> --- >> > >> src/udscs.c | 60 ++++++++++++++++++++ >> > >> src/vdagent/vdagent.c | 148 >> > >> ++++++++++++++++++++++++++------------------------ >> > >> src/vdagent/vdagent.h | 8 +++ >> > >> 3 files changed, 145 insertions(+), 71 deletions(-) >> > >> >> > >> diff --git a/src/udscs.c b/src/udscs.c >> > >> index 8b16f89..3a1ea44 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); >> > >> >> > >> + 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); >> > >> + >> > >> 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 462b5fe..3474f17 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,17 +290,48 @@ 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; >> > >> + agent->quit = TRUE; >> > >> + 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); >> > >> + >> > >> vdagent_finalize_file_xfer(self); >> > >> vdagent_x11_destroy(self->x11, self->conn == NULL); >> > >> udscs_destroy_connection(&self->conn); >> > >> >> > >> + if (self->timer_source_id > 0) >> > >> + g_source_remove(self->timer_source_id); >> > >> + if (self->x11_io_watch_id > 0) >> > >> + g_source_remove(self->x11_io_watch_id); >> > >> + g_clear_pointer(&self->x11_channel, g_io_channel_unref); >> > >> + g_clear_pointer(&self->loop, g_main_loop_unref); >> > >> + >> > >> G_OBJECT_CLASS(vdagent_parent_class)->finalize(gobject); >> > >> } >> > >> >> > >> @@ -324,29 +341,53 @@ 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; >> > >> + >> > >> + agent->x11_io_watch_id = >> > >> + 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; >> > > >> > > This is introducing a bug. agent->parent_socket is set to 0 >> > > but on next program iteration (in main) agent->parent_socket >> > > will be set to old parent_socket which in the meantime has been >> > > closed. This potentially will trigger some operation on a different >> > > file descriptor. >> > > >> > > Maybe better to use a global "parent_socket" variable. >> > > Would also be better to use -1 as invalid value instead of 0, >> > > but this is not a regression. >> > > >> > >> + } >> > >> + >> > >> + agent->timer_source_id = 0; >> > >> + return G_SOURCE_REMOVE; >> > >> + >> > >> +err_init: >> > >> + g_main_loop_quit(agent->loop); >> > >> + agent->timer_source_id = 0; >> > >> + agent->quit = TRUE; >> > >> + 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 +413,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,45 +433,18 @@ 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; >> > >> - } >> > >> + if (vdagent_init_async_cb(agent) == G_SOURCE_CONTINUE) >> > >> + agent->timer_source_id = g_timeout_add_seconds(1, >> > >> vdagent_init_async_cb, agent); >> > >> >> > >> - 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; >> > >> - } >> > >> + if (!agent->quit) >> > >> + g_main_loop_run(agent->loop); >> > >> >> > >> - if (FD_ISSET(x11_fd, &readfds)) >> > >> - vdagent_x11_do_read(agent->x11); >> > >> - udscs_client_handle_fds(&agent->conn, &readfds, &writefds); >> > >> + if (!agent->quit && do_daemonize) { >> > > >> > > Maybe would be better to use the old global "quit" variable instead? >> > > >> > > I think all these regression came from the fact the VDAgent is badly >> > > incapsulating >> > > the program. If is representing the entire program should not be >> destroyed >> > > and created again but destroyed only when program exit. >> > > >> > > Is basically representing some partial state of the program but also >> > > implementing the program itself. This is the reason you need to save >> some >> > > state and restore it (quit, parent_socket, version_mismatch). >> > >> > I would make the variables quit, parent_socket, version_mismatch global, >> > as you suggested. >> > This way the VDAgent object would represent one "program iteration", >> > which makes perfect sense to me. >> > Would you be OK with that, or do you think bigger refactor should be >> > done when you say >> > that "VDAgent is badly incapsulating the program"? >> > >> > > >> > >> + g_clear_object(&agent); >> > >> + goto reconnect; >> > >> } >> > >> - >> > >> -end_main: >> > >> g_clear_object(&agent); >> > >> - if (!quit && do_daemonize) >> > >> - goto reconnect; >> > >> >> > >> g_free(fx_dir); >> > >> g_free(portdev); >> > >> diff --git a/src/vdagent/vdagent.h b/src/vdagent/vdagent.h >> > >> index 8376315..cd1d788 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,8 +40,15 @@ typedef struct VDAgent { >> > >> struct vdagent_x11 *x11; >> > >> struct vdagent_file_xfers *xfers; >> > >> struct udscs_connection *conn; >> > >> + GIOChannel *x11_channel; >> > >> + >> > >> + GMainLoop *loop; >> > >> + guint timer_source_id; >> > >> + guint x11_io_watch_id; >> > >> >> > >> int parent_socket; >> > >> + >> > >> + gboolean quit; >> > >> } VDAgent; >> > >> >> > >> typedef struct VDAgentClass { >> > > >> > > Frediano >> > >> > Thanks, >> > Jakub >> > >> >> Was looking at something more radical like removing entirely 6/8, >> see https://cgit.freedesktop.org/~fziglio/vd_agent_linux/commit/ >> ?h=janub4&id=cbdff50a3e6e74d7e5e1ff41c806e2ebe25c713b. >> I still didn't test it. Looking back at at 6/8 after making udcsc code >> independent >> again GObject does not make any improvement adding 80 lines for no >> features and >> an extra dependencies (GObject library). >> >> Frediano >> > > I don't like that you encapsulate half of the variables in VDAgent struct > and half not. > I understand that this minimizes the amount of changes, but nevertheless. > I think we should either make all variables global, or put them all > (excluding parent_socket, version_mismatch, quit) in the struct. > I prefer the latter. What do you think? > > Jakub > > Fully agree, I should have commented it. > That's why I didn't posted the patch but a link, not only because I > didn't tested it. > Let me move some field inside the structure. > > > Ok, got some code, still to test, see https://cgit.freedesktop.org/~ > fziglio/vd_agent_linux/log/?h=janub4 > (changed patches 6 and 7) > > Frediano > It looks good to me. I just noticed one mistake: - in vdagent_init_file_xfer(), we don't need to set agent->xfers = NULL, since we already syslog and return when it isn't NULL. This goes back to the commit "vdagent: move file xfer initialization", but I removed it in "vdagent: Introduce VDAgent object". My mistake, sorry. Jakub Removed the offending line. Tested and working fine. Do you want me to send a new version based on my replacements or do you want to pick up these patches? Frediano Great, I think you can send it. Jakub
_______________________________________________ Spice-devel mailing list Spice-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/spice-devel