chiark / gitweb /
nspawn: introduce the new /machine/ tree in the cgroup tree and move containers there
[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_owner_uid(pid_t pid, uid_t *uid) {
76         int r;
77         char *root, *cgroup, *p, *cc;
78         struct stat st;
79
80         if (pid < 0)
81                 return -EINVAL;
82
83         if (!uid)
84                 return -EINVAL;
85
86         r = cg_pid_get_path_shifted(pid, &root, &cgroup);
87         if (r < 0)
88                 return r;
89
90         if (!startswith(cgroup, "/user/")) {
91                 free(cgroup);
92                 free(root);
93                 return -ENOENT;
94         }
95
96         p = strchr(cgroup + 6, '/');
97         if (!p) {
98                 free(cgroup);
99                 return -ENOENT;
100         }
101
102         p++;
103         p += strcspn(p, "/");
104         *p = 0;
105
106         r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, root, cgroup, &cc);
107         free(root);
108         free(cgroup);
109
110         if (r < 0)
111                 return -ENOMEM;
112
113         r = lstat(cc, &st);
114         free(cc);
115
116         if (r < 0)
117                 return -errno;
118
119         if (!S_ISDIR(st.st_mode))
120                 return -ENOTDIR;
121
122         *uid = st.st_uid;
123         return 0;
124 }
125
126 _public_ int sd_uid_get_state(uid_t uid, char**state) {
127         char *p, *s = NULL;
128         int r;
129
130         if (!state)
131                 return -EINVAL;
132
133         if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
134                 return -ENOMEM;
135
136         r = parse_env_file(p, NEWLINE, "STATE", &s, NULL);
137         free(p);
138
139         if (r == -ENOENT) {
140                 free(s);
141                 s = strdup("offline");
142                 if (!s)
143                         return -ENOMEM;
144
145                 *state = s;
146                 return 0;
147         } else if (r < 0) {
148                 free(s);
149                 return r;
150         } else if (!s)
151                 return -EIO;
152
153         *state = s;
154         return 0;
155 }
156
157 _public_ int sd_uid_is_on_seat(uid_t uid, int require_active, const char *seat) {
158         char *p, *w, *t, *state, *s = NULL;
159         size_t l;
160         int r;
161         const char *variable;
162
163         if (!seat)
164                 return -EINVAL;
165
166         variable = require_active ? "ACTIVE_UID" : "UIDS";
167
168         p = strappend("/run/systemd/seats/", seat);
169         if (!p)
170                 return -ENOMEM;
171
172         r = parse_env_file(p, NEWLINE, variable, &s, NULL);
173         free(p);
174
175         if (r < 0) {
176                 free(s);
177                 return r;
178         }
179
180         if (!s)
181                 return -EIO;
182
183         if (asprintf(&t, "%lu", (unsigned long) uid) < 0) {
184                 free(s);
185                 return -ENOMEM;
186         }
187
188         FOREACH_WORD(w, l, s, state) {
189                 if (strneq(t, w, l)) {
190                         free(s);
191                         free(t);
192
193                         return 1;
194                 }
195         }
196
197         free(s);
198         free(t);
199
200         return 0;
201 }
202
203 static int uid_get_array(uid_t uid, const char *variable, char ***array) {
204         char *p, *s = NULL;
205         char **a;
206         int r;
207
208         if (asprintf(&p, "/run/systemd/users/%lu", (unsigned long) uid) < 0)
209                 return -ENOMEM;
210
211         r = parse_env_file(p, NEWLINE,
212                            variable, &s,
213                            NULL);
214         free(p);
215
216         if (r < 0) {
217                 free(s);
218
219                 if (r == -ENOENT) {
220                         if (array)
221                                 *array = NULL;
222                         return 0;
223                 }
224
225                 return r;
226         }
227
228         if (!s) {
229                 if (array)
230                         *array = NULL;
231                 return 0;
232         }
233
234         a = strv_split(s, " ");
235         free(s);
236
237         if (!a)
238                 return -ENOMEM;
239
240         strv_uniq(a);
241         r = strv_length(a);
242
243         if (array)
244                 *array = a;
245         else
246                 strv_free(a);
247
248         return r;
249 }
250
251 _public_ int sd_uid_get_sessions(uid_t uid, int require_active, char ***sessions) {
252         return uid_get_array(
253                         uid,
254                         require_active == 0 ? "ONLINE_SESSIONS" :
255                         require_active > 0  ? "ACTIVE_SESSIONS" :
256                                               "SESSIONS",
257                         sessions);
258 }
259
260 _public_ int sd_uid_get_seats(uid_t uid, int require_active, char ***seats) {
261         return uid_get_array(
262                         uid,
263                         require_active == 0 ? "ONLINE_SEATS" :
264                         require_active > 0  ? "ACTIVE_SEATS" :
265                                               "SEATS",
266                         seats);
267 }
268
269 static int file_of_session(const char *session, char **_p) {
270         char *p;
271         int r;
272
273         assert(_p);
274
275         if (session)
276                 p = strappend("/run/systemd/sessions/", session);
277         else {
278                 char *buf;
279
280                 r = sd_pid_get_session(0, &buf);
281                 if (r < 0)
282                         return r;
283
284                 p = strappend("/run/systemd/sessions/", buf);
285                 free(buf);
286         }
287
288         if (!p)
289                 return -ENOMEM;
290
291         *_p = p;
292         return 0;
293 }
294
295 _public_ int sd_session_is_active(const char *session) {
296         int r;
297         char *p, *s = NULL;
298
299         r = file_of_session(session, &p);
300         if (r < 0)
301                 return r;
302
303         r = parse_env_file(p, NEWLINE, "ACTIVE", &s, NULL);
304         free(p);
305
306         if (r < 0) {
307                 free(s);
308                 return r;
309         }
310
311         if (!s)
312                 return -EIO;
313
314         r = parse_boolean(s);
315         free(s);
316
317         return r;
318 }
319
320 _public_ int sd_session_get_state(const char *session, char **state) {
321         char *p, *s = NULL;
322         int r;
323
324         if (!state)
325                 return -EINVAL;
326
327         r = file_of_session(session, &p);
328         if (r < 0)
329                 return r;
330
331         r = parse_env_file(p, NEWLINE, "STATE", &s, NULL);
332         free(p);
333
334         if (r < 0) {
335                 free(s);
336                 return r;
337         } else if (!s)
338                 return -EIO;
339
340         *state = s;
341         return 0;
342 }
343
344 _public_ int sd_session_get_uid(const char *session, uid_t *uid) {
345         int r;
346         char *p, *s = NULL;
347
348         if (!uid)
349                 return -EINVAL;
350
351         r = file_of_session(session, &p);
352         if (r < 0)
353                 return r;
354
355         r = parse_env_file(p, NEWLINE, "UID", &s, NULL);
356         free(p);
357
358         if (r < 0) {
359                 free(s);
360                 return r;
361         }
362
363         if (!s)
364                 return -EIO;
365
366         r = parse_uid(s, uid);
367         free(s);
368
369         return r;
370 }
371
372 static int session_get_string(const char *session, const char *field, char **value) {
373         char *p, *s = NULL;
374         int r;
375
376         if (!value)
377                 return -EINVAL;
378
379         r = file_of_session(session, &p);
380         if (r < 0)
381                 return r;
382
383         r = parse_env_file(p, NEWLINE, field, &s, NULL);
384         free(p);
385
386         if (r < 0) {
387                 free(s);
388                 return r;
389         }
390
391         if (isempty(s))
392                 return -ENOENT;
393
394         *value = s;
395         return 0;
396 }
397
398 _public_ int sd_session_get_seat(const char *session, char **seat) {
399         return session_get_string(session, "SEAT", seat);
400 }
401
402 _public_ int sd_session_get_tty(const char *session, char **tty) {
403         return session_get_string(session, "TTY", tty);
404 }
405
406 _public_ int sd_session_get_service(const char *session, char **service) {
407         return session_get_string(session, "SERVICE", service);
408 }
409
410 _public_ int sd_session_get_type(const char *session, char **type) {
411         return session_get_string(session, "TYPE", type);
412 }
413
414 _public_ int sd_session_get_class(const char *session, char **class) {
415         return session_get_string(session, "CLASS", class);
416 }
417
418 _public_ int sd_session_get_display(const char *session, char **display) {
419         return session_get_string(session, "DISPLAY", display);
420 }
421
422 static int file_of_seat(const char *seat, char **_p) {
423         char *p;
424         int r;
425
426         assert(_p);
427
428         if (seat)
429                 p = strappend("/run/systemd/seats/", seat);
430         else {
431                 char *buf;
432
433                 r = sd_session_get_seat(NULL, &buf);
434                 if (r < 0)
435                         return r;
436
437                 p = strappend("/run/systemd/seats/", buf);
438                 free(buf);
439         }
440
441         if (!p)
442                 return -ENOMEM;
443
444         *_p = p;
445         return 0;
446 }
447
448 _public_ int sd_seat_get_active(const char *seat, char **session, uid_t *uid) {
449         char *p, *s = NULL, *t = NULL;
450         int r;
451
452         if (!session && !uid)
453                 return -EINVAL;
454
455         r = file_of_seat(seat, &p);
456         if (r < 0)
457                 return r;
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         r = file_of_seat(seat, &p);
507         if (r < 0)
508                 return r;
509
510         r = parse_env_file(p, NEWLINE,
511                            "SESSIONS", &s,
512                            "ACTIVE_SESSIONS", &t,
513                            NULL);
514         free(p);
515
516         if (r < 0) {
517                 free(s);
518                 free(t);
519                 return r;
520         }
521
522         if (s) {
523                 a = strv_split(s, " ");
524                 if (!a) {
525                         free(s);
526                         free(t);
527                         return -ENOMEM;
528                 }
529         }
530
531         free(s);
532
533         if (uids && t) {
534                 char *w, *state;
535                 size_t l;
536
537                 FOREACH_WORD(w, l, t, state)
538                         n++;
539
540                 if (n == 0)
541                         b = NULL;
542                 else {
543                         unsigned i = 0;
544
545                         b = new(uid_t, n);
546                         if (!b) {
547                                 strv_free(a);
548                                 return -ENOMEM;
549                         }
550
551                         FOREACH_WORD(w, l, t, state) {
552                                 char *k;
553
554                                 k = strndup(w, l);
555                                 if (!k) {
556                                         free(t);
557                                         free(b);
558                                         strv_free(a);
559                                         return -ENOMEM;
560                                 }
561
562                                 r = parse_uid(k, b + i);
563                                 free(k);
564                                 if (r < 0)
565                                         continue;
566
567                                 i++;
568                         }
569                 }
570         }
571
572         free(t);
573
574         r = strv_length(a);
575
576         if (sessions)
577                 *sessions = a;
578         else
579                 strv_free(a);
580
581         if (uids)
582                 *uids = b;
583
584         if (n_uids)
585                 *n_uids = n;
586
587         return r;
588 }
589
590 static int seat_get_can(const char *seat, const char *variable) {
591         char *p, *s = NULL;
592         int r;
593
594         r = file_of_seat(seat, &p);
595         if (r < 0)
596                 return r;
597
598         r = parse_env_file(p, NEWLINE,
599                            variable, &s,
600                            NULL);
601         free(p);
602
603         if (r < 0) {
604                 free(s);
605                 return r;
606         }
607
608         if (s) {
609                 r = parse_boolean(s);
610                 free(s);
611         } else
612                 r = 0;
613
614         return r;
615 }
616
617 _public_ int sd_seat_can_multi_session(const char *seat) {
618         return seat_get_can(seat, "CAN_MULTI_SESSION");
619 }
620
621 _public_ int sd_seat_can_tty(const char *seat) {
622         return seat_get_can(seat, "CAN_TTY");
623 }
624
625 _public_ int sd_seat_can_graphical(const char *seat) {
626         return seat_get_can(seat, "CAN_GRAPHICAL");
627 }
628
629 _public_ int sd_get_seats(char ***seats) {
630         return get_files_in_directory("/run/systemd/seats/", seats);
631 }
632
633 _public_ int sd_get_sessions(char ***sessions) {
634         return get_files_in_directory("/run/systemd/sessions/", sessions);
635 }
636
637 _public_ int sd_get_uids(uid_t **users) {
638         DIR *d;
639         int r = 0;
640         unsigned n = 0;
641         uid_t *l = NULL;
642
643         d = opendir("/run/systemd/users/");
644         if (!d)
645                 return -errno;
646
647         for (;;) {
648                 struct dirent *de;
649                 union dirent_storage buf;
650                 int k;
651                 uid_t uid;
652
653                 k = readdir_r(d, &buf.de, &de);
654                 if (k != 0) {
655                         r = -k;
656                         goto finish;
657                 }
658
659                 if (!de)
660                         break;
661
662                 dirent_ensure_type(d, de);
663
664                 if (!dirent_is_file(de))
665                         continue;
666
667                 k = parse_uid(de->d_name, &uid);
668                 if (k < 0)
669                         continue;
670
671                 if (users) {
672                         if ((unsigned) r >= n) {
673                                 uid_t *t;
674
675                                 n = MAX(16, 2*r);
676                                 t = realloc(l, sizeof(uid_t) * n);
677                                 if (!t) {
678                                         r = -ENOMEM;
679                                         goto finish;
680                                 }
681
682                                 l = t;
683                         }
684
685                         assert((unsigned) r < n);
686                         l[r++] = uid;
687                 } else
688                         r++;
689         }
690
691 finish:
692         if (d)
693                 closedir(d);
694
695         if (r >= 0) {
696                 if (users)
697                         *users = l;
698         } else
699                 free(l);
700
701         return r;
702 }
703
704 static inline int MONITOR_TO_FD(sd_login_monitor *m) {
705         return (int) (unsigned long) m - 1;
706 }
707
708 static inline sd_login_monitor* FD_TO_MONITOR(int fd) {
709         return (sd_login_monitor*) (unsigned long) (fd + 1);
710 }
711
712 _public_ int sd_login_monitor_new(const char *category, sd_login_monitor **m) {
713         int fd, k;
714         bool good = false;
715
716         if (!m)
717                 return -EINVAL;
718
719         fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC);
720         if (fd < 0)
721                 return -errno;
722
723         if (!category || streq(category, "seat")) {
724                 k = inotify_add_watch(fd, "/run/systemd/seats/", IN_MOVED_TO|IN_DELETE);
725                 if (k < 0) {
726                         close_nointr_nofail(fd);
727                         return -errno;
728                 }
729
730                 good = true;
731         }
732
733         if (!category || streq(category, "session")) {
734                 k = inotify_add_watch(fd, "/run/systemd/sessions/", IN_MOVED_TO|IN_DELETE);
735                 if (k < 0) {
736                         close_nointr_nofail(fd);
737                         return -errno;
738                 }
739
740                 good = true;
741         }
742
743         if (!category || streq(category, "uid")) {
744                 k = inotify_add_watch(fd, "/run/systemd/users/", IN_MOVED_TO|IN_DELETE);
745                 if (k < 0) {
746                         close_nointr_nofail(fd);
747                         return -errno;
748                 }
749
750                 good = true;
751         }
752
753         if (!good) {
754                 close_nointr(fd);
755                 return -EINVAL;
756         }
757
758         *m = FD_TO_MONITOR(fd);
759         return 0;
760 }
761
762 _public_ sd_login_monitor* sd_login_monitor_unref(sd_login_monitor *m) {
763         int fd;
764
765         if (!m)
766                 return NULL;
767
768         fd = MONITOR_TO_FD(m);
769         close_nointr(fd);
770
771         return NULL;
772 }
773
774 _public_ int sd_login_monitor_flush(sd_login_monitor *m) {
775
776         if (!m)
777                 return -EINVAL;
778
779         return flush_fd(MONITOR_TO_FD(m));
780 }
781
782 _public_ int sd_login_monitor_get_fd(sd_login_monitor *m) {
783
784         if (!m)
785                 return -EINVAL;
786
787         return MONITOR_TO_FD(m);
788 }
789
790 _public_ int sd_login_monitor_get_events(sd_login_monitor *m) {
791
792         if (!m)
793                 return -EINVAL;
794
795         /* For now we will only return POLLIN here, since we don't
796          * need anything else ever for inotify.  However, let's have
797          * this API to keep our options open should we later on need
798          * it. */
799         return POLLIN;
800 }
801
802 _public_ int sd_login_monitor_get_timeout(sd_login_monitor *m, uint64_t *timeout_usec) {
803
804         if (!m)
805                 return -EINVAL;
806         if (!timeout_usec)
807                 return -EINVAL;
808
809         /* For now we will only return (uint64_t) -1, since we don't
810          * need any timeout. However, let's have this API to keep our
811          * options open should we later on need it. */
812         *timeout_usec = (uint64_t) -1;
813         return 0;
814 }