[gnome-battery-bench] Catch errors opening /dev/uinput and pass them back to the client



commit c9f969f7a50adcc3ae9e52ada0dce7e43185f225
Author: Owen W. Taylor <otaylor fishsoup net>
Date:   Tue Jan 20 14:51:31 2015 -0500

    Catch errors opening /dev/uinput and pass them back to the client
    
    If we can't open /dev/uinput, store a reason in a GError, and pass
    that back over D-Bus to the client.
    
    This prevents mysterious problems where, if /dev/uinput doesn't exist,
    the helper simply exits on CreatePlayer.

 src/commandline.c   |    6 +++-
 src/evdev-player.c  |   84 +++++++++++++++++++++++++++++++++++++++-----------
 src/evdev-player.h  |    3 +-
 src/remote-player.c |    2 +
 src/replay-helper.c |   15 +++++++--
 5 files changed, 86 insertions(+), 24 deletions(-)
---
diff --git a/src/commandline.c b/src/commandline.c
index c5fddd6..7002a30 100644
--- a/src/commandline.c
+++ b/src/commandline.c
@@ -160,7 +160,11 @@ play(int argc, char **argv)
 static int
 play_local(int argc, char **argv)
 {
-    GbbEventPlayer *player = GBB_EVENT_PLAYER(gbb_evdev_player_new("Gnome Battery Bench Test"));
+    GError *error = NULL;
+    GbbEventPlayer *player = GBB_EVENT_PLAYER(gbb_evdev_player_new("Gnome Battery Bench Test", &error));
+    if (player == NULL)
+        die(error->message);
+
     return do_play(player, argc, argv);
 }
 
diff --git a/src/evdev-player.c b/src/evdev-player.c
index 4637631..bb8eb83 100644
--- a/src/evdev-player.c
+++ b/src/evdev-player.c
@@ -24,7 +24,9 @@ struct _GbbEvdevPlayer {
 
     char *filename;
     gint64 start_time;
+    int uinput_fd_keyboard;
     struct libevdev_uinput *uidev_keyboard;
+    int uinput_fd_mouse;
     struct libevdev_uinput *uidev_mouse;
     GDataInputStream *input;
 
@@ -131,8 +133,14 @@ gbb_evdev_player_finalize(GObject *object)
 {
     GbbEvdevPlayer *player = GBB_EVDEV_PLAYER(object);
 
-    libevdev_uinput_destroy(player->uidev_keyboard);
-    libevdev_uinput_destroy(player->uidev_mouse);
+    if (player->uinput_fd_keyboard >= 0)
+        close(player->uinput_fd_keyboard);
+    if (player->uidev_keyboard)
+        libevdev_uinput_destroy(player->uidev_keyboard);
+    if (player->uinput_fd_mouse >= 0)
+        close(player->uinput_fd_keyboard);
+    if (player->uidev_mouse)
+        libevdev_uinput_destroy(player->uidev_mouse);
 
     g_clear_object(&player->input);
 
@@ -184,6 +192,8 @@ gbb_evdev_player_stop(GbbEventPlayer *event_player)
 static void
 gbb_evdev_player_init(GbbEvdevPlayer *player)
 {
+    player->uinput_fd_keyboard = -1;
+    player->uinput_fd_mouse = -1;
 }
 
 static void
@@ -197,11 +207,50 @@ gbb_evdev_player_class_init(GbbEvdevPlayerClass *player_class)
     event_player_class->stop = gbb_evdev_player_stop;
 }
 
+static struct libevdev_uinput *
+create_uinput(struct libevdev *dev,
+              int             *fd_return,
+              GError         **error)
+{
+    /* Since we don't need to access the FD directly, we could use
+     * LIBEVDEV_UINPUT_OPEN_MANAGED instead of opening /dev/input directly.
+     * However, a bug in libevdev 1.2.2 and earlier means that we don't get the
+     * right return code if we use that option - -EBADF is always returned.
+     * We open directly so that the error message will be useful.
+     */
+    int fd = open("/dev/uinput", O_CLOEXEC | O_RDWR);
+    if (fd < 0) {
+        if (errno == EACCES)
+            g_set_error(error, G_IO_ERROR, g_io_error_from_errno(errno),
+                        "Need to be root to simulate events");
+        else if (errno == ENOENT)
+            g_set_error(error, G_IO_ERROR, g_io_error_from_errno(errno),
+                        "Can't open /dev/uinput: %s. The kernel may not be compiled with uinput support.", 
strerror(errno));
+        else
+            g_set_error(error, G_IO_ERROR, g_io_error_from_errno(errno),
+                        "Can't open /dev/uinput: %s", strerror(errno));
+
+        return NULL;
+    }
+
+    static struct libevdev_uinput *uinput;
+    int rc = libevdev_uinput_create_from_device(dev, fd, &uinput);
+    if (rc != 0) {
+        g_set_error(error, G_IO_ERROR, g_io_error_from_errno(-rc),
+                    "Can't create uinput device: %s", strerror(-rc));
+        close(fd);
+        return NULL;
+    }
+
+    *fd_return = fd;
+    return uinput;
+}
+
 GbbEvdevPlayer *
-gbb_evdev_player_new(const char *name)
+gbb_evdev_player_new(const char *name,
+                     GError    **error)
 {
     GbbEvdevPlayer *player;
-    int rc;
     struct libevdev *dev;
     int i;
 
@@ -222,17 +271,11 @@ gbb_evdev_player_new(const char *name)
     for (i = 1; i <= 255 - 8; i++)
         libevdev_enable_event_code(dev, EV_KEY, i, NULL);
     libevdev_enable_event_type(dev, EV_KEY);
-    open("/about to create", O_RDONLY);
-    rc = libevdev_uinput_create_from_device(dev,
-                                            LIBEVDEV_UINPUT_OPEN_MANAGED,
-                                            &player->uidev_keyboard);
-    if (rc != 0) {
-        if (rc == -EBADF)
-            die("Need to be root to simulate events");
-        else
-            die("Can't create uinput: %s\n", strerror(-rc));
-    }
+
+    player->uidev_keyboard = create_uinput(dev, &player->uinput_fd_keyboard, error);
     libevdev_free(dev);
+    if (!player->uidev_keyboard)
+        goto error;
 
     dev = libevdev_new();
     char *mouse_name = g_strconcat(name, " - simulated mouse", NULL);
@@ -250,16 +293,19 @@ gbb_evdev_player_new(const char *name)
     libevdev_enable_event_type(dev, EV_REL);
     libevdev_enable_event_code(dev, EV_REL, REL_WHEEL, NULL);
 
-    rc = libevdev_uinput_create_from_device(dev,
-                                            LIBEVDEV_UINPUT_OPEN_MANAGED,
-                                            &player->uidev_mouse);
-    if (rc != 0)
-        die("Can't create uinput: %s\n", strerror(-rc));
+    player->uidev_mouse = create_uinput(dev, &player->uinput_fd_mouse, error);
     libevdev_free(dev);
+    if (!player->uidev_mouse)
+        goto error;
 
     gbb_event_player_set_ready (GBB_EVENT_PLAYER(player),
                                 libevdev_uinput_get_devnode(player->uidev_keyboard),
                                 libevdev_uinput_get_devnode(player->uidev_mouse));
 
     return player;
+
+error:
+    g_object_unref(player);
+
+    return NULL;
 }
diff --git a/src/evdev-player.h b/src/evdev-player.h
index 663a986..6faca3d 100644
--- a/src/evdev-player.h
+++ b/src/evdev-player.h
@@ -12,7 +12,8 @@ typedef struct _GbbEvdevPlayer GbbEvdevPlayer;
 #define GBB_IS_EVDEV_PLAYER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GBB_TYPE_EVDEV_PLAYER))
 #define GBB_EVDEV_PLAYER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GBB_TYPE_EVDEV_PLAYER, 
GbbEvdevPlayerClass))
 
-GbbEvdevPlayer *gbb_evdev_player_new(const char *name);
+GbbEvdevPlayer *gbb_evdev_player_new(const char *name,
+                                     GError    **error);
 
 GType gbb_evdev_player_get_type(void);
 
diff --git a/src/remote-player.c b/src/remote-player.c
index 0fdb044..11dac23 100644
--- a/src/remote-player.c
+++ b/src/remote-player.c
@@ -75,6 +75,7 @@ on_play_reply(GObject      *source_object,
             return;
         }
 
+        g_dbus_error_strip_remote_error(error);
         g_warning("Failed to play:s %s", error->message);
         g_clear_error(&error);
     } else {
@@ -214,6 +215,7 @@ on_create_player_reply(GObject      *source_object,
             return;
         }
 
+        g_dbus_error_strip_remote_error(error);
         die("Error calling CreatePlayer: %s", error->message);
     }
 
diff --git a/src/replay-helper.c b/src/replay-helper.c
index 5330bf9..a6d8705 100644
--- a/src/replay-helper.c
+++ b/src/replay-helper.c
@@ -39,11 +39,14 @@ player_destroy(Player *player)
         g_signal_handler_disconnect(player->player, player->finished_connection);
     }
 
-    g_object_unref(player->player);
+    g_clear_object(&player->player);
 
     g_dbus_connection_signal_unsubscribe(player->connection,
                                          player->creator_changed_connection);
-    g_dbus_connection_unregister_object(player->connection, player->registration_id);
+
+    if (player->registration_id)
+        g_dbus_connection_unregister_object(player->connection, player->registration_id);
+
     g_object_unref(player->connection);
     g_free(player->name);
     g_free(player->path);
@@ -234,7 +237,13 @@ on_checked_authorization(GObject      *source_object,
                                                                              on_name_owner_changed,
                                                                              player, NULL);
 
-    player->player = GBB_EVENT_PLAYER(gbb_evdev_player_new(player->name));
+    GbbEvdevPlayer *evdev_player = gbb_evdev_player_new(player->name, &error);
+    if (evdev_player == NULL) {
+        g_dbus_method_invocation_take_error(invocation, error);
+        player_destroy(player);
+        return;
+    }
+    player->player = GBB_EVENT_PLAYER(evdev_player);
 
     player->registration_id = g_dbus_connection_register_object(connection,
                                                                 player->path,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]