chiark / gitweb /
login: introduce sd_session_get_tty()
[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(
263                         uid,
264                         require_active == 0 ? "ONLINE_SESSIONS" :
265                         require_active > 0  ? "ACTIVE_SESSIONS" :
266                                               "SESSIONS",
267                         sessions);
268 }
269
270 _public_ int sd_uid_get_seats(uid_t uid, int require_active, char ***seats) {
271         return uid_get_array(
272                         uid,
273                         require_active == 0 ? "ONLINE_SEATS" :
274                         require_active > 0  ? "ACTIVE_SEATS" :
275                                               "SEATS",
276                         seats);
277 }
278
279 static int file_of_session(const char *session, char **_p) {
280         char *p;
281         int r;
282
283         assert(_p);
284
285         if (session)
286                 p = strappend("/run/systemd/sessions/", session);
287         else {
288                 char *buf;
289
290                 r = sd_pid_get_session(0, &buf);
291                 if (r < 0)
292                         return r;
293
294                 p = strappend("/run/systemd/sessions/", buf);
295                 free(buf);
296         }
297
298         if (!p)
299                 return -ENOMEM;
300
301         *_p = p;
302         return 0;
303 }
304
305 _public_ int sd_session_is_active(const char *session) {
306         int r;
307         char *p, *s = NULL;
308
309         r = file_of_session(session, &p);
310         if (r < 0)
311                 return r;
312
313         r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL);
314         free(p);
315
316         if (r < 0) {
317                 free(s);
318                 return r;
319         }
320
321         if (!s)
322                 return -EIO;
323
324         r = parse_boolean(s);
325         free(s);
326
327         return r;
328 }
329
330 _public_ int sd_session_get_state(const char *session, char **state) {
331         char *p, *s = NULL;
332         int r;
333
334         if (!state)
335                 return -EINVAL;
336
337         r = file_of_session(session, &p);
338         if (r < 0)
339                 return r;
340
341         r = parse_env_file(p, NEWLINE, "STATE", &s, NULL);
342         free(p);
343
344         if (r < 0) {
345                 free(s);
346                 return r;
347         } else if (!s)
348                 return -EIO;
349
350         *state = s;
351         return 0;
352 }
353
354 _public_ int sd_session_get_uid(const char *session, uid_t *uid) {
355         int r;
356         char *p, *s = NULL;
357
358         if (!uid)
359                 return -EINVAL;
360
361         r = file_of_session(session, &p);
362         if (r < 0)
363                 return r;
364
365         r = parse_env_file(p, NEWLINE, "UID", &s, NULL);
366         free(p);
367
368         if (r < 0) {
369                 free(s);
370                 return r;
371         }
372
373         if (!s)
374                 return -EIO;
375
376         r = parse_uid(s, uid);
377         free(s);
378
379         return r;
380 }
381
382 static int session_get_string(const char *session, const char *field, char **value) {
383         char *p, *s = NULL;
384         int r;
385
386         if (!value)
387                 return -EINVAL;
388
389         r = file_of_session(session, &p);
390         if (r < 0)
391                 return r;
392
393         r = parse_env_file(p, NEWLINE, field, &s, NULL);
394         free(p);
395
396         if (r < 0) {
397                 free(s);
398                 return r;
399         }
400
401         if (isempty(s))
402                 return -ENOENT;
403
404         *value = s;
405         return 0;
406 }
407
408 _public_ int sd_session_get_seat(const char *session, char **seat) {
409         return session_get_string(session, "SEAT", seat);
410 }
411
412 _public_ int sd_session_get_tty(const char *session, char **tty) {
413         return session_get_string(session, "TTY", tty);
414 }
415
416 _public_ int sd_session_get_service(const char *session, char **service) {
417         return session_get_string(session, "SERVICE", service);
418 }
419
420 _public_ int sd_session_get_type(const char *session, char **type) {
421         return session_get_string(session, "TYPE", type);
422 }
423
424 _public_ int sd_session_get_class(const char *session, char **class) {
425         return session_get_string(session, "CLASS", class);
426 }
427
428 _public_ int sd_session_get_display(const char *session, char **display) {
429         return session_get_string(session, "DISPLAY", display);
430 }
431
432 static int file_of_seat(const char *seat, char **_p) {
433         char *p;
434         int r;
435
436         assert(_p);
437
438         if (seat)
439                 p = strappend("/run/systemd/seats/", seat);
440         else {
441                 char *buf;
442
443                 r = sd_session_get_seat(NULL, &buf);
444                 if (r < 0)
445                         return r;
446
447                 p = strappend("/run/systemd/seats/", buf);
448                 free(buf);
449         }
450
451         if (!p)
452                 return -ENOMEM;
453
454         *_p = p;
455         return 0;
456 }
457
458 _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
459         char *p, *s = NULL, *t = NULL;
460         int r;
461
462         if (!session && !uid)
463                 return -EINVAL;
464
465         r = file_of_seat(seat, &p);
466         if (r < 0)
467                 return r;
468
469         r = parse_env_file(p, NEWLINE,
470                            "ACTIVE", &s,
471                            "ACTIVE_UID", &t,
472                            NULL);
473         free(p);
474
475         if (r < 0) {
476                 free(s);
477                 free(t);
478                 return r;
479         }
480
481         if (session && !s)  {
482                 free(t);
483                 return -ENOENT;
484         }
485
486         if (uid && !t) {
487                 free(s);
488                 return -ENOENT;
489         }
490
491         if (uid && t) {
492                 r = parse_uid(t, uid);
493                 if (r < 0) {
494                         free(t);
495                         free(s);
496                         return r;
497                 }
498         }
499
500         free(t);
501
502         if (session && s)
503                 *session = s;
504         else
505                 free(s);
506
507         return 0;
508 }
509
510 _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uids, unsigned *n_uids) {
511         char *p, *s = NULL, *t = NULL, **a = NULL;
512         uid_t *b = NULL;
513         unsigned n = 0;
514         int r;
515
516         r = file_of_seat(seat, &p);
517         if (r < 0)
518                 return r;
519
520         r = parse_env_file(p, NEWLINE,
521                            "SESSIONS", &s,
522                            "ACTIVE_SESSIONS", &t,
523                            NULL);
524         free(p);
525
526         if (r < 0) {
527                 free(s);
528                 free(t);
529                 return r;
530         }
531
532         if (s) {
533                 a = strv_split(s, " ");
534                 if (!a) {
535                         free(s);
536                         free(t);
537                         return -ENOMEM;
538                 }
539         }
540
541         free(s);
542
543         if (uids && t) {
544                 char *w, *state;
545                 size_t l;
546
547                 FOREACH_WORD(w, l, t, state)
548                         n++;
549
550                 if (n == 0)
551                         b = NULL;
552                 else {
553                         unsigned i = 0;
554
555                         b = new(uid_t, n);
556                         if (!b) {
557                                 strv_free(a);
558                                 return -ENOMEM;
559                         }
560
561                         FOREACH_WORD(w, l, t, state) {
562                                 char *k;
563
564                                 k = strndup(w, l);
565                                 if (!k) {
566                                         free(t);
567                                         free(b);
568                                         strv_free(a);
569                                         return -ENOMEM;
570                                 }
571
572                                 r = parse_uid(k, b + i);
573                                 free(k);
574                                 if (r < 0)
575                                         continue;
576
577                                 i++;
578                         }
579                 }
580         }
581
582         free(t);
583
584         r = strv_length(a);
585
586         if (sessions)
587                 *sessions = a;
588         else
589                 strv_free(a);
590
591         if (uids)
592                 *uids = b;
593
594         if (n_uids)
595                 *n_uids = n;
596
597         return r;
598 }
599
600 static int seat_get_can(const char *seat, const char *variable) {
601         char *p, *s = NULL;
602         int r;
603
604         r = file_of_seat(seat, &p);
605         if (r < 0)
606                 return r;
607
608         r = parse_env_file(p, NEWLINE,
609                            variable, &s,
610                            NULL);
611         free(p);
612
613         if (r < 0) {
614                 free(s);
615                 return r;
616         }
617
618         if (s) {
619                 r = parse_boolean(s);
620                 free(s);
621         } else
622                 r = 0;
623
624         return r;
625 }
626
627 _public_ int sd_seat_can_multi_session(const char *seat) {
628         return seat_get_can(seat, "CAN_MULTI_SESSION");
629 }
630
631 _public_ int sd_seat_can_tty(const char *seat) {
632         return seat_get_can(seat, "CAN_TTY");
633 }
634
635 _public_ int sd_seat_can_graphical(const char *seat) {
636         return seat_get_can(seat, "CAN_GRAPHICAL");
637 }
638
639 _public_ int sd_get_seats(char ***seats) {
640         return get_files_in_directory("/run/systemd/seats/", seats);
641 }
642
643 _public_ int sd_get_sessions(char ***sessions) {
644         return get_files_in_directory("/run/systemd/sessions/", sessions);
645 }
646
647 _public_ int sd_get_uids(uid_t **users) {
648         DIR *d;
649         int r = 0;
650         unsigned n = 0;
651         uid_t *l = NULL;
652
653         d = opendir("/run/systemd/users/");
654         if (!d)
655                 return -errno;
656
657         for (;;) {
658                 struct dirent *de;
659                 union dirent_storage buf;
660                 int k;
661                 uid_t uid;
662
663                 k = readdir_r(d, &buf.de, &de);
664                 if (k != 0) {
665                         r = -k;
666                         goto finish;
667                 }
668
669                 if (!de)
670                         break;
671
672                 dirent_ensure_type(d, de);
673
674                 if (!dirent_is_file(de))
675                         continue;
676
677                 k = parse_uid(de->d_name, &uid);
678                 if (k < 0)
679                         continue;
680
681                 if (users) {
682                         if ((unsigned) r >= n) {
683                                 uid_t *t;
684
685                                 n = MAX(16, 2*r);
686                                 t = realloc(l, sizeof(uid_t) * n);
687                                 if (!t) {
688                                         r = -ENOMEM;
689                                         goto finish;
690                                 }
691
692                                 l = t;
693                         }
694
695                         assert((unsigned) r < n);
696                         l[r++] = uid;
697                 } else
698                         r++;
699         }
700
701 finish:
702         if (d)
703                 closedir(d);
704
705         if (r >= 0) {
706                 if (users)
707                         *users = l;
708         } else
709                 free(l);
710
711         return r;
712 }
713
714 static inline int MONITOR_TO_FD(sd_login_monitor *m) {
715         return (int) (unsigned long) m - 1;
716 }
717
718 static inline sd_login_monitor* FD_TO_MONITOR(int fd) {
719         return (sd_login_monitor*) (unsigned long) (fd + 1);
720 }
721
722 _public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) {
723         int fd, k;
724         bool good = false;
725
726         if (!m)
727                 return -EINVAL;
728
729         fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
730         if (fd < 0)
731                 return errno;
732
733         if (!category || streq(category, "seat")) {
734                 k = inotify_add_watch(fd, "/run/systemd/seats/", IN_MOVED_TO|IN_DELETE);
735                 if (k < 0) {
736                         close_nointr_nofail(fd);
737                         return -errno;
738                 }
739
740                 good = true;
741         }
742
743         if (!category || streq(category, "session")) {
744                 k = inotify_add_watch(fd, "/run/systemd/sessions/", IN_MOVED_TO|IN_DELETE);
745                 if (k < 0) {
746                         close_nointr_nofail(fd);
747                         return -errno;
748                 }
749
750                 good = true;
751         }
752
753         if (!category || streq(category, "uid")) {
754                 k = inotify_add_watch(fd, "/run/systemd/users/", IN_MOVED_TO|IN_DELETE);
755                 if (k < 0) {
756                         close_nointr_nofail(fd);
757                         return -errno;
758                 }
759
760                 good = true;
761         }
762
763         if (!good) {
764                 close_nointr(fd);
765                 return -EINVAL;
766         }
767
768         *m = FD_TO_MONITOR(fd);
769         return 0;
770 }
771
772 _public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
773         int fd;
774
775         if (!m)
776                 return NULL;
777
778         fd = MONITOR_TO_FD(m);
779         close_nointr(fd);
780
781         return NULL;
782 }
783
784 _public_ int sd_login_monitor_flush(sd_login_monitor *m) {
785
786         if (!m)
787                 return -EINVAL;
788
789         return flush_fd(MONITOR_TO_FD(m));
790 }
791
792 _public_ int sd_login_monitor_get_fd(sd_login_monitor *m) {
793
794         if (!m)
795                 return -EINVAL;
796
797         return MONITOR_TO_FD(m);
798 }