i3
sd-daemon.c
Go to the documentation of this file.
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 "sd-daemon.h"
32
33#include <errno.h>
34#include <netinet/in.h>
35#include <stdarg.h>
36#include <stddef.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <sys/fcntl.h>
41#include <sys/socket.h>
42#include <sys/stat.h>
43#include <sys/types.h>
44#include <sys/un.h>
45#include <unistd.h>
46
47int sd_listen_fds(int unset_environment) {
48 int r, fd;
49 const char *e;
50 char *p = NULL;
51 unsigned long l;
52
53 if (!(e = getenv("LISTEN_PID"))) {
54 r = 0;
55 goto finish;
56 }
57
58 errno = 0;
59 l = strtoul(e, &p, 10);
60
61 if (errno != 0) {
62 r = -errno;
63 goto finish;
64 }
65
66 if (!p || *p || l <= 0) {
67 r = -EINVAL;
68 goto finish;
69 }
70
71 /* Is this for us? */
72 if (getpid() != (pid_t)l) {
73 r = 0;
74 goto finish;
75 }
76
77 if (!(e = getenv("LISTEN_FDS"))) {
78 r = 0;
79 goto finish;
80 }
81
82 errno = 0;
83 l = strtoul(e, &p, 10);
84
85 if (errno != 0) {
86 r = -errno;
87 goto finish;
88 }
89
90 if (!p || *p) {
91 r = -EINVAL;
92 goto finish;
93 }
94
95 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int)l; fd++) {
96 int flags;
97
98 if ((flags = fcntl(fd, F_GETFD)) < 0) {
99 r = -errno;
100 goto finish;
101 }
102
103 if (flags & FD_CLOEXEC) {
104 continue;
105 }
106
107 if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
108 r = -errno;
109 goto finish;
110 }
111 }
112
113 r = (int)l;
114
115finish:
116 if (unset_environment) {
117 unsetenv("LISTEN_PID");
118 unsetenv("LISTEN_FDS");
119 }
120
121 return r;
122}
123
124int sd_is_fifo(int fd, const char *path) {
125 struct stat st_fd;
126
127 if (fd < 0) {
128 return -EINVAL;
129 }
130
131 memset(&st_fd, 0, sizeof(st_fd));
132 if (fstat(fd, &st_fd) < 0) {
133 return -errno;
134 }
135
136 if (!S_ISFIFO(st_fd.st_mode)) {
137 return 0;
138 }
139
140 if (path) {
141 struct stat st_path;
142
143 memset(&st_path, 0, sizeof(st_path));
144 if (stat(path, &st_path) < 0) {
145 if (errno == ENOENT || errno == ENOTDIR) {
146 return 0;
147 }
148
149 return -errno;
150 }
151
152 return st_path.st_dev == st_fd.st_dev &&
153 st_path.st_ino == st_fd.st_ino;
154 }
155
156 return 1;
157}
158
159static int sd_is_socket_internal(int fd, int type, int listening) {
160 struct stat st_fd;
161
162 if (fd < 0 || type < 0) {
163 return -EINVAL;
164 }
165
166 if (fstat(fd, &st_fd) < 0) {
167 return -errno;
168 }
169
170 if (!S_ISSOCK(st_fd.st_mode)) {
171 return 0;
172 }
173
174 if (type != 0) {
175 int other_type = 0;
176 socklen_t l = sizeof(other_type);
177
178 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0) {
179 return -errno;
180 }
181
182 if (l != sizeof(other_type)) {
183 return -EINVAL;
184 }
185
186 if (other_type != type) {
187 return 0;
188 }
189 }
190
191 if (listening >= 0) {
192 int accepting = 0;
193 socklen_t l = sizeof(accepting);
194
195 if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0) {
196 return -errno;
197 }
198
199 if (l != sizeof(accepting)) {
200 return -EINVAL;
201 }
202
203 if (!accepting != !listening) {
204 return 0;
205 }
206 }
207
208 return 1;
209}
210
212 struct sockaddr sa;
213 struct sockaddr_in in4;
214 struct sockaddr_in6 in6;
215 struct sockaddr_un un;
216 struct sockaddr_storage storage;
217};
218
219int sd_is_socket(int fd, int family, int type, int listening) {
220 int r;
221
222 if (family < 0) {
223 return -EINVAL;
224 }
225
226 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) {
227 return r;
228 }
229
230 if (family > 0) {
231 union sockaddr_union sockaddr;
232 socklen_t l;
233
234 memset(&sockaddr, 0, sizeof(sockaddr));
235 l = sizeof(sockaddr);
236
237 if (getsockname(fd, &sockaddr.sa, &l) < 0) {
238 return -errno;
239 }
240
241 if (l < sizeof(sa_family_t)) {
242 return -EINVAL;
243 }
244
245 return sockaddr.sa.sa_family == family;
246 }
247
248 return 1;
249}
250
251int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
252 union sockaddr_union sockaddr;
253 socklen_t l;
254 int r;
255
256 if (family != 0 && family != AF_INET && family != AF_INET6) {
257 return -EINVAL;
258 }
259
260 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) {
261 return r;
262 }
263
264 memset(&sockaddr, 0, sizeof(sockaddr));
265 l = sizeof(sockaddr);
266
267 if (getsockname(fd, &sockaddr.sa, &l) < 0) {
268 return -errno;
269 }
270
271 if (l < sizeof(sa_family_t)) {
272 return -EINVAL;
273 }
274
275 if (sockaddr.sa.sa_family != AF_INET &&
276 sockaddr.sa.sa_family != AF_INET6) {
277 return 0;
278 }
279
280 if (family > 0) {
281 if (sockaddr.sa.sa_family != family) {
282 return 0;
283 }
284 }
285
286 if (port > 0) {
287 if (sockaddr.sa.sa_family == AF_INET) {
288 if (l < sizeof(struct sockaddr_in)) {
289 return -EINVAL;
290 }
291
292 return htons(port) == sockaddr.in4.sin_port;
293 } else {
294 if (l < sizeof(struct sockaddr_in6)) {
295 return -EINVAL;
296 }
297
298 return htons(port) == sockaddr.in6.sin6_port;
299 }
300 }
301
302 return 1;
303}
304
305int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
306 union sockaddr_union sockaddr;
307 socklen_t l;
308 int r;
309
310 if ((r = sd_is_socket_internal(fd, type, listening)) <= 0) {
311 return r;
312 }
313
314 memset(&sockaddr, 0, sizeof(sockaddr));
315 l = sizeof(sockaddr);
316
317 if (getsockname(fd, &sockaddr.sa, &l) < 0) {
318 return -errno;
319 }
320
321 if (l < sizeof(sa_family_t)) {
322 return -EINVAL;
323 }
324
325 if (sockaddr.sa.sa_family != AF_UNIX) {
326 return 0;
327 }
328
329 if (path) {
330 if (length <= 0) {
331 length = strlen(path);
332 }
333
334 if (length <= 0) {
335 /* Unnamed socket */
336 return l == offsetof(struct sockaddr_un, sun_path);
337 }
338
339 if (path[0]) {
340 /* Normal path socket */
341 return (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
342 memcmp(path, sockaddr.un.sun_path, length + 1) == 0;
343 } else {
344 /* Abstract namespace socket */
345 return (l == offsetof(struct sockaddr_un, sun_path) + length) &&
346 memcmp(path, sockaddr.un.sun_path, length) == 0;
347 }
348 }
349
350 return 1;
351}
352
353int sd_notify(int unset_environment, const char *state) {
354#if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
355 return 0;
356#else
357 int fd = -1, r;
358 struct msghdr msghdr;
359 struct iovec iovec;
360 union sockaddr_union sockaddr;
361 const char *e;
362
363 if (!state) {
364 r = -EINVAL;
365 goto finish;
366 }
367
368 if (!(e = getenv("NOTIFY_SOCKET"))) {
369 return 0;
370 }
371
372 /* Must be an abstract socket, or an absolute path */
373 if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
374 r = -EINVAL;
375 goto finish;
376 }
377
378 if (strlen(e) > sizeof(sockaddr.un.sun_path)) {
379 r = -EINVAL;
380 goto finish;
381 }
382
383 if ((fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) {
384 r = -errno;
385 goto finish;
386 }
387
388 memset(&sockaddr, 0, sizeof(sockaddr));
389 sockaddr.sa.sa_family = AF_UNIX;
390 strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path) - 1);
391
392 if (sockaddr.un.sun_path[0] == '@') {
393 sockaddr.un.sun_path[0] = 0;
394 }
395
396 memset(&iovec, 0, sizeof(iovec));
397 iovec.iov_base = (char *)state;
398 iovec.iov_len = strlen(state);
399
400 memset(&msghdr, 0, sizeof(msghdr));
401 msghdr.msg_name = &sockaddr;
402 msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
403
404 if (msghdr.msg_namelen > sizeof(struct sockaddr_un)) {
405 msghdr.msg_namelen = sizeof(struct sockaddr_un);
406 }
407
408 msghdr.msg_iov = &iovec;
409 msghdr.msg_iovlen = 1;
410
411 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
412 r = -errno;
413 goto finish;
414 }
415
416 r = 1;
417
418finish:
419 if (unset_environment) {
420 unsetenv("NOTIFY_SOCKET");
421 }
422
423 if (fd >= 0) {
424 close(fd);
425 }
426
427 return r;
428#endif
429}
430
431int sd_notifyf(int unset_environment, const char *format, ...) {
432#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
433 return 0;
434#else
435 va_list ap;
436 char *p = NULL;
437 int r;
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 r = sd_notify(unset_environment, p);
448 free(p);
449
450 return r;
451#endif
452}
453
454int sd_booted(void) {
455#if defined(DISABLE_SYSTEMD) || !defined(__linux__)
456 return 0;
457#else
458
459 struct stat a, b;
460
461 /* We simply test whether the systemd cgroup hierarchy is mounted */
462
463 if (lstat("/sys/fs/cgroup", &a) < 0) {
464 return 0;
465 }
466
467 if (lstat("/sys/fs/cgroup/systemd", &b) < 0) {
468 return 0;
469 }
470
471 return a.st_dev != b.st_dev;
472#endif
473}
static cmdp_state state
static int sd_is_socket_internal(int fd, int type, int listening)
Definition sd-daemon.c:159
int sd_booted(void)
Definition sd-daemon.c:454
int sd_notify(int unset_environment, const char *state)
Definition sd-daemon.c:353
int sd_is_fifo(int fd, const char *path)
Definition sd-daemon.c:124
int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port)
Definition sd-daemon.c:251
int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length)
Definition sd-daemon.c:305
int sd_notifyf(int unset_environment, const char *format,...)
Definition sd-daemon.c:431
int sd_listen_fds(int unset_environment)
Definition sd-daemon.c:47
int sd_is_socket(int fd, int family, int type, int listening)
Definition sd-daemon.c:219
#define SD_LISTEN_FDS_START
Definition sd-daemon.h:102
struct sockaddr_in in4
Definition sd-daemon.c:213
struct sockaddr_un un
Definition sd-daemon.c:215
struct sockaddr_storage storage
Definition sd-daemon.c:216
struct sockaddr_in6 in6
Definition sd-daemon.c:214
struct sockaddr sa
Definition sd-daemon.c:212