1 /*
2  * Copyright © 2013 David Herrmann
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the
13  * next paragraph) shall be included in all copies or substantial
14  * portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25 
26 #include "config.h"
27 
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <signal.h>
31 #include <stdarg.h>
32 #include <stdbool.h>
33 #include <stdint.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/stat.h>
38 #include <systemd/sd-login.h>
39 #include <unistd.h>
40 
41 #include <libweston/libweston.h>
42 #include "backend.h"
43 #include "dbus.h"
44 #include "launcher-impl.h"
45 
46 #define DRM_MAJOR 226
47 
48 /* major()/minor() */
49 #ifdef MAJOR_IN_MKDEV
50 #include <sys/mkdev.h>
51 #endif
52 #ifdef MAJOR_IN_SYSMACROS
53 #include <sys/sysmacros.h>
54 #endif
55 
56 struct launcher_logind {
57 	struct weston_launcher base;
58 	struct weston_compositor *compositor;
59 	bool sync_drm;
60 	char *seat;
61 	char *sid;
62 	unsigned int vtnr;
63 	int vt;
64 	int kb_mode;
65 
66 	DBusConnection *dbus;
67 	struct wl_event_source *dbus_ctx;
68 	char *spath;
69 	DBusPendingCall *pending_active;
70 };
71 
72 static int
launcher_logind_take_device(struct launcher_logind * wl,uint32_t major,uint32_t minor,bool * paused_out)73 launcher_logind_take_device(struct launcher_logind *wl, uint32_t major,
74 			  uint32_t minor, bool *paused_out)
75 {
76 	DBusMessage *m, *reply;
77 	bool b;
78 	int r, fd;
79 	dbus_bool_t paused;
80 
81 	m = dbus_message_new_method_call("org.freedesktop.login1",
82 					 wl->spath,
83 					 "org.freedesktop.login1.Session",
84 					 "TakeDevice");
85 	if (!m)
86 		return -ENOMEM;
87 
88 	b = dbus_message_append_args(m,
89 				     DBUS_TYPE_UINT32, &major,
90 				     DBUS_TYPE_UINT32, &minor,
91 				     DBUS_TYPE_INVALID);
92 	if (!b) {
93 		r = -ENOMEM;
94 		goto err_unref;
95 	}
96 
97 	reply = dbus_connection_send_with_reply_and_block(wl->dbus, m,
98 							  -1, NULL);
99 	if (!reply) {
100 		r = -ENODEV;
101 		goto err_unref;
102 	}
103 
104 	b = dbus_message_get_args(reply, NULL,
105 				  DBUS_TYPE_UNIX_FD, &fd,
106 				  DBUS_TYPE_BOOLEAN, &paused,
107 				  DBUS_TYPE_INVALID);
108 	if (!b) {
109 		r = -ENODEV;
110 		goto err_reply;
111 	}
112 
113 	r = fd;
114 	if (paused_out)
115 		*paused_out = paused;
116 
117 err_reply:
118 	dbus_message_unref(reply);
119 err_unref:
120 	dbus_message_unref(m);
121 	return r;
122 }
123 
124 static void
launcher_logind_release_device(struct launcher_logind * wl,uint32_t major,uint32_t minor)125 launcher_logind_release_device(struct launcher_logind *wl, uint32_t major,
126 			     uint32_t minor)
127 {
128 	DBusMessage *m;
129 	bool b;
130 
131 	m = dbus_message_new_method_call("org.freedesktop.login1",
132 					 wl->spath,
133 					 "org.freedesktop.login1.Session",
134 					 "ReleaseDevice");
135 	if (m) {
136 		b = dbus_message_append_args(m,
137 					     DBUS_TYPE_UINT32, &major,
138 					     DBUS_TYPE_UINT32, &minor,
139 					     DBUS_TYPE_INVALID);
140 		if (b)
141 			dbus_connection_send(wl->dbus, m, NULL);
142 		dbus_message_unref(m);
143 	}
144 }
145 
146 static void
launcher_logind_pause_device_complete(struct launcher_logind * wl,uint32_t major,uint32_t minor)147 launcher_logind_pause_device_complete(struct launcher_logind *wl, uint32_t major,
148 				    uint32_t minor)
149 {
150 	DBusMessage *m;
151 	bool b;
152 
153 	m = dbus_message_new_method_call("org.freedesktop.login1",
154 					 wl->spath,
155 					 "org.freedesktop.login1.Session",
156 					 "PauseDeviceComplete");
157 	if (m) {
158 		b = dbus_message_append_args(m,
159 					     DBUS_TYPE_UINT32, &major,
160 					     DBUS_TYPE_UINT32, &minor,
161 					     DBUS_TYPE_INVALID);
162 		if (b)
163 			dbus_connection_send(wl->dbus, m, NULL);
164 		dbus_message_unref(m);
165 	}
166 }
167 
168 static int
launcher_logind_open(struct weston_launcher * launcher,const char * path,int flags)169 launcher_logind_open(struct weston_launcher *launcher, const char *path, int flags)
170 {
171 	struct launcher_logind *wl = wl_container_of(launcher, wl, base);
172 	struct stat st;
173 	int fl, r, fd;
174 
175 	r = stat(path, &st);
176 	if (r < 0)
177 		return -1;
178 	if (!S_ISCHR(st.st_mode)) {
179 		errno = ENODEV;
180 		return -1;
181 	}
182 
183 	fd = launcher_logind_take_device(wl, major(st.st_rdev),
184 				       minor(st.st_rdev), NULL);
185 	if (fd < 0)
186 		return fd;
187 
188 	/* Compared to weston_launcher_open() we cannot specify the open-mode
189 	 * directly. Instead, logind passes us an fd with sane default modes.
190 	 * For DRM and evdev this means O_RDWR | O_CLOEXEC. If we want
191 	 * something else, we need to change it afterwards. We currently
192 	 * only support setting O_NONBLOCK. Changing access-modes is not
193 	 * possible so accept whatever logind passes us. */
194 
195 	fl = fcntl(fd, F_GETFL);
196 	if (fl < 0) {
197 		r = -errno;
198 		goto err_close;
199 	}
200 
201 	if (flags & O_NONBLOCK)
202 		fl |= O_NONBLOCK;
203 
204 	r = fcntl(fd, F_SETFL, fl);
205 	if (r < 0) {
206 		r = -errno;
207 		goto err_close;
208 	}
209 	return fd;
210 
211 err_close:
212 	close(fd);
213 	launcher_logind_release_device(wl, major(st.st_rdev),
214 				     minor(st.st_rdev));
215 	errno = -r;
216 	return -1;
217 }
218 
219 static void
launcher_logind_close(struct weston_launcher * launcher,int fd)220 launcher_logind_close(struct weston_launcher *launcher, int fd)
221 {
222 	struct launcher_logind *wl = wl_container_of(launcher, wl, base);
223 	struct stat st;
224 	int r;
225 
226 	r = fstat(fd, &st);
227 	close(fd);
228 	if (r < 0) {
229 		weston_log("logind: cannot fstat fd: %s\n", strerror(errno));
230 		return;
231 	}
232 
233 	if (!S_ISCHR(st.st_mode)) {
234 		weston_log("logind: invalid device passed\n");
235 		return;
236 	}
237 
238 	launcher_logind_release_device(wl, major(st.st_rdev),
239 				     minor(st.st_rdev));
240 }
241 
242 static int
launcher_logind_activate_vt(struct weston_launcher * launcher,int vt)243 launcher_logind_activate_vt(struct weston_launcher *launcher, int vt)
244 {
245 	struct launcher_logind *wl = wl_container_of(launcher, wl, base);
246 	DBusMessage *m;
247 	bool b;
248 	int r;
249 
250 	m = dbus_message_new_method_call("org.freedesktop.login1",
251 					 "/org/freedesktop/login1/seat/self",
252 					 "org.freedesktop.login1.Seat",
253 					 "SwitchTo");
254 	if (!m)
255 		return -ENOMEM;
256 
257 	b = dbus_message_append_args(m,
258 				     DBUS_TYPE_UINT32, &vt,
259 				     DBUS_TYPE_INVALID);
260 	if (!b) {
261 		r = -ENOMEM;
262 		goto err_unref;
263 	}
264 
265 	dbus_connection_send(wl->dbus, m, NULL);
266 	r = 0;
267 
268  err_unref:
269 	dbus_message_unref(m);
270 	return r;
271 }
272 
273 static void
launcher_logind_set_active(struct launcher_logind * wl,bool active)274 launcher_logind_set_active(struct launcher_logind *wl, bool active)
275 {
276 	if (wl->compositor->session_active == active)
277 		return;
278 
279 	wl->compositor->session_active = active;
280 
281 	wl_signal_emit(&wl->compositor->session_signal,
282 		       wl->compositor);
283 }
284 
285 static void
parse_active(struct launcher_logind * wl,DBusMessage * m,DBusMessageIter * iter)286 parse_active(struct launcher_logind *wl, DBusMessage *m, DBusMessageIter *iter)
287 {
288 	DBusMessageIter sub;
289 	dbus_bool_t b;
290 
291 	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
292 		return;
293 
294 	dbus_message_iter_recurse(iter, &sub);
295 
296 	if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
297 		return;
298 
299 	dbus_message_iter_get_basic(&sub, &b);
300 
301 	/* If the backend requested DRM master-device synchronization, we only
302 	 * wake-up the compositor once the master-device is up and running. For
303 	 * other backends, we immediately forward the Active-change event. */
304 	if (!wl->sync_drm || !b)
305 		launcher_logind_set_active(wl, b);
306 }
307 
308 static void
get_active_cb(DBusPendingCall * pending,void * data)309 get_active_cb(DBusPendingCall *pending, void *data)
310 {
311 	struct launcher_logind *wl = data;
312 	DBusMessageIter iter;
313 	DBusMessage *m;
314 	int type;
315 
316 	dbus_pending_call_unref(wl->pending_active);
317 	wl->pending_active = NULL;
318 
319 	m = dbus_pending_call_steal_reply(pending);
320 	if (!m)
321 		return;
322 
323 	type = dbus_message_get_type(m);
324 	if (type == DBUS_MESSAGE_TYPE_METHOD_RETURN &&
325 	    dbus_message_iter_init(m, &iter))
326 		parse_active(wl, m, &iter);
327 
328 	dbus_message_unref(m);
329 }
330 
331 static void
launcher_logind_get_active(struct launcher_logind * wl)332 launcher_logind_get_active(struct launcher_logind *wl)
333 {
334 	DBusPendingCall *pending;
335 	DBusMessage *m;
336 	bool b;
337 	const char *iface, *name;
338 
339 	m = dbus_message_new_method_call("org.freedesktop.login1",
340 					 wl->spath,
341 					 "org.freedesktop.DBus.Properties",
342 					 "Get");
343 	if (!m)
344 		return;
345 
346 	iface = "org.freedesktop.login1.Session";
347 	name = "Active";
348 	b = dbus_message_append_args(m,
349 				     DBUS_TYPE_STRING, &iface,
350 				     DBUS_TYPE_STRING, &name,
351 				     DBUS_TYPE_INVALID);
352 	if (!b)
353 		goto err_unref;
354 
355 	b = dbus_connection_send_with_reply(wl->dbus, m, &pending, -1);
356 	if (!b)
357 		goto err_unref;
358 
359 	b = dbus_pending_call_set_notify(pending, get_active_cb, wl, NULL);
360 	if (!b) {
361 		dbus_pending_call_cancel(pending);
362 		dbus_pending_call_unref(pending);
363 		goto err_unref;
364 	}
365 
366 	if (wl->pending_active) {
367 		dbus_pending_call_cancel(wl->pending_active);
368 		dbus_pending_call_unref(wl->pending_active);
369 	}
370 	wl->pending_active = pending;
371 	return;
372 
373 err_unref:
374 	dbus_message_unref(m);
375 }
376 
377 static void
disconnected_dbus(struct launcher_logind * wl)378 disconnected_dbus(struct launcher_logind *wl)
379 {
380 	weston_log("logind: dbus connection lost, exiting..\n");
381 	exit(-1);
382 }
383 
384 static void
session_removed(struct launcher_logind * wl,DBusMessage * m)385 session_removed(struct launcher_logind *wl, DBusMessage *m)
386 {
387 	const char *name, *obj;
388 	bool r;
389 
390 	r = dbus_message_get_args(m, NULL,
391 				  DBUS_TYPE_STRING, &name,
392 				  DBUS_TYPE_OBJECT_PATH, &obj,
393 				  DBUS_TYPE_INVALID);
394 	if (!r) {
395 		weston_log("logind: cannot parse SessionRemoved dbus signal\n");
396 		return;
397 	}
398 
399 	if (!strcmp(name, wl->sid)) {
400 		weston_log("logind: our session got closed, exiting..\n");
401 		exit(-1);
402 	}
403 }
404 
405 static void
property_changed(struct launcher_logind * wl,DBusMessage * m)406 property_changed(struct launcher_logind *wl, DBusMessage *m)
407 {
408 	DBusMessageIter iter, sub, entry;
409 	const char *interface, *name;
410 
411 	if (!dbus_message_iter_init(m, &iter) ||
412 	    dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
413 		goto error;
414 
415 	dbus_message_iter_get_basic(&iter, &interface);
416 
417 	if (!dbus_message_iter_next(&iter) ||
418 	    dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
419 		goto error;
420 
421 	dbus_message_iter_recurse(&iter, &sub);
422 
423 	while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_DICT_ENTRY) {
424 		dbus_message_iter_recurse(&sub, &entry);
425 
426 		if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
427 			goto error;
428 
429 		dbus_message_iter_get_basic(&entry, &name);
430 		if (!dbus_message_iter_next(&entry))
431 			goto error;
432 
433 		if (!strcmp(name, "Active")) {
434 			parse_active(wl, m, &entry);
435 			return;
436 		}
437 
438 		dbus_message_iter_next(&sub);
439 	}
440 
441 	if (!dbus_message_iter_next(&iter) ||
442 	    dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
443 		goto error;
444 
445 	dbus_message_iter_recurse(&iter, &sub);
446 
447 	while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
448 		dbus_message_iter_get_basic(&sub, &name);
449 
450 		if (!strcmp(name, "Active")) {
451 			launcher_logind_get_active(wl);
452 			return;
453 		}
454 
455 		dbus_message_iter_next(&sub);
456 	}
457 
458 	return;
459 
460 error:
461 	weston_log("logind: cannot parse PropertiesChanged dbus signal\n");
462 }
463 
464 static void
device_paused(struct launcher_logind * wl,DBusMessage * m)465 device_paused(struct launcher_logind *wl, DBusMessage *m)
466 {
467 	bool r;
468 	const char *type;
469 	uint32_t major, minor;
470 
471 	r = dbus_message_get_args(m, NULL,
472 				  DBUS_TYPE_UINT32, &major,
473 				  DBUS_TYPE_UINT32, &minor,
474 				  DBUS_TYPE_STRING, &type,
475 				  DBUS_TYPE_INVALID);
476 	if (!r) {
477 		weston_log("logind: cannot parse PauseDevice dbus signal\n");
478 		return;
479 	}
480 
481 	/* "pause" means synchronous pausing. Acknowledge it unconditionally
482 	 * as we support asynchronous device shutdowns, anyway.
483 	 * "force" means asynchronous pausing.
484 	 * "gone" means the device is gone. We handle it the same as "force" as
485 	 * a following udev event will be caught, too.
486 	 *
487 	 * If it's our main DRM device, tell the compositor to go asleep. */
488 
489 	if (!strcmp(type, "pause"))
490 		launcher_logind_pause_device_complete(wl, major, minor);
491 
492 	if (wl->sync_drm && wl->compositor->backend->device_changed)
493 		wl->compositor->backend->device_changed(wl->compositor,
494 							makedev(major,minor),
495 							false);
496 }
497 
498 static void
device_resumed(struct launcher_logind * wl,DBusMessage * m)499 device_resumed(struct launcher_logind *wl, DBusMessage *m)
500 {
501 	bool r;
502 	uint32_t major, minor;
503 
504 	r = dbus_message_get_args(m, NULL,
505 				  DBUS_TYPE_UINT32, &major,
506 				  DBUS_TYPE_UINT32, &minor,
507 				  DBUS_TYPE_INVALID);
508 	if (!r) {
509 		weston_log("logind: cannot parse ResumeDevice dbus signal\n");
510 		return;
511 	}
512 
513 	/* DeviceResumed messages provide us a new file-descriptor for
514 	 * resumed devices. For DRM devices it's the same as before, for evdev
515 	 * devices it's a new open-file. As we reopen evdev devices, anyway,
516 	 * there is no need for us to handle this event for evdev. For DRM, we
517 	 * notify the compositor to wake up. */
518 
519 	if (wl->sync_drm && wl->compositor->backend->device_changed)
520 		wl->compositor->backend->device_changed(wl->compositor,
521 							makedev(major,minor),
522 							true);
523 }
524 
525 static DBusHandlerResult
filter_dbus(DBusConnection * c,DBusMessage * m,void * data)526 filter_dbus(DBusConnection *c, DBusMessage *m, void *data)
527 {
528 	struct launcher_logind *wl = data;
529 
530 	if (dbus_message_is_signal(m, DBUS_INTERFACE_LOCAL, "Disconnected")) {
531 		disconnected_dbus(wl);
532 	} else if (dbus_message_is_signal(m, "org.freedesktop.login1.Manager",
533 					  "SessionRemoved")) {
534 		session_removed(wl, m);
535 	} else if (dbus_message_is_signal(m, "org.freedesktop.DBus.Properties",
536 					  "PropertiesChanged")) {
537 		property_changed(wl, m);
538 	} else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session",
539 					  "PauseDevice")) {
540 		device_paused(wl, m);
541 	} else if (dbus_message_is_signal(m, "org.freedesktop.login1.Session",
542 					  "ResumeDevice")) {
543 		device_resumed(wl, m);
544 	}
545 
546 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
547 }
548 
549 static int
launcher_logind_setup_dbus(struct launcher_logind * wl)550 launcher_logind_setup_dbus(struct launcher_logind *wl)
551 {
552 	bool b;
553 	int r;
554 
555 	r = asprintf(&wl->spath, "/org/freedesktop/login1/session/%s",
556 		     wl->sid);
557 	if (r < 0)
558 		return -ENOMEM;
559 
560 	b = dbus_connection_add_filter(wl->dbus, filter_dbus, wl, NULL);
561 	if (!b) {
562 		weston_log("logind: cannot add dbus filter\n");
563 		r = -ENOMEM;
564 		goto err_spath;
565 	}
566 
567 	r = weston_dbus_add_match_signal(wl->dbus,
568 					 "org.freedesktop.login1",
569 					 "org.freedesktop.login1.Manager",
570 					 "SessionRemoved",
571 					 "/org/freedesktop/login1");
572 	if (r < 0) {
573 		weston_log("logind: cannot add dbus match\n");
574 		goto err_spath;
575 	}
576 
577 	r = weston_dbus_add_match_signal(wl->dbus,
578 					"org.freedesktop.login1",
579 					"org.freedesktop.login1.Session",
580 					"PauseDevice",
581 					wl->spath);
582 	if (r < 0) {
583 		weston_log("logind: cannot add dbus match\n");
584 		goto err_spath;
585 	}
586 
587 	r = weston_dbus_add_match_signal(wl->dbus,
588 					"org.freedesktop.login1",
589 					"org.freedesktop.login1.Session",
590 					"ResumeDevice",
591 					wl->spath);
592 	if (r < 0) {
593 		weston_log("logind: cannot add dbus match\n");
594 		goto err_spath;
595 	}
596 
597 	r = weston_dbus_add_match_signal(wl->dbus,
598 					"org.freedesktop.login1",
599 					"org.freedesktop.DBus.Properties",
600 					"PropertiesChanged",
601 					wl->spath);
602 	if (r < 0) {
603 		weston_log("logind: cannot add dbus match\n");
604 		goto err_spath;
605 	}
606 
607 	return 0;
608 
609 err_spath:
610 	/* don't remove any dbus-match as the connection is closed, anyway */
611 	free(wl->spath);
612 	return r;
613 }
614 
615 static void
launcher_logind_destroy_dbus(struct launcher_logind * wl)616 launcher_logind_destroy_dbus(struct launcher_logind *wl)
617 {
618 	/* don't remove any dbus-match as the connection is closed, anyway */
619 	free(wl->spath);
620 }
621 
622 static int
launcher_logind_take_control(struct launcher_logind * wl)623 launcher_logind_take_control(struct launcher_logind *wl)
624 {
625 	DBusError err;
626 	DBusMessage *m, *reply;
627 	dbus_bool_t force;
628 	bool b;
629 	int r;
630 
631 	dbus_error_init(&err);
632 
633 	m = dbus_message_new_method_call("org.freedesktop.login1",
634 					 wl->spath,
635 					 "org.freedesktop.login1.Session",
636 					 "TakeControl");
637 	if (!m)
638 		return -ENOMEM;
639 
640 	force = false;
641 	b = dbus_message_append_args(m,
642 				     DBUS_TYPE_BOOLEAN, &force,
643 				     DBUS_TYPE_INVALID);
644 	if (!b) {
645 		r = -ENOMEM;
646 		goto err_unref;
647 	}
648 
649 	reply = dbus_connection_send_with_reply_and_block(wl->dbus,
650 							  m, -1, &err);
651 	if (!reply) {
652 		if (dbus_error_has_name(&err, DBUS_ERROR_UNKNOWN_METHOD))
653 			weston_log("logind: old systemd version detected\n");
654 		else
655 			weston_log("logind: cannot take control over session %s\n", wl->sid);
656 
657 		dbus_error_free(&err);
658 		r = -EIO;
659 		goto err_unref;
660 	}
661 
662 	dbus_message_unref(reply);
663 	dbus_message_unref(m);
664 	return 0;
665 
666 err_unref:
667 	dbus_message_unref(m);
668 	return r;
669 }
670 
671 static void
launcher_logind_release_control(struct launcher_logind * wl)672 launcher_logind_release_control(struct launcher_logind *wl)
673 {
674 	DBusMessage *m;
675 
676 	m = dbus_message_new_method_call("org.freedesktop.login1",
677 					 wl->spath,
678 					 "org.freedesktop.login1.Session",
679 					 "ReleaseControl");
680 	if (m) {
681 		dbus_connection_send(wl->dbus, m, NULL);
682 		dbus_message_unref(m);
683 	}
684 }
685 
686 static int
weston_sd_session_get_vt(const char * sid,unsigned int * out)687 weston_sd_session_get_vt(const char *sid, unsigned int *out)
688 {
689 #ifdef HAVE_SYSTEMD_LOGIN_209
690 	return sd_session_get_vt(sid, out);
691 #else
692 	int r;
693 	char *tty;
694 
695 	r = sd_session_get_tty(sid, &tty);
696 	if (r < 0)
697 		return r;
698 
699 	r = sscanf(tty, "tty%u", out);
700 	free(tty);
701 
702 	if (r != 1)
703 		return -EINVAL;
704 
705 	return 0;
706 #endif
707 }
708 
709 static int
launcher_logind_activate(struct launcher_logind * wl)710 launcher_logind_activate(struct launcher_logind *wl)
711 {
712 	DBusMessage *m;
713 
714 	m = dbus_message_new_method_call("org.freedesktop.login1",
715 					 wl->spath,
716 					 "org.freedesktop.login1.Session",
717 					 "Activate");
718 	if (!m)
719 		return -ENOMEM;
720 
721 	dbus_connection_send(wl->dbus, m, NULL);
722 	return 0;
723 }
724 
725 static int
launcher_logind_connect(struct weston_launcher ** out,struct weston_compositor * compositor,int tty,const char * seat_id,bool sync_drm)726 launcher_logind_connect(struct weston_launcher **out, struct weston_compositor *compositor,
727 			int tty, const char *seat_id, bool sync_drm)
728 {
729 	struct launcher_logind *wl;
730 	struct wl_event_loop *loop;
731 	char *t;
732 	int r;
733 
734 	wl = zalloc(sizeof(*wl));
735 	if (wl == NULL) {
736 		r = -ENOMEM;
737 		goto err_out;
738 	}
739 
740 	wl->base.iface = &launcher_logind_iface;
741 	wl->compositor = compositor;
742 	wl->sync_drm = sync_drm;
743 
744 	wl->seat = strdup(seat_id);
745 	if (!wl->seat) {
746 		r = -ENOMEM;
747 		goto err_wl;
748 	}
749 
750 	r = sd_pid_get_session(getpid(), &wl->sid);
751 	if (r < 0) {
752 		weston_log("logind: not running in a systemd session\n");
753 		goto err_seat;
754 	}
755 
756 	t = NULL;
757 	r = sd_session_get_seat(wl->sid, &t);
758 	if (r < 0) {
759 		weston_log("logind: failed to get session seat\n");
760 		free(t);
761 		goto err_session;
762 	} else if (strcmp(seat_id, t)) {
763 		weston_log("logind: weston's seat '%s' differs from session-seat '%s'\n",
764 			   seat_id, t);
765 		r = -EINVAL;
766 		free(t);
767 		goto err_session;
768 	}
769 
770 	r = strcmp(t, "seat0");
771 	free(t);
772 	if (r == 0) {
773 		r = weston_sd_session_get_vt(wl->sid, &wl->vtnr);
774 		if (r < 0) {
775 			weston_log("logind: session not running on a VT\n");
776 			goto err_session;
777 		} else if (tty > 0 && wl->vtnr != (unsigned int )tty) {
778 			weston_log("logind: requested VT --tty=%d differs from real session VT %u\n",
779 				   tty, wl->vtnr);
780 			r = -EINVAL;
781 			goto err_session;
782 		}
783 	}
784 
785 	loop = wl_display_get_event_loop(compositor->wl_display);
786 	r = weston_dbus_open(loop, DBUS_BUS_SYSTEM, &wl->dbus, &wl->dbus_ctx);
787 	if (r < 0) {
788 		weston_log("logind: cannot connect to system dbus\n");
789 		goto err_session;
790 	}
791 
792 	r = launcher_logind_setup_dbus(wl);
793 	if (r < 0)
794 		goto err_dbus;
795 
796 	r = launcher_logind_take_control(wl);
797 	if (r < 0)
798 		goto err_dbus_cleanup;
799 
800 	r = launcher_logind_activate(wl);
801 	if (r < 0)
802 		goto err_dbus_cleanup;
803 
804 	weston_log("logind: session control granted\n");
805 	* (struct launcher_logind **) out = wl;
806 	return 0;
807 
808 err_dbus_cleanup:
809 	launcher_logind_destroy_dbus(wl);
810 err_dbus:
811 	weston_dbus_close(wl->dbus, wl->dbus_ctx);
812 err_session:
813 	free(wl->sid);
814 err_seat:
815 	free(wl->seat);
816 err_wl:
817 	free(wl);
818 err_out:
819 	weston_log("logind: cannot setup systemd-logind helper (%d), using legacy fallback\n", r);
820 	errno = -r;
821 	return -1;
822 }
823 
824 static void
launcher_logind_destroy(struct weston_launcher * launcher)825 launcher_logind_destroy(struct weston_launcher *launcher)
826 {
827 	struct launcher_logind *wl = wl_container_of(launcher, wl, base);
828 
829 	if (wl->pending_active) {
830 		dbus_pending_call_cancel(wl->pending_active);
831 		dbus_pending_call_unref(wl->pending_active);
832 	}
833 
834 	launcher_logind_release_control(wl);
835 	launcher_logind_destroy_dbus(wl);
836 	weston_dbus_close(wl->dbus, wl->dbus_ctx);
837 	free(wl->sid);
838 	free(wl->seat);
839 	free(wl);
840 }
841 
842 static int
launcher_logind_get_vt(struct weston_launcher * launcher)843 launcher_logind_get_vt(struct weston_launcher *launcher)
844 {
845 	struct launcher_logind *wl = wl_container_of(launcher, wl, base);
846 	return wl->vtnr;
847 }
848 
849 const struct launcher_interface launcher_logind_iface = {
850 	.connect = launcher_logind_connect,
851 	.destroy = launcher_logind_destroy,
852 	.open = launcher_logind_open,
853 	.close = launcher_logind_close,
854 	.activate_vt = launcher_logind_activate_vt,
855 	.get_vt = launcher_logind_get_vt,
856 };
857