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