1 /*
2 * Copyright (C) 2019 Purism SPC
3 * SPDX-License-Identifier: GPL-3.0+
4 * Author: Guido Günther <agx@sigxcpu.org>
5 */
6
7 #define G_LOG_DOMAIN "phoc-server"
8
9 #include "config.h"
10 #include "server.h"
11
12 #include <errno.h>
13
14 G_DEFINE_TYPE(PhocServer, phoc_server, G_TYPE_OBJECT);
15
16 typedef struct {
17 GSource source;
18 struct wl_display *display;
19 } WaylandEventSource;
20
21 static gboolean
wayland_event_source_prepare(GSource * base,int * timeout)22 wayland_event_source_prepare (GSource *base,
23 int *timeout)
24 {
25 WaylandEventSource *source = (WaylandEventSource *)base;
26
27 *timeout = -1;
28
29 wl_display_flush_clients (source->display);
30
31 return FALSE;
32 }
33
34 static gboolean
wayland_event_source_dispatch(GSource * base,GSourceFunc callback,void * data)35 wayland_event_source_dispatch (GSource *base,
36 GSourceFunc callback,
37 void *data)
38 {
39 WaylandEventSource *source = (WaylandEventSource *)base;
40 struct wl_event_loop *loop = wl_display_get_event_loop (source->display);
41
42 wl_event_loop_dispatch (loop, 0);
43
44 return TRUE;
45 }
46
47 static GSourceFuncs wayland_event_source_funcs = {
48 wayland_event_source_prepare,
49 NULL,
50 wayland_event_source_dispatch,
51 NULL
52 };
53
54 static GSource *
wayland_event_source_new(struct wl_display * display)55 wayland_event_source_new (struct wl_display *display)
56 {
57 WaylandEventSource *source;
58 struct wl_event_loop *loop = wl_display_get_event_loop (display);
59
60 source = (WaylandEventSource *) g_source_new (&wayland_event_source_funcs,
61 sizeof (WaylandEventSource));
62 source->display = display;
63 g_source_add_unix_fd (&source->source,
64 wl_event_loop_get_fd (loop),
65 G_IO_IN | G_IO_ERR);
66
67 return &source->source;
68 }
69
70 static void
phoc_wayland_init(PhocServer * self)71 phoc_wayland_init (PhocServer *self)
72 {
73 GSource *wayland_event_source;
74
75 wayland_event_source = wayland_event_source_new (self->wl_display);
76 self->wl_source = g_source_attach (wayland_event_source, NULL);
77 }
78
79
80 static void
on_session_exit(GPid pid,gint status,PhocServer * self)81 on_session_exit (GPid pid, gint status, PhocServer *self)
82 {
83 g_autoptr(GError) err = NULL;
84
85 g_return_if_fail (PHOC_IS_SERVER (self));
86 g_spawn_close_pid (pid);
87 if (g_spawn_check_exit_status (status, &err)) {
88 self->exit_status = 0;
89 } else {
90 if (err->domain == G_SPAWN_EXIT_ERROR)
91 self->exit_status = err->code;
92 else
93 g_warning ("Session terminated: %s (%d)", err->message, self->exit_status);
94 }
95 if (!(self->debug_flags & PHOC_SERVER_DEBUG_FLAG_NO_QUIT))
96 g_main_loop_quit (self->mainloop);
97 }
98
99
100 static void
on_child_setup(gpointer unused)101 on_child_setup (gpointer unused)
102 {
103 sigset_t mask;
104
105 /* phoc wants SIGUSR1 blocked due to wlroots/xwayland but we
106 don't want to inherit that to childs */
107 sigemptyset(&mask);
108 sigaddset(&mask, SIGUSR1);
109 sigprocmask(SIG_UNBLOCK, &mask, NULL);
110 }
111
112
113 static gboolean
phoc_startup_session_in_idle(PhocServer * self)114 phoc_startup_session_in_idle(PhocServer *self)
115 {
116 GPid pid;
117 g_autoptr(GError) err = NULL;
118 gchar *cmd[] = { "/bin/sh", "-c", self->session, NULL };
119
120 if (g_spawn_async (NULL, cmd, NULL,
121 G_SPAWN_DO_NOT_REAP_CHILD,
122 on_child_setup, self, &pid, &err)) {
123 g_child_watch_add (pid, (GChildWatchFunc)on_session_exit, self);
124 } else {
125 g_warning ("Failed to launch session: %s", err->message);
126 g_main_loop_quit (self->mainloop);
127 }
128 return FALSE;
129 }
130
131 static void
phoc_startup_session(PhocServer * server)132 phoc_startup_session (PhocServer *server)
133 {
134 gint id;
135
136 id = g_idle_add ((GSourceFunc) phoc_startup_session_in_idle, server);
137 g_source_set_name_by_id (id, "[phoc] phoc_startup_session");
138 }
139
140
141 static void
phoc_server_constructed(GObject * object)142 phoc_server_constructed (GObject *object)
143 {
144 PhocServer *self = PHOC_SERVER (object);
145
146 self->wl_display = wl_display_create();
147 if (self->wl_display == NULL)
148 g_error("Could not create wayland display");
149
150 self->backend = wlr_backend_autocreate(self->wl_display, NULL);
151 if (self->backend == NULL)
152 g_error("Could not create backend");
153
154 self->renderer = wlr_backend_get_renderer(self->backend);
155 if (self->renderer == NULL)
156 g_error("Could not create renderer");
157
158 self->data_device_manager =
159 wlr_data_device_manager_create(self->wl_display);
160 wlr_renderer_init_wl_display(self->renderer, self->wl_display);
161
162 G_OBJECT_CLASS (phoc_server_parent_class)->constructed (object);
163 }
164
165
166 static void
phoc_server_dispose(GObject * object)167 phoc_server_dispose (GObject *object)
168 {
169 PhocServer *self = PHOC_SERVER (object);
170
171 if (self->backend) {
172 wl_display_destroy_clients (self->wl_display);
173 wlr_backend_destroy(self->backend);
174 self->backend = NULL;
175 }
176
177 G_OBJECT_CLASS (phoc_server_parent_class)->dispose (object);
178 }
179
180 static void
phoc_server_finalize(GObject * object)181 phoc_server_finalize (GObject *object)
182 {
183 PhocServer *self = PHOC_SERVER (object);
184
185 if (self->wl_source) {
186 g_source_remove (self->wl_source);
187 self->wl_source = 0;
188 }
189 g_clear_object (&self->desktop);
190 g_clear_pointer (&self->session, g_free);
191
192 if (self->inited) {
193 g_unsetenv("WAYLAND_DISPLAY");
194 self->inited = FALSE;
195 }
196
197 wl_display_destroy (self->wl_display);
198 G_OBJECT_CLASS (phoc_server_parent_class)->finalize (object);
199 }
200
201
202 static void
phoc_server_class_init(PhocServerClass * klass)203 phoc_server_class_init (PhocServerClass *klass)
204 {
205 GObjectClass *object_class = G_OBJECT_CLASS (klass);
206
207 object_class->constructed = phoc_server_constructed;
208 object_class->finalize = phoc_server_finalize;
209 object_class->dispose = phoc_server_dispose;
210 }
211
212 static void
phoc_server_init(PhocServer * self)213 phoc_server_init (PhocServer *self)
214 {
215 }
216
217 PhocServer *
phoc_server_get_default(void)218 phoc_server_get_default (void)
219 {
220 static PhocServer *instance;
221
222 if (G_UNLIKELY (instance == NULL)) {
223 g_debug("Creating server");
224 instance = g_object_new (PHOC_TYPE_SERVER, NULL);
225 g_object_add_weak_pointer (G_OBJECT (instance), (gpointer *)&instance);
226 }
227
228 return instance;
229 }
230
231 /**
232 * phoc_server_setup:
233 *
234 * Perform wayland server intialization: parse command line and config,
235 * create the wayland socket, setup env vars.
236 *
237 * Returns: %TRUE on success, %FALSE otherwise
238 */
239 gboolean
phoc_server_setup(PhocServer * self,const char * config_path,const char * session,GMainLoop * mainloop,PhocServerDebugFlags debug_flags)240 phoc_server_setup (PhocServer *self, const char *config_path,
241 const char *session, GMainLoop *mainloop,
242 PhocServerDebugFlags debug_flags)
243 {
244 g_assert (!self->inited);
245
246 self->config = roots_config_create(config_path);
247 if (!self->config) {
248 g_warning("Failed to parse config");
249 return FALSE;
250 }
251
252 self->mainloop = mainloop;
253 self->exit_status = 1;
254 self->desktop = phoc_desktop_new (self->config);
255 self->input = phoc_input_new (self->config);
256 self->session = g_strdup (session);
257 self->mainloop = mainloop;
258 self->debug_flags = debug_flags;
259
260 const char *socket = wl_display_add_socket_auto(self->wl_display);
261 if (!socket) {
262 g_warning("Unable to open wayland socket: %s", strerror(errno));
263 wlr_backend_destroy(self->backend);
264 return FALSE;
265 }
266
267 g_print ("Running compositor on wayland display '%s'\n", socket);
268
269 if (!wlr_backend_start(self->backend)) {
270 g_warning("Failed to start backend");
271 wlr_backend_destroy(self->backend);
272 wl_display_destroy(self->wl_display);
273 return FALSE;
274 }
275
276 setenv("WAYLAND_DISPLAY", socket, true);
277 #ifdef PHOC_XWAYLAND
278 if (self->desktop->xwayland != NULL) {
279 struct roots_seat *xwayland_seat =
280 phoc_input_get_seat(self->input, ROOTS_CONFIG_DEFAULT_SEAT_NAME);
281 wlr_xwayland_set_seat(self->desktop->xwayland, xwayland_seat->seat);
282 }
283 #endif
284
285 phoc_wayland_init (self);
286 if (self->session)
287 phoc_startup_session (self);
288
289 self->inited = TRUE;
290 return TRUE;
291 }
292
293 /**
294 * phoc_server_get_exit_status:
295 *
296 * Return the session's exit status. This is only meaningful
297 * if the session has ended.
298 *
299 * Returns: The session's exit status.
300 */
301 gint
phoc_server_get_session_exit_status(PhocServer * self)302 phoc_server_get_session_exit_status (PhocServer *self)
303 {
304 return self->exit_status;
305 }
306