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