compositor: refactor client forking code

shell.c and tablet-shell.c had almost the same code for forking their
special shell client. Generalise this code and put it into
wlsc_client_launch() in compositor.c.

Improve error cleanup and reporting in wlsc_client_launch().

Signed-off-by: Pekka Paalanen <ppaalanen@gmail.com>
diff --git a/compositor/compositor.c b/compositor/compositor.c
index 2c04c3b..79440fd 100644
--- a/compositor/compositor.c
+++ b/compositor/compositor.c
@@ -34,6 +34,8 @@
 #include <assert.h>
 #include <sys/ioctl.h>
 #include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/socket.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <math.h>
@@ -84,6 +86,75 @@
 }
 
 static void
+child_client_exec(int sockfd, const char *path)
+{
+	int flags;
+	char s[32];
+
+	/* SOCK_CLOEXEC closes both ends, so we need to unset
+	 * the flag on the client fd. */
+	flags = fcntl(sockfd, F_GETFD);
+	if (flags != -1)
+		fcntl(sockfd, F_SETFD, flags & ~FD_CLOEXEC);
+
+	snprintf(s, sizeof s, "%d", sockfd);
+	setenv("WAYLAND_SOCKET", s, 1);
+
+	if (execl(path, path, NULL) < 0)
+		fprintf(stderr, "compositor: executing '%s' failed: %m\n",
+			path);
+}
+
+WL_EXPORT struct wl_client *
+wlsc_client_launch(struct wlsc_compositor *compositor,
+		   struct wlsc_process *proc,
+		   const char *path,
+		   wlsc_process_cleanup_func_t cleanup)
+{
+	int sv[2];
+	pid_t pid;
+	struct wl_client *client;
+
+	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
+		fprintf(stderr, "wlsc_client_launch: "
+			"socketpair failed while launching '%s': %m\n",
+			path);
+		return NULL;
+	}
+
+	pid = fork();
+	if (pid == -1) {
+		close(sv[0]);
+		close(sv[1]);
+		fprintf(stderr,  "wlsc_client_launch: "
+			"fork failed while launching '%s': %m\n", path);
+		return NULL;
+	}
+
+	if (pid == 0) {
+		child_client_exec(sv[1], path);
+		exit(-1);
+	}
+
+	close(sv[1]);
+
+	client = wl_client_create(compositor->wl_display, sv[0]);
+	if (!client) {
+		close(sv[0]);
+		fprintf(stderr, "wlsc_client_launch: "
+			"wl_client_create failed while launching '%s'.\n",
+			path);
+		return NULL;
+	}
+
+	proc->pid = pid;
+	proc->cleanup = cleanup;
+	wlsc_watch_process(proc);
+
+	return client;
+}
+
+static void
 surface_handle_buffer_destroy(struct wl_listener *listener,
 			      struct wl_resource *resource, uint32_t time)
 {