chiark / gitweb /
c6224f8c7031eb12c6982c12f25fc8feb8f4103e
[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 = (n_fds ? CMSG_SPACE(sizeof(int) * n_fds) : 0) +
460                                         CMSG_SPACE(sizeof(struct ucred)) * have_pid;
461                 msghdr.msg_control = alloca(msghdr.msg_controllen);
462
463                 cmsg = CMSG_FIRSTHDR(&msghdr);
464                 if (n_fds > 0) {
465                         cmsg->cmsg_level = SOL_SOCKET;
466                         cmsg->cmsg_type = SCM_RIGHTS;
467                         cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds);
468
469                         memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds);
470
471                         if (have_pid)
472                                 assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg));
473                 }
474
475                 if (have_pid) {
476                         struct ucred *ucred;
477
478                         cmsg->cmsg_level = SOL_SOCKET;
479                         cmsg->cmsg_type = SCM_CREDENTIALS;
480                         cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
481
482                         ucred = (struct ucred*) CMSG_DATA(cmsg);
483                         ucred->pid = pid;
484                         ucred->uid = getuid();
485                         ucred->gid = getgid();
486                 }
487         }
488
489         /* First try with fake ucred data, as requested */
490         if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
491                 r = 1;
492                 goto finish;
493         }
494
495         /* If that failed, try with our own ucred instead */
496         if (have_pid) {
497                 msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred));
498                 if (msghdr.msg_controllen == 0)
499                         msghdr.msg_control = NULL;
500
501                 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
502                         r = 1;
503                         goto finish;
504                 }
505         }
506
507         r = -errno;
508
509 finish:
510         if (unset_environment)
511                 unsetenv("NOTIFY_SOCKET");
512
513         return r;
514 }
515
516 _public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) {
517         return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0);
518 }
519
520 _public_ int sd_notify(int unset_environment, const char *state) {
521         return sd_pid_notify_with_fds(0, unset_environment, state, NULL, 0);
522 }
523
524 /// UNNEEDED by elogind
525 #if 0
526 _public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) {
527         _cleanup_free_ char *p = NULL;
528         int r;
529
530         if (format) {
531                 va_list ap;
532
533                 va_start(ap, format);
534                 r = vasprintf(&p, format, ap);
535                 va_end(ap);
536
537                 if (r < 0 || !p)
538                         return -ENOMEM;
539         }
540
541         return sd_pid_notify(pid, unset_environment, p);
542 }
543
544 _public_ int sd_notifyf(int unset_environment, const char *format, ...) {
545         _cleanup_free_ char *p = NULL;
546         int r;
547
548         if (format) {
549                 va_list ap;
550
551                 va_start(ap, format);
552                 r = vasprintf(&p, format, ap);
553                 va_end(ap);
554
555                 if (r < 0 || !p)
556                         return -ENOMEM;
557         }
558
559         return sd_pid_notify(0, unset_environment, p);
560 }
561
562 _public_ int sd_booted(void) {
563         /* We test whether the runtime unit file directory has been
564          * created. This takes place in mount-setup.c, so is
565          * guaranteed to happen very early during boot. */
566
567         return laccess("/run/systemd/system/", F_OK) >= 0;
568 }
569 #endif // 0
570
571 _public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
572         const char *s, *p = ""; /* p is set to dummy value to do unsetting */
573         uint64_t u;
574         int r = 0;
575
576         s = getenv("WATCHDOG_USEC");
577         if (!s)
578                 goto finish;
579
580         r = safe_atou64(s, &u);
581         if (r < 0)
582                 goto finish;
583         if (u <= 0) {
584                 r = -EINVAL;
585                 goto finish;
586         }
587
588         p = getenv("WATCHDOG_PID");
589         if (p) {
590                 pid_t pid;
591
592                 r = parse_pid(p, &pid);
593                 if (r < 0)
594                         goto finish;
595
596                 /* Is this for us? */
597                 if (getpid() != pid) {
598                         r = 0;
599                         goto finish;
600                 }
601         }
602
603         if (usec)
604                 *usec = u;
605
606         r = 1;
607
608 finish:
609         if (unset_environment && s)
610                 unsetenv("WATCHDOG_USEC");
611         if (unset_environment && p)
612                 unsetenv("WATCHDOG_PID");
613
614         return r;
615 }