i3
sd-daemon.c
Go to the documentation of this file.
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 #include <stddef.h>
44 
45 #include "sd-daemon.h"
46 
47 int sd_listen_fds(int unset_environment) {
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  if (errno == ENOENT || errno == ENOTDIR)
146  return 0;
147 
148  return -errno;
149  }
150 
151  return st_path.st_dev == st_fd.st_dev &&
152  st_path.st_ino == st_fd.st_ino;
153  }
154 
155  return 1;
156 }
157 
158 static int sd_is_socket_internal(int fd, int type, int listening) {
159  struct stat st_fd;
160 
161  if (fd < 0 || type < 0)
162  return -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 
202  struct sockaddr sa;
203  struct sockaddr_in in4;
204  struct sockaddr_in6 in6;
205  struct sockaddr_un un;
206  struct sockaddr_storage storage;
207 };
208 
209 int sd_is_socket(int fd, int family, int type, int listening) {
210  int r;
211 
212  if (family < 0)
213  return -EINVAL;
214 
215  if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
216  return r;
217 
218  if (family > 0) {
219  union sockaddr_union sockaddr;
220  socklen_t l;
221 
222  memset(&sockaddr, 0, sizeof(sockaddr));
223  l = sizeof(sockaddr);
224 
225  if (getsockname(fd, &sockaddr.sa, &l) < 0)
226  return -errno;
227 
228  if (l < sizeof(sa_family_t))
229  return -EINVAL;
230 
231  return sockaddr.sa.sa_family == family;
232  }
233 
234  return 1;
235 }
236 
237 int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port) {
238  union sockaddr_union sockaddr;
239  socklen_t l;
240  int r;
241 
242  if (family != 0 && family != AF_INET && family != AF_INET6)
243  return -EINVAL;
244 
245  if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
246  return r;
247 
248  memset(&sockaddr, 0, sizeof(sockaddr));
249  l = sizeof(sockaddr);
250 
251  if (getsockname(fd, &sockaddr.sa, &l) < 0)
252  return -errno;
253 
254  if (l < sizeof(sa_family_t))
255  return -EINVAL;
256 
257  if (sockaddr.sa.sa_family != AF_INET &&
258  sockaddr.sa.sa_family != AF_INET6)
259  return 0;
260 
261  if (family > 0)
262  if (sockaddr.sa.sa_family != family)
263  return 0;
264 
265  if (port > 0) {
266  if (sockaddr.sa.sa_family == AF_INET) {
267  if (l < sizeof(struct sockaddr_in))
268  return -EINVAL;
269 
270  return htons(port) == sockaddr.in4.sin_port;
271  } else {
272  if (l < sizeof(struct sockaddr_in6))
273  return -EINVAL;
274 
275  return htons(port) == sockaddr.in6.sin6_port;
276  }
277  }
278 
279  return 1;
280 }
281 
282 int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length) {
283  union sockaddr_union sockaddr;
284  socklen_t l;
285  int r;
286 
287  if ((r = sd_is_socket_internal(fd, type, listening)) <= 0)
288  return r;
289 
290  memset(&sockaddr, 0, sizeof(sockaddr));
291  l = sizeof(sockaddr);
292 
293  if (getsockname(fd, &sockaddr.sa, &l) < 0)
294  return -errno;
295 
296  if (l < sizeof(sa_family_t))
297  return -EINVAL;
298 
299  if (sockaddr.sa.sa_family != AF_UNIX)
300  return 0;
301 
302  if (path) {
303  if (length <= 0)
304  length = strlen(path);
305 
306  if (length <= 0)
307  /* Unnamed socket */
308  return l == offsetof(struct sockaddr_un, sun_path);
309 
310  if (path[0])
311  /* Normal path socket */
312  return (l >= offsetof(struct sockaddr_un, sun_path) + length + 1) &&
313  memcmp(path, sockaddr.un.sun_path, length + 1) == 0;
314  else
315  /* Abstract namespace socket */
316  return (l == offsetof(struct sockaddr_un, sun_path) + length) &&
317  memcmp(path, sockaddr.un.sun_path, length) == 0;
318  }
319 
320  return 1;
321 }
322 
323 int sd_notify(int unset_environment, const char *state) {
324 #if defined(DISABLE_SYSTEMD) || !defined(__linux__) || !defined(SOCK_CLOEXEC)
325  return 0;
326 #else
327  int fd = -1, r;
328  struct msghdr msghdr;
329  struct iovec iovec;
330  union sockaddr_union sockaddr;
331  const char *e;
332 
333  if (!state) {
334  r = -EINVAL;
335  goto finish;
336  }
337 
338  if (!(e = getenv("NOTIFY_SOCKET")))
339  return 0;
340 
341  /* Must be an abstract socket, or an absolute path */
342  if ((e[0] != '@' && e[0] != '/') || e[1] == 0) {
343  r = -EINVAL;
344  goto finish;
345  }
346 
347  if ((fd = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)) < 0) {
348  r = -errno;
349  goto finish;
350  }
351 
352  memset(&sockaddr, 0, sizeof(sockaddr));
353  sockaddr.sa.sa_family = AF_UNIX;
354  strncpy(sockaddr.un.sun_path, e, sizeof(sockaddr.un.sun_path));
355 
356  if (sockaddr.un.sun_path[0] == '@')
357  sockaddr.un.sun_path[0] = 0;
358 
359  memset(&iovec, 0, sizeof(iovec));
360  iovec.iov_base = (char *)state;
361  iovec.iov_len = strlen(state);
362 
363  memset(&msghdr, 0, sizeof(msghdr));
364  msghdr.msg_name = &sockaddr;
365  msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + strlen(e);
366 
367  if (msghdr.msg_namelen > sizeof(struct sockaddr_un))
368  msghdr.msg_namelen = sizeof(struct sockaddr_un);
369 
370  msghdr.msg_iov = &iovec;
371  msghdr.msg_iovlen = 1;
372 
373  if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
374  r = -errno;
375  goto finish;
376  }
377 
378  r = 1;
379 
380 finish:
381  if (unset_environment)
382  unsetenv("NOTIFY_SOCKET");
383 
384  if (fd >= 0)
385  close(fd);
386 
387  return r;
388 #endif
389 }
390 
391 int sd_notifyf(int unset_environment, const char *format, ...) {
392 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
393  return 0;
394 #else
395  va_list ap;
396  char *p = NULL;
397  int r;
398 
399  va_start(ap, format);
400  r = vasprintf(&p, format, ap);
401  va_end(ap);
402 
403  if (r < 0 || !p)
404  return -ENOMEM;
405 
406  r = sd_notify(unset_environment, p);
407  free(p);
408 
409  return r;
410 #endif
411 }
412 
413 int sd_booted(void) {
414 #if defined(DISABLE_SYSTEMD) || !defined(__linux__)
415  return 0;
416 #else
417 
418  struct stat a, b;
419 
420  /* We simply test whether the systemd cgroup hierarchy is
421  * mounted */
422 
423  if (lstat("/sys/fs/cgroup", &a) < 0)
424  return 0;
425 
426  if (lstat("/sys/fs/cgroup/systemd", &b) < 0)
427  return 0;
428 
429  return a.st_dev != b.st_dev;
430 #endif
431 }
#define SD_LISTEN_FDS_START
Definition: sd-daemon.h:102
int sd_listen_fds(int unset_environment)
Definition: sd-daemon.c:47
int sd_is_socket_unix(int fd, int type, int listening, const char *path, size_t length)
Definition: sd-daemon.c:282
int sd_notify(int unset_environment, const char *state)
Definition: sd-daemon.c:323
static int sd_is_socket_internal(int fd, int type, int listening)
Definition: sd-daemon.c:158
struct sockaddr_in in4
Definition: sd-daemon.c:203
int sd_is_socket_inet(int fd, int family, int type, int listening, uint16_t port)
Definition: sd-daemon.c:237
struct sockaddr_storage storage
Definition: sd-daemon.c:206
static cmdp_state state
int sd_notifyf(int unset_environment, const char *format,...)
Definition: sd-daemon.c:391
int sd_booted(void)
Definition: sd-daemon.c:413
struct sockaddr_un un
Definition: sd-daemon.c:205
int sd_is_fifo(int fd, const char *path)
Definition: sd-daemon.c:127
int sd_is_socket(int fd, int family, int type, int listening)
Definition: sd-daemon.c:209
struct sockaddr sa
Definition: sd-daemon.c:202
struct sockaddr_in6 in6
Definition: sd-daemon.c:204