chiark / gitweb /
Merge nss-myhostname
[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(
263                         uid,
264                         require_active == 0 ? "ONLINE_SESSIONS" :
265                         require_active > 0  ? "ACTIVE_SESSIONS" :
266                                               "SESSIONS",
267                         sessions);
268 }
269
270 _public_ int sd_uid_get_seats(uid_t uid, int require_active, char ***seats) {
271         return uid_get_array(
272                         uid,
273                         require_active == 0 ? "ONLINE_SEATS" :
274                         require_active > 0  ? "ACTIVE_SEATS" :
275                                               "SEATS",
276                         seats);
277 }
278
279 static int file_of_session(const char *session, char **_p) {
280         char *p;
281         int r;
282
283         assert(_p);
284
285         if (session)
286                 p = strappend("/run/systemd/sessions/", session);
287         else {
288                 char *buf;
289
290                 r = sd_pid_get_session(0, &buf);
291                 if (r < 0)
292                         return r;
293
294                 p = strappend("/run/systemd/sessions/", buf);
295                 free(buf);
296         }
297
298         if (!p)
299                 return -ENOMEM;
300
301         *_p = p;
302         return 0;
303 }
304
305 _public_ int sd_session_is_active(const char *session) {
306         int r;
307         char *p, *s = NULL;
308
309         r = file_of_session(session, &p);
310         if (r < 0)
311                 return r;
312
313         r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL);
314         free(p);
315
316         if (r < 0) {
317                 free(s);
318                 return r;
319         }
320
321         if (!s)
322                 return -EIO;
323
324         r = parse_boolean(s);
325         free(s);
326
327         return r;
328 }
329
330 _public_ int sd_session_get_state(const char *session, char **state) {
331         char *p, *s = NULL;
332         int r;
333
334         if (!state)
335                 return -EINVAL;
336
337         r = file_of_session(session, &p);
338         if (r < 0)
339                 return r;
340
341         r = parse_env_file(p, NEWLINE, "STATE", &s, NULL);
342         free(p);
343
344         if (r < 0) {
345                 free(s);
346                 return r;
347         } else if (!s)
348                 return -EIO;
349
350         *state = s;
351         return 0;
352 }
353
354 _public_ int sd_session_get_uid(const char *session, uid_t *uid) {
355         int r;
356         char *p, *s = NULL;
357
358         if (!uid)
359                 return -EINVAL;
360
361         r = file_of_session(session, &p);
362         if (r < 0)
363                 return r;
364
365         r = parse_env_file(p, NEWLINE, "UID", &s, NULL);
366         free(p);
367
368         if (r < 0) {
369                 free(s);
370                 return r;
371         }
372
373         if (!s)
374                 return -EIO;
375
376         r = parse_uid(s, uid);
377         free(s);
378
379         return r;
380 }
381
382 static int session_get_string(const char *session, const char *field, char **value) {
383         char *p, *s = NULL;
384         int r;
385
386         if (!value)
387                 return -EINVAL;
388
389         r = file_of_session(session, &p);
390         if (r < 0)
391                 return r;
392
393         r = parse_env_file(p, NEWLINE, field, &s, NULL);
394         free(p);
395
396         if (r < 0) {
397                 free(s);
398                 return r;
399         }
400
401         if (isempty(s))
402                 return -ENOENT;
403
404         *value = s;
405         return 0;
406 }
407
408 _public_ int sd_session_get_seat(const char *session, char **seat) {
409         return session_get_string(session, "SEAT", seat);
410 }
411
412 _public_ int sd_session_get_service(const char *session, char **service) {
413         return session_get_string(session, "SERVICE", service);
414 }
415
416 _public_ int sd_session_get_type(const char *session, char **type) {
417         return session_get_string(session, "TYPE", type);
418 }
419
420 _public_ int sd_session_get_class(const char *session, char **class) {
421         return session_get_string(session, "CLASS", class);
422 }
423
424 _public_ int sd_session_get_display(const char *session, char **display) {
425         return session_get_string(session, "DISPLAY", display);
426 }
427
428 static int file_of_seat(const char *seat, char **_p) {
429         char *p;
430         int r;
431
432         assert(_p);
433
434         if (seat)
435                 p = strappend("/run/systemd/seats/", seat);
436         else {
437                 char *buf;
438
439                 r = sd_session_get_seat(NULL, &buf);
440                 if (r < 0)
441                         return r;
442
443                 p = strappend("/run/systemd/seats/", buf);
444                 free(buf);
445         }
446
447         if (!p)
448                 return -ENOMEM;
449
450         *_p = p;
451         return 0;
452 }
453
454 _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
455         char *p, *s = NULL, *t = NULL;
456         int r;
457
458         if (!session && !uid)
459                 return -EINVAL;
460
461         r = file_of_seat(seat, &p);
462         if (r < 0)
463                 return r;
464
465         r = parse_env_file(p, NEWLINE,
466                            "ACTIVE", &s,
467                            "ACTIVE_UID", &t,
468                            NULL);
469         free(p);
470
471         if (r < 0) {
472                 free(s);
473                 free(t);
474                 return r;
475         }
476
477         if (session && !s)  {
478                 free(t);
479                 return -ENOENT;
480         }
481
482         if (uid && !t) {
483                 free(s);
484                 return -ENOENT;
485         }
486
487         if (uid && t) {
488                 r = parse_uid(t, uid);
489                 if (r < 0) {
490                         free(t);
491                         free(s);
492                         return r;
493                 }
494         }
495
496         free(t);
497
498         if (session && s)
499                 *session = s;
500         else
501                 free(s);
502
503         return 0;
504 }
505
506 _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uids, unsigned *n_uids) {
507         char *p, *s = NULL, *t = NULL, **a = NULL;
508         uid_t *b = NULL;
509         unsigned n = 0;
510         int r;
511
512         r = file_of_seat(seat, &p);
513         if (r < 0)
514                 return r;
515
516         r = parse_env_file(p, NEWLINE,
517                            "SESSIONS", &s,
518                            "ACTIVE_SESSIONS", &t,
519                            NULL);
520         free(p);
521
522         if (r < 0) {
523                 free(s);
524                 free(t);
525                 return r;
526         }
527
528         if (s) {
529                 a = strv_split(s, " ");
530                 if (!a) {
531                         free(s);
532                         free(t);
533                         return -ENOMEM;
534                 }
535         }
536
537         free(s);
538
539         if (uids && t) {
540                 char *w, *state;
541                 size_t l;
542
543                 FOREACH_WORD(w, l, t, state)
544                         n++;
545
546                 if (n == 0)
547                         b = NULL;
548                 else {
549                         unsigned i = 0;
550
551                         b = new(uid_t, n);
552                         if (!b) {
553                                 strv_free(a);
554                                 return -ENOMEM;
555                         }
556
557                         FOREACH_WORD(w, l, t, state) {
558                                 char *k;
559
560                                 k = strndup(w, l);
561                                 if (!k) {
562                                         free(t);
563                                         free(b);
564                                         strv_free(a);
565                                         return -ENOMEM;
566                                 }
567
568                                 r = parse_uid(k, b + i);
569                                 free(k);
570                                 if (r < 0)
571                                         continue;
572
573                                 i++;
574                         }
575                 }
576         }
577
578         free(t);
579
580         r = strv_length(a);
581
582         if (sessions)
583                 *sessions = a;
584         else
585                 strv_free(a);
586
587         if (uids)
588                 *uids = b;
589
590         if (n_uids)
591                 *n_uids = n;
592
593         return r;
594 }
595
596 static int seat_get_can(const char *seat, const char *variable) {
597         char *p, *s = NULL;
598         int r;
599
600         r = file_of_seat(seat, &p);
601         if (r < 0)
602                 return r;
603
604         r = parse_env_file(p, NEWLINE,
605                            variable, &s,
606                            NULL);
607         free(p);
608
609         if (r < 0) {
610                 free(s);
611                 return r;
612         }
613
614         if (s) {
615                 r = parse_boolean(s);
616                 free(s);
617         } else
618                 r = 0;
619
620         return r;
621 }
622
623 _public_ int sd_seat_can_multi_session(const char *seat) {
624         return seat_get_can(seat, "CAN_MULTI_SESSION");
625 }
626
627 _public_ int sd_seat_can_tty(const char *seat) {
628         return seat_get_can(seat, "CAN_TTY");
629 }
630
631 _public_ int sd_seat_can_graphical(const char *seat) {
632         return seat_get_can(seat, "CAN_GRAPHICAL");
633 }
634
635 _public_ int sd_get_seats(char ***seats) {
636         return get_files_in_directory("/run/systemd/seats/", seats);
637 }
638
639 _public_ int sd_get_sessions(char ***sessions) {
640         return get_files_in_directory("/run/systemd/sessions/", sessions);
641 }
642
643 _public_ int sd_get_uids(uid_t **users) {
644         DIR *d;
645         int r = 0;
646         unsigned n = 0;
647         uid_t *l = NULL;
648
649         d = opendir("/run/systemd/users/");
650         if (!d)
651                 return -errno;
652
653         for (;;) {
654                 struct dirent *de;
655                 union dirent_storage buf;
656                 int k;
657                 uid_t uid;
658
659                 k = readdir_r(d, &buf.de, &de);
660                 if (k != 0) {
661                         r = -k;
662                         goto finish;
663                 }
664
665                 if (!de)
666                         break;
667
668                 dirent_ensure_type(d, de);
669
670                 if (!dirent_is_file(de))
671                         continue;
672
673                 k = parse_uid(de->d_name, &uid);
674                 if (k < 0)
675                         continue;
676
677                 if (users) {
678                         if ((unsigned) r >= n) {
679                                 uid_t *t;
680
681                                 n = MAX(16, 2*r);
682                                 t = realloc(l, sizeof(uid_t) * n);
683                                 if (!t) {
684                                         r = -ENOMEM;
685                                         goto finish;
686                                 }
687
688                                 l = t;
689                         }
690
691                         assert((unsigned) r < n);
692                         l[r++] = uid;
693                 } else
694                         r++;
695         }
696
697 finish:
698         if (d)
699                 closedir(d);
700
701         if (r >= 0) {
702                 if (users)
703                         *users = l;
704         } else
705                 free(l);
706
707         return r;
708 }
709
710 static inline int MONITOR_TO_FD(sd_login_monitor *m) {
711         return (int) (unsigned long) m - 1;
712 }
713
714 static inline sd_login_monitor* FD_TO_MONITOR(int fd) {
715         return (sd_login_monitor*) (unsigned long) (fd + 1);
716 }
717
718 _public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) {
719         int fd, k;
720         bool good = false;
721
722         if (!m)
723                 return -EINVAL;
724
725         fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
726         if (fd < 0)
727                 return errno;
728
729         if (!category || streq(category, "seat")) {
730                 k = inotify_add_watch(fd, "/run/systemd/seats/", IN_MOVED_TO|IN_DELETE);
731                 if (k < 0) {
732                         close_nointr_nofail(fd);
733                         return -errno;
734                 }
735
736                 good = true;
737         }
738
739         if (!category || streq(category, "session")) {
740                 k = inotify_add_watch(fd, "/run/systemd/sessions/", IN_MOVED_TO|IN_DELETE);
741                 if (k < 0) {
742                         close_nointr_nofail(fd);
743                         return -errno;
744                 }
745
746                 good = true;
747         }
748
749         if (!category || streq(category, "uid")) {
750                 k = inotify_add_watch(fd, "/run/systemd/users/", IN_MOVED_TO|IN_DELETE);
751                 if (k < 0) {
752                         close_nointr_nofail(fd);
753                         return -errno;
754                 }
755
756                 good = true;
757         }
758
759         if (!good) {
760                 close_nointr(fd);
761                 return -EINVAL;
762         }
763
764         *m = FD_TO_MONITOR(fd);
765         return 0;
766 }
767
768 _public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
769         int fd;
770
771         if (!m)
772                 return NULL;
773
774         fd = MONITOR_TO_FD(m);
775         close_nointr(fd);
776
777         return NULL;
778 }
779
780 _public_ int sd_login_monitor_flush(sd_login_monitor *m) {
781
782         if (!m)
783                 return -EINVAL;
784
785         return flush_fd(MONITOR_TO_FD(m));
786 }
787
788 _public_ int sd_login_monitor_get_fd(sd_login_monitor *m) {
789
790         if (!m)
791                 return -EINVAL;
792
793         return MONITOR_TO_FD(m);
794 }