chiark / gitweb /
login: add an api to determine the slice a PID is located in to libsystemd-login
[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 #include <sys/poll.h>
27
28 #include "util.h"
29 #include "cgroup-util.h"
30 #include "macro.h"
31 #include "sd-login.h"
32 #include "strv.h"
33 #include "fileio.h"
34
35 _public_ int sd_pid_get_session(pid_t pid, char **session) {
36         if (pid < 0)
37                 return -EINVAL;
38
39         if (!session)
40                 return -EINVAL;
41
42         return cg_pid_get_session(pid, session);
43 }
44
45 _public_ int sd_pid_get_unit(pid_t pid, char **unit) {
46
47         if (pid < 0)
48                 return -EINVAL;
49         if (!unit)
50                 return -EINVAL;
51
52         return cg_pid_get_unit(pid, unit);
53 }
54
55 _public_ int sd_pid_get_user_unit(pid_t pid, char **unit) {
56
57         if (pid < 0)
58                 return -EINVAL;
59         if (!unit)
60                 return -EINVAL;
61
62         return cg_pid_get_user_unit(pid, unit);
63 }
64
65 _public_ int sd_pid_get_machine_name(pid_t pid, char **name) {
66
67         if (pid < 0)
68                 return -EINVAL;
69         if (!name)
70                 return -EINVAL;
71
72         return cg_pid_get_machine_name(pid, name);
73 }
74
75 _public_ int sd_pid_get_slice(pid_t pid, char **slice) {
76
77         if (pid < 0)
78                 return -EINVAL;
79         if (!slice)
80                 return -EINVAL;
81
82         return cg_pid_get_slice(pid, slice);
83 }
84
85 _public_ int sd_pid_get_owner_uid(pid_t pid, uid_t *uid) {
86
87         if (pid < 0)
88                 return -EINVAL;
89
90         if (!uid)
91                 return -EINVAL;
92
93         return cg_pid_get_owner_uid(pid, uid);
94 }
95
96 _public_ int sd_uid_get_state(uid_t uid, char**state) {
97         char *p, *s = NULL;
98         int r;
99
100         if (!state)
101                 return -EINVAL;
102
103         if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
104                 return -ENOMEM;
105
106         r = parse_env_file(p, NEWLINE, "STATE", &s, NULL);
107         free(p);
108
109         if (r == -ENOENT) {
110                 free(s);
111                 s = strdup("offline");
112                 if (!s)
113                         return -ENOMEM;
114
115                 *state = s;
116                 return 0;
117         } else if (r < 0) {
118                 free(s);
119                 return r;
120         } else if (!s)
121                 return -EIO;
122
123         *state = s;
124         return 0;
125 }
126
127 _public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) {
128         char *w, *state;
129         _cleanup_free_ char *t = NULL, *s = NULL, *p = NULL;
130         size_t l;
131         int r;
132         const char *variable;
133
134         if (!seat)
135                 return -EINVAL;
136
137         variable = require_active ? "ACTIVE_UID" : "UIDS";
138
139         p = strappend("/run/systemd/seats/", seat);
140         if (!p)
141                 return -ENOMEM;
142
143         r = parse_env_file(p, NEWLINE, variable, &s, NULL);
144
145         if (r < 0)
146                 return r;
147
148         if (!s)
149                 return -EIO;
150
151         if (asprintf(&t, "%lu", (unsigned long) uid) < 0)
152                 return -ENOMEM;
153
154         FOREACH_WORD(w, l, s, state) {
155                 if (strneq(t, w, l))
156                         return 1;
157         }
158
159         return 0;
160 }
161
162 static int uid_get_array(uid_t uid, const char *variable, char ***array) {
163         _cleanup_free_ char *p = NULL, *s = NULL;
164         char **a;
165         int r;
166
167         if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
168                 return -ENOMEM;
169
170         r = parse_env_file(p, NEWLINE,
171                            variable, &s,
172                            NULL);
173         if (r < 0) {
174                 if (r == -ENOENT) {
175                         if (array)
176                                 *array = NULL;
177                         return 0;
178                 }
179
180                 return r;
181         }
182
183         if (!s) {
184                 if (array)
185                         *array = NULL;
186                 return 0;
187         }
188
189         a = strv_split(s, " ");
190
191         if (!a)
192                 return -ENOMEM;
193
194         strv_uniq(a);
195         r = strv_length(a);
196
197         if (array)
198                 *array = a;
199         else
200                 strv_free(a);
201
202         return r;
203 }
204
205 _public_ int sd_uid_get_sessions(uid_t uid, int require_active, char ***sessions) {
206         return uid_get_array(
207                         uid,
208                         require_active == 0 ? "ONLINE_SESSIONS" :
209                         require_active > 0  ? "ACTIVE_SESSIONS" :
210                                               "SESSIONS",
211                         sessions);
212 }
213
214 _public_ int sd_uid_get_seats(uid_t uid, int require_active, char ***seats) {
215         return uid_get_array(
216                         uid,
217                         require_active == 0 ? "ONLINE_SEATS" :
218                         require_active > 0  ? "ACTIVE_SEATS" :
219                                               "SEATS",
220                         seats);
221 }
222
223 static int file_of_session(const char *session, char **_p) {
224         char *p;
225         int r;
226
227         assert(_p);
228
229         if (session)
230                 p = strappend("/run/systemd/sessions/", session);
231         else {
232                 char *buf;
233
234                 r = sd_pid_get_session(0, &buf);
235                 if (r < 0)
236                         return r;
237
238                 p = strappend("/run/systemd/sessions/", buf);
239                 free(buf);
240         }
241
242         if (!p)
243                 return -ENOMEM;
244
245         *_p = p;
246         return 0;
247 }
248
249 _public_ int sd_session_is_active(const char *session) {
250         int r;
251         _cleanup_free_ char *p = NULL, *s = NULL;
252
253         r = file_of_session(session, &p);
254         if (r < 0)
255                 return r;
256
257         r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL);
258
259         if (r < 0)
260                 return r;
261
262         if (!s)
263                 return -EIO;
264
265         r = parse_boolean(s);
266
267         return r;
268 }
269
270 _public_ int sd_session_get_state(const char *session, char **state) {
271         _cleanup_free_ char *p = NULL, *s = NULL;
272         int r;
273
274         if (!state)
275                 return -EINVAL;
276
277         r = file_of_session(session, &p);
278         if (r < 0)
279                 return r;
280
281         r = parse_env_file(p, NEWLINE, "STATE", &s, NULL);
282
283         if (r < 0)
284                 return r;
285         else if (!s)
286                 return -EIO;
287
288         *state = s;
289         s = NULL;
290
291         return 0;
292 }
293
294 _public_ int sd_session_get_uid(const char *session, uid_t *uid) {
295         int r;
296         _cleanup_free_ char *p = NULL, *s = NULL;
297
298         if (!uid)
299                 return -EINVAL;
300
301         r = file_of_session(session, &p);
302         if (r < 0)
303                 return r;
304
305         r = parse_env_file(p, NEWLINE, "UID", &s, NULL);
306
307         if (r < 0)
308                 return r;
309
310         if (!s)
311                 return -EIO;
312
313         r = parse_uid(s, uid);
314
315         return r;
316 }
317
318 static int session_get_string(const char *session, const char *field, char **value) {
319         _cleanup_free_ char *p = NULL, *s = NULL;
320         int r;
321
322         if (!value)
323                 return -EINVAL;
324
325         r = file_of_session(session, &p);
326         if (r < 0)
327                 return r;
328
329         r = parse_env_file(p, NEWLINE, field, &s, NULL);
330
331         if (r < 0)
332                 return r;
333
334         if (isempty(s))
335                 return -ENOENT;
336
337         *value = s;
338         s = NULL;
339         return 0;
340 }
341
342 _public_ int sd_session_get_seat(const char *session, char **seat) {
343         return session_get_string(session, "SEAT", seat);
344 }
345
346 _public_ int sd_session_get_tty(const char *session, char **tty) {
347         return session_get_string(session, "TTY", tty);
348 }
349
350 _public_ int sd_session_get_service(const char *session, char **service) {
351         return session_get_string(session, "SERVICE", service);
352 }
353
354 _public_ int sd_session_get_type(const char *session, char **type) {
355         return session_get_string(session, "TYPE", type);
356 }
357
358 _public_ int sd_session_get_class(const char *session, char **class) {
359         return session_get_string(session, "CLASS", class);
360 }
361
362 _public_ int sd_session_get_display(const char *session, char **display) {
363         return session_get_string(session, "DISPLAY", display);
364 }
365
366 static int file_of_seat(const char *seat, char **_p) {
367         char *p;
368         int r;
369
370         assert(_p);
371
372         if (seat)
373                 p = strappend("/run/systemd/seats/", seat);
374         else {
375                 _cleanup_free_ char *buf = NULL;
376
377                 r = sd_session_get_seat(NULL, &buf);
378                 if (r < 0)
379                         return r;
380
381                 p = strappend("/run/systemd/seats/", buf);
382         }
383
384         if (!p)
385                 return -ENOMEM;
386
387         *_p = p;
388         p = NULL;
389         return 0;
390 }
391
392 _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
393         _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
394         int r;
395
396         if (!session && !uid)
397                 return -EINVAL;
398
399         r = file_of_seat(seat, &p);
400         if (r < 0)
401                 return r;
402
403         r = parse_env_file(p, NEWLINE,
404                            "ACTIVE", &s,
405                            "ACTIVE_UID", &t,
406                            NULL);
407         if (r < 0)
408                 return r;
409
410         if (session && !s)
411                 return -ENOENT;
412
413         if (uid && !t)
414                 return -ENOENT;
415
416         if (uid && t) {
417                 r = parse_uid(t, uid);
418                 if (r < 0)
419                         return r;
420         }
421
422         if (session && s) {
423                 *session = s;
424                 s = NULL;
425         }
426
427         return 0;
428 }
429
430 _public_ int sd_seat_get_sessions(const char *seat, char ***sessions, uid_t **uids, unsigned *n_uids) {
431         _cleanup_free_ char *p = NULL, *s = NULL, *t = NULL;
432         _cleanup_strv_free_ char **a = NULL;
433         _cleanup_free_ uid_t *b = NULL;
434         unsigned n = 0;
435         int r;
436
437         r = file_of_seat(seat, &p);
438         if (r < 0)
439                 return r;
440
441         r = parse_env_file(p, NEWLINE,
442                            "SESSIONS", &s,
443                            "ACTIVE_SESSIONS", &t,
444                            NULL);
445
446         if (r < 0)
447                 return r;
448
449         if (s) {
450                 a = strv_split(s, " ");
451                 if (!a)
452                         return -ENOMEM;
453         }
454
455         if (uids && t) {
456                 char *w, *state;
457                 size_t l;
458
459                 FOREACH_WORD(w, l, t, state)
460                         n++;
461
462                 if (n > 0) {
463                         unsigned i = 0;
464
465                         b = new(uid_t, n);
466                         if (!b)
467                                 return -ENOMEM;
468
469                         FOREACH_WORD(w, l, t, state) {
470                                 _cleanup_free_ char *k = NULL;
471
472                                 k = strndup(w, l);
473                                 if (!k)
474                                         return -ENOMEM;
475
476                                 r = parse_uid(k, b + i);
477
478                                 if (r < 0)
479                                         continue;
480
481                                 i++;
482                         }
483                 }
484         }
485
486         r = strv_length(a);
487
488         if (sessions) {
489                 *sessions = a;
490                 a = NULL;
491         }
492
493         if (uids) {
494                 *uids = b;
495                 b = NULL;
496         }
497
498         if (n_uids)
499                 *n_uids = n;
500
501         return r;
502 }
503
504 static int seat_get_can(const char *seat, const char *variable) {
505         _cleanup_free_ char *p = NULL, *s = NULL;
506         int r;
507
508         r = file_of_seat(seat, &p);
509         if (r < 0)
510                 return r;
511
512         r = parse_env_file(p, NEWLINE,
513                            variable, &s,
514                            NULL);
515         if (r < 0)
516                 return r;
517
518         if (s)
519                 r = parse_boolean(s);
520         else
521                 r = 0;
522
523         return r;
524 }
525
526 _public_ int sd_seat_can_multi_session(const char *seat) {
527         return seat_get_can(seat, "CAN_MULTI_SESSION");
528 }
529
530 _public_ int sd_seat_can_tty(const char *seat) {
531         return seat_get_can(seat, "CAN_TTY");
532 }
533
534 _public_ int sd_seat_can_graphical(const char *seat) {
535         return seat_get_can(seat, "CAN_GRAPHICAL");
536 }
537
538 _public_ int sd_get_seats(char ***seats) {
539         return get_files_in_directory("/run/systemd/seats/", seats);
540 }
541
542 _public_ int sd_get_sessions(char ***sessions) {
543         return get_files_in_directory("/run/systemd/sessions/", sessions);
544 }
545
546 _public_ int sd_get_uids(uid_t **users) {
547         _cleanup_closedir_ DIR *d;
548         int r = 0;
549         unsigned n = 0;
550         _cleanup_free_ uid_t *l = NULL;
551
552         d = opendir("/run/systemd/users/");
553         if (!d)
554                 return -errno;
555
556         for (;;) {
557                 struct dirent *de;
558                 union dirent_storage buf;
559                 int k;
560                 uid_t uid;
561
562                 k = readdir_r(d, &buf.de, &de);
563                 if (k != 0)
564                         return -k;
565
566                 if (!de)
567                         break;
568
569                 dirent_ensure_type(d, de);
570
571                 if (!dirent_is_file(de))
572                         continue;
573
574                 k = parse_uid(de->d_name, &uid);
575                 if (k < 0)
576                         continue;
577
578                 if (users) {
579                         if ((unsigned) r >= n) {
580                                 uid_t *t;
581
582                                 n = MAX(16, 2*r);
583                                 t = realloc(l, sizeof(uid_t) * n);
584                                 if (!t)
585                                         return -ENOMEM;
586
587                                 l = t;
588                         }
589
590                         assert((unsigned) r < n);
591                         l[r++] = uid;
592                 } else
593                         r++;
594         }
595
596         if (users) {
597                 *users = l;
598                 l = NULL;
599         }
600
601         return r;
602 }
603
604 _public_ int sd_get_machine_names(char ***machines) {
605         return get_files_in_directory("/run/systemd/machines/", machines);
606 }
607
608 static inline int MONITOR_TO_FD(sd_login_monitor *m) {
609         return (int) (unsigned long) m - 1;
610 }
611
612 static inline sd_login_monitor* FD_TO_MONITOR(int fd) {
613         return (sd_login_monitor*) (unsigned long) (fd + 1);
614 }
615
616 _public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) {
617         int fd, k;
618         bool good = false;
619
620         if (!m)
621                 return -EINVAL;
622
623         fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
624         if (fd < 0)
625                 return -errno;
626
627         if (!category || streq(category, "seat")) {
628                 k = inotify_add_watch(fd, "/run/systemd/seats/", IN_MOVED_TO|IN_DELETE);
629                 if (k < 0) {
630                         close_nointr_nofail(fd);
631                         return -errno;
632                 }
633
634                 good = true;
635         }
636
637         if (!category || streq(category, "session")) {
638                 k = inotify_add_watch(fd, "/run/systemd/sessions/", IN_MOVED_TO|IN_DELETE);
639                 if (k < 0) {
640                         close_nointr_nofail(fd);
641                         return -errno;
642                 }
643
644                 good = true;
645         }
646
647         if (!category || streq(category, "uid")) {
648                 k = inotify_add_watch(fd, "/run/systemd/users/", IN_MOVED_TO|IN_DELETE);
649                 if (k < 0) {
650                         close_nointr_nofail(fd);
651                         return -errno;
652                 }
653
654                 good = true;
655         }
656
657         if (!category || streq(category, "machine")) {
658                 k = inotify_add_watch(fd, "/run/systemd/machines/", IN_MOVED_TO|IN_DELETE);
659                 if (k < 0) {
660                         close_nointr_nofail(fd);
661                         return -errno;
662                 }
663
664                 good = true;
665         }
666
667         if (!good) {
668                 close_nointr(fd);
669                 return -EINVAL;
670         }
671
672         *m = FD_TO_MONITOR(fd);
673         return 0;
674 }
675
676 _public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
677         int fd;
678
679         if (!m)
680                 return NULL;
681
682         fd = MONITOR_TO_FD(m);
683         close_nointr(fd);
684
685         return NULL;
686 }
687
688 _public_ int sd_login_monitor_flush(sd_login_monitor *m) {
689
690         if (!m)
691                 return -EINVAL;
692
693         return flush_fd(MONITOR_TO_FD(m));
694 }
695
696 _public_ int sd_login_monitor_get_fd(sd_login_monitor *m) {
697
698         if (!m)
699                 return -EINVAL;
700
701         return MONITOR_TO_FD(m);
702 }
703
704 _public_ int sd_login_monitor_get_events(sd_login_monitor *m) {
705
706         if (!m)
707                 return -EINVAL;
708
709         /* For now we will only return POLLIN here, since we don't
710          * need anything else ever for inotify.  However, let's have
711          * this API to keep our options open should we later on need
712          * it. */
713         return POLLIN;
714 }
715
716 _public_ int sd_login_monitor_get_timeout(sd_login_monitor *m, uint64_t *timeout_usec) {
717
718         if (!m)
719                 return -EINVAL;
720         if (!timeout_usec)
721                 return -EINVAL;
722
723         /* For now we will only return (uint64_t) -1, since we don't
724          * need any timeout. However, let's have this API to keep our
725          * options open should we later on need it. */
726         *timeout_usec = (uint64_t) -1;
727         return 0;
728 }