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