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