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