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