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