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