chiark / gitweb /
sd-daemon: fix sd_is_mq for non-mq fds
[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         /* Check that the fd is valid */
319         assert_return(fcntl(fd, F_GETFD) >= 0, -errno);
320
321         if (mq_getattr(fd, &attr) < 0) {
322                 if (errno == EBADF)
323                         /* A non-mq fd (or an invalid one, but we ruled that out above) */
324                         return 0;
325                 return -errno;
326         }
327
328         if (path) {
329                 char fpath[PATH_MAX];
330                 struct stat a, b;
331
332                 assert_return(path_is_absolute(path), -EINVAL);
333
334                 if (fstat(fd, &a) < 0)
335                         return -errno;
336
337                 strncpy(stpcpy(fpath, "/dev/mqueue"), path, sizeof(fpath) - 12);
338                 fpath[sizeof(fpath)-1] = 0;
339
340                 if (stat(fpath, &b) < 0)
341                         return -errno;
342
343                 if (a.st_dev != b.st_dev ||
344                     a.st_ino != b.st_ino)
345                         return 0;
346         }
347
348         return 1;
349 }
350 #endif // 0
351
352 _public_ int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds) {
353         union sockaddr_union sockaddr = {
354                 .sa.sa_family = AF_UNIX,
355         };
356         struct iovec iovec = {
357                 .iov_base = (char*) state,
358         };
359         struct msghdr msghdr = {
360                 .msg_iov = &iovec,
361                 .msg_iovlen = 1,
362                 .msg_name = &sockaddr,
363         };
364         _cleanup_close_ int fd = -1;
365         struct cmsghdr *cmsg = NULL;
366         const char *e;
367         bool have_pid;
368         int r;
369
370         if (!state) {
371                 r = -EINVAL;
372                 goto finish;
373         }
374
375         if (n_fds > 0 && !fds) {
376                 r = -EINVAL;
377                 goto finish;
378         }
379
380         e = getenv("NOTIFY_SOCKET");
381         if (!e)
382                 return 0;
383
384         /* Must be an abstract socket, or an absolute path */
385         if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
386                 r = -EINVAL;
387                 goto finish;
388         }
389
390         fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
391         if (fd < 0) {
392                 r = -errno;
393                 goto finish;
394         }
395
396         iovec.iov_len = strlen(state);
397
398         strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
399         if (sockaddr.un.sun_path[0] == '@')
400                 sockaddr.un.sun_path[0] = 0;
401
402         msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
403         if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
404                 msghdr.msg_namelen = sizeof(struct sockaddr_un);
405
406         have_pid = pid != 0 && pid != getpid();
407
408         if (n_fds > 0 || have_pid) {
409                 msghdr.msg_controllen = CMSG_SPACE(sizeof(int) * n_fds) +
410                                         CMSG_SPACE(sizeof(struct ucred) * have_pid);
411                 msghdr.msg_control = alloca(msghdr.msg_controllen);
412
413                 cmsg = CMSG_FIRSTHDR(&msghdr);
414                 if (n_fds > 0) {
415                         cmsg->cmsg_level = SOL_SOCKET;
416                         cmsg->cmsg_type = SCM_RIGHTS;
417                         cmsg->cmsg_len = CMSG_LEN(sizeof(int) * n_fds);
418
419                         memcpy(CMSG_DATA(cmsg), fds, sizeof(int) * n_fds);
420
421                         if (have_pid)
422                                 assert_se(cmsg = CMSG_NXTHDR(&msghdr, cmsg));
423                 }
424
425                 if (have_pid) {
426                         struct ucred *ucred;
427
428                         cmsg->cmsg_level = SOL_SOCKET;
429                         cmsg->cmsg_type = SCM_CREDENTIALS;
430                         cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
431
432                         ucred = (struct ucred*) CMSG_DATA(cmsg);
433                         ucred->pid = pid;
434                         ucred->uid = getuid();
435                         ucred->gid = getgid();
436                 }
437         }
438
439         /* First try with fake ucred data, as requested */
440         if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
441                 r = 1;
442                 goto finish;
443         }
444
445         /* If that failed, try with our own ucred instead */
446         if (have_pid) {
447                 msghdr.msg_controllen -= CMSG_SPACE(sizeof(struct ucred));
448                 if (msghdr.msg_controllen == 0)
449                         msghdr.msg_control = NULL;
450
451                 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) >= 0) {
452                         r = 1;
453                         goto finish;
454                 }
455         }
456
457         r = -errno;
458
459 finish:
460         if (unset_environment)
461                 unsetenv("NOTIFY_SOCKET");
462
463         return r;
464 }
465
466 _public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) {
467         return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0);
468 }
469
470 _public_ int sd_notify(int unset_environment, const char *state) {
471         return sd_pid_notify_with_fds(0, unset_environment, state, NULL, 0);
472 }
473
474 /// UNNEEDED by elogind
475 #if 0
476 _public_ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) {
477         _cleanup_free_ char *p = NULL;
478         int r;
479
480         if (format) {
481                 va_list ap;
482
483                 va_start(ap, format);
484                 r = vasprintf(&p, format, ap);
485                 va_end(ap);
486
487                 if (r < 0 || !p)
488                         return -ENOMEM;
489         }
490
491         return sd_pid_notify(pid, unset_environment, p);
492 }
493
494 _public_ int sd_notifyf(int unset_environment, const char *format, ...) {
495         _cleanup_free_ char *p = NULL;
496         int r;
497
498         if (format) {
499                 va_list ap;
500
501                 va_start(ap, format);
502                 r = vasprintf(&p, format, ap);
503                 va_end(ap);
504
505                 if (r < 0 || !p)
506                         return -ENOMEM;
507         }
508
509         return sd_pid_notify(0, unset_environment, p);
510 }
511
512 _public_ int sd_booted(void) {
513         struct stat st;
514
515         /* We test whether the runtime unit file directory has been
516          * created. This takes place in mount-setup.c, so is
517          * guaranteed to happen very early during boot. */
518
519         if (lstat("/run/systemd/system/", &st) < 0)
520                 return 0;
521
522         return !!S_ISDIR(st.st_mode);
523 }
524 #endif // 0
525
526 _public_ int sd_watchdog_enabled(int unset_environment, uint64_t *usec) {
527         const char *s, *p = ""; /* p is set to dummy value to do unsetting */
528         uint64_t u;
529         int r = 0;
530
531         s = getenv("WATCHDOG_USEC");
532         if (!s)
533                 goto finish;
534
535         r = safe_atou64(s, &u);
536         if (r < 0)
537                 goto finish;
538         if (u <= 0) {
539                 r = -EINVAL;
540                 goto finish;
541         }
542
543         p = getenv("WATCHDOG_PID");
544         if (p) {
545                 pid_t pid;
546
547                 r = parse_pid(p, &pid);
548                 if (r < 0)
549                         goto finish;
550
551                 /* Is this for us? */
552                 if (getpid() != pid) {
553                         r = 0;
554                         goto finish;
555                 }
556         }
557
558         if (usec)
559                 *usec = u;
560
561         r = 1;
562
563 finish:
564         if (unset_environment && s)
565                 unsetenv("WATCHDOG_USEC");
566         if (unset_environment && p)
567                 unsetenv("WATCHDOG_PID");
568
569         return r;
570 }