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