]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/libsystemd-daemon/sd-daemon.c
Always use errno > 0 to help gcc
[thirdparty/systemd.git] / src / libsystemd-daemon / sd-daemon.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 Copyright 2010 Lennart Poettering
5
6 Permission is hereby granted, free of charge, to any person
7 obtaining a copy of this software and associated documentation files
8 (the "Software"), to deal in the Software without restriction,
9 including without limitation the rights to use, copy, modify, merge,
10 publish, distribute, sublicense, and/or sell copies of the Software,
11 and to permit persons to whom the Software is furnished to do so,
12 subject to the following conditions:
13
14 The above copyright notice and this permission notice shall be
15 included in all copies or substantial portions of the Software.
16
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
21 BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
22 ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25 ***/
26
27 #ifndef _GNU_SOURCE
28 # define _GNU_SOURCE
29 #endif
30
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/socket.h>
34 #include <sys/un.h>
35 #ifdef __BIONIC__
36 # include <linux/fcntl.h>
37 #else
38 # include <sys/fcntl.h>
39 #endif
40 #include <netinet/in.h>
41 #include <stdlib.h>
42 #include <errno.h>
43 #include <unistd.h>
44 #include <string.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <stddef.h>
48 #include <limits.h>
49
50 #if defined(__linux__)
51 # include <mqueue.h>
52 #endif
53
54 #include "sd-daemon.h"
55
56 #if (__GNUC__ >= 4)
57 # ifdef SD_EXPORT_SYMBOLS
58 /* Export symbols */
59 # define _sd_export_ __attribute__ ((visibility("default")))
60 # else
61 /* Don't export the symbols */
62 # define _sd_export_ __attribute__ ((visibility("hidden")))
63 # endif
64 #else
65 # define _sd_export_
66 #endif
67
68 _sd_export_ int sd_listen_fds(int unset_environment) {
69
70 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
71 return 0;
72 #else
73 int r, fd;
74 const char *e;
75 char *p = NULL;
76 unsigned long l;
77
78 e = getenv("LISTEN_PID");
79 if (!e) {
80 r = 0;
81 goto finish;
82 }
83
84 errno = 0;
85 l = strtoul(e, &p, 10);
86
87 if (errno > 0) {
88 r = -errno;
89 goto finish;
90 }
91
92 if (!p || p == e || *p || l <= 0) {
93 r = -EINVAL;
94 goto finish;
95 }
96
97 /* Is this for us? */
98 if (getpid() != (pid_t) l) {
99 r = 0;
100 goto finish;
101 }
102
103 e = getenv("LISTEN_FDS");
104 if (!e) {
105 r = 0;
106 goto finish;
107 }
108
109 errno = 0;
110 l = strtoul(e, &p, 10);
111
112 if (errno > 0) {
113 r = -errno;
114 goto finish;
115 }
116
117 if (!p || p == e || *p) {
118 r = -EINVAL;
119 goto finish;
120 }
121
122 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
123 int flags;
124
125 flags = fcntl(fd, F_GETFD);
126 if (flags < 0) {
127 r = -errno;
128 goto finish;
129 }
130
131 if (flags & FD_CLOEXEC)
132 continue;
133
134 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
135 r = -errno;
136 goto finish;
137 }
138 }
139
140 r = (int) l;
141
142 finish:
143 if (unset_environment) {
144 unsetenv("LISTEN_PID");
145 unsetenv("LISTEN_FDS");
146 }
147
148 return r;
149 #endif
150 }
151
152 _sd_export_ int sd_is_fifo(int fd, const char *path) {
153 struct stat st_fd;
154
155 if (fd < 0)
156 return -EINVAL;
157
158 if (fstat(fd, &st_fd) < 0)
159 return -errno;
160
161 if (!S_ISFIFO(st_fd.st_mode))
162 return 0;
163
164 if (path) {
165 struct stat st_path;
166
167 if (stat(path, &st_path) < 0) {
168
169 if (errno == ENOENT || errno == ENOTDIR)
170 return 0;
171
172 return -errno;
173 }
174
175 return
176 st_path.st_dev == st_fd.st_dev &&
177 st_path.st_ino == st_fd.st_ino;
178 }
179
180 return 1;
181 }
182
183 _sd_export_ int sd_is_special(int fd, const char *path) {
184 struct stat st_fd;
185
186 if (fd < 0)
187 return -EINVAL;
188
189 if (fstat(fd, &st_fd) < 0)
190 return -errno;
191
192 if (!S_ISREG(st_fd.st_mode) && !S_ISCHR(st_fd.st_mode))
193 return 0;
194
195 if (path) {
196 struct stat st_path;
197
198 if (stat(path, &st_path) < 0) {
199
200 if (errno == ENOENT || errno == ENOTDIR)
201 return 0;
202
203 return -errno;
204 }
205
206 if (S_ISREG(st_fd.st_mode) && S_ISREG(st_path.st_mode))
207 return
208 st_path.st_dev == st_fd.st_dev &&
209 st_path.st_ino == st_fd.st_ino;
210 else if (S_ISCHR(st_fd.st_mode) && S_ISCHR(st_path.st_mode))
211 return st_path.st_rdev == st_fd.st_rdev;
212 else
213 return 0;
214 }
215
216 return 1;
217 }
218
219 static int sd_is_socket_internal(int fd, int type, int listening) {
220 struct stat st_fd;
221
222 if (fd < 0 || type < 0)
223 return -EINVAL;
224
225 if (fstat(fd, &st_fd) < 0)
226 return -errno;
227
228 if (!S_ISSOCK(st_fd.st_mode))
229 return 0;
230
231 if (type != 0) {
232 int other_type = 0;
233 socklen_t l = sizeof(other_type);
234
235 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
236 return -errno;
237
238 if (l != sizeof(other_type))
239 return -EINVAL;
240
241 if (other_type != type)
242 return 0;
243 }
244
245 if (listening >= 0) {
246 int accepting = 0;
247 socklen_t l = sizeof(accepting);
248
249 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
250 return -errno;
251
252 if (l != sizeof(accepting))
253 return -EINVAL;
254
255 if (!accepting != !listening)
256 return 0;
257 }
258
259 return 1;
260 }
261
262 union sockaddr_union {
263 struct sockaddr sa;
264 struct sockaddr_in in4;
265 struct sockaddr_in6 in6;
266 struct sockaddr_un un;
267 struct sockaddr_storage storage;
268 };
269
270 _sd_export_ int sd_is_socket(int fd, int family, int type, int listening) {
271 int r;
272
273 if (family < 0)
274 return -EINVAL;
275
276 r = sd_is_socket_internal(fd, type, listening);
277 if (r <= 0)
278 return r;
279
280 if (family > 0) {
281 union sockaddr_union sockaddr;
282 socklen_t l;
283
284 memset(&sockaddr, 0, sizeof(sockaddr));
285 l = sizeof(sockaddr);
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 return sockaddr.sa.sa_family == family;
294 }
295
296 return 1;
297 }
298
299 _sd_export_ int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
300 union sockaddr_union sockaddr;
301 socklen_t l;
302 int r;
303
304 if (family != 0 && family != AF_INET && family != AF_INET6)
305 return -EINVAL;
306
307 r = sd_is_socket_internal(fd, type, listening);
308 if (r <= 0)
309 return r;
310
311 memset(&sockaddr, 0, sizeof(sockaddr));
312 l = sizeof(sockaddr);
313
314 if (getsockname(fd, &sockaddr.sa, &l) < 0)
315 return -errno;
316
317 if (l < sizeof(sa_family_t))
318 return -EINVAL;
319
320 if (sockaddr.sa.sa_family != AF_INET &&
321 sockaddr.sa.sa_family != AF_INET6)
322 return 0;
323
324 if (family > 0)
325 if (sockaddr.sa.sa_family != family)
326 return 0;
327
328 if (port > 0) {
329 if (sockaddr.sa.sa_family == AF_INET) {
330 if (l < sizeof(struct sockaddr_in))
331 return -EINVAL;
332
333 return htons(port) == sockaddr.in4.sin_port;
334 } else {
335 if (l < sizeof(struct sockaddr_in6))
336 return -EINVAL;
337
338 return htons(port) == sockaddr.in6.sin6_port;
339 }
340 }
341
342 return 1;
343 }
344
345 _sd_export_ int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
346 union sockaddr_union sockaddr;
347 socklen_t l;
348 int r;
349
350 r = sd_is_socket_internal(fd, type, listening);
351 if (r <= 0)
352 return r;
353
354 memset(&sockaddr, 0, sizeof(sockaddr));
355 l = sizeof(sockaddr);
356
357 if (getsockname(fd, &sockaddr.sa, &l) < 0)
358 return -errno;
359
360 if (l < sizeof(sa_family_t))
361 return -EINVAL;
362
363 if (sockaddr.sa.sa_family != AF_UNIX)
364 return 0;
365
366 if (path) {
367 if (length == 0)
368 length = strlen(path);
369
370 if (length == 0)
371 /* Unnamed socket */
372 return l == offsetof(struct sockaddr_un, sun_path);
373
374 if (path[0])
375 /* Normal path socket */
376 return
377 (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
378 memcmp(path, sockaddr.un.sun_path, length+1) == 0;
379 else
380 /* Abstract namespace socket */
381 return
382 (l == offsetof(struct sockaddr_un, sun_path) + length) &&
383 memcmp(path, sockaddr.un.sun_path, length) == 0;
384 }
385
386 return 1;
387 }
388
389 _sd_export_ int sd_is_mq(int fd, const char *path) {
390 #if !defined(__linux__)
391 return 0;
392 #else
393 struct mq_attr attr;
394
395 if (fd < 0)
396 return -EINVAL;
397
398 if (mq_getattr(fd, &attr) < 0)
399 return -errno;
400
401 if (path) {
402 char fpath[PATH_MAX];
403 struct stat a, b;
404
405 if (path[0] != '/')
406 return -EINVAL;
407
408 if (fstat(fd, &a) < 0)
409 return -errno;
410
411 strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
412 fpath[sizeof(fpath)-1] = 0;
413
414 if (stat(fpath, &b) < 0)
415 return -errno;
416
417 if (a.st_dev != b.st_dev ||
418 a.st_ino != b.st_ino)
419 return 0;
420 }
421
422 return 1;
423 #endif
424 }
425
426 _sd_export_ int sd_notify(int unset_environment, const char *state) {
427 #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
428 return 0;
429 #else
430 int fd = -1, r;
431 struct msghdr msghdr;
432 struct iovec iovec;
433 union sockaddr_union sockaddr;
434 const char *e;
435
436 if (!state) {
437 r = -EINVAL;
438 goto finish;
439 }
440
441 e = getenv("NOTIFY_SOCKET");
442 if (!e)
443 return 0;
444
445 /* Must be an abstract socket, or an absolute path */
446 if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
447 r = -EINVAL;
448 goto finish;
449 }
450
451 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
452 if (fd < 0) {
453 r = -errno;
454 goto finish;
455 }
456
457 memset(&sockaddr, 0, sizeof(sockaddr));
458 sockaddr.sa.sa_family = AF_UNIX;
459 strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
460
461 if (sockaddr.un.sun_path[0] == '@')
462 sockaddr.un.sun_path[0] = 0;
463
464 memset(&iovec, 0, sizeof(iovec));
465 iovec.iov_base = (char*) state;
466 iovec.iov_len = strlen(state);
467
468 memset(&msghdr, 0, sizeof(msghdr));
469 msghdr.msg_name = &sockaddr;
470 msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
471
472 if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
473 msghdr.msg_namelen = sizeof(struct sockaddr_un);
474
475 msghdr.msg_iov = &iovec;
476 msghdr.msg_iovlen = 1;
477
478 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
479 r = -errno;
480 goto finish;
481 }
482
483 r = 1;
484
485 finish:
486 if (unset_environment)
487 unsetenv("NOTIFY_SOCKET");
488
489 if (fd >= 0)
490 close(fd);
491
492 return r;
493 #endif
494 }
495
496 _sd_export_ int sd_notifyf(int unset_environment, const char *format, ...) {
497 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
498 return 0;
499 #else
500 va_list ap;
501 char *p = NULL;
502 int r;
503
504 va_start(ap, format);
505 r = vasprintf(&p, format, ap);
506 va_end(ap);
507
508 if (r < 0 || !p)
509 return -ENOMEM;
510
511 r = sd_notify(unset_environment, p);
512 free(p);
513
514 return r;
515 #endif
516 }
517
518 _sd_export_ int sd_booted(void) {
519 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
520 return 0;
521 #else
522 struct stat st;
523
524 /* We test whether the runtime unit file directory has been
525 * created. This takes place in mount-setup.c, so is
526 * guaranteed to happen very early during boot. */
527
528 if (lstat("/run/systemd/system/", &st) < 0)
529 return 0;
530
531 return !!S_ISDIR(st.st_mode);
532 #endif
533 }