D-Bus  1.6.12
sd-daemon.c
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 <sys/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 
44 #include "sd-daemon.h"
45 
46 int sd_listen_fds(int unset_environment) {
47 
48 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
49  return 0;
50 #else
51  int r, fd;
52  const char *e;
53  char *p = NULL;
54  unsigned long l;
55 
56  if (!(e = getenv("LISTEN_PID"))) {
57  r = 0;
58  goto finish;
59  }
60 
61  errno = 0;
62  l = strtoul(e, &p, 10);
63 
64  if (errno != 0) {
65  r = -errno;
66  goto finish;
67  }
68 
69  if (!p || *p || l <= 0) {
70  r = -EINVAL;
71  goto finish;
72  }
73 
74  /* Is this for us? */
75  if (getpid() != (pid_t) l) {
76  r = 0;
77  goto finish;
78  }
79 
80  if (!(e = getenv("LISTEN_FDS"))) {
81  r = 0;
82  goto finish;
83  }
84 
85  errno = 0;
86  l = strtoul(e, &p, 10);
87 
88  if (errno != 0) {
89  r = -errno;
90  goto finish;
91  }
92 
93  if (!p || *p) {
94  r = -EINVAL;
95  goto finish;
96  }
97 
98  for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + (int) l; fd ++) {
99  int flags;
100 
101  if ((flags = fcntl(fd, F_GETFD)) < 0) {
102  r = -errno;
103  goto finish;
104  }
105 
106  if (flags & FD_CLOEXEC)
107  continue;
108 
109  if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
110  r = -errno;
111  goto finish;
112  }
113  }
114 
115  r = (int) l;
116 
117 finish:
118  if (unset_environment) {
119  unsetenv("LISTEN_PID");
120  unsetenv("LISTEN_FDS");
121  }
122 
123  return r;
124 #endif
125 }
126 
127 int sd_is_fifo(int fd, const char *path) {
128  struct stat st_fd;
129 
130  if (fd < 0)
131  return -EINVAL;
132 
133  memset(&st_fd, 0, sizeof(st_fd));
134  if (fstat(fd, &st_fd) < 0)
135  return -errno;
136 
137  if (!S_ISFIFO(st_fd.st_mode))
138  return 0;
139 
140  if (path) {
141  struct stat st_path;
142 
143  memset(&st_path, 0, sizeof(st_path));
144  if (stat(path, &st_path) < 0) {
145 
146  if (errno == ENOENT || errno == ENOTDIR)
147  return 0;
148 
149  return -errno;
150  }
151 
152  return
153  st_path.st_dev == st_fd.st_dev &&
154  st_path.st_ino == st_fd.st_ino;
155  }
156 
157  return 1;
158 }
159 
160 static int sd_is_socket_internal(int fd, int type, int listening) {
161  struct stat st_fd;
162 
163  if (fd < 0 || type < 0)
164  return -EINVAL;
165 
166  if (fstat(fd, &st_fd) < 0)
167  return -errno;
168 
169  if (!S_ISSOCK(st_fd.st_mode))
170  return 0;
171 
172  if (type != 0) {
173  int other_type = 0;
174  socklen_t l = sizeof(other_type);
175 
176  if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &other_type, &l) < 0)
177  return -errno;
178 
179  if (l != sizeof(other_type))
180  return -EINVAL;
181 
182  if (other_type != type)
183  return 0;
184  }
185 
186  if (listening >= 0) {
187  int accepting = 0;
188  socklen_t l = sizeof(accepting);
189 
190  if (getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &accepting, &l) < 0)
191  return -errno;
192 
193  if (l != sizeof(accepting))
194  return -EINVAL;
195 
196  if (!accepting != !listening)
197  return 0;
198  }
199 
200  return 1;
201 }
202 
204  struct sockaddr sa;
205  struct sockaddr_in in4;
206  struct sockaddr_in6 in6;
207  struct sockaddr_un un;
208  struct sockaddr_storage storage;
209 };
210 
211 int sd_is_socket(int fd, int family, int type, int listening) {
212  int r;
213 
214  if (family < 0)
215  return -EINVAL;
216 
217  if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
218  return r;
219 
220  if (family > 0) {
221  union sockaddr_union sockaddr;
222  socklen_t l;
223 
224  memset(&sockaddr, 0, sizeof(sockaddr));
225  l = sizeof(sockaddr);
226 
227  if (getsockname(fd, &sockaddr.sa, &l) < 0)
228  return -errno;
229 
230  if (l < sizeof(sa_family_t))
231  return -EINVAL;
232 
233  return sockaddr.sa.sa_family == family;
234  }
235 
236  return 1;
237 }
238 
239 int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
240  union sockaddr_union sockaddr;
241  socklen_t l;
242  int r;
243 
244  if (family != 0 && family != AF_INET && family != AF_INET6)
245  return -EINVAL;
246 
247  if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
248  return r;
249 
250  memset(&sockaddr, 0, sizeof(sockaddr));
251  l = sizeof(sockaddr);
252 
253  if (getsockname(fd, &sockaddr.sa, &l) < 0)
254  return -errno;
255 
256  if (l < sizeof(sa_family_t))
257  return -EINVAL;
258 
259  if (sockaddr.sa.sa_family != AF_INET &&
260  sockaddr.sa.sa_family != AF_INET6)
261  return 0;
262 
263  if (family > 0)
264  if (sockaddr.sa.sa_family != family)
265  return 0;
266 
267  if (port > 0) {
268  if (sockaddr.sa.sa_family == AF_INET) {
269  if (l < sizeof(struct sockaddr_in))
270  return -EINVAL;
271 
272  return htons(port) == sockaddr.in4.sin_port;
273  } else {
274  if (l < sizeof(struct sockaddr_in6))
275  return -EINVAL;
276 
277  return htons(port) == sockaddr.in6.sin6_port;
278  }
279  }
280 
281  return 1;
282 }
283 
284 int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
285  union sockaddr_union sockaddr;
286  socklen_t l;
287  int r;
288 
289  if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
290  return r;
291 
292  memset(&sockaddr, 0, sizeof(sockaddr));
293  l = sizeof(sockaddr);
294 
295  if (getsockname(fd, &sockaddr.sa, &l) < 0)
296  return -errno;
297 
298  if (l < sizeof(sa_family_t))
299  return -EINVAL;
300 
301  if (sockaddr.sa.sa_family != AF_UNIX)
302  return 0;
303 
304  if (path) {
305  if (length <= 0)
306  length = strlen(path);
307 
308  if (length <= 0)
309  /* Unnamed socket */
310  return l == sizeof(sa_family_t);
311 
312  if (path[0])
313  /* Normal path socket */
314  return
315  (l >= sizeof(sa_family_t) + length + 1) &&
316  memcmp(path, sockaddr.un.sun_path, length+1) == 0;
317  else
318  /* Abstract namespace socket */
319  return
320  (l == sizeof(sa_family_t) + length) &&
321  memcmp(path, sockaddr.un.sun_path, length) == 0;
322  }
323 
324  return 1;
325 }
326 
327 int sd_notify(int unset_environment, const char *state) {
328 #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
329  return 0;
330 #else
331  int fd = -1, r;
332  struct msghdr msghdr;
333  struct iovec iovec;
334  union sockaddr_union sockaddr;
335  const char *e;
336 
337  if (!state) {
338  r = -EINVAL;
339  goto finish;
340  }
341 
342  if (!(e = getenv("NOTIFY_SOCKET")))
343  return 0;
344 
345  /* Must be an abstract socket, or an absolute path */
346  if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
347  r = -EINVAL;
348  goto finish;
349  }
350 
351  if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0) {
352  r = -errno;
353  goto finish;
354  }
355 
356  memset(&sockaddr, 0, sizeof(sockaddr));
357  sockaddr.sa.sa_family = AF_UNIX;
358  strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
359 
360  if (sockaddr.un.sun_path[0] == '@')
361  sockaddr.un.sun_path[0] = 0;
362 
363  memset(&iovec, 0, sizeof(iovec));
364  iovec.iov_base = (char*) state;
365  iovec.iov_len = strlen(state);
366 
367  memset(&msghdr, 0, sizeof(msghdr));
368  msghdr.msg_name = &sockaddr;
369  msghdr.msg_namelen = sizeof(sa_family_t) + strlen(e);
370 
371  if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
372  msghdr.msg_namelen = sizeof(struct sockaddr_un);
373 
374  msghdr.msg_iov = &iovec;
375  msghdr.msg_iovlen = 1;
376 
377  if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
378  r = -errno;
379  goto finish;
380  }
381 
382  r = 1;
383 
384 finish:
385  if (unset_environment)
386  unsetenv("NOTIFY_SOCKET");
387 
388  if (fd >= 0)
389  close(fd);
390 
391  return r;
392 #endif
393 }
394 
395 int sd_notifyf(int unset_environment, const char *format, ...) {
396 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
397  return 0;
398 #else
399  va_list ap;
400  char *p = NULL;
401  int r;
402 
403  va_start(ap, format);
404  r = vasprintf(&p, format, ap);
405  va_end(ap);
406 
407  if (r < 0 || !p)
408  return -ENOMEM;
409 
410  r = sd_notify(unset_environment, p);
411  free(p);
412 
413  return r;
414 #endif
415 }
416 
417 int sd_booted(void) {
418 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
419  return 0;
420 #else
421 
422  struct stat a, b;
423 
424  /* We simply test whether the systemd cgroup hierarchy is
425  * mounted */
426 
427  if (lstat("/sys/fs/cgroup", &a) < 0)
428  return 0;
429 
430  if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
431  return 0;
432 
433  return a.st_dev != b.st_dev;
434 #endif
435 }
#define NULL
A null pointer, defined appropriately for C or C++.