ecore-drm: Fix various issues with logind code
authorChris Michael <cp.michael@samsung.com>
Thu, 22 Jan 2015 17:38:29 +0000 (12:38 -0500)
committerChris Michael <cp.michael@samsung.com>
Mon, 26 Jan 2015 13:37:08 +0000 (08:37 -0500)
Summary: This fixes a few issues with the logind code such as: Getting
the VT from systemd (if available), Properly setup the VT using proper
'open' flags, adding a 'restore' function to reset the tty properly,
and handle take/release session.

@fix

Signed-off-by: Chris Michael <cp.michael@samsung.com>
src/lib/ecore_drm/ecore_drm_logind.c

index 9a8d7f135733cd9e84e68122b5c4c21fc86064fc..dfc5402373f6d80f0b10ba6eed5803981a553ee4 100644 (file)
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <linux/vt.h>
-#include <linux/kd.h>
-#include <linux/major.h>
 #include "ecore_drm_private.h"
-#include "ecore_drm_logind.h"
 
 #ifndef KDSKBMUTE
 # define KDSKBMUTE 0x4B51
 #endif
 
-static Ecore_Event_Handler *active_hdl;
-static char *sid;
+static Ecore_Event_Handler *active_hdlr;
 
-static Eina_Bool
+#ifdef HAVE_SYSTEMD
+static inline Eina_Bool 
+_ecore_drm_logind_vt_get(Ecore_Drm_Device *dev)
+{
+   int ret;
+   char *tty;
+
+   ret = sd_session_get_tty(dev->session, &tty);
+   if (ret < 0)
+     {
+        ERR("Could not get systemd tty: %m");
+        return EINA_FALSE;
+     }
+
+   ret = sscanf(tty, "tty%u", &dev->vt);
+   free(tty);
+
+   if (ret != 1) return EINA_FALSE;
+
+   return EINA_TRUE;
+}
+#endif
+
+static Eina_Bool 
+_ecore_drm_logind_vt_setup(Ecore_Drm_Device *dev)
+{
+   struct stat st;
+   char buff[64];
+   struct vt_mode vtmode = { 0 };
+
+   snprintf(buff, sizeof(buff), "/dev/tty%d", dev->vt);
+   buff[sizeof(buff) - 1] = 0;
+
+   dev->tty.fd = open(buff, (O_RDWR | O_CLOEXEC | O_NONBLOCK));
+   if (dev->tty.fd < 0)
+     {
+        ERR("Could not open VT %s %m", buff);
+        return EINA_FALSE;
+     }
+
+   if ((fstat(dev->tty.fd, &st) == -1) || 
+       (major(st.st_rdev) != TTY_MAJOR) || 
+       (minor(st.st_rdev) <= 0) || (minor(st.st_rdev) >= 64))
+     {
+        ERR("TTY %s is not a virtual terminal", buff);
+        goto stat_err;
+     }
+
+   if (ioctl(dev->tty.fd, KDGKBMODE, &dev->tty.kbd_mode) < 0)
+     {
+        ERR("Could not read keyboard mode of %s: %m", buff);
+        dev->tty.kbd_mode = K_UNICODE;
+     }
+   else if (dev->tty.kbd_mode == K_OFF)
+     dev->tty.kbd_mode = K_UNICODE;
+
+   if ((ioctl(dev->tty.fd, KDSKBMUTE, 1) < 0) && 
+       (ioctl(dev->tty.fd, KDSKBMODE, K_OFF) < 0))
+     {
+        ERR("Could not set K_OFF keyboard mode on %s: %m", buff);
+        goto stat_err;
+     }
+
+   if (ioctl(dev->tty.fd, KDSETMODE, KD_GRAPHICS) < 0)
+     {
+        ERR("Could not set KD_GRAPHICS mode on %s: %m", buff);
+        goto kbdmode_err;
+     }
+
+   vtmode.mode = VT_PROCESS;
+   vtmode.waitv =0 ;
+   vtmode.relsig = SIGUSR1;
+   vtmode.acqsig = SIGUSR2;
+
+   if (ioctl(dev->tty.fd, VT_SETMODE, &vtmode) < 0)
+     {
+        ERR("Could not take over virtual terminal: %m");
+        goto mode_err;
+     }
+
+   return EINA_TRUE;
+
+mode_err:
+   ioctl(dev->tty.fd, KDSETMODE, KD_TEXT);
+kbdmode_err:
+   ioctl(dev->tty.fd, KDSKBMUTE, 0);
+   ioctl(dev->tty.fd, KDSKBMODE, dev->tty.kbd_mode);
+stat_err:
+   close(dev->tty.fd);
+   return EINA_FALSE;
+}
+
+static void 
+_ecore_drm_logind_vt_destroy(Ecore_Drm_Device *dev)
+{
+   _ecore_drm_logind_restore(dev);
+   close(dev->tty.fd);
+}
+
+static Eina_Bool 
+_ecore_drm_logind_cb_vt_signal(void *data, int type EINA_UNUSED, void *event)
+{
+   Ecore_Drm_Device *dev;
+   Ecore_Event_Signal_User *ev;
+   siginfo_t sig;
+
+   ev = event;
+   sig = ev->data;
+
+   if (sig.si_code != SI_KERNEL) return ECORE_CALLBACK_RENEW;
+   if (!(dev = data)) return ECORE_CALLBACK_RENEW;
+
+   switch (ev->number)
+     {
+      case 1:
+        _ecore_drm_event_activate_send(EINA_FALSE);
+        ioctl(dev->tty.fd, VT_RELDISP, 1);
+        break;
+      case 2:
+        ioctl(dev->tty.fd, VT_RELDISP, VT_ACKACQ);
+        _ecore_drm_event_activate_send(EINA_TRUE);
+        break;
+      default:
+        break;
+     }
+
+   return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool 
 _ecore_drm_logind_cb_activate(void *data, int type EINA_UNUSED, void *event)
 {
-   Ecore_Drm_Event_Activate *e;
+   Ecore_Drm_Event_Activate *ev;
    Ecore_Drm_Device *dev;
    Ecore_Drm_Output *output;
    Eina_List *l;
 
    if ((!event) || (!data)) return ECORE_CALLBACK_RENEW;
 
-   e = event;
+   ev = event;
    dev = data;
 
-   if (e->active)
+   if (ev->active)
      {
         /* set output mode */
         EINA_LIST_FOREACH(dev->outputs, l, output)
@@ -48,38 +170,94 @@ _ecore_drm_logind_cb_activate(void *data, int type EINA_UNUSED, void *event)
    return ECORE_CALLBACK_PASS_ON;
 }
 
-Eina_Bool
+Eina_Bool 
 _ecore_drm_logind_connect(Ecore_Drm_Device *dev)
 {
 #ifdef HAVE_SYSTEMD
-   /* get sd-login properties we need */
-   if (sd_pid_get_session(getpid(), &sid) < 0) return EINA_FALSE;
-#endif
+   char *seat;
+
+   /* get session id */
+   if (sd_pid_get_session(getpid(), &dev->session) < 0)
+     {
+        ERR("Could not get systemd session: %m");
+        return EINA_FALSE;
+     }
 
-   /* try to init dbus */
-   if (!_ecore_drm_dbus_init(sid))
+   if (sd_session_get_seat(dev->session, &seat) < 0)
+     {
+        ERR("Could not get systemd seat: %m");
+        free(seat);
+        return EINA_FALSE;
+     }
+   else if (strcmp(dev->seat, seat))
      {
-        free(sid);
+        ERR("Session seat '%s' differs from device seat '%s'", seat, dev->seat);
+        free(seat);
         return EINA_FALSE;
      }
 
-   active_hdl =
-      ecore_event_handler_add(ECORE_DRM_EVENT_ACTIVATE,
-                              _ecore_drm_logind_cb_activate, dev);
+   free(seat);
+
+   if (!_ecore_drm_logind_vt_get(dev)) return EINA_FALSE;
+#endif
+
+   if (!_ecore_drm_dbus_init(dev)) return EINA_FALSE;
+
+   /* take control of session */
+   if (!_ecore_drm_dbus_session_take(dev->session))
+     {
+        ERR("Could not take control of session");
+        goto take_err;
+     }
+
+   /* setup vt */
+   if (!_ecore_drm_logind_vt_setup(dev))
+     {
+        ERR("Could not setup vt '%d'", dev->vt);
+        goto vt_err;
+     }
+
+   /* setup handler for vt signals */
+   dev->tty.event_hdlr = 
+     ecore_event_handler_add(ECORE_EVENT_SIGNAL_USER, 
+                             _ecore_drm_logind_cb_vt_signal, dev);
+
+   active_hdlr = 
+     ecore_event_handler_add(ECORE_DRM_EVENT_ACTIVATE, 
+                             _ecore_drm_logind_cb_activate, dev);
 
    return EINA_TRUE;
+
+vt_err:
+   _ecore_drm_dbus_session_release(dev->session);
+take_err:
+   _ecore_drm_dbus_shutdown();
+   return EINA_FALSE;
 }
 
-void
-_ecore_drm_logind_disconnect(Ecore_Drm_Device *dev EINA_UNUSED)
+void 
+_ecore_drm_logind_disconnect(Ecore_Drm_Device *dev)
 {
+   if (active_hdlr) ecore_event_handler_del(active_hdlr);
+   active_hdlr = NULL;
+
+   _ecore_drm_logind_vt_destroy(dev);
+   _ecore_drm_dbus_session_release(dev->session);
    _ecore_drm_dbus_shutdown();
+}
 
-   if (active_hdl)
-     {
-        ecore_event_handler_del(active_hdl);
-        active_hdl = NULL;
-     }
+void 
+_ecore_drm_logind_restore(Ecore_Drm_Device *dev)
+{
+   struct vt_mode vtmode = { 0 };
+
+   if ((!dev) || (dev->tty.fd < 0)) return;
+
+   ioctl(dev->tty.fd, KDSETMODE, KD_TEXT);
+   ioctl(dev->tty.fd, KDSKBMUTE, 0);
+   ioctl(dev->tty.fd, KDSKBMODE, dev->tty.kbd_mode);
+   vtmode.mode = VT_AUTO;
+   ioctl(dev->tty.fd, VT_SETMODE, &vtmode);
 }
 
 Eina_Bool