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