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