chiark / gitweb /
logind: rework button setting semantics
[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 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 }