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)
 {
diff --git a/compositor/compositor.h b/compositor/compositor.h
index 1d55158..7f1dd74 100644
--- a/compositor/compositor.h
+++ b/compositor/compositor.h
@@ -418,12 +418,22 @@
 wlsc_load_image(const char *filename,
 		int32_t *width_arg, int32_t *height_arg, uint32_t *stride_arg);
 
+struct wlsc_process;
+typedef void (*wlsc_process_cleanup_func_t)(struct wlsc_process *process,
+					    int status);
+
 struct wlsc_process {
 	pid_t pid;
-	void (*cleanup)(struct wlsc_process *process, int status);
+	wlsc_process_cleanup_func_t cleanup;
 	struct wl_list link;
 };
 
+struct wl_client *
+wlsc_client_launch(struct wlsc_compositor *compositor,
+		   struct wlsc_process *proc,
+		   const char *path,
+		   wlsc_process_cleanup_func_t cleanup);
+
 int
 wlsc_data_device_manager_init(struct wlsc_compositor *compositor);
 void
diff --git a/compositor/shell.c b/compositor/shell.c
index b7cfdd3..e104941 100644
--- a/compositor/shell.c
+++ b/compositor/shell.c
@@ -27,9 +27,6 @@
 #include <string.h>
 #include <unistd.h>
 #include <linux/input.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <fcntl.h>
 #include <assert.h>
 
 #include <wayland-server.h>
@@ -880,44 +877,14 @@
 launch_desktop_shell_process(struct wl_shell *shell)
 {
 	const char *shell_exe = LIBEXECDIR "/wayland-desktop-shell";
-	struct wlsc_compositor *compositor = shell->compositor;
-	char s[32];
-	int sv[2], flags;
 
-	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
-		fprintf(stderr, "socketpair failed\n");
+	shell->child.client = wlsc_client_launch(shell->compositor,
+						 &shell->child.process,
+						 shell_exe,
+						 desktop_shell_sigchld);
+
+	if (!shell->child.client)
 		return -1;
-	}
-
-	shell->child.process.pid = fork();
-	shell->child.process.cleanup = desktop_shell_sigchld;
-
-	switch (shell->child.process.pid) {
-	case 0:
-		/* SOCK_CLOEXEC closes both ends, so we need to unset
-		 * the flag on the client fd. */
-		flags = fcntl(sv[1], F_GETFD);
-		if (flags != -1)
-			fcntl(sv[1], F_SETFD, flags & ~FD_CLOEXEC);
-
-		snprintf(s, sizeof s, "%d", sv[1]);
-		setenv("WAYLAND_SOCKET", s, 1);
-		if (execl(shell_exe, shell_exe, NULL) < 0)
-			fprintf(stderr, "%s: running '%s' failed: %m\n",
-				__func__, shell_exe);
-		exit(-1);
-
-	default:
-		close(sv[1]);
-		shell->child.client =
-			wl_client_create(compositor->wl_display, sv[0]);
-		wlsc_watch_process(&shell->child.process);
-		break;
-
-	case -1:
-		fprintf(stderr, "%s: fork failed: %m\n", __func__);
-		return -1;
-	}
 	return 0;
 }
 
diff --git a/compositor/tablet-shell.c b/compositor/tablet-shell.c
index 416a971..6901acd 100644
--- a/compositor/tablet-shell.c
+++ b/compositor/tablet-shell.c
@@ -25,9 +25,6 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <fcntl.h>
 #include <linux/input.h>
 
 #include "compositor.h"
@@ -369,43 +366,10 @@
 launch_ux_daemon(struct tablet_shell *shell)
 {
 	const char *shell_exe = LIBEXECDIR "/wayland-tablet-shell";
-	struct wlsc_compositor *compositor = shell->compositor;
-	char s[32];
-	int sv[2], flags;
 
-	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) < 0) {
-		fprintf(stderr, "socketpair failed\n");
-		return;
-	}
-
-	shell->process.pid = fork();
-	shell->process.cleanup = tablet_shell_sigchld;
-
-	switch (shell->process.pid) {
-	case 0:
-		/* SOCK_CLOEXEC closes both ends, so we need to unset
-		 * the flag on the client fd. */
-		flags = fcntl(sv[1], F_GETFD);
-		if (flags != -1)
-			fcntl(sv[1], F_SETFD, flags & ~FD_CLOEXEC);
-
-		snprintf(s, sizeof s, "%d", sv[1]);
-		setenv("WAYLAND_SOCKET", s, 1);
-		if (execl(shell_exe, shell_exe, NULL) < 0)
-			fprintf(stderr, "exec failed: %m\n");
-		exit(-1);
-
-	default:
-		close(sv[1]);
-		shell->client =
-			wl_client_create(compositor->wl_display, sv[0]);
-		wlsc_watch_process(&shell->process);
-		break;
-
-	case -1:
-		fprintf(stderr, "failed to fork\n");
-		break;
-	}
+	shell->client = wlsc_client_launch(shell->compositor,
+					   &shell->process,
+					   shell_exe, tablet_shell_sigchld);
 }
 
 static void