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