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