chiark / gitweb /
logind: expose CanGraphical and CanTTY properties on seat objects
[elogind.git] / src / login / sd-login.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 <unistd.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <sys/inotify.h>
26
27 #include "util.h"
28 #include "cgroup-util.h"
29 #include "macro.h"
30 #include "sd-login.h"
31 #include "strv.h"
32
33 _public_ int sd_pid_get_session(pid_t pid, char **session) {
34         int r;
35         char *cgroup, *p;
36
37         if (pid < 0)
38                 return -EINVAL;
39
40         if (!session)
41                 return -EINVAL;
42
43         r = cg_pid_get_cgroup(pid, NULL, &cgroup);
44         if (r < 0)
45                 return r;
46
47         if (!startswith(cgroup, "/user/")) {
48                 free(cgroup);
49                 return -ENOENT;
50         }
51
52         p = strchr(cgroup + 6, '/');
53         if (!p) {
54                 free(cgroup);
55                 return -ENOENT;
56         }
57
58         p++;
59         if (startswith(p, "shared/") || streq(p, "shared")) {
60                 free(cgroup);
61                 return -ENOENT;
62         }
63
64         p = strndup(p, strcspn(p, "/"));
65         free(cgroup);
66
67         if (!p)
68                 return -ENOMEM;
69
70         *session = p;
71         return 0;
72 }
73
74 _public_ int sd_pid_get_unit(pid_t pid, char **unit) {
75
76         if (pid < 0)
77                 return -EINVAL;
78
79         if (!unit)
80                 return -EINVAL;
81
82         return cg_pid_get_unit(pid, unit);
83 }
84
85 _public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) {
86         int r;
87         char *root, *cgroup, *p, *cc;
88         struct stat st;
89
90         if (pid < 0)
91                 return -EINVAL;
92
93         if (!uid)
94                 return -EINVAL;
95
96         r = cg_pid_get_cgroup(pid, &root, &cgroup);
97         if (r < 0)
98                 return r;
99
100         if (!startswith(cgroup, "/user/")) {
101                 free(cgroup);
102                 free(root);
103                 return -ENOENT;
104         }
105
106         p = strchr(cgroup + 6, '/');
107         if (!p) {
108                 free(cgroup);
109                 return -ENOENT;
110         }
111
112         p++;
113         p += strcspn(p, "/");
114         *p = 0;
115
116         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, cgroup, &cc);
117         free(root);
118         free(cgroup);
119
120         if (r < 0)
121                 return -ENOMEM;
122
123         r = lstat(cc, &st);
124         free(cc);
125
126         if (r < 0)
127                 return -errno;
128
129         if (!S_ISDIR(st.st_mode))
130                 return -ENOTDIR;
131
132         *uid = st.st_uid;
133         return 0;
134 }
135
136 _public_ int sd_uid_get_state(uid_t uid, char**state) {
137         char *p, *s = NULL;
138         int r;
139
140         if (!state)
141                 return -EINVAL;
142
143         if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
144                 return -ENOMEM;
145
146         r = parse_env_file(p, NEWLINE, "STATE", &s, NULL);
147         free(p);
148
149         if (r == -ENOENT) {
150                 free(s);
151                 s = strdup("offline");
152                 if (!s)
153                         return -ENOMEM;
154
155                 *state = s;
156                 return 0;
157         } else if (r < 0) {
158                 free(s);
159                 return r;
160         } else if (!s)
161                 return -EIO;
162
163         *state = s;
164         return 0;
165 }
166
167 _public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) {
168         char *p, *w, *t, *state, *s = NULL;
169         size_t l;
170         int r;
171         const char *variable;
172
173         if (!seat)
174                 return -EINVAL;
175
176         variable = require_active ? "ACTIVE_UID" : "UIDS";
177
178         p = strappend("/run/systemd/seats/", seat);
179         if (!p)
180                 return -ENOMEM;
181
182         r = parse_env_file(p, NEWLINE, variable, &s, NULL);
183         free(p);
184
185         if (r < 0) {
186                 free(s);
187                 return r;
188         }
189
190         if (!s)
191                 return -EIO;
192
193         if (asprintf(&t, "%lu", (unsigned long) uid) < 0) {
194                 free(s);
195                 return -ENOMEM;
196         }
197
198         FOREACH_WORD(w, l, s, state) {
199                 if (strncmp(t, w, l) == 0) {
200                         free(s);
201                         free(t);
202
203                         return 1;
204                 }
205         }
206
207         free(s);
208         free(t);
209
210         return 0;
211 }
212
213 static int uid_get_array(uid_t uid, const char *variable, char ***array) {
214         char *p, *s = NULL;
215         char **a;
216         int r;
217
218         if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
219                 return -ENOMEM;
220
221         r = parse_env_file(p, NEWLINE,
222                            variable, &s,
223                            NULL);
224         free(p);
225
226         if (r < 0) {
227                 free(s);
228
229                 if (r == -ENOENT) {
230                         if (array)
231                                 *array = NULL;
232                         return 0;
233                 }
234
235                 return r;
236         }
237
238         if (!s) {
239                 if (array)
240                         *array = NULL;
241                 return 0;
242         }
243
244         a = strv_split(s, " ");
245         free(s);
246
247         if (!a)
248                 return -ENOMEM;
249
250         strv_uniq(a);
251         r = strv_length(a);
252
253         if (array)
254                 *array = a;
255         else
256                 strv_free(a);
257
258         return r;
259 }
260
261 _public_ int sd_uid_get_sessions(uid_t uid, int require_active, char ***sessions) {
262         return uid_get_array(uid, require_active ? "ACTIVE_SESSIONS" : "SESSIONS", sessions);
263 }
264
265 _public_ int sd_uid_get_seats(uid_t uid, int require_active, char ***seats) {
266         return uid_get_array(uid, require_active ? "ACTIVE_SEATS" : "SEATS", seats);
267 }
268
269 static int file_of_session(const char *session, char **_p) {
270         char *p;
271         int r;
272
273         assert(_p);
274
275         if (session)
276                 p = strappend("/run/systemd/sessions/", session);
277         else {
278                 char *buf;
279
280                 r = sd_pid_get_session(0, &buf);
281                 if (r < 0)
282                         return r;
283
284                 p = strappend("/run/systemd/sessions/", buf);
285                 free(buf);
286         }
287
288         if (!p)
289                 return -ENOMEM;
290
291         *_p = p;
292         return 0;
293 }
294
295 _public_ int sd_session_is_active(const char *session) {
296         int r;
297         char *p, *s = NULL;
298
299         r = file_of_session(session, &p);
300         if (r < 0)
301                 return r;
302
303         r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL);
304         free(p);
305
306         if (r < 0) {
307                 free(s);
308                 return r;
309         }
310
311         if (!s)
312                 return -EIO;
313
314         r = parse_boolean(s);
315         free(s);
316
317         return r;
318 }
319
320 _public_ int sd_session_get_uid(const char *session, uid_t *uid) {
321         int r;
322         char *p, *s = NULL;
323
324         if (!uid)
325                 return -EINVAL;
326
327         r = file_of_session(session, &p);
328         if (r < 0)
329                 return r;
330
331         r = parse_env_file(p, NEWLINE, "UID", &s, NULL);
332         free(p);
333
334         if (r < 0) {
335                 free(s);
336                 return r;
337         }
338
339         if (!s)
340                 return -EIO;
341
342         r = parse_uid(s, uid);
343         free(s);
344
345         return r;
346 }
347
348 static int session_get_string(const char *session, const char *field, char **value) {
349         char *p, *s = NULL;
350         int r;
351
352         if (!value)
353                 return -EINVAL;
354
355         r = file_of_session(session, &p);
356         if (r < 0)
357                 return r;
358
359         r = parse_env_file(p, NEWLINE, field, &s, NULL);
360         free(p);
361
362         if (r < 0) {
363                 free(s);
364                 return r;
365         }
366
367         if (isempty(s))
368                 return -ENOENT;
369
370         *value = s;
371         return 0;
372 }
373
374 _public_ int sd_session_get_seat(const char *session, char **seat) {
375         return session_get_string(session, "SEAT", seat);
376 }
377
378 _public_ int sd_session_get_service(const char *session, char **service) {
379         return session_get_string(session, "SERVICE", service);
380 }
381
382 _public_ int sd_session_get_type(const char *session, char **type) {
383         return session_get_string(session, "TYPE", type);
384 }
385
386 _public_ int sd_session_get_class(const char *session, char **class) {
387         return session_get_string(session, "CLASS", class);
388 }
389
390 _public_ int sd_session_get_display(const char *session, char **display) {
391         return session_get_string(session, "DISPLAY", display);
392 }
393
394 static int file_of_seat(const char *seat, char **_p) {
395         char *p;
396         int r;
397
398         assert(_p);
399
400         if (seat)
401                 p = strappend("/run/systemd/seats/", seat);
402         else {
403                 char *buf;
404
405                 r = sd_session_get_seat(NULL, &buf);
406                 if (r < 0)
407                         return r;
408
409                 p = strappend("/run/systemd/seats/", buf);
410                 free(buf);
411         }
412
413         if (!p)
414                 return -ENOMEM;
415
416         *_p = p;
417         return 0;
418 }
419
420 _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
421         char *p, *s = NULL, *t = NULL;
422         int r;
423
424         if (!session && !uid)
425                 return -EINVAL;
426
427         r = file_of_seat(seat, &p);
428         if (r < 0)
429                 return r;
430
431         r = parse_env_file(p, NEWLINE,
432                            "ACTIVE", &s,
433                            "ACTIVE_UID", &t,
434                            NULL);
435         free(p);
436
437         if (r < 0) {
438                 free(s);
439                 free(t);
440                 return r;
441         }
442
443         if (session && !s)  {
444                 free(t);
445                 return -ENOENT;
446         }
447
448         if (uid && !t) {
449                 free(s);
450                 return -ENOENT;
451         }
452
453         if (uid && t) {
454                 r = parse_uid(t, uid);
455                 if (r < 0) {
456                         free(t);
457                         free(s);
458                         return r;
459                 }
460         }
461
462         free(t);
463
464         if (session && s)
465                 *session = s;
466         else
467                 free(s);
468
469         return 0;
470 }
471
472 _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uids, unsigned *n_uids) {
473         char *p, *s = NULL, *t = NULL, **a = NULL;
474         uid_t *b = NULL;
475         unsigned n = 0;
476         int r;
477
478         r = file_of_seat(seat, &p);
479         if (r < 0)
480                 return r;
481
482         r = parse_env_file(p, NEWLINE,
483                            "SESSIONS", &s,
484                            "ACTIVE_SESSIONS", &t,
485                            NULL);
486         free(p);
487
488         if (r < 0) {
489                 free(s);
490                 free(t);
491                 return r;
492         }
493
494         if (s) {
495                 a = strv_split(s, " ");
496                 if (!a) {
497                         free(s);
498                         free(t);
499                         return -ENOMEM;
500                 }
501         }
502
503         free(s);
504
505         if (uids && t) {
506                 char *w, *state;
507                 size_t l;
508
509                 FOREACH_WORD(w, l, t, state)
510                         n++;
511
512                 if (n == 0)
513                         b = NULL;
514                 else {
515                         unsigned i = 0;
516
517                         b = new(uid_t, n);
518                         if (!b) {
519                                 strv_free(a);
520                                 return -ENOMEM;
521                         }
522
523                         FOREACH_WORD(w, l, t, state) {
524                                 char *k;
525
526                                 k = strndup(w, l);
527                                 if (!k) {
528                                         free(t);
529                                         free(b);
530                                         strv_free(a);
531                                         return -ENOMEM;
532                                 }
533
534                                 r = parse_uid(k, b + i);
535                                 free(k);
536                                 if (r < 0)
537                                         continue;
538
539                                 i++;
540                         }
541                 }
542         }
543
544         free(t);
545
546         r = strv_length(a);
547
548         if (sessions)
549                 *sessions = a;
550         else
551                 strv_free(a);
552
553         if (uids)
554                 *uids = b;
555
556         if (n_uids)
557                 *n_uids = n;
558
559         return r;
560 }
561
562 _public_ int sd_seat_can_multi_session(const char *seat) {
563         char *p, *s = NULL;
564         int r;
565
566         r = file_of_seat(seat, &p);
567         if (r < 0)
568                 return r;
569
570         r = parse_env_file(p, NEWLINE,
571                            "CAN_MULTI_SESSION", &s,
572                            NULL);
573         free(p);
574
575         if (r < 0) {
576                 free(s);
577                 return r;
578         }
579
580         if (s) {
581                 r = parse_boolean(s);
582                 free(s);
583         } else
584                 r = 0;
585
586         return r;
587 }
588
589 _public_ int sd_get_seats(char ***seats) {
590         return get_files_in_directory("/run/systemd/seats/", seats);
591 }
592
593 _public_ int sd_get_sessions(char ***sessions) {
594         return get_files_in_directory("/run/systemd/sessions/", sessions);
595 }
596
597 _public_ int sd_get_uids(uid_t **users) {
598         DIR *d;
599         int r = 0;
600         unsigned n = 0;
601         uid_t *l = NULL;
602
603         d = opendir("/run/systemd/users/");
604         if (!d)
605                 return -errno;
606
607         for (;;) {
608                 struct dirent buffer, *de;
609                 int k;
610                 uid_t uid;
611
612                 k = readdir_r(d, &buffer, &de);
613                 if (k != 0) {
614                         r = -k;
615                         goto finish;
616                 }
617
618                 if (!de)
619                         break;
620
621                 dirent_ensure_type(d, de);
622
623                 if (!dirent_is_file(de))
624                         continue;
625
626                 k = parse_uid(de->d_name, &uid);
627                 if (k < 0)
628                         continue;
629
630                 if (users) {
631                         if ((unsigned) r >= n) {
632                                 uid_t *t;
633
634                                 n = MAX(16, 2*r);
635                                 t = realloc(l, sizeof(uid_t) * n);
636                                 if (!t) {
637                                         r = -ENOMEM;
638                                         goto finish;
639                                 }
640
641                                 l = t;
642                         }
643
644                         assert((unsigned) r < n);
645                         l[r++] = uid;
646                 } else
647                         r++;
648         }
649
650 finish:
651         if (d)
652                 closedir(d);
653
654         if (r >= 0) {
655                 if (users)
656                         *users = l;
657         } else
658                 free(l);
659
660         return r;
661 }
662
663 static inline int MONITOR_TO_FD(sd_login_monitor *m) {
664         return (int) (unsigned long) m - 1;
665 }
666
667 static inline sd_login_monitor* FD_TO_MONITOR(int fd) {
668         return (sd_login_monitor*) (unsigned long) (fd + 1);
669 }
670
671 _public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) {
672         int fd, k;
673         bool good = false;
674
675         if (!m)
676                 return -EINVAL;
677
678         fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
679         if (fd < 0)
680                 return errno;
681
682         if (!category || streq(category, "seat")) {
683                 k = inotify_add_watch(fd, "/run/systemd/seats/", IN_MOVED_TO|IN_DELETE);
684                 if (k < 0) {
685                         close_nointr_nofail(fd);
686                         return -errno;
687                 }
688
689                 good = true;
690         }
691
692         if (!category || streq(category, "session")) {
693                 k = inotify_add_watch(fd, "/run/systemd/sessions/", IN_MOVED_TO|IN_DELETE);
694                 if (k < 0) {
695                         close_nointr_nofail(fd);
696                         return -errno;
697                 }
698
699                 good = true;
700         }
701
702         if (!category || streq(category, "uid")) {
703                 k = inotify_add_watch(fd, "/run/systemd/users/", IN_MOVED_TO|IN_DELETE);
704                 if (k < 0) {
705                         close_nointr_nofail(fd);
706                         return -errno;
707                 }
708
709                 good = true;
710         }
711
712         if (!good) {
713                 close_nointr(fd);
714                 return -EINVAL;
715         }
716
717         *m = FD_TO_MONITOR(fd);
718         return 0;
719 }
720
721 _public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
722         int fd;
723
724         if (!m)
725                 return NULL;
726
727         fd = MONITOR_TO_FD(m);
728         close_nointr(fd);
729
730         return NULL;
731 }
732
733 _public_ int sd_login_monitor_flush(sd_login_monitor *m) {
734
735         if (!m)
736                 return -EINVAL;
737
738         return flush_fd(MONITOR_TO_FD(m));
739 }
740
741 _public_ int sd_login_monitor_get_fd(sd_login_monitor *m) {
742
743         if (!m)
744                 return -EINVAL;
745
746         return MONITOR_TO_FD(m);
747 }