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