]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/sd-login.c
systemctl: show main and control PID explicitly in cgroup-show
[thirdparty/systemd.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 static int pid_get_cgroup(pid_t pid, char **root, char **cgroup) {
34 char *cg_process, *cg_init, *p;
35 int r;
36
37 if (pid == 0)
38 pid = getpid();
39
40 if (pid <= 0)
41 return -EINVAL;
42
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
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;
75 }
76
77 if (root) {
78 cg_process[p-cg_process] = 0;
79 *root = cg_process;
80 } else
81 free(cg_process);
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);
99 return -ENOENT;
100 }
101
102 p = strchr(cgroup + 6, '/');
103 if (!p) {
104 free(cgroup);
105 return -ENOENT;
106 }
107
108 p++;
109 if (startswith(p, "shared/") || streq(p, "shared")) {
110 free(cgroup);
111 return -ENOENT;
112 }
113
114 p = strndup(p, strcspn(p, "/"));
115 free(cgroup);
116
117 if (!p)
118 return -ENOMEM;
119
120 *session = p;
121 return 0;
122 }
123
124 _public_ int sd_pid_get_unit(pid_t pid, char **unit) {
125 int r;
126 char *cgroup, *p, *at, *b;
127 size_t k;
128
129 if (!unit)
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;
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
167 free(cgroup);
168
169 if (!b)
170 return -ENOMEM;
171
172 *unit = b;
173 return 0;
174 }
175
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
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
255 _public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) {
256 char *p, *w, *t, *state, *s = NULL;
257 size_t l;
258 int r;
259 const char *variable;
260
261 if (!seat)
262 return -EINVAL;
263
264 variable = require_active ? "ACTIVE_UID" : "UIDS";
265
266 p = strappend("/run/systemd/seats/", seat);
267 if (!p)
268 return -ENOMEM;
269
270 r = parse_env_file(p, NEWLINE, variable, &s, NULL);
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
301 static int uid_get_array(uid_t uid, const char *variable, char ***array) {
302 char *p, *s = NULL;
303 char **a;
304 int r;
305
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) {
318 if (array)
319 *array = NULL;
320 return 0;
321 }
322
323 return r;
324 }
325
326 if (!s) {
327 if (array)
328 *array = NULL;
329 return 0;
330 }
331
332 a = strv_split(s, " ");
333 free(s);
334
335 if (!a)
336 return -ENOMEM;
337
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;
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);
351 }
352
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);
355 }
356
357 static int file_of_session(const char *session, char **_p) {
358 char *p;
359 int r;
360
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 }
375
376 if (!p)
377 return -ENOMEM;
378
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
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;
411
412 if (!uid)
413 return -EINVAL;
414
415 r = file_of_session(session, &p);
416 if (r < 0)
417 return r;
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
430 r = parse_uid(s, uid);
431 free(s);
432
433 return r;
434 }
435
436 static int session_get_string(const char *session, const char *field, char **value) {
437 char *p, *s = NULL;
438 int r;
439
440 if (!value)
441 return -EINVAL;
442
443 r = file_of_session(session, &p);
444 if (r < 0)
445 return r;
446
447 r = parse_env_file(p, NEWLINE, field, &s, NULL);
448 free(p);
449
450 if (r < 0) {
451 free(s);
452 return r;
453 }
454
455 if (isempty(s))
456 return -ENOENT;
457
458 *value = s;
459 return 0;
460 }
461
462 _public_ int sd_session_get_seat(const char *session, char **seat) {
463 return session_get_string(session, "SEAT", seat);
464 }
465
466 _public_ int sd_session_get_service(const char *session, char **service) {
467 return session_get_string(session, "SERVICE", service);
468 }
469
470 _public_ int sd_session_get_type(const char *session, char **type) {
471 return session_get_string(session, "TYPE", type);
472 }
473
474 _public_ int sd_session_get_class(const char *session, char **class) {
475 return session_get_string(session, "CLASS", class);
476 }
477
478 _public_ int sd_session_get_display(const char *session, char **display) {
479 return session_get_string(session, "DISPLAY", display);
480 }
481
482 static 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
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
512 if (!session && !uid)
513 return -EINVAL;
514
515 r = file_of_seat(seat, &p);
516 if (r < 0)
517 return r;
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);
533 return -ENOENT;
534 }
535
536 if (uid && !t) {
537 free(s);
538 return -ENOENT;
539 }
540
541 if (uid && t) {
542 r = parse_uid(t, uid);
543 if (r < 0) {
544 free(t);
545 free(s);
546 return r;
547 }
548 }
549
550 free(t);
551
552 if (session && s)
553 *session = s;
554 else
555 free(s);
556
557 return 0;
558 }
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
566 r = file_of_seat(seat, &p);
567 if (r < 0)
568 return r;
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
582 if (s) {
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;
596
597 FOREACH_WORD(w, l, t, state)
598 n++;
599
600 if (n == 0)
601 b = NULL;
602 else {
603 unsigned i = 0;
604
605 b = new(uid_t, n);
606 if (!b) {
607 strv_free(a);
608 return -ENOMEM;
609 }
610
611 FOREACH_WORD(w, l, t, state) {
612 char *k;
613
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 }
629 }
630 }
631
632 free(t);
633
634 r = strv_length(a);
635
636 if (sessions)
637 *sessions = a;
638 else
639 strv_free(a);
640
641 if (uids)
642 *uids = b;
643
644 if (n_uids)
645 *n_uids = n;
646
647 return r;
648 }
649
650 _public_ int sd_seat_can_multi_session(const char *seat) {
651 char *p, *s = NULL;
652 int r;
653
654 r = file_of_seat(seat, &p);
655 if (r < 0)
656 return r;
657
658 r = parse_env_file(p, NEWLINE,
659 "CAN_MULTI_SESSION", &s,
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
677 _public_ int sd_get_seats(char ***seats) {
678 return get_files_in_directory("/run/systemd/seats/", seats);
679 }
680
681 _public_ int sd_get_sessions(char ***sessions) {
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
691 d = opendir("/run/systemd/users/");
692 if (!d)
693 return -errno;
694
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
718 if (users) {
719 if ((unsigned) r >= n) {
720 uid_t *t;
721
722 n = MAX(16, 2*r);
723 t = realloc(l, sizeof(uid_t) * n);
724 if (!t) {
725 r = -ENOMEM;
726 goto finish;
727 }
728
729 l = t;
730 }
731
732 assert((unsigned) r < n);
733 l[r++] = uid;
734 } else
735 r++;
736 }
737
738 finish:
739 if (d)
740 closedir(d);
741
742 if (r >= 0) {
743 if (users)
744 *users = l;
745 } else
746 free(l);
747
748 return r;
749 }
750
751 static inline int MONITOR_TO_FD(sd_login_monitor *m) {
752 return (int) (unsigned long) m - 1;
753 }
754
755 static 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) {
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 }