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