xwayland: Create and bind to unix & abstract sockets
authorChris Michael <cp.michael@samsung.com>
Sun, 6 Jul 2014 14:12:03 +0000 (10:12 -0400)
committerMike Blumenkrantz <zmike@osg.samsung.com>
Fri, 26 Jun 2015 00:04:30 +0000 (20:04 -0400)
NB: XWayland server needs the sockets setup prior to launching it so
we add some code to create & bind the needed sockets before starting
the XWayland binary

Signed-off-by: Chris Michael <cp.michael@samsung.com>
src/modules/xwayland/e_mod_main.c

index a6e5ed7..fe8b64e 100644 (file)
@@ -1,11 +1,26 @@
 #include "e.h"
 #include "e_comp_wl.h"
+#include <sys/socket.h>
+#include <sys/un.h>
 
 /* local structures */
 typedef struct _E_XWayland_Server E_XWayland_Server;
 struct _E_XWayland_Server
 {
    int disp;
+   int abs_fd, unx_fd;
+   char lock[256];
+
+   struct wl_event_loop *loop;
+
+   Ecore_Fd_Handler *abs_hdlr, *unx_hdlr;
+   Ecore_Event_Handler *sig_hdlr;
+
+   struct 
+     {
+        pid_t pid;
+        /* cleanup_func func; */
+     } process;
 };
 
 /* local variables */
@@ -13,14 +28,12 @@ static E_XWayland_Server *exs;
 
 /* local functions */
 static Eina_Bool 
-_lock_create(int disp)
+_lock_create(char *lock)
 {
-   char lock[256], pid[16], *end;
+   char pid[16], *end;
    int fd, size;
    pid_t opid;
 
-   /* assemble lock file name */
-   snprintf(lock, sizeof(lock), "/tmp/.X%d-lock", disp);
    fd = open(lock, (O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL), 0444);
    if ((fd < 0) && (errno == EEXIST))
      {
@@ -77,6 +90,158 @@ _lock_create(int disp)
    return EINA_TRUE;
 }
 
+static int 
+_abstract_socket_bind(int disp)
+{
+   struct sockaddr_un addr;
+   socklen_t size, nsize;
+   int fd;
+
+   /* try to create a local socket */
+   if ((fd = socket(PF_LOCAL, (SOCK_STREAM | SOCK_CLOEXEC), 0)) < 0) 
+     return -1;
+
+   addr.sun_family = AF_LOCAL;
+   nsize = snprintf(addr.sun_path, sizeof(addr.sun_path), 
+                    "%c/tmp/.X11-unix/X%d", 0, disp);
+   size = offsetof(struct sockaddr_un, sun_path) + nsize;
+
+   /* try to bind to the socket */
+   if (bind(fd, (struct sockaddr *)&addr, size) < 0)
+     {
+        ERR("Failed to bind to abstract socket %s: %m", addr.sun_path + 1);
+        goto err;
+     }
+
+   /* try to listen on the bound socket */
+   if (listen(fd, 1) < 0) goto err;
+
+   return fd;
+
+err:
+   close(fd);
+   return -1;
+}
+
+static int 
+_unix_socket_bind(int disp)
+{
+   struct sockaddr_un addr;
+   socklen_t size, nsize;
+   int fd;
+
+   /* try to create a local socket */
+   if ((fd = socket(PF_LOCAL, (SOCK_STREAM | SOCK_CLOEXEC), 0)) < 0) 
+     return -1;
+
+   addr.sun_family = AF_LOCAL;
+   nsize = snprintf(addr.sun_path, sizeof(addr.sun_path), 
+                    "/tmp/.X11-unix/X%d", disp) + 1;
+   size = offsetof(struct sockaddr_un, sun_path) + nsize;
+
+   unlink(addr.sun_path);
+
+   /* try to bind to the socket */
+   if (bind(fd, (struct sockaddr *)&addr, size) < 0)
+     {
+        ERR("Failed to bind to abstract socket %s: %m", addr.sun_path + 1);
+        goto err;
+     }
+
+   /* try to listen on the bound socket */
+   if (listen(fd, 1) < 0) goto err;
+
+   return fd;
+
+err:
+   close(fd);
+   return -1;
+}
+
+static Eina_Bool 
+_cb_xserver_event(void *data EINA_UNUSED, Ecore_Fd_Handler *hdlr EINA_UNUSED)
+{
+   int socks[2], wms[2], fd;
+   char disp[8], s[8], *xserver = NULL;
+   char abs_fd[8], unx_fd[8], wm_fd[8];
+
+   if (socketpair(AF_UNIX, (SOCK_STREAM | SOCK_CLOEXEC), 0, socks) < 0)
+     {
+        ERR("XServer Socketpair failed: %m");
+        return ECORE_CALLBACK_RENEW;
+     }
+
+   if (socketpair(AF_UNIX, (SOCK_STREAM | SOCK_CLOEXEC), 0, wms) < 0)
+     {
+        ERR("Window Manager Socketpair failed: %m");
+        return ECORE_CALLBACK_RENEW;
+     }
+
+   exs->process.pid = fork();
+   switch (exs->process.pid)
+     {
+      case 0:
+        /* dup will unset CLOEXEC on the client as cloexec closes both ends */
+        if ((fd = dup(socks[1]) < 0)) goto fail;
+        snprintf(s, sizeof(s), "%d", fd);
+        setenv("WAYLAND_SOCKET", s, 1);
+
+        if ((fd = dup(exs->abs_fd)) < 0) goto fail;
+        snprintf(abs_fd, sizeof(abs_fd), "%d", fd);
+
+        if ((fd = dup(exs->unx_fd)) < 0) goto fail;
+        snprintf(unx_fd, sizeof(unx_fd), "%d", fd);
+
+        if ((fd = dup(wms[1])) < 0) goto fail;
+        snprintf(wm_fd, sizeof(wm_fd), "%d", fd);
+
+        /* ignore usr1 and have X send it to the parent process */
+        signal(SIGUSR1, SIG_IGN);
+
+        /* FIXME: need to get the patch of xwayland */
+        snprintf(disp, sizeof(disp), ":%d", exs->disp);
+
+        DBG("XWAYLAND: %s", XWAYLAND_BIN);
+        if (execl(xserver, xserver, disp, "-rootless", "-listen", abs_fd,
+                  "-listen", unx_fd, "-wm", wm_fd, "-terminate", NULL) < 0)
+          {
+             ERR("Failed to exec XWayland: %m");
+          }
+
+fail:
+        _exit(EXIT_FAILURE);
+
+        break;
+      default:
+        close(socks[1]);
+        /* TODO: client_create */
+        close(wms[1]);
+        /* TODO */
+        /* TODO: remove event sources */
+        break;
+      case -1:
+        ERR("Failed to fork: %m");
+        break;
+     }
+
+   return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool 
+_cb_signal_event(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
+{
+   /* E_XWayland_Server *exs; */
+
+   /* NB: SIGUSR1 comes from XWayland Server when it has finished 
+    * initialized. */
+
+   /* if (!(exs = data)) return ECORE_CALLBACK_RENEW; */
+
+   /* TODO: create "window manager" process */
+
+   return ECORE_CALLBACK_RENEW;
+}
+
 /* module functions */
 EAPI E_Module_Api e_modapi = { E_MODULE_API_VERSION, "XWayland" };
 
@@ -84,6 +249,7 @@ EAPI void *
 e_modapi_init(E_Module *m)
 {
    E_Comp *comp;
+   char disp[8];
 
    /* try to get the running compositor */
    if (!(comp = e_comp_get(NULL))) return NULL;
@@ -99,7 +265,11 @@ e_modapi_init(E_Module *m)
    exs->disp = 0;
 
 lock:
-   if (!_lock_create(exs->disp))
+   /* assemble lock file name */
+   snprintf(exs->lock, sizeof(exs->lock), "/tmp/.X%d-lock", exs->disp);
+
+   /* try to create the xserver lock file */
+   if (!_lock_create(exs->lock))
      {
         if (errno == EAGAIN)
           goto lock;
@@ -115,15 +285,45 @@ lock:
           }
      }
 
-   /* TODO: bind sockets */
+   /* try to bind abstract socket */
+   exs->abs_fd = _abstract_socket_bind(exs->disp);
+   if ((exs->abs_fd < 0) && (errno == EADDRINUSE))
+     {
+        exs->disp++;
+        unlink(exs->lock);
+        goto lock;
+     }
+
+   /* try to bind unix socket */
+   if ((exs->unx_fd = _unix_socket_bind(exs->disp)) < 0)
+     {
+        unlink(exs->lock);
+        close(exs->abs_fd);
+        free(exs);
+        return NULL;
+     }
+
+   /* assemble x11 display name and set it */
+   snprintf(disp, sizeof(disp), ":%d", exs->disp);
+   setenv("DISPLAY", disp, 1);
+
+   /* setup ecore_fd handlers for abstract and unix socket fds */
+   exs->abs_hdlr = 
+     ecore_main_fd_handler_add(exs->abs_fd, ECORE_FD_READ, 
+                               _cb_xserver_event, NULL, NULL, NULL);
+   exs->unx_hdlr = 
+     ecore_main_fd_handler_add(exs->unx_fd, ECORE_FD_READ, 
+                               _cb_xserver_event, NULL, NULL, NULL);
+
+   /* setup listener for SIGUSR1 */
+   exs->sig_hdlr = 
+     ecore_event_handler_add(ECORE_EVENT_SIGNAL_USER, _cb_signal_event, exs);
 
    return m;
 }
 
 EAPI int 
-e_modapi_shutdown(E_Module *m)
+e_modapi_shutdown(E_Module *m EINA_UNUSED)
 {
    return 1;
 }
-
-