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