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