chiark / gitweb /
Merge pull request #7 from elogind/dev_v228-r1
[elogind.git] / src / libelogind / sd-daemon / sd-daemon.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 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 <errno.h>
23 #include <limits.h>
24 #include <mqueue.h>
25 #include <netinet/in.h>
26 #include <stdarg.h>
27 #include <stddef.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/socket.h>
32 #include <sys/stat.h>
33 #include <sys/un.h>
34 #include <unistd.h>
35
36 #include "sd-daemon.h"
37
38 #include "alloc-util.h"
39 #include "fd-util.h"
40 //#include "fs-util.h"
41 #include "parse-util.h"
42 #include "path-util.h"
43 #include "socket-util.h"
44 #include "strv.h"
45 #include "util.h"
46
47 #define SNDBUF_SIZE (8*1024*1024)
48
49 #if 0 /// UNNEEDED by elogind
50 static void unsetenv_all(bool unset_environment) {
51
52         if (!unset_environment)
53                 return;
54
55         unsetenv("LISTEN_PID");
56         unsetenv("LISTEN_FDS");
57         unsetenv("LISTEN_FDNAMES");
58 }
59
60 _public_ int sd_listen_fds(int unset_environment) {
61         const char *e;
62         int n, r, fd;
63         pid_t pid;
64
65         e = getenv("LISTEN_PID");
66         if (!e) {
67                 r = 0;
68                 goto finish;
69         }
70
71         r = parse_pid(e, &pid);
72         if (r < 0)
73                 goto finish;
74
75         /* Is this for us? */
76         if (getpid() != pid) {
77                 r = 0;
78                 goto finish;
79         }
80
81         e = getenv("LISTEN_FDS");
82         if (!e) {
83                 r = 0;
84                 goto finish;
85         }
86
87         r = safe_atoi(e, &n);
88         if (r < 0)
89                 goto finish;
90
91         assert_cc(SD_LISTEN_FDS_START < INT_MAX);
92         if (n <= 0 || n > INT_MAX - SD_LISTEN_FDS_START) {
93                 r = -EINVAL;
94                 goto finish;
95         }
96
97         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd ++) {
98                 r = fd_cloexec(fd, true);
99                 if (r < 0)
100                         goto finish;
101         }
102
103         r = n;
104
105 finish:
106         unsetenv_all(unset_environment);
107         return r;
108         }
109
110 _public_ int sd_listen_fds_with_names(int unset_environment, char ***names) {
111         _cleanup_strv_free_ char **l = NULL;
112         bool have_names;
113         int n_names = 0, n_fds;
114         const char *e;
115         int r;
116
117         if (!names)
118                 return sd_listen_fds(unset_environment);
119
120         e = getenv("LISTEN_FDNAMES");
121         if (e) {
122                 n_names = strv_split_extract(&l, e, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
123                 if (n_names < 0) {
124                         unsetenv_all(unset_environment);
125                         return n_names;
126                 }
127
128                 have_names = true;
129         } else
130                 have_names = false;
131
132         n_fds = sd_listen_fds(unset_environment);
133         if (n_fds <= 0)
134                 return n_fds;
135
136         if (have_names) {
137                 if (n_names != n_fds)
138                         return -EINVAL;
139         } else {
140                 r = strv_extend_n(&l, "unknown", n_fds);
141                 if (r < 0)
142         return r;
143 }
144
145         *names = l;
146         l = NULL;
147
148         return n_fds;
149 }
150
151 _public_ int sd_is_fifo(int fd, const char *path) {
152         struct stat st_fd;
153
154         assert_return(fd >= 0, -EBADF);
155
156         if (fstat(fd, &st_fd) < 0)
157                 return -errno;
158
159         if (!S_ISFIFO(st_fd.st_mode))
160                 return 0;
161
162         if (path) {
163                 struct stat st_path;
164
165                 if (stat(path, &st_path) < 0) {
166
167                         if (errno == ENOENT || errno == ENOTDIR)
168                                 return 0;
169
170                         return -errno;
171                 }
172
173                 return
174                         st_path.st_dev == st_fd.st_dev &&
175                         st_path.st_ino == st_fd.st_ino;
176         }
177
178         return 1;
179 }
180
181 _public_ int sd_is_special(int fd, const char *path) {
182         struct stat st_fd;
183
184         assert_return(fd >= 0, -EBADF);
185
186         if (fstat(fd, &st_fd) < 0)
187                 return -errno;
188
189         if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode))
190                 return 0;
191
192         if (path) {
193                 struct stat st_path;
194
195                 if (stat(path, &st_path) < 0) {
196
197                         if (errno == ENOENT || errno == ENOTDIR)
198                                 return 0;
199
200                         return -errno;
201                 }
202
203                 if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode))
204                         return
205                                 st_path.st_dev == st_fd.st_dev &&
206                                 st_path.st_ino == st_fd.st_ino;
207                 else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode))
208                         return st_path.st_rdev == st_fd.st_rdev;
209                 else
210                         return 0;
211         }
212
213         return 1;
214 }
215 #endif // 0
216
217 static int sd_is_socket_internal(int fd, int type, int listening) {
218         struct stat st_fd;
219
220         assert_return(fd >= 0, -EBADF);
221         assert_return(type >= 0, -EINVAL);
222
223         if (fstat(fd, &st_fd) < 0)
224                 return -errno;
225
226         if (!S_ISSOCK(st_fd.st_mode))
227                 return 0;
228
229         if (type != 0) {
230                 int other_type = 0;
231                 socklen_t l = sizeof(other_type);
232
233                 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
234                         return -errno;
235
236                 if (l != sizeof(other_type))
237                         return -EINVAL;
238
239                 if (other_type != type)
240                         return 0;
241         }
242
243         if (listening >= 0) {
244                 int accepting = 0;
245                 socklen_t l = sizeof(accepting);
246
247                 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
248                         return -errno;
249
250                 if (l != sizeof(accepting))
251                         return -EINVAL;
252
253                 if (!accepting != !listening)
254                         return 0;
255         }
256
257         return 1;
258 }
259
260 _public_ int sd_is_socket(int fd, int family, int type, int listening) {
261         int r;
262
263         assert_return(fd >= 0, -EBADF);
264         assert_return(family >= 0, -EINVAL);
265
266         r = sd_is_socket_internal(fd, type, listening);
267         if (r <= 0)
268                 return r;
269
270         if (family > 0) {
271                 union sockaddr_union sockaddr = {};
272                 socklen_t l = sizeof(sockaddr);
273
274                 if (getsockname(fd, &sockaddr.sa, &l) < 0)
275                         return -errno;
276
277                 if (l < sizeof(sa_family_t))
278                         return -EINVAL;
279
280                 return sockaddr.sa.sa_family == family;
281         }
282
283         return 1;
284 }
285
286 #if 0 /// UNNEEDED by elogind
287 _public_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
288         union sockaddr_union sockaddr = {};
289         socklen_t l = sizeof(sockaddr);
290         int r;
291
292         assert_return(fd >= 0, -EBADF);
293         assert_return(IN_SET(family, 0, AF_INET, AF_INET6), -EINVAL);
294
295         r = sd_is_socket_internal(fd, type, listening);
296         if (r <= 0)
297                 return r;
298
299         if (getsockname(fd, &sockaddr.sa, &l) < 0)
300                 return -errno;
301
302         if (l < sizeof(sa_family_t))
303                 return -EINVAL;
304
305         if (sockaddr.sa.sa_family != AF_INET &&
306             sockaddr.sa.sa_family != AF_INET6)
307                 return 0;
308
309         if (family != 0)
310                 if (sockaddr.sa.sa_family != family)
311                         return 0;
312
313         if (port > 0) {
314                 if (sockaddr.sa.sa_family == AF_INET) {
315                         if (l < sizeof(struct sockaddr_in))
316                                 return -EINVAL;
317
318                         return htons(port) == sockaddr.in.sin_port;
319                 } else {
320                         if (l < sizeof(struct sockaddr_in6))
321                                 return -EINVAL;
322
323                         return htons(port) == sockaddr.in6.sin6_port;
324                 }
325         }
326
327         return 1;
328 }
329
330 _public_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
331         union sockaddr_union sockaddr = {};
332         socklen_t l = sizeof(sockaddr);
333         int r;
334
335         assert_return(fd >= 0, -EBADF);
336
337         r = sd_is_socket_internal(fd, type, listening);
338         if (r <= 0)
339                 return r;
340
341         if (getsockname(fd, &sockaddr.sa, &l) < 0)
342                 return -errno;
343
344         if (l < sizeof(sa_family_t))
345                 return -EINVAL;
346
347         if (sockaddr.sa.sa_family != AF_UNIX)
348                 return 0;
349
350         if (path) {
351                 if (length == 0)
352                         length = strlen(path);
353
354                 if (length == 0)
355                         /* Unnamed socket */
356                         return l == offsetof(struct sockaddr_un, sun_path);
357
358                 if (path[0])
359                         /* Normal path socket */
360                         return
361                                 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
362                                 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
363                 else
364                         /* Abstract namespace socket */
365                         return
366                                 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
367                                 memcmp(path, sockaddr.un.sun_path, length) == 0;
368         }
369
370         return 1;
371 }
372
373 _public_ int sd_is_mq(int fd, const char *path) {
374         struct mq_attr attr;
375
376         /* Check that the fd is valid */
377         assert_return(fcntl(fd, F_GETFD) >= 0, -errno);
378
379         if (mq_getattr(fd, &attr) < 0) {
380                 if (errno == EBADF)
381                         /* A non-mq fd (or an invalid one, but we ruled that out above) */
382                         return 0;
383                 return -errno;
384         }
385
386         if (path) {
387                 char fpath[PATH_MAX];
388                 struct stat a, b;
389
390                 assert_return(path_is_absolute(path), -EINVAL);
391
392                 if (fstat(fd, &a) < 0)
393                         return -errno;
394
395                 strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
396                 fpath[sizeof(fpath)-1] = 0;
397
398                 if (stat(fpath, &b) < 0)
399                         return -errno;
400
401                 if (a.st_dev != b.st_dev ||
402                     a.st_ino != b.st_ino)
403                         return 0;
404         }
405
406         return 1;
407 }
408 #endif // 0
409
410 _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds) {
411         union sockaddr_union sockaddr = {
412                 .sa.sa_family = AF_UNIX,
413         };
414         struct iovec iovec = {
415                 .iov_base = (char*) state,
416         };
417         struct msghdr msghdr = {
418                 .msg_iov = &iovec,
419                 .msg_iovlen = 1,
420                 .msg_name = &sockaddr,
421         };
422         _cleanup_close_ int fd = -1;
423         struct cmsghdr *cmsg = NULL;
424         const char *e;
425         bool have_pid;
426         int r;
427
428         if (!state) {
429                 r = -EINVAL;
430                 goto finish;
431         }
432
433         if (n_fds > 0 && !fds) {
434                 r = -EINVAL;
435                 goto finish;
436         }
437
438         e = getenv("NOTIFY_SOCKET");
439         if (!e)
440                 return 0;
441
442         /* Must be an abstract socket, or an absolute path */
443         if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
444                 r = -EINVAL;
445                 goto finish;
446         }
447
448         if (strlen(e) > sizeof(sockaddr.un.sun_path)) {
449                 r = -EINVAL;
450                 goto finish;
451         }
452
453         fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
454         if (fd < 0) {
455                 r = -errno;
456                 goto finish;
457         }
458
459         fd_inc_sndbuf(fd, SNDBUF_SIZE);
460
461         iovec.iov_len = strlen(state);
462
463         strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
464         if (sockaddr.un.sun_path[0] == '@')
465                 sockaddr.un.sun_path[0] = 0;
466
467         msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
468         if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
469                 msghdr.msg_namelen = sizeof(struct sockaddr_un);
470
471         have_pid = pid != 0 && pid != getpid();
472
473         if (n_fds > 0 || have_pid) {
474                 /* CMSG_SPACE(0) may return value different then zero, which results in miscalculated controllen. */
475                 msghdr.msg_controllen =
476                         (n_fds > 0 ? CMSG_SPACE(sizeof(int) * n_fds) : 0) +
477                         (have_pid ? CMSG_SPACE(sizeof(struct ucred)) : 0);
478
479                 msghdr.msg_control = alloca0(msghdr.msg_controllen);
480
481                 cmsg = CMSG_FIRSTHDR(&msghdr);
482                 if (n_fds > 0) {
483                         cmsg->cmsg_level = SOL_SOCKET;
484                         cmsg->cmsg_type = SCM_RIGHTS;
485                         cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds);
486
487                         memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds);
488
489                         if (have_pid)
490                                 assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg));
491                 }
492
493                 if (have_pid) {
494                         struct ucred *ucred;
495
496                         cmsg->cmsg_level = SOL_SOCKET;
497                         cmsg->cmsg_type = SCM_CREDENTIALS;
498                         cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
499
500                         ucred = (struct ucred*) CMSG_DATA(cmsg);
501                         ucred->pid = pid;
502                         ucred->uid = getuid();
503                         ucred->gid = getgid();
504                 }
505         }
506
507         /* First try with fake ucred data, as requested */
508         if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
509                 r = 1;
510                 goto finish;
511         }
512
513         /* If that failed, try with our own ucred instead */
514         if (have_pid) {
515                 msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred));
516                 if (msghdr.msg_controllen == 0)
517                         msghdr.msg_control = NULL;
518
519                 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
520                         r = 1;
521                         goto finish;
522                 }
523         }
524
525         r = -errno;
526
527 finish:
528         if (unset_environment)
529                 unsetenv("NOTIFY_SOCKET");
530
531         return r;
532 }
533
534 #if 0 /// UNNEEDED by elogind
535 _public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) {
536         return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0);
537 }
538 #endif // 0
539
540 _public_ int sd_notify(int unset_environment, const char *state) {
541         return sd_pid_notify_with_fds(0, unset_environment, state, NULL, 0);
542 }
543
544 #if 0 /// UNNEEDED by elogind
545 _public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) {
546         _cleanup_free_ char *p = NULL;
547         int r;
548
549         if (format) {
550                 va_list ap;
551
552                 va_start(ap, format);
553                 r = vasprintf(&p, format, ap);
554                 va_end(ap);
555
556                 if (r < 0 || !p)
557                         return -ENOMEM;
558         }
559
560         return sd_pid_notify(pid, unset_environment, p);
561 }
562
563 _public_ int sd_notifyf(int unset_environment, const char *format, ...) {
564         _cleanup_free_ char *p = NULL;
565         int r;
566
567         if (format) {
568                 va_list ap;
569
570                 va_start(ap, format);
571                 r = vasprintf(&p, format, ap);
572                 va_end(ap);
573
574                 if (r < 0 || !p)
575                         return -ENOMEM;
576         }
577
578         return sd_pid_notify(0, unset_environment, p);
579 }
580
581 _public_ int sd_booted(void) {
582         /* We test whether the runtime unit file directory has been
583          * created. This takes place in mount-setup.c, so is
584          * guaranteed to happen very early during boot. */
585
586         return laccess("/run/systemd/system/", F_OK) >= 0;
587 }
588 #endif // 0
589
590 _public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
591         const char *s, *p = ""; /* p is set to dummy value to do unsetting */
592         uint64_t u;
593         int r = 0;
594
595         s = getenv("WATCHDOG_USEC");
596         if (!s)
597                 goto finish;
598
599         r = safe_atou64(s, &u);
600         if (r < 0)
601                 goto finish;
602         if (u <= 0 || u >= USEC_INFINITY) {
603                 r = -EINVAL;
604                 goto finish;
605         }
606
607         p = getenv("WATCHDOG_PID");
608         if (p) {
609                 pid_t pid;
610
611                 r = parse_pid(p, &pid);
612                 if (r < 0)
613                         goto finish;
614
615                 /* Is this for us? */
616                 if (getpid() != pid) {
617                         r = 0;
618                         goto finish;
619                 }
620         }
621
622         if (usec)
623                 *usec = u;
624
625         r = 1;
626
627 finish:
628         if (unset_environment && s)
629                 unsetenv("WATCHDOG_USEC");
630         if (unset_environment && p)
631                 unsetenv("WATCHDOG_PID");
632
633         return r;
634 }