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