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