chiark / gitweb /
update TODO
[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 General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU 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 static int file_of_seat(const char *seat, char **_p) {
479         char *p;
480         int r;
481
482         assert(_p);
483
484         if (seat)
485                 p = strappend("/run/systemd/seats/", seat);
486         else {
487                 char *buf;
488
489                 r = sd_session_get_seat(NULL, &buf);
490                 if (r < 0)
491                         return r;
492
493                 p = strappend("/run/systemd/seats/", buf);
494                 free(buf);
495         }
496
497         if (!p)
498                 return -ENOMEM;
499
500         *_p = p;
501         return 0;
502 }
503
504 _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
505         char *p, *s = NULL, *t = NULL;
506         int r;
507
508         if (!session && !uid)
509                 return -EINVAL;
510
511         r = file_of_seat(seat, &p);
512         if (r < 0)
513                 return r;
514
515         r = parse_env_file(p, NEWLINE,
516                            "ACTIVE", &s,
517                            "ACTIVE_UID", &t,
518                            NULL);
519         free(p);
520
521         if (r < 0) {
522                 free(s);
523                 free(t);
524                 return r;
525         }
526
527         if (session && !s)  {
528                 free(t);
529                 return -ENOENT;
530         }
531
532         if (uid && !t) {
533                 free(s);
534                 return -ENOENT;
535         }
536
537         if (uid && t) {
538                 r = parse_uid(t, uid);
539                 if (r < 0) {
540                         free(t);
541                         free(s);
542                         return r;
543                 }
544         }
545
546         free(t);
547
548         if (session && s)
549                 *session = s;
550         else
551                 free(s);
552
553         return 0;
554 }
555
556 _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uids, unsigned *n_uids) {
557         char *p, *s = NULL, *t = NULL, **a = NULL;
558         uid_t *b = NULL;
559         unsigned n = 0;
560         int r;
561
562         r = file_of_seat(seat, &p);
563         if (r < 0)
564                 return r;
565
566         r = parse_env_file(p, NEWLINE,
567                            "SESSIONS", &s,
568                            "ACTIVE_SESSIONS", &t,
569                            NULL);
570         free(p);
571
572         if (r < 0) {
573                 free(s);
574                 free(t);
575                 return r;
576         }
577
578         if (s) {
579                 a = strv_split(s, " ");
580                 if (!a) {
581                         free(s);
582                         free(t);
583                         return -ENOMEM;
584                 }
585         }
586
587         free(s);
588
589         if (uids && t) {
590                 char *w, *state;
591                 size_t l;
592
593                 FOREACH_WORD(w, l, t, state)
594                         n++;
595
596                 if (n == 0)
597                         b = NULL;
598                 else {
599                         unsigned i = 0;
600
601                         b = new(uid_t, n);
602                         if (!b) {
603                                 strv_free(a);
604                                 return -ENOMEM;
605                         }
606
607                         FOREACH_WORD(w, l, t, state) {
608                                 char *k;
609
610                                 k = strndup(w, l);
611                                 if (!k) {
612                                         free(t);
613                                         free(b);
614                                         strv_free(a);
615                                         return -ENOMEM;
616                                 }
617
618                                 r = parse_uid(k, b + i);
619                                 free(k);
620                                 if (r < 0)
621                                         continue;
622
623                                 i++;
624                         }
625                 }
626         }
627
628         free(t);
629
630         r = strv_length(a);
631
632         if (sessions)
633                 *sessions = a;
634         else
635                 strv_free(a);
636
637         if (uids)
638                 *uids = b;
639
640         if (n_uids)
641                 *n_uids = n;
642
643         return r;
644 }
645
646 _public_ int sd_seat_can_multi_session(const char *seat) {
647         char *p, *s = NULL;
648         int r;
649
650         r = file_of_seat(seat, &p);
651         if (r < 0)
652                 return r;
653
654         r = parse_env_file(p, NEWLINE,
655                            "CAN_MULTI_SESSION", &s,
656                            NULL);
657         free(p);
658
659         if (r < 0) {
660                 free(s);
661                 return r;
662         }
663
664         if (s) {
665                 r = parse_boolean(s);
666                 free(s);
667         } else
668                 r = 0;
669
670         return r;
671 }
672
673 _public_ int sd_get_seats(char ***seats) {
674         return get_files_in_directory("/run/systemd/seats/", seats);
675 }
676
677 _public_ int sd_get_sessions(char ***sessions) {
678         return get_files_in_directory("/run/systemd/sessions/", sessions);
679 }
680
681 _public_ int sd_get_uids(uid_t **users) {
682         DIR *d;
683         int r = 0;
684         unsigned n = 0;
685         uid_t *l = NULL;
686
687         d = opendir("/run/systemd/users/");
688         if (!d)
689                 return -errno;
690
691         for (;;) {
692                 struct dirent buffer, *de;
693                 int k;
694                 uid_t uid;
695
696                 k = readdir_r(d, &buffer, &de);
697                 if (k != 0) {
698                         r = -k;
699                         goto finish;
700                 }
701
702                 if (!de)
703                         break;
704
705                 dirent_ensure_type(d, de);
706
707                 if (!dirent_is_file(de))
708                         continue;
709
710                 k = parse_uid(de->d_name, &uid);
711                 if (k < 0)
712                         continue;
713
714                 if (users) {
715                         if ((unsigned) r >= n) {
716                                 uid_t *t;
717
718                                 n = MAX(16, 2*r);
719                                 t = realloc(l, sizeof(uid_t) * n);
720                                 if (!t) {
721                                         r = -ENOMEM;
722                                         goto finish;
723                                 }
724
725                                 l = t;
726                         }
727
728                         assert((unsigned) r < n);
729                         l[r++] = uid;
730                 } else
731                         r++;
732         }
733
734 finish:
735         if (d)
736                 closedir(d);
737
738         if (r >= 0) {
739                 if (users)
740                         *users = l;
741         } else
742                 free(l);
743
744         return r;
745 }
746
747 static inline int MONITOR_TO_FD(sd_login_monitor *m) {
748         return (int) (unsigned long) m - 1;
749 }
750
751 static inline sd_login_monitor* FD_TO_MONITOR(int fd) {
752         return (sd_login_monitor*) (unsigned long) (fd + 1);
753 }
754
755 _public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) {
756         int fd, k;
757         bool good = false;
758
759         if (!m)
760                 return -EINVAL;
761
762         fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
763         if (fd < 0)
764                 return errno;
765
766         if (!category || streq(category, "seat")) {
767                 k = inotify_add_watch(fd, "/run/systemd/seats/", IN_MOVED_TO|IN_DELETE);
768                 if (k < 0) {
769                         close_nointr_nofail(fd);
770                         return -errno;
771                 }
772
773                 good = true;
774         }
775
776         if (!category || streq(category, "session")) {
777                 k = inotify_add_watch(fd, "/run/systemd/sessions/", IN_MOVED_TO|IN_DELETE);
778                 if (k < 0) {
779                         close_nointr_nofail(fd);
780                         return -errno;
781                 }
782
783                 good = true;
784         }
785
786         if (!category || streq(category, "uid")) {
787                 k = inotify_add_watch(fd, "/run/systemd/users/", IN_MOVED_TO|IN_DELETE);
788                 if (k < 0) {
789                         close_nointr_nofail(fd);
790                         return -errno;
791                 }
792
793                 good = true;
794         }
795
796         if (!good) {
797                 close_nointr(fd);
798                 return -EINVAL;
799         }
800
801         *m = FD_TO_MONITOR(fd);
802         return 0;
803 }
804
805 _public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
806         int fd;
807
808         if (!m)
809                 return NULL;
810
811         fd = MONITOR_TO_FD(m);
812         close_nointr(fd);
813
814         return NULL;
815 }
816
817 _public_ int sd_login_monitor_flush(sd_login_monitor *m) {
818
819         if (!m)
820                 return -EINVAL;
821
822         return flush_fd(MONITOR_TO_FD(m));
823 }
824
825 _public_ int sd_login_monitor_get_fd(sd_login_monitor *m) {
826
827         if (!m)
828                 return -EINVAL;
829
830         return MONITOR_TO_FD(m);
831 }