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