chiark / gitweb /
7bbb3517be740228a648b89371698e00556ee3ae
[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, -EINVAL);
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, -EINVAL);
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, -EINVAL);
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, -EINVAL);
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, -EINVAL);
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, -EINVAL);
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 _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds) {
311         union sockaddr_union sockaddr = {
312                 .sa.sa_family = AF_UNIX,
313         };
314         struct iovec iovec = {
315                 .iov_base = (char*) state,
316         };
317         struct msghdr msghdr = {
318                 .msg_iov = &iovec,
319                 .msg_iovlen = 1,
320                 .msg_name = &sockaddr,
321         };
322         _cleanup_close_ int fd = -1;
323         struct cmsghdr *cmsg = NULL;
324         const char *e;
325         bool have_pid;
326         int r;
327
328         if (!state) {
329                 r = -EINVAL;
330                 goto finish;
331         }
332
333         if (n_fds > 0 && !fds) {
334                 r = -EINVAL;
335                 goto finish;
336         }
337
338         e = getenv("NOTIFY_SOCKET");
339         if (!e)
340                 return 0;
341
342         /* Must be an abstract socket, or an absolute path */
343         if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
344                 r = -EINVAL;
345                 goto finish;
346         }
347
348         fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
349         if (fd < 0) {
350                 r = -errno;
351                 goto finish;
352         }
353
354         iovec.iov_len = strlen(state);
355
356         strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
357         if (sockaddr.un.sun_path[0] == '@')
358                 sockaddr.un.sun_path[0] = 0;
359
360         msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
361         if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
362                 msghdr.msg_namelen = sizeof(struct sockaddr_un);
363
364         have_pid = pid != 0 && pid != getpid();
365
366         if (n_fds > 0 || have_pid) {
367                 msghdr.msg_controllen = CMSG_SPACE(sizeof(int) * n_fds) +
368                                         CMSG_SPACE(sizeof(struct ucred) * have_pid);
369                 msghdr.msg_control = alloca(msghdr.msg_controllen);
370
371                 cmsg = CMSG_FIRSTHDR(&msghdr);
372                 if (n_fds > 0) {
373                         cmsg->cmsg_level = SOL_SOCKET;
374                         cmsg->cmsg_type = SCM_RIGHTS;
375                         cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds);
376
377                         memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds);
378
379                         if (have_pid)
380                                 assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg));
381                 }
382
383                 if (have_pid) {
384                         struct ucred *ucred;
385
386                         cmsg->cmsg_level = SOL_SOCKET;
387                         cmsg->cmsg_type = SCM_CREDENTIALS;
388                         cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
389
390                         ucred = (struct ucred*) CMSG_DATA(cmsg);
391                         ucred->pid = pid;
392                         ucred->uid = getuid();
393                         ucred->gid = getgid();
394                 }
395         }
396
397         /* First try with fake ucred data, as requested */
398         if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
399                 r = 1;
400                 goto finish;
401         }
402
403         /* If that failed, try with our own ucred instead */
404         if (have_pid) {
405                 msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred));
406                 if (msghdr.msg_controllen == 0)
407                         msghdr.msg_control = NULL;
408
409                 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
410                         r = 1;
411                         goto finish;
412                 }
413         }
414
415         r = -errno;
416
417 finish:
418         if (unset_environment)
419                 unsetenv("NOTIFY_SOCKET");
420
421         return r;
422 }
423
424 _public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) {
425         return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0);
426 }
427
428 _public_ int sd_notify(int unset_environment, const char *state) {
429         return sd_pid_notify_with_fds(0, unset_environment, state, NULL, 0);
430 }
431
432 _public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) {
433         _cleanup_free_ char *p = NULL;
434         int r;
435
436         if (format) {
437                 va_list ap;
438
439                 va_start(ap, format);
440                 r = vasprintf(&p, format, ap);
441                 va_end(ap);
442
443                 if (r < 0 || !p)
444                         return -ENOMEM;
445         }
446
447         return sd_pid_notify(pid, unset_environment, p);
448 }
449
450 _public_ int sd_notifyf(int unset_environment, const char *format, ...) {
451         _cleanup_free_ char *p = NULL;
452         int r;
453
454         if (format) {
455                 va_list ap;
456
457                 va_start(ap, format);
458                 r = vasprintf(&p, format, ap);
459                 va_end(ap);
460
461                 if (r < 0 || !p)
462                         return -ENOMEM;
463         }
464
465         return sd_pid_notify(0, unset_environment, p);
466 }
467
468 _public_ int sd_booted(void) {
469         struct stat st;
470
471         /* We test whether the runtime unit file directory has been
472          * created. This takes place in mount-setup.c, so is
473          * guaranteed to happen very early during boot. */
474
475         if (lstat("/run/systemd/system/", &st) < 0)
476                 return 0;
477
478         return !!S_ISDIR(st.st_mode);
479 }
480
481 _public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
482         const char *s, *p = ""; /* p is set to dummy value to do unsetting */
483         uint64_t u;
484         int r = 0;
485
486         s = getenv("WATCHDOG_USEC");
487         if (!s)
488                 goto finish;
489
490         r = safe_atou64(s, &u);
491         if (r < 0)
492                 goto finish;
493         if (u <= 0) {
494                 r = -EINVAL;
495                 goto finish;
496         }
497
498         p = getenv("WATCHDOG_PID");
499         if (p) {
500                 pid_t pid;
501
502                 r = parse_pid(p, &pid);
503                 if (r < 0)
504                         goto finish;
505
506                 /* Is this for us? */
507                 if (getpid() != pid) {
508                         r = 0;
509                         goto finish;
510                 }
511         }
512
513         if (usec)
514                 *usec = u;
515
516         r = 1;
517
518 finish:
519         if (unset_environment && s)
520                 unsetenv("WATCHDOG_USEC");
521         if (unset_environment && p)
522                 unsetenv("WATCHDOG_PID");
523
524         return r;
525 }