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