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