chiark / gitweb /
logind: port logind to libsystemd-bus
[elogind.git] / src / login / logind-session-dbus.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <string.h>
24
25 #include "util.h"
26 #include "strv.h"
27 #include "bus-util.h"
28
29 #include "logind.h"
30 #include "logind-session.h"
31 #include "logind-session-device.h"
32
33 static int property_get_user(
34                 sd_bus *bus,
35                 const char *path,
36                 const char *interface,
37                 const char *property,
38                 sd_bus_message *reply,
39                 sd_bus_error *error,
40                 void *userdata) {
41
42         _cleanup_free_ char *p = NULL;
43         Session *s = userdata;
44
45         assert(bus);
46         assert(reply);
47         assert(s);
48
49         p = user_bus_path(s->user);
50         if (!p)
51                 return -ENOMEM;
52
53         return sd_bus_message_append(reply, "(uo)", (uint32_t) s->user->uid, p);
54 }
55
56 static int property_get_name(
57                 sd_bus *bus,
58                 const char *path,
59                 const char *interface,
60                 const char *property,
61                 sd_bus_message *reply,
62                 sd_bus_error *error,
63                 void *userdata) {
64
65         Session *s = userdata;
66
67         assert(bus);
68         assert(reply);
69         assert(s);
70
71         return sd_bus_message_append(reply, "s", s->user->name);
72 }
73
74 static int property_get_seat(
75                 sd_bus *bus,
76                 const char *path,
77                 const char *interface,
78                 const char *property,
79                 sd_bus_message *reply,
80                 sd_bus_error *error,
81                 void *userdata) {
82
83         _cleanup_free_ char *p = NULL;
84         Session *s = userdata;
85
86         assert(bus);
87         assert(reply);
88         assert(s);
89
90         p = s->seat ? seat_bus_path(s->seat) : strdup("/");
91         if (!p)
92                 return -ENOMEM;
93
94         return sd_bus_message_append(reply, "(so)", s->seat ? s->seat->id : "", p);
95 }
96
97 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, session_type, SessionType);
98 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_class, session_class, SessionClass);
99
100 static int property_get_active(
101                 sd_bus *bus,
102                 const char *path,
103                 const char *interface,
104                 const char *property,
105                 sd_bus_message *reply,
106                 sd_bus_error *error,
107                 void *userdata) {
108
109         Session *s = userdata;
110
111         assert(bus);
112         assert(reply);
113         assert(s);
114
115         return sd_bus_message_append(reply, "b", session_is_active(s));
116 }
117
118 static int property_get_state(
119                 sd_bus *bus,
120                 const char *path,
121                 const char *interface,
122                 const char *property,
123                 sd_bus_message *reply,
124                 sd_bus_error *error,
125                 void *userdata) {
126
127         Session *s = userdata;
128
129         assert(bus);
130         assert(reply);
131         assert(s);
132
133         return sd_bus_message_append(reply, "s", session_state_to_string(session_get_state(s)));
134 }
135
136 static int property_get_idle_hint(
137                 sd_bus *bus,
138                 const char *path,
139                 const char *interface,
140                 const char *property,
141                 sd_bus_message *reply,
142                 sd_bus_error *error,
143                 void *userdata) {
144
145         Session *s = userdata;
146
147         assert(bus);
148         assert(reply);
149         assert(s);
150
151         return sd_bus_message_append(reply, "b", session_get_idle_hint(s, NULL) > 0);
152 }
153
154 static int property_get_idle_since_hint(
155                 sd_bus *bus,
156                 const char *path,
157                 const char *interface,
158                 const char *property,
159                 sd_bus_message *reply,
160                 sd_bus_error *error,
161                 void *userdata) {
162
163         Session *s = userdata;
164         dual_timestamp t;
165         uint64_t u;
166         int r;
167
168         assert(bus);
169         assert(reply);
170         assert(s);
171
172         r = session_get_idle_hint(s, &t);
173         if (r < 0)
174                 return r;
175
176         u = streq(property, "IdleSinceHint") ? t.realtime : t.monotonic;
177
178         return sd_bus_message_append(reply, "t", u);
179 }
180
181 static int method_terminate(sd_bus *bus, sd_bus_message *message, void *userdata) {
182         Session *s = userdata;
183         int r;
184
185         assert(bus);
186         assert(message);
187         assert(s);
188
189         r = session_stop(s);
190         if (r < 0)
191                 return sd_bus_reply_method_errno(bus, message, r, NULL);
192
193         return sd_bus_reply_method_return(bus, message, NULL);
194 }
195
196 static int method_activate(sd_bus *bus, sd_bus_message *message, void *userdata) {
197         Session *s = userdata;
198         int r;
199
200         assert(bus);
201         assert(message);
202         assert(s);
203
204         r = session_activate(s);
205         if (r < 0)
206                 return sd_bus_reply_method_errno(bus, message, r, NULL);
207
208         return sd_bus_reply_method_return(bus, message, NULL);
209 }
210
211 static int method_lock(sd_bus *bus, sd_bus_message *message, void *userdata) {
212         Session *s = userdata;
213         int r;
214
215         assert(bus);
216         assert(message);
217         assert(s);
218
219         r = session_send_lock(s, streq(sd_bus_message_get_member(message), "Lock"));
220         if (r < 0)
221                 return sd_bus_reply_method_errno(bus, message, r, NULL);
222
223         return sd_bus_reply_method_return(bus, message, NULL);
224 }
225
226 static int method_set_idle_hint(sd_bus *bus, sd_bus_message *message, void *userdata) {
227         Session *s = userdata;
228         uid_t uid;
229         int r, b;
230
231         assert(bus);
232         assert(message);
233         assert(s);
234
235         r = sd_bus_message_read(message, "b", &b);
236         if (r < 0)
237                 return sd_bus_reply_method_errno(bus, message, r, NULL);
238
239         r = sd_bus_get_owner_uid(bus, sd_bus_message_get_sender(message), &uid);
240         if (r < 0)
241                 return sd_bus_reply_method_errno(bus, message, r, NULL);
242
243         if (uid != 0 && uid != s->user->uid)
244                 return sd_bus_reply_method_errorf(bus, message, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session my set idle hint");
245
246         session_set_idle_hint(s, b);
247
248         return sd_bus_reply_method_return(bus, message, NULL);
249 }
250
251 static int method_kill(sd_bus *bus, sd_bus_message *message, void *userdata) {
252         Session *s = userdata;
253         const char *swho;
254         int32_t signo;
255         KillWho who;
256         int r;
257
258         assert(bus);
259         assert(message);
260         assert(s);
261
262         r = sd_bus_message_read(message, "si", &swho, &signo);
263         if (r < 0)
264                 return sd_bus_reply_method_errno(bus, message, r, NULL);
265
266         if (isempty(swho))
267                 who = KILL_ALL;
268         else {
269                 who = kill_who_from_string(swho);
270                 if (who < 0)
271                         return sd_bus_reply_method_errorf(bus, message, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
272         }
273
274         if (signo <= 0 || signo >= _NSIG)
275                 return sd_bus_reply_method_errorf(bus, message, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
276
277         r = session_kill(s, who, signo);
278         if (r < 0)
279                 return sd_bus_reply_method_errno(bus, message, r, NULL);
280
281         return sd_bus_reply_method_return(bus, message, NULL);
282 }
283
284 static int method_take_control(sd_bus *bus, sd_bus_message *message, void *userdata) {
285         Session *s = userdata;
286         int r, force;
287         uid_t uid;
288
289         assert(bus);
290         assert(message);
291         assert(s);
292
293         r = sd_bus_message_read(message, "b", &force);
294         if (r < 0)
295                 return sd_bus_reply_method_errno(bus, message, r, NULL);
296
297         r = sd_bus_get_owner_uid(bus, sd_bus_message_get_sender(message), &uid);
298         if (r < 0)
299                 return sd_bus_reply_method_errno(bus, message, r, NULL);
300
301         if (uid != 0 && (force || uid != s->user->uid))
302                 return sd_bus_reply_method_errorf(bus, message, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may take control");
303
304         r = session_set_controller(s, sd_bus_message_get_sender(message), force);
305         if (r < 0)
306                 return sd_bus_reply_method_errno(bus, message, r, NULL);
307
308         return sd_bus_reply_method_return(bus, message, NULL);
309 }
310
311 static int method_release_control(sd_bus *bus, sd_bus_message *message, void *userdata) {
312         Session *s = userdata;
313
314         assert(bus);
315         assert(message);
316         assert(s);
317
318         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
319                 return sd_bus_reply_method_errorf(bus, message, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
320
321         session_drop_controller(s);
322
323         return sd_bus_reply_method_return(bus, message, NULL);
324 }
325
326 static int method_take_device(sd_bus *bus, sd_bus_message *message, void *userdata) {
327         Session *s = userdata;
328         uint32_t major, minor;
329         SessionDevice *sd;
330         dev_t dev;
331         int r;
332
333         assert(bus);
334         assert(message);
335         assert(s);
336
337         r = sd_bus_message_read(message, "uu", &major, &minor);
338         if (r < 0)
339                 return sd_bus_reply_method_errno(bus, message, r, NULL);
340
341         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
342                 return sd_bus_reply_method_errorf(bus, message, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
343
344         dev = makedev(major, minor);
345         sd = hashmap_get(s->devices, &dev);
346         if (sd)
347                 /* We don't allow retrieving a device multiple times.
348                  * The related ReleaseDevice call is not ref-counted.
349                  * The caller should use dup() if it requires more
350                  * than one fd (it would be functionally
351                  * equivalent). */
352                 return sd_bus_reply_method_errorf(bus, message, BUS_ERROR_DEVICE_IS_TAKEN, "Device already taken");
353
354         r = session_device_new(s, dev, &sd);
355         if (r < 0)
356                 return sd_bus_reply_method_errno(bus, message, r, NULL);
357
358         r = sd_bus_reply_method_return(bus, message, "hb", sd->fd, !sd->active);
359         if (r < 0)
360                 session_device_free(sd);
361
362         return r;
363 }
364
365 static int method_release_device(sd_bus *bus, sd_bus_message *message, void *userdata) {
366         Session *s = userdata;
367         uint32_t major, minor;
368         SessionDevice *sd;
369         dev_t dev;
370         int r;
371
372         assert(bus);
373         assert(message);
374         assert(s);
375
376         r = sd_bus_message_read(message, "uu", &major, &minor);
377         if (r < 0)
378                 return sd_bus_reply_method_errno(bus, message, r, NULL);
379
380         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
381                 return sd_bus_reply_method_errorf(bus, message, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
382
383         dev = makedev(major, minor);
384         sd = hashmap_get(s->devices, &dev);
385         if (!sd)
386                 return sd_bus_reply_method_errorf(bus, message, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken");
387
388         session_device_free(sd);
389         return sd_bus_reply_method_return(bus, message, NULL);
390 }
391
392 static int method_pause_device_complete(sd_bus *bus, sd_bus_message *message, void *userdata) {
393         Session *s = userdata;
394         uint32_t major, minor;
395         SessionDevice *sd;
396         dev_t dev;
397         int r;
398
399         assert(bus);
400         assert(message);
401         assert(s);
402
403         r = sd_bus_message_read(message, "uu", &major, &minor);
404         if (r < 0)
405                 return sd_bus_reply_method_errno(bus, message, r, NULL);
406
407         if (!session_is_controller(s, sd_bus_message_get_sender(message)))
408                 return sd_bus_reply_method_errorf(bus, message, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
409
410         dev = makedev(major, minor);
411         sd = hashmap_get(s->devices, &dev);
412         if (!sd)
413                 return sd_bus_reply_method_errorf(bus, message, BUS_ERROR_DEVICE_NOT_TAKEN, "Device not taken");
414
415         session_device_complete_pause(sd);
416
417         return sd_bus_reply_method_return(bus, message, NULL);
418 }
419
420 const sd_bus_vtable session_vtable[] = {
421         SD_BUS_VTABLE_START(0),
422
423         SD_BUS_PROPERTY("Id", "s", NULL, offsetof(Session, id), 0),
424         SD_BUS_PROPERTY("User", "(uo)", property_get_user, 0, 0),
425         SD_BUS_PROPERTY("Name", "s", property_get_name, 0, 0),
426         SD_BUS_PROPERTY("Timestamp", "t", NULL, offsetof(Session, timestamp.realtime), 0),
427         SD_BUS_PROPERTY("TimestampMonotonic", "t", NULL, offsetof(Session, timestamp.monotonic), 0),
428         SD_BUS_PROPERTY("VTNr", "u", NULL, offsetof(Session, vtnr), 0),
429         SD_BUS_PROPERTY("Seat", "(so)", property_get_seat, 0, 0),
430         SD_BUS_PROPERTY("TTY", "s", NULL, offsetof(Session, tty), 0),
431         SD_BUS_PROPERTY("Display", "s", NULL, offsetof(Session, display), 0),
432         SD_BUS_PROPERTY("Remote", "b", bus_property_get_bool, offsetof(Session, remote), 0),
433         SD_BUS_PROPERTY("RemoteHost", "s", NULL, offsetof(Session, remote_host), 0),
434         SD_BUS_PROPERTY("RemoteUser", "s", NULL, offsetof(Session, remote_user), 0),
435         SD_BUS_PROPERTY("Service", "s", NULL, offsetof(Session, service), 0),
436         SD_BUS_PROPERTY("Scope", "s", NULL, offsetof(Session, scope), 0),
437         SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Session, leader), 0),
438         SD_BUS_PROPERTY("Audit", "u", NULL, offsetof(Session, audit_id), 0),
439         SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Session, type), 0),
440         SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), 0),
441         SD_BUS_PROPERTY("Active", "b", property_get_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
442         SD_BUS_PROPERTY("State", "s", property_get_state, 0, 0),
443         SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
444         SD_BUS_PROPERTY("IdleSinceHint", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
445         SD_BUS_PROPERTY("IdleSinceHintMonotonic", "t", property_get_idle_since_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
446
447         SD_BUS_METHOD("Terminate", NULL, NULL, method_terminate, 0),
448         SD_BUS_METHOD("Activate", NULL, NULL, method_activate, 0),
449         SD_BUS_METHOD("Lock", NULL, NULL, method_lock, 0),
450         SD_BUS_METHOD("Unlock", NULL, NULL, method_lock, 0),
451         SD_BUS_METHOD("SetIdleHint", "b", NULL, method_set_idle_hint, 0),
452         SD_BUS_METHOD("Kill", "si", NULL, method_kill, 0),
453         SD_BUS_METHOD("TakeControl", "b", NULL, method_take_control, 0),
454         SD_BUS_METHOD("ReleaseControl", NULL, NULL, method_release_control, 0),
455         SD_BUS_METHOD("TakeDevice", "uu", "hb", method_take_device, 0),
456         SD_BUS_METHOD("ReleaseDevice", "uu", NULL, method_release_device, 0),
457         SD_BUS_METHOD("PauseDeviceComplete", "uu", NULL, method_pause_device_complete, 0),
458
459         SD_BUS_SIGNAL("PauseDevice", "uus", 0),
460         SD_BUS_SIGNAL("ResumeDevice", "uuh", 0),
461         SD_BUS_SIGNAL("Lock", NULL, 0),
462         SD_BUS_SIGNAL("Unlock", NULL, 0),
463
464         SD_BUS_VTABLE_END
465 };
466
467 int session_object_find(sd_bus *bus, const char *path, const char *interface, void **found, void *userdata) {
468         _cleanup_free_ char *e = NULL;
469         Manager *m = userdata;
470         Session *session;
471         const char *p;
472
473         assert(bus);
474         assert(path);
475         assert(interface);
476         assert(found);
477         assert(m);
478
479         p = startswith(path, "/org/freedesktop/login1/session/");
480         if (!p)
481                 return 0;
482
483         e = bus_path_unescape(p);
484         if (!e)
485                 return -ENOMEM;
486
487         session = hashmap_get(m->sessions, e);
488         if (!session)
489                 return 0;
490
491         *found = session;
492         return 1;
493 }
494
495 char *session_bus_path(Session *s) {
496         _cleanup_free_ char *t = NULL;
497
498         assert(s);
499
500         t = bus_path_escape(s->id);
501         if (!t)
502                 return NULL;
503
504         return strappend("/org/freedesktop/login1/session/", t);
505 }
506
507 int session_node_enumerator(sd_bus *bus, const char *path, char ***nodes, void *userdata) {
508         _cleanup_strv_free_ char **l = NULL;
509         Manager *m = userdata;
510         Session *session;
511         Iterator i;
512         int r;
513
514         assert(bus);
515         assert(path);
516         assert(nodes);
517
518         HASHMAP_FOREACH(session, m->sessions, i) {
519                 char *p;
520
521                 p = session_bus_path(session);
522                 if (!p)
523                         return -ENOMEM;
524
525                 r = strv_push(&l, p);
526                 if (r < 0) {
527                         free(p);
528                         return r;
529                 }
530         }
531
532         *nodes = l;
533         l = NULL;
534
535         return 1;
536 }
537
538 int session_send_signal(Session *s, bool new_session) {
539         _cleanup_free_ char *p = NULL;
540
541         assert(s);
542
543         p = session_bus_path(s);
544         if (!p)
545                 return -ENOMEM;
546
547         return sd_bus_emit_signal(
548                         s->manager->bus,
549                         "/org/freedesktop/login1",
550                         "org.freedesktop.login1.Manager",
551                         new_session ? "SessionNew" : "SessionRemoved",
552                         "so", s->id, p);
553 }
554
555 int session_send_changed(Session *s, const char *properties, ...) {
556         _cleanup_free_ char *p = NULL;
557         char **l;
558
559         assert(s);
560
561         if (!s->started)
562                 return 0;
563
564         p = session_bus_path(s);
565         if (!p)
566                 return -ENOMEM;
567
568         l = strv_from_stdarg_alloca(properties);
569
570         return sd_bus_emit_properties_changed_strv(s->manager->bus, p, "org.freedesktop.login1.Session", l);
571 }
572
573 int session_send_lock(Session *s, bool lock) {
574         _cleanup_free_ char *p = NULL;
575
576         assert(s);
577
578         p = session_bus_path(s);
579         if (!p)
580                 return -ENOMEM;
581
582         return sd_bus_emit_signal(
583                         s->manager->bus,
584                         p,
585                         "org.freedesktop.login1.Session",
586                         lock ? "Lock" : "Unlock",
587                         NULL);
588 }
589
590 int session_send_lock_all(Manager *m, bool lock) {
591         Session *session;
592         Iterator i;
593         int r = 0;
594
595         assert(m);
596
597         HASHMAP_FOREACH(session, m->sessions, i) {
598                 int k;
599
600                 k = session_send_lock(session, lock);
601                 if (k < 0)
602                         r = k;
603         }
604
605         return r;
606 }
607
608 int session_send_create_reply(Session *s, sd_bus_error *error) {
609         _cleanup_bus_message_unref_ sd_bus_message *c = NULL;
610         _cleanup_close_ int fifo_fd = -1;
611         _cleanup_free_ char *p = NULL;
612
613         assert(s);
614
615         /* This is called after the session scope was successfully
616          * created, and finishes where bus_manager_create_session()
617          * left off. */
618
619         if (!s->create_message)
620                 return 0;
621
622         c = s->create_message;
623         s->create_message = NULL;
624
625         if (error)
626                 return sd_bus_reply_method_error(s->manager->bus, c, error);
627
628         /* Update the session state file before we notify the client
629          * about the result. */
630         session_save(s);
631
632         fifo_fd = session_create_fifo(s);
633         if (fifo_fd < 0)
634                 return fifo_fd;
635
636         p = session_bus_path(s);
637         if (!p)
638                 return -ENOMEM;
639
640         return sd_bus_reply_method_return(
641                         s->manager->bus, c,
642                         "soshsub",
643                         s->id,
644                         p,
645                         s->user->runtime_path,
646                         fifo_fd,
647                         s->seat ? s->seat->id : "",
648                         (uint32_t) s->vtnr,
649                         false);
650 }