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