chiark / gitweb /
login: implement sd_session_get_service()
[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;
127
128         if (!unit)
129                 return -EINVAL;
130
131         r = pid_get_cgroup(pid, NULL, &cgroup);
132         if (r < 0)
133                 return r;
134
135         if (!startswith(cgroup, "/system/")) {
136                 free(cgroup);
137                 return -ENOENT;
138         }
139
140         p = cgroup + 8;
141         p = strndup(p, strcspn(p, "/"));
142         free(cgroup);
143
144         if (!p)
145                 return -ENOMEM;
146
147         *unit = p;
148         return 0;
149 }
150
151 _public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) {
152         int r;
153         char *root, *cgroup, *p, *cc;
154         struct stat st;
155
156         if (!uid)
157                 return -EINVAL;
158
159         r = pid_get_cgroup(pid, &root, &cgroup);
160         if (r < 0)
161                 return r;
162
163         if (!startswith(cgroup, "/user/")) {
164                 free(cgroup);
165                 free(root);
166                 return -ENOENT;
167         }
168
169         p = strchr(cgroup + 6, '/');
170         if (!p) {
171                 free(cgroup);
172                 return -ENOENT;
173         }
174
175         p++;
176         p += strcspn(p, "/");
177         *p = 0;
178
179         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, cgroup, &cc);
180         free(root);
181         free(cgroup);
182
183         if (r < 0)
184                 return -ENOMEM;
185
186         r = lstat(cc, &st);
187         free(cc);
188
189         if (r < 0)
190                 return -errno;
191
192         if (!S_ISDIR(st.st_mode))
193                 return -ENOTDIR;
194
195         *uid = st.st_uid;
196         return 0;
197 }
198
199 _public_ int sd_uid_get_state(uid_t uid, char**state) {
200         char *p, *s = NULL;
201         int r;
202
203         if (!state)
204                 return -EINVAL;
205
206         if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
207                 return -ENOMEM;
208
209         r = parse_env_file(p, NEWLINE, "STATE", &s, NULL);
210         free(p);
211
212         if (r == -ENOENT) {
213                 free(s);
214                 s = strdup("offline");
215                 if (!s)
216                         return -ENOMEM;
217
218                 *state = s;
219                 return 0;
220         } else if (r < 0) {
221                 free(s);
222                 return r;
223         } else if (!s)
224                 return -EIO;
225
226         *state = s;
227         return 0;
228 }
229
230 _public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) {
231         char *p, *w, *t, *state, *s = NULL;
232         size_t l;
233         int r;
234         const char *variable;
235
236         if (!seat)
237                 return -EINVAL;
238
239         variable = require_active ? "ACTIVE_UID" : "UIDS";
240
241         p = strappend("/run/systemd/seats/", seat);
242         if (!p)
243                 return -ENOMEM;
244
245         r = parse_env_file(p, NEWLINE, variable, &s, NULL);
246         free(p);
247
248         if (r < 0) {
249                 free(s);
250                 return r;
251         }
252
253         if (!s)
254                 return -EIO;
255
256         if (asprintf(&t, "%lu", (unsigned long) uid) < 0) {
257                 free(s);
258                 return -ENOMEM;
259         }
260
261         FOREACH_WORD(w, l, s, state) {
262                 if (strncmp(t, w, l) == 0) {
263                         free(s);
264                         free(t);
265
266                         return 1;
267                 }
268         }
269
270         free(s);
271         free(t);
272
273         return 0;
274 }
275
276 static int uid_get_array(uid_t uid, const char *variable, char ***array) {
277         char *p, *s = NULL;
278         char **a;
279         int r;
280
281         if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
282                 return -ENOMEM;
283
284         r = parse_env_file(p, NEWLINE,
285                            variable, &s,
286                            NULL);
287         free(p);
288
289         if (r < 0) {
290                 free(s);
291
292                 if (r == -ENOENT) {
293                         if (array)
294                                 *array = NULL;
295                         return 0;
296                 }
297
298                 return r;
299         }
300
301         if (!s) {
302                 if (array)
303                         *array = NULL;
304                 return 0;
305         }
306
307         a = strv_split(s, " ");
308         free(s);
309
310         if (!a)
311                 return -ENOMEM;
312
313         strv_uniq(a);
314         r = strv_length(a);
315
316         if (array)
317                 *array = a;
318         else
319                 strv_free(a);
320
321         return r;
322 }
323
324 _public_ int sd_uid_get_sessions(uid_t uid, int require_active, char ***sessions) {
325         return uid_get_array(uid, require_active ? "ACTIVE_SESSIONS" : "SESSIONS", sessions);
326 }
327
328 _public_ int sd_uid_get_seats(uid_t uid, int require_active, char ***seats) {
329         return uid_get_array(uid, require_active ? "ACTIVE_SEATS" : "SEATS", seats);
330 }
331
332 _public_ int sd_session_is_active(const char *session) {
333         int r;
334         char *p, *s = NULL;
335
336         if (!session)
337                 return -EINVAL;
338
339         p = strappend("/run/systemd/sessions/", session);
340         if (!p)
341                 return -ENOMEM;
342
343         r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL);
344         free(p);
345
346         if (r < 0) {
347                 free(s);
348                 return r;
349         }
350
351         if (!s)
352                 return -EIO;
353
354         r = parse_boolean(s);
355         free(s);
356
357         return r;
358 }
359
360 _public_ int sd_session_get_uid(const char *session, uid_t *uid) {
361         int r;
362         char *p, *s = NULL;
363
364         if (!session)
365                 return -EINVAL;
366         if (!uid)
367                 return -EINVAL;
368
369         p = strappend("/run/systemd/sessions/", session);
370         if (!p)
371                 return -ENOMEM;
372
373         r = parse_env_file(p, NEWLINE, "UID", &s, NULL);
374         free(p);
375
376         if (r < 0) {
377                 free(s);
378                 return r;
379         }
380
381         if (!s)
382                 return -EIO;
383
384         r = parse_uid(s, uid);
385         free(s);
386
387         return r;
388 }
389
390 _public_ int sd_session_get_seat(const char *session, char **seat) {
391         char *p, *s = NULL;
392         int r;
393
394         if (!session)
395                 return -EINVAL;
396         if (!seat)
397                 return -EINVAL;
398
399         p = strappend("/run/systemd/sessions/", session);
400         if (!p)
401                 return -ENOMEM;
402
403         r = parse_env_file(p, NEWLINE, "SEAT", &s, NULL);
404         free(p);
405
406         if (r < 0) {
407                 free(s);
408                 return r;
409         }
410
411         if (isempty(s))
412                 return -ENOENT;
413
414         *seat = s;
415         return 0;
416 }
417
418 _public_ int sd_session_get_service(const char *session, char **service) {
419         char *p, *s = NULL;
420         int r;
421
422         if (!session)
423                 return -EINVAL;
424         if (!service)
425                 return -EINVAL;
426
427         p = strappend("/run/systemd/sessions/", session);
428         if (!p)
429                 return -ENOMEM;
430
431         r = parse_env_file(p, NEWLINE, "SERVICE", &s, NULL);
432         free(p);
433
434         if (r < 0) {
435                 free(s);
436                 return r;
437         }
438
439         if (isempty(s))
440                 return -ENOENT;
441
442         *service = s;
443         return 0;
444 }
445
446 _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
447         char *p, *s = NULL, *t = NULL;
448         int r;
449
450         if (!seat)
451                 return -EINVAL;
452         if (!session && !uid)
453                 return -EINVAL;
454
455         p = strappend("/run/systemd/seats/", seat);
456         if (!p)
457                 return -ENOMEM;
458
459         r = parse_env_file(p, NEWLINE,
460                            "ACTIVE", &s,
461                            "ACTIVE_UID", &t,
462                            NULL);
463         free(p);
464
465         if (r < 0) {
466                 free(s);
467                 free(t);
468                 return r;
469         }
470
471         if (session && !s)  {
472                 free(t);
473                 return -ENOENT;
474         }
475
476         if (uid && !t) {
477                 free(s);
478                 return -ENOENT;
479         }
480
481         if (uid && t) {
482                 r = parse_uid(t, uid);
483                 if (r < 0) {
484                         free(t);
485                         free(s);
486                         return r;
487                 }
488         }
489
490         free(t);
491
492         if (session && s)
493                 *session = s;
494         else
495                 free(s);
496
497         return 0;
498 }
499
500 _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uids, unsigned *n_uids) {
501         char *p, *s = NULL, *t = NULL, **a = NULL;
502         uid_t *b = NULL;
503         unsigned n = 0;
504         int r;
505
506         if (!seat)
507                 return -EINVAL;
508
509         p = strappend("/run/systemd/seats/", seat);
510         if (!p)
511                 return -ENOMEM;
512
513         r = parse_env_file(p, NEWLINE,
514                            "SESSIONS", &s,
515                            "ACTIVE_SESSIONS", &t,
516                            NULL);
517         free(p);
518
519         if (r < 0) {
520                 free(s);
521                 free(t);
522                 return r;
523         }
524
525         if (s) {
526                 a = strv_split(s, " ");
527                 if (!a) {
528                         free(s);
529                         free(t);
530                         return -ENOMEM;
531                 }
532         }
533
534         free(s);
535
536         if (uids && t) {
537                 char *w, *state;
538                 size_t l;
539
540                 FOREACH_WORD(w, l, t, state)
541                         n++;
542
543                 if (n == 0)
544                         b = NULL;
545                 else {
546                         unsigned i = 0;
547
548                         b = new(uid_t, n);
549                         if (!b) {
550                                 strv_free(a);
551                                 return -ENOMEM;
552                         }
553
554                         FOREACH_WORD(w, l, t, state) {
555                                 char *k;
556
557                                 k = strndup(w, l);
558                                 if (!k) {
559                                         free(t);
560                                         free(b);
561                                         strv_free(a);
562                                         return -ENOMEM;
563                                 }
564
565                                 r = parse_uid(k, b + i);
566                                 free(k);
567                                 if (r < 0)
568                                         continue;
569
570                                 i++;
571                         }
572                 }
573         }
574
575         free(t);
576
577         r = strv_length(a);
578
579         if (sessions)
580                 *sessions = a;
581         else
582                 strv_free(a);
583
584         if (uids)
585                 *uids = b;
586
587         if (n_uids)
588                 *n_uids = n;
589
590         return r;
591 }
592
593 _public_ int sd_seat_can_multi_session(const char *seat) {
594         char *p, *s = NULL;
595         int r;
596
597         if (!seat)
598                 return -EINVAL;
599
600         p = strappend("/run/systemd/seats/", seat);
601         if (!p)
602                 return -ENOMEM;
603
604         r = parse_env_file(p, NEWLINE,
605                            "CAN_MULTI_SESSION", &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_get_seats(char ***seats) {
624         return get_files_in_directory("/run/systemd/seats/", seats);
625 }
626
627 _public_ int sd_get_sessions(char ***sessions) {
628         return get_files_in_directory("/run/systemd/sessions/", sessions);
629 }
630
631 _public_ int sd_get_uids(uid_t **users) {
632         DIR *d;
633         int r = 0;
634         unsigned n = 0;
635         uid_t *l = NULL;
636
637         d = opendir("/run/systemd/users/");
638         if (!d)
639                 return -errno;
640
641         for (;;) {
642                 struct dirent buffer, *de;
643                 int k;
644                 uid_t uid;
645
646                 k = readdir_r(d, &buffer, &de);
647                 if (k != 0) {
648                         r = -k;
649                         goto finish;
650                 }
651
652                 if (!de)
653                         break;
654
655                 dirent_ensure_type(d, de);
656
657                 if (!dirent_is_file(de))
658                         continue;
659
660                 k = parse_uid(de->d_name, &uid);
661                 if (k < 0)
662                         continue;
663
664                 if (users) {
665                         if ((unsigned) r >= n) {
666                                 uid_t *t;
667
668                                 n = MAX(16, 2*r);
669                                 t = realloc(l, sizeof(uid_t) * n);
670                                 if (!t) {
671                                         r = -ENOMEM;
672                                         goto finish;
673                                 }
674
675                                 l = t;
676                         }
677
678                         assert((unsigned) r < n);
679                         l[r++] = uid;
680                 } else
681                         r++;
682         }
683
684 finish:
685         if (d)
686                 closedir(d);
687
688         if (r >= 0) {
689                 if (users)
690                         *users = l;
691         } else
692                 free(l);
693
694         return r;
695 }
696
697 static inline int MONITOR_TO_FD(sd_login_monitor *m) {
698         return (int) (unsigned long) m - 1;
699 }
700
701 static inline sd_login_monitor* FD_TO_MONITOR(int fd) {
702         return (sd_login_monitor*) (unsigned long) (fd + 1);
703 }
704
705 _public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) {
706         int fd, k;
707         bool good = false;
708
709         if (!m)
710                 return -EINVAL;
711
712         fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
713         if (fd < 0)
714                 return errno;
715
716         if (!category || streq(category, "seat")) {
717                 k = inotify_add_watch(fd, "/run/systemd/seats/", IN_MOVED_TO|IN_DELETE);
718                 if (k < 0) {
719                         close_nointr_nofail(fd);
720                         return -errno;
721                 }
722
723                 good = true;
724         }
725
726         if (!category || streq(category, "session")) {
727                 k = inotify_add_watch(fd, "/run/systemd/sessions/", IN_MOVED_TO|IN_DELETE);
728                 if (k < 0) {
729                         close_nointr_nofail(fd);
730                         return -errno;
731                 }
732
733                 good = true;
734         }
735
736         if (!category || streq(category, "uid")) {
737                 k = inotify_add_watch(fd, "/run/systemd/users/", IN_MOVED_TO|IN_DELETE);
738                 if (k < 0) {
739                         close_nointr_nofail(fd);
740                         return -errno;
741                 }
742
743                 good = true;
744         }
745
746         if (!good) {
747                 close_nointr(fd);
748                 return -EINVAL;
749         }
750
751         *m = FD_TO_MONITOR(fd);
752         return 0;
753 }
754
755 _public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
756         int fd;
757
758         if (!m)
759                 return NULL;
760
761         fd = MONITOR_TO_FD(m);
762         close_nointr(fd);
763
764         return NULL;
765 }
766
767 _public_ int sd_login_monitor_flush(sd_login_monitor *m) {
768
769         if (!m)
770                 return -EINVAL;
771
772         return flush_fd(MONITOR_TO_FD(m));
773 }
774
775 _public_ int sd_login_monitor_get_fd(sd_login_monitor *m) {
776
777         if (!m)
778                 return -EINVAL;
779
780         return MONITOR_TO_FD(m);
781 }